MPCMeter — индикация прогресса просмотра видео. Arduino + JavaScript

от автора


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

Спасибо моему анонимному хабрадедуморозу за подарочек (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, как время будет — попробую перенести всё на неё.
Ссылки:

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

ссылка на оригинал статьи http://habrahabr.ru/post/254911/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *