Здравствуй!
Решил написать статью о своем опыте работы с 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 $filen");}
}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 33[37;1;41m shutdown 33[0m";
else
echo "LIRC on PHP deamon 33[37;1;42m started 33[0m";
fi
tput sgr0
;;
*)
log_action_msg "Usage: /etc/init.d/mylirc {start|stop|restart|status}" || true
exit 1
esac
exit 0
P.S. Программу откатал, уже две недели работает стабильно. Пожелания и комментарии приветствуются.
Автор: dimka-exe