Здравствуй, Хабр!
Решил написать статью о своем опыте работы с IR на Cubieboard.
Не так давно заказал себе это замечательное устройство (заказывал специально для будущего домашнего сервера, и, следовательно, использовать на ней только серверную ОCь) Выбор пал на Cubian (http://cubian.org/) в виду того, что строить свой велосипед пока не хочется.
Предполагалось развернуть на кубике nginx, php5, samba, mocd и несколько своих сервисов для управления блоком реле.
В ходе изучения кубика решил попробовать настроить IR на прием команд с пульта от старого TV тюнера, решение в виде lirc развернуть не получилось, стал искать другие решения, ничего доброго не нашел.
Написал свое решение, которым теперь делюсь с вами.
Изначально хотелось обойтись только bash, но перечитывать раз в секунду или чаще/реже устройство меня не привлекало, зачем в пустую гонять процессор, лишний раз нагревая эту малышку. Тогда решение пришло в сторону php, так как он все равно будет установлен.
Недавно на хабре наткнулся на статью, мое решение близко к предложенному @ntfs1984, но не такое же.
Так как комментировать мне пока не позволяется, решил поиграть в песочнице.
Приступим.
Изначально, после установки системы, устройство не доступно, так как не загружены соответствующие модули, в случае cubieboard a10 и cubian — это модуль sunxi_ir
Проверяем его загрузку командой lsmod
Обращаю ваше внимание, все команды я выполняю от root'а.
По умолчанию система этот модуль не загружает, для загрузки вручную можно воспользоваться командой:
В случае успешного запуска модуля, modprobe ничего не скажет, а вот в списке lsmod появится заветная строчка. Также важным фактором, чтобы удостовериться, что все заработало, нужно проверить, появилось ли устройство ввода:
в моем случае это было event1
самый верный способ проверить, то ли это устройство, это выполнить
hexdump выводит в «красивом» формате данные, плюс узнать код нажатой кнопки можем таким же образом, но можно обойтись и без него.
На каждую нажатую кнопку получаем четыре строки, первая пара строк — кнопка на пульте была нажата(0090 0001), вторая — кнопку отпустили(0090 0000). Из этих строк можно получить много полезного, я сильно не углублялся, а лишь обращал внимание на код кнопки (в примере это 0090). Но могу сказать, что 532a -это код пульта, если кто решит привязать программу и к пульту.
Если все так и есть, переходим к следующему этапу — настройка автозагрузки
и в конце добавить лишь одну строку
sunxi_ir
это имя нашего модуля, который мы загружали выше. Пример моего modules:
Сохранили и вышли.
Итак, приготовления работы с IR окончены, теперь перейдем к самой важной части, это написание программы, которая сможет читать устройство и выполнять необходимые действия.
Нужен демон, который будет следить за нажатиями, изначально планировалось следить как-то за устройством, а в случае нажатой кнопки — выполнять. Сразу же скажу, не хотелось создавать бесконечный цикл, который бы проверял через секунду, нажата ли кнопка на пульте или нет. Хотелось создать такую программу, которая бы срабатывала только по нажатию, в остальное время просто дремала. В результате родилась идея работы php с файлами, когда можно вычитывать побитно файл:
Сохраним файл как /tmp/tst
Позволим файлу быть исполняемым
Теперь, усложняя switch для каждой кнопки, получим обработку каждой кнопки пульта. Ну и, собственно, не выводить на экран «пустые» слова, а назначить необходимое действие.
Немного усложнив программу, получим (/usr/sbin/mylirc):
Программа пишет логи, если был передан соответствующий параметр(--log) и ругается, если не передан параметр --device
Файл настроек (/etc/mylirc/buttom_avermedia.php):
Программа led green 1(0) (это мой скрипт на bash'е, который поместил в /usr/sbin/)
включала и выключала на кубике лампочку (в примере зеленая), но можно выполнять любую консольную команду, какую вы пожелаете. Выполнение идет от root'а, соответственно это нужно будет учитывать и понижать привилегии до нужного пользователя при необходимости.
Программу нужно запускать при старте системы в виде демона, чтобы можно было запустить/остановить/перезапустить/проверить работу программы(/usr/sbin/mylirc), за основу которого был взят /etc/init.d/ssh скрипт, переписан и сохранен как /etc/init.d/01_lirc:
Теперь проверить работу демона можно командами, запуск:
и аналогично restart
Для добавление в автозагрузку демона необходимо выполнить:
Убрать из автозагрузки:
P.S. Программу откатал, уже две недели работает стабильно. Пожелания и комментарии приветствуются.
P.S.S.
edwardoid предложил составить скрипт, который бы генерировал файл настроек (похожий на /etc/mylirc/buttom_avermedia.php ),
Решил написать статью о своем опыте работы с IR на Cubieboard.
Не так давно заказал себе это замечательное устройство (заказывал специально для будущего домашнего сервера, и, следовательно, использовать на ней только серверную ОCь) Выбор пал на Cubian (http://cubian.org/) в виду того, что строить свой велосипед пока не хочется.
Предполагалось развернуть на кубике nginx, php5, samba, mocd и несколько своих сервисов для управления блоком реле.
В ходе изучения кубика решил попробовать настроить IR на прием команд с пульта от старого TV тюнера, решение в виде lirc развернуть не получилось, стал искать другие решения, ничего доброго не нашел.
Написал свое решение, которым теперь делюсь с вами.
Изначально хотелось обойтись только bash, но перечитывать раз в секунду или чаще/реже устройство меня не привлекало, зачем в пустую гонять процессор, лишний раз нагревая эту малышку. Тогда решение пришло в сторону php, так как он все равно будет установлен.
Недавно на хабре наткнулся на статью, мое решение близко к предложенному @ntfs1984, но не такое же.
Так как комментировать мне пока не позволяется, решил поиграть в песочнице.
Приступим.
Изначально, после установки системы, устройство не доступно, так как не загружены соответствующие модули, в случае cubieboard a10 и cubian — это модуль sunxi_ir
Проверяем его загрузку командой lsmod
Обращаю ваше внимание, все команды я выполняю от root'а.
По умолчанию система этот модуль не загружает, для загрузки вручную можно воспользоваться командой:
modprobe sunxi_ir
В случае успешного запуска модуля, modprobe ничего не скажет, а вот в списке lsmod появится заветная строчка. Также важным фактором, чтобы удостовериться, что все заработало, нужно проверить, появилось ли устройство ввода:
в моем случае это было event1
самый верный способ проверить, то ли это устройство, это выполнить
cat /dev/input/event1 | hexdump
(Остановить процесс можно [Ctrl]+[C])hexdump выводит в «красивом» формате данные, плюс узнать код нажатой кнопки можем таким же образом, но можно обойтись и без него.
На каждую нажатую кнопку получаем четыре строки, первая пара строк — кнопка на пульте была нажата(0090 0001), вторая — кнопку отпустили(0090 0000). Из этих строк можно получить много полезного, я сильно не углублялся, а лишь обращал внимание на код кнопки (в примере это 0090). Но могу сказать, что 532a -это код пульта, если кто решит привязать программу и к пульту.
Если все так и есть, переходим к следующему этапу — настройка автозагрузки
nano /etc/modules
и в конце добавить лишь одну строку
sunxi_ir
это имя нашего модуля, который мы загружали выше. Пример моего modules:
Сохранили и вышли.
Итак, приготовления работы с IR окончены, теперь перейдем к самой важной части, это написание программы, которая сможет читать устройство и выполнять необходимые действия.
Нужен демон, который будет следить за нажатиями, изначально планировалось следить как-то за устройством, а в случае нажатой кнопки — выполнять. Сразу же скажу, не хотелось создавать бесконечный цикл, который бы проверял через секунду, нажата ли кнопка на пульте или нет. Хотелось создать такую программу, которая бы срабатывала только по нажатию, в остальное время просто дремала. В результате родилась идея работы php с файлами, когда можно вычитывать побитно файл:
#!/usr/bin/php5
<?php
$dev="/dev/input/event1";
$f=fopen($dev, 'rb');
if($f){
while (!feof($f)){
$b=fread($f,32); //каждая кнопка формирует данные по 64 бита, 32-нажата кнопка, еще 32 - отпустили
$d=bin2hex($b); //получили hex строку
$d=substr($d,18,8); //код кнопки пульта и её статус
$button=substr($d,0,4); //кнопка
$key=substr($d,4); //0001 или 0000
switch($button){
case "0090": echo "POWER ".($key=="0001"?"down":"up")."\n";
}
//Можно и так:
/*if($key=="0001"){
if($button=="0090"){
//выполнить код по нажатию кнопки на пульте
}
}else{
if($button=="0090"){
//выполнить код когда кнопку на пульте отпустили
}
}
*/
}
}
?>
Сохраним файл как /tmp/tst
Позволим файлу быть исполняемым
chmod +x /tmp/ts
Запускаем, проверяем:Теперь, усложняя switch для каждой кнопки, получим обработку каждой кнопки пульта. Ну и, собственно, не выводить на экран «пустые» слова, а назначить необходимое действие.
Немного усложнив программу, получим (/usr/sbin/mylirc):
#!/usr/bin/php5
<?php
function AccessFile($file,$access){
if(file_exists($file)){ //файл существует!
if($f=@fopen($file,$access)){fclose($f);}else{exit("not have access to $file\n");}
}else{
return "$file not found (may not have access to the folder)\n";
}
}
$dev="";
$log=0;
if(count($argv)==1){
exit("Use ".dirname( __FILE__ )."/".basename($_SERVER['SCRIPT_FILENAME'])." --device=device [--log=logfile]\n");
}else{
for($i=1;$i<count($argv);$i++){
$cmd=explode("=",$argv[$i]);
switch($cmd[0]){
case "--device": $dev=$cmd[1]; echo AccessFile($cmd[1],"rb"); break;
case "--log": $log=$cmd[1]; echo AccessFile($cmd[1],"w"); break;
}
}
}
if($dev==""){
exit("Use ".dirname( __FILE__ )."/".basename($_SERVER['SCRIPT_FILENAME'])." --device=device [--log=logfile]\n");
}
include("/etc/mylirc/buttom_avermedia.php");
if($log){exec("echo `date` > $log");}
$f=fopen($dev, 'rb');
if($f){
while (!feof($f)){
$b=fread($f,32);
$d=bin2hex($b);
$d=substr($d,18,8);
$button =substr($d,0,4);
$key =substr($d,4);
$l=0;
for($i=0;$i<count($command);$i++){
if(trim($button)==trim($command[$i][1]) && trim($key)==trim($command[$i][2])){
$l=1;
if(trim($command[$i][3])!=""){ //Назначили клавишу
exec($command[$i][3]." > /dev/null 2>/dev/null &");
}else{ //Если не назначили, в логах можно будет узнать:
if($log){exec("echo ".$command[$i][0]." ".$command[$i][1]." cmd=null >> $log");}//логи
}
}
}
if(!$l){ //Если клавиша нажата, но в файле настроек не прописана:
if($log){exec("echo not buttom $button $key >> $log");}//логи
}
}
}
?>
Программа пишет логи, если был передан соответствующий параметр(--log) и ругается, если не передан параметр --device
Файл настроек (/etc/mylirc/buttom_avermedia.php):
<?php
$command=array();
//формат:
//название, код, ключ, команда
$command[]=array("POWER", "00ff","0001","led green 1");
$command[]=array("POWER", "00ff","0000","");
$command[]=array("NUMB 1", "0005","0001","led green 0");
$command[]=array("NUMB 1", "0005","0000","");
//и так далее
?>
Программа led green 1(0) (это мой скрипт на bash'е, который поместил в /usr/sbin/)
включала и выключала на кубике лампочку (в примере зеленая), но можно выполнять любую консольную команду, какую вы пожелаете. Выполнение идет от root'а, соответственно это нужно будет учитывать и понижать привилегии до нужного пользователя при необходимости.
Программу нужно запускать при старте системы в виде демона, чтобы можно было запустить/остановить/перезапустить/проверить работу программы(/usr/sbin/mylirc), за основу которого был взят /etc/init.d/ssh скрипт, переписан и сохранен как /etc/init.d/01_lirc:
#!/bin/sh
### BEGIN INIT INFO
# Provides: 01_lirc
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: LIRC on PHP server
### END INIT INFO
set -e
# /etc/init.d/01_lirc: start and stop the "LIRC on PHP" daemon
test -x /usr/sbin/mylirc || exit 0
( /usr/sbin/mylirc -\? 2>&1 | grep -q mylirc ) 2>/dev/null || exit 0
umask 022
. /lib/lsb/init-functions
run_by_init() {
([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}
export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
case "$1" in
start)
log_daemon_msg "Starting LIRC on PHP deamon" "mylirc" || true
if start-stop-daemon --quiet --oknodo --exec /usr/sbin/mylirc --background --start -- --device=/dev/input/event1 --log=/var/log/log_lirc; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
stop)
log_daemon_msg "Stopping LIRC on PHP deamon" "mylirc" || true
if start-stop-daemon --stop --oknodo --name mylirc --retry=KILL/1; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
restart)
log_daemon_msg "Restarting LIRC on PHP deamon" "mylirc" || true
start-stop-daemon --stop --oknodo --name mylirc --retry=KILL/1;
if start-stop-daemon --quiet --oknodo --exec /usr/sbin/mylirc --background --start -- --device=/dev/input/event1 --log=/var/log/log_lirc; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
status)
ID=`(lsof | grep mylirc | grep /dev/ 2>&1) 1>/dev/null || echo 0;`
if [ "$ID" = "0" ]; then
echo "LIRC on PHP deamon \033[37;1;41m shutdown \033[0m";
else
echo "LIRC on PHP deamon \033[37;1;42m started \033[0m";
fi
tput sgr0
;;
*)
log_action_msg "Usage: /etc/init.d/mylirc {start|stop|restart|status}" || true
exit 1
esac
exit 0
Теперь проверить работу демона можно командами, запуск:
/etc/init.d/01_lirc start
Остановка:/etc/init.d/01_lirc stop
Статус:/etc/init.d/01_lirc status
и аналогично restart
Для добавление в автозагрузку демона необходимо выполнить:
cd /etc/init.d/
update-rc.d 01_lirc defaults
Убрать из автозагрузки:
cd /etc/init.d/
update-rc.d 01_lirc remove
P.S. Программу откатал, уже две недели работает стабильно. Пожелания и комментарии приветствуются.
P.S.S.
edwardoid предложил составить скрипт, который бы генерировал файл настроек (похожий на /etc/mylirc/buttom_avermedia.php ),
вот результат
Спасибо за идею!#!/usr/bin/php5
<?php
declare(ticks = 1);
pcntl_signal(SIGINT, "signal_handler");
function signal_handler($signal){
global $f,$out;
if($signal==SIGINT){
fclose($f);
exec('echo ?\> >> '.$out);
exit("\n$out create!\n");
}
}
function AccessFile($file,$access){
if(file_exists($file)){ //файл существует!
if($f=@fopen($file,$access)){fclose($f);}else{exit("not have access to $file\n");}
}else{
return "$file not found (may not have access to the folder)\n";
}
}
$dev="";
$out="";
if(count($argv)==1){
exit("Use ".dirname( __FILE__ )."/".basename($_SERVER['SCRIPT_FILENAME'])." --device=device --out=config.php\n");
}else{
for($i=1;$i<count($argv);$i++){
$cmd=explode("=",$argv[$i]);
switch($cmd[0]){
case "--device": $dev=$cmd[1]; echo AccessFile($cmd[1],"rb"); break;
case "--out": $out=$cmd[1]; echo AccessFile($cmd[1],"w"); break;
}
}
}
if(($dev=="")||($out=="")){
exit("Use ".dirname( __FILE__ )."/".basename($_SERVER['SCRIPT_FILENAME'])." --device=device --out=config.php\n");
}
echo "press [Ctrl]+[C] to stop and press any button on the remote\n";
$f=fopen($dev, 'rb');
if($f){
exec('echo \<?php > '.$out);
exec('echo " \$command=array();" >> '.$out);
$i=0;
$j=1;
while (!feof($f)){
$b=fread($f,32);
$b=bin2hex($b);
$b=substr($b,18,8);
$button =substr($b,0,4);
$key =substr($b,4);
$i=$i+($j++)%2; //Нумерация кнопок
$but='$command[]=array("'.$i.'", "'.$button.'","'.$key.'",""); // #'.$i;
exec('echo \' '.$but.'\' >> '.$out);
echo $but."\n"; //Выводим и на экран, чтобы понимать, что кнопки нажимаются и все работает
}
}
?>