Вместо введения


Спасибо моему анонимному хабрадедуморозу за подарочек (pro mini). Долго колебался, что с ней делать. Махнул рукой и заказал в дополнение китайскую посылочку…
Спустя месяцок настало и моё время “помигать светодиодом” ;).

Идея


Пока ехала посылка, думал что бы такого сделать. В итоге принято решение сделать что-нибудь чуть больше, чем помигать светодиодом.
Идея заключается в следующем — отобразить прогресс просмотра видео, используя какой-нибудь индикатор.

Задача


  1. вытащить статистику по запущенному фильму;
  2. обработать и передать в arduino данные;
  3. отобразить прогресс на индикаторе.

Для нетерпеливых сразу результат




Реализация


1. MPC

Сколько себя помню, на ПК у меня всегда установлен Media PLayer Classic. Оказывается MPC умеет отдавать статистику по, скажем так, текущей сессии в виде html-странички со следующим содержанием:
HTML
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>MPC-HC WebServer - Variables</title>
        <link rel="stylesheet" href="default.css">
        <link rel="icon" href="favicon.ico">
    </head>
    <body>
        <!--[if lt IE 8]>
            <div class="browser-warning"><strong>Warning!</strong> You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</div>
        <![endif]-->
        <p id="filepatharg">D:%5cFILMS%5cIts.Always.Sunny.in.Philadelphia.S10E10.720p.WEB.rus.LostFilm.TV.mp4</p>
        <p id="filepath">D:\FILMS\Its.Always.Sunny.in.Philadelphia.S10E10.720p.WEB.rus.LostFilm.TV.mp4</p>
        <p id="filedirarg">D:%5cFILMS</p>
        <p id="filedir">D:\FILMS</p>
        <p id="state">1</p>
        <p id="statestring">Пауза</p>
        <p id="position">1069384</p>
        <p id="positionstring">00:17:49</p>
        <p id="duration">1255210</p>
        <p id="durationstring">00:20:55</p>
        <p id="volumelevel">75</p>
        <p id="muted">0</p>
        <p id="playbackrate">1</p>
        <p id="reloadtime">0</p>
    </body>
</html>


Что бы добиться от него такого функционала, нужно проставить галочку "Слушать порт" в настройках
Настройки


Всё самое важное содержится в тегах
<p id="параметр">значение</p>

Остаётся только разобрать их и выбрать нужное.
2. NodeJS

Разбирать страничку будем при помощи NodeJS. Кстати, страница доступна по адресу localhost:13580/variables.html (порт из настроек).
Всю логику можно разделить на 3 блока:
  • модуль работы с MPC web-сервером;
  • модуль обработки данных;
  • модуль работы с arduino.

Далее основные моменты. Весь проект вместе с кодом можно глянуть на imagegithub
Немного js
При помощи родного http.get и обещаний обращаюсь к нашей странице
main.prototype.get = function(){
	var that = this
	return new Promise(function(done){
		http.get(that.mpc_uri, function(res) {
		  res.on('data', function (chunk) {
			that.mpc_obj.parse(chunk)
			done(that.mpc_obj.get());
		  });
		}).on('error', function(e) {
			done(e.message);
		});
	})
}

Методом parse объекта mpc_obj разбираю полученные данные
MPC_obj.prototype.parse = function(data){
	var obj = {}
	data.toString().split('\n').forEach(function(line){
		// отбираем параграфы, так как параметры только в них
		if (match=/\<p/.test(line)){
			var name2 = line.match(/id\=\"(\w+)\"/)[1]
			if (!obj.hasOwnProperty(name2))
				 obj[name2]=line.substring(line.indexOf('>')+1,line.lastIndexOf('<'))
		}
	})
	this.settings=obj;
}

На выходе имеем вот такой объект
{ filepatharg: 'D:%5cmbrr555.avi',
  filepath: 'D:\\mbrr555.avi',
  filedirarg: 'D:%5c',
  filedir: 'D:\\',
  state: '1',
  statestring: 'Пауза',
  position: '1023',
  positionstring: '00:00:01',
  duration: '114906',
  durationstring: '00:01:54',
  volumelevel: '77',
  muted: '0',
  playbackrate: '1',
  reloadtime: '0' }

Подготавливаем данные для arduino
// Возвращает прогресс просмотренного
MPC_obj.prototype.getProgress = function(){
	return (this.settings["position"]/this.settings["duration"]).toFixed(5)
}
// Возвращает прогресс для ШИМ (PWM) 	1,0 -> 255
MPC_obj.prototype.PWM = function(){
	return (this.getProgress()*255).toFixed(0)
}

Общение с arduino происходит путём обмена данными на com-порту. Для работы с com выбрал библиотеку imagenode-serialport
Подключаемся с её помощью к порту com5 и устанавливаем скорость
var SerialPort = require('serialport').SerialPort;
var serialPort = new SerialPort("COM5", {
  baudrate: 57600
}, false);

Открываем порт и посылаем в ответ arduino наш прогресс каждый раз, когда она попросит
serialPort.open(function (error) {
	if ( error ) {
		console.log('failed to open: '+error);
   } else {
	console.log('open');
	//получил данные от arduino
	serialPort.on('data', function(data) {
		main.init(function(a){
			//послал в ответ прогресс фильма
			serialPort.write(a);
		});
	 });
	}
});


3. Arduino

Теперь железо. Данные для arduino мы подготовили, теперь определимся с той самой индикацией. Для отображения прогресса я выбрал вот такой очень дешёвый модуль вольтметра и немного его «подкрасил»

Чуть подробней
Оказывается, крышка держалась на… липкой ленте и легко снималась

Ну а далее размеры и пара новых панелек


В arduino мы посылаем данные о прогрессе фильма и пропорционально изменяем напряжение на одной из ног микроконтроллера. Принципиальная схема (наличие светодиода для того, что бы им помигать в конце сеанса ;))

Теперь запрограммируем наш микроконтроллер.
В переменную inByte будем считывать данные, полученные по com-порту. Вешаем на 3й пин светодиод, а на 9й — вольтметр. Устанавливаем точно такую же скорость как и раньше. loop — наш бесконечный цикл. Каждые 100мс отправляем в порт абстрактные данные Serial.print('A'). Ждём наличие ответа Serial.available() > 0. Если на ПК запущен MPC и nodeJS-приложение, которое возвращает прогресс, то чуть подсветим светодиод на 3м пине analogWrite(3, 5) и передадим на 9й пин вольтметра значение прогресса analogWrite(9, inByte). Функция analogWrite передаёт значения от 0 до 255, что в нашем случае соответствует от 0В до 5В. Тут нужно почитать про ШИМ (PWM) — это всё благодаря ему.
Когда мы просмотрели половину фильма, в этот момент на 9й пин будет передано 255/2 = analogWrite(9, 127), что соответствует 2,5В на вольтметре. Как только фильм закончен — бодренько мигаем светодиодом ;)
int inByte = 0;  
void setup()  {
  pinMode(9, OUTPUT);
  pinMode(3, OUTPUT);
  analogWrite(3, 0);
  analogWrite(9, 0);
  Serial.begin(57600);
}
 void loop()  {
  Serial.print('A');
  delay(100); 
   if (Serial.available() > 0) {
    analogWrite(3, 5);
    inByte = Serial.parseInt();
    analogWrite(9, inByte);
    if (inByte==255)
    analogWrite(3, 255);
  }
}

Вот так это всё выглядит в живую на макетке

да ещё и на видео ;)

Кстати в этом проекте использован как раз подарочек от того самого хабрадедамороза.
PS.
Рядом лежит Attiny13, как время будет — попробую перенести всё на неё.
Ссылки:

Если где-то написал полную ерунду или ошибся — подсказывайте, подправлю ;). Спасибо за внимание.

