...

воскресенье, 5 апреля 2015 г.

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, как время будет — попробую перенести всё на неё.

Ссылки:

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

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий