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

Обращаю ваше внимание, все команды я выполняю от 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 

P.S. Программу откатал, уже две недели работает стабильно. Пожелания и комментарии приветствуются.

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


Комментарии

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

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