Комментарии (15)


  1. XlebNick
    05.04.2015 11:03

    Сложно представить, в каких случаях девайс может стать необходимым, но выглядит круто, надо бы сделать себе как-нибудь


    1. IncorrecTSW
      05.04.2015 11:28
      +2

      Это именно тот случай когда он нужен (исключительно сделанный своими руками) и не важно зачем.


    1. apple01
      05.04.2015 16:49

      Поскольку у вас уже есть статистика проигрываемого ролика, добавив пульт дистанционного управления можно организовать функции управления проигрыванием, такие как запоминание текущей позиции, перемотка на 10 сек. назад и т.д. Можно также разработать свой уникальный пульт с индикатором прогресса прямо на пульте, только я бы предложил не стрелочный а линейный индикатор.


    1. Alexeyslav
      06.04.2015 14:20

      Повторить 1 в один не так интересно.
      Есть же прекрасные индикаторные лампы ИН-9 и ИН-13. Вот где фантазии разгуляться.


  1. HWman
    05.04.2015 14:17
    +1

    Если хотите, могу написать прошивку для ATtiny13 на BASCOM-AVR.


    1. andreevich Автор
      05.04.2015 14:22
      +1

      Хочется попытаться разобраться, но буду иметь в виду ;), спасибо.


  1. iliasam
    05.04.2015 14:58
    +2

    У выбранного вольтметра шкала нелинейная. Насколько я понял, это нигде не учитывается?


    1. andreevich Автор
      05.04.2015 15:17

      Именно так, но на данном этапе не стал заморачиваться. Показывает-то он правильно, просто первых 2/5 фильма стрелка чаще меняет своё положение ;). Я так понимаю, что при значении меньшем 2/5*255 нужно применять «замедляющий» коэффициент. Благо это всё поправимо ;). Цифровой вольтметр в руки, снять пару показаний и сделать поправку. Так сказать программно откалибровать.
      Кстати брал его чуть меньше чем за 1,5$ — в этом его плюс ).


      1. HWman
        05.04.2015 15:57

        К слову, насчёт цифрового вольтметра, я когда-то вот так баловался:
        vk.com/video256435878_172288690


  1. svd71
    06.04.2015 09:25

    А что за чудесный компонент у вас в ISIS, иммитирующий работу Arduino? и где его можно заиметь?


    1. andreevich Автор
      06.04.2015 09:36

      Честно говоря, не нашёл в этом Proteus такого элемента и проста загуглил. Вот ссылочка . Возможно есть что-то получше.


      1. svd71
        06.04.2015 11:22

        Благодарствую очень!

        Как раз читал инфу на сайте лабсцентра, что в новых версиях эта библиотека должна быть. Но оказалось, что достаточно с вашей ссылки файлы добавить…


    1. Alexeyslav
      06.04.2015 14:25

      Разве иммитирует? Это же считай голый контроллер. Большинство реальных цепей контроллера таких как питание, тактирование от кварца и т.д. всеравно не эмулируются и существуют только для красоты.
      Поэтому… создать такой компонент можно очень просто — нарисовать его как субмодуль а внутри расположить контроллер.


      1. svd71
        06.04.2015 16:12

        любое преобразование процессорных команд в код текущего процессора есть эмуляция.

        во вторых у Уны(если не ошибаюсь) окромя основного контроллера имеются вспомогательные, позволяющих без кода организовать многие функции.

        в третьиз это уже удобная платформа для отладки шилдов для ардуино.

        и четвертым самая важная функция — связь времени и пространства: например моим детям скучно и не интересно и непонятно возиться со схемой, малопохожей на реальные железки. а отсуствие интереся убивает желания, что уже меня огорчает.


        1. Alexeyslav
          06.04.2015 16:52

          Эмуляция это не иммитация. На уровне самого протеуса, данная ардуина и контроллер это одно и то же, только внешний облик нарисован другой.