Cubieboard A10. Учимся управлять системой с пульта дистанционного управления

в 3:00, , рубрики: cubic, cubieboard, Гаджеты. Устройства для гиков, пульт дистанционного управления, умный дом, Электроника для начинающих, метки: , ,

Здравствуй!

Cubieboard A10. Учимся управлять системой с пульта дистанционного управления
Решил написать статью о своем опыте работы с IR на Cubieboard.

Не так давно заказал себе это замечательное устройство (заказывал специально для будущего домашнего сервера, и, следовательно, использовать на ней только серверную ОCь) Выбор пал на Cubian (http://cubian.org/) в виду того, что строить свой велосипед пока не хочется.

Предполагалось развернуть на кубике nginx, php5, samba, mocd и несколько своих сервисов для управления боком реле.
В ходе изучения кубика решил попробовать настроить IR на прием команд с пульта от старого TV тюнера, решение в виде lirc развернуть не получилось, стал искать другие решения, ничего доброго не нашел.

Написал свое решение, которым теперь делюсь с вами.

Изначально хотелось обойтись только bash, но перечитывать раз в секунду или чаще/реже устройство меня не привлекало, зачем в пустую гонять процессор, лишний раз нагревая эту малышку. Тогда решение пришло в сторону php, так как он все равно будет установлен.

Недавно на хабре наткнулся на статью, мое решение близко к предложенному ntfs1984, но не такое же.
Так как комментировать мне пока не позволяется, решил поиграть в песочнице.

Приступим.
Изначально, после установки системы, устройство не доступно, так как не загружены соответствующие модули, в случае cubieboard a10 и cubian — это модуль sunxi_ir
Проверяем его загрузку командой lsmod
Cubieboard A10. Учимся управлять системой с пульта дистанционного управления
Обращаю ваше внимание, все команды я выполняю от root'а.
По умолчанию система этот модуль не загружает, для загрузки вручную можно воспользоваться командой:

modprobe sunxi_ir

В случае успешного запуска модуля, modprobe ничего не скажет, а вот в списке lsmod появится заветная строчка. Также важным фактором, чтобы удостовериться, что все заработало, нужно проверить, появилось ли устройство ввода:
Cubieboard A10. Учимся управлять системой с пульта дистанционного управления
в моем случае это было event1
самый верный способ проверить, то ли это устройство, это выполнить

cat /dev/input/event1 | hexdump

(Остановить процесс можно [Ctrl]+[C])
hexdump выводит в «красивом» формате данные, плюс узнать код нажатой кнопки можем таким же образом, но можно обойтись и без него.
Cubieboard A10. Учимся управлять системой с пульта дистанционного управления
На каждую нажатую кнопку получаем четыре строки, первая пара строк — кнопка на пульте была нажата(0090 0001), вторая — кнопку отпустили(0090 0000). Из этих строк можно получить много полезного, я сильно не углублялся, а лишь обращал внимание на код кнопки (в примере это 0090). Но могу сказать, что 532a -это код пульта, если кто решит привязать программу и к пульту.
Если все так и есть, переходим к следующему этапу — настройка автозагрузки

nano /etc/modules

и в конце добавить лишь одну строку
sunxi_ir
это имя нашего модуля, который мы загружали выше. Пример моего modules:
Cubieboard A10. Учимся управлять системой с пульта дистанционного управления
Сохранили и вышли.

Итак, приготовления работы с 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

Запускаем, проверяем:
Cubieboard A10. Учимся управлять системой с пульта дистанционного управления
Теперь, усложняя 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js