Читаем нажатия ИК-пульта под Linux

89165f43fe4e0a88f5c3acfff19627b8.jpg

На Али можно найти пульт, приёмник которого будет притворяться устройством ввода. Но я опишу способ приёма нажатий с разных пультов, и действия можно настроить на своё усмотрение.

Нажатия будем принимать с помощью YS-IRTM, это дешевая плата с ИК-приёмником и передатчиком, использует микроконтроллер на базе 8051, в котором прошивка для декодирования сигналов с пультов. Интерфейс подключения: UART 5V. Есть подробное описание в этом репозитории на GitHub.

Может читать NEC протокол, который используется на большинстве дешевых пультов. Но есть недостаток, не принимает коды повторов, если клавиша удерживается. Существуют и другие ИК-протоколы, например у Sony есть свой, поэтому пульты Sony не поддерживаются.

Подключение через USB-UART адаптер

Подойдёт любой что поддерживается ядром Linux. Для этого примера я взял CH341T. Отвлечёмся на настройку его в системе. Ведь неудобно и опасно часто использовать sudo, поэтому разрешим пользоваться адаптером обычному пользователю.

Подключаем UART адаптер и ищем его в выводе lsusb:

ID 1a86:5523 QinHeng Electronics CH341 in serial mode, usb to serial port converter

Добавляем файл /etc/udev/rules.d/80-user.rules с таким содержимым:

# CH341 serial
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="5523", MODE="0666", TAG+="uaccess"

Достаточно переподключить адаптер и правило будет работать.

Приём сигнала с пульта

По протоколу нажатие передаётся как четыре байта, каждый второй байт это инверсия битов предыдущего.

Например: 40 bf 09 f6

Где первый байт — это идентификатор пульта, второй байт это инверсия первого. Третий байт и его инверсия — это код клавиши на пульте. Так как 8 бит мало для идентификатора, то некоторые производители используют второй байт не равный инверсии первого, то есть как 16-бит идентификатор. Поэтому YS-IRTM возвращает три байта.

Так можно прочитать нажатия на пульте через YS-IRTM подключенный к UART адаптеру:

$ stty -F /dev/ttyUSB0 9600 raw
$ hexdump -v -e '3/1 "%02x " "\n"' /dev/ttyUSB0

При желании можно написать скрипт на bash, который ждёт строки от hexdump и вызывает xdotool с разными параметрами в зависимости от результата. Вот заготовка:

#!/bin/bash
set -e
stty -F /dev/ttyUSB0 9600 raw
stdbuf -oL hexdump -v -e '3/1 "%02x " "\n"' /dev/ttyUSB0 | (
  while IFS= read s; do
    echo $s
    case "$s" in
    "40 bf 46") echo action1 ;;
    "40 bf 47") echo action2 ;;
    esac
  done
)

Чтение YS-IRTM на Си

Ниже пример на Си, что читает коды нажатых клавиш.

#define _GNU_SOURCE 1

#include 
#include 
#include 
#include 

int main(int argc, char **argv) {
	const char *dev = argc > 1 ? argv[1] : "/dev/ttyUSB0";
	int serial = open(dev, O_RDWR | O_NOCTTY | O_SYNC);
	if (serial < 0) return 1;

	struct termios tty = { 0 };
	cfsetispeed(&tty, B9600);
	cfsetospeed(&tty, B9600);
	tty.c_cflag = CS8 | CLOCAL | CREAD;
	tty.c_iflag = IGNPAR;
	tty.c_oflag = 0;
	tty.c_lflag = 0;
	tty.c_cc[VMIN] = 3; // min bytes required
	tty.c_cc[VTIME] = 1; // 1/10s between bytes
	tcflush(serial, TCIFLUSH);
	tcsetattr(serial, TCSANOW, &tty);

	for (;;) {
		unsigned char buf[3];
		int len = read(serial, buf, 3);
		if (len != 3) break;
		printf("%02x %02x %02x\n", buf[0], buf[1], buf[2]);
	}
	close(serial);
}

Теперь мы можем получать нажатия, но нужно их как-то передать системе как нажатия на клавиатуре. Для этого можно использовать библиотеку XTest для X11. Описание здесь.

Вызов выглядит так: XTestFakeKeyEvent(display, keycode, is_press, delay), где «keycode» это код на клавиатуре, «is_press» это состояние клавиши (нажали/отпустили), и «delay» это задержка в миллисекундах.

Пример, который получает код клавиши XK_Left, и зажимает её на одну секунду.

#include 
#include 
#include 
 
int main(int argc, char **argv) {
	Display *display = XOpenDisplay(NULL);
	if (!display) return 1;
	int keysym = XK_Left;
	int keycode = XKeysymToKeycode(display, keysym);
	XTestFakeKeyEvent(display, keycode, 1, 0);
	XTestFakeKeyEvent(display, keycode, 0, 1000);
 	XSync(display, False);
 	XCloseDisplay(display);
}

Компилировать так: cc -O2 example.c -o main -lX11 -lXtst. Вероятно, вам понадобится установка пакетов для разработки (пример для Ubuntu): sudo apt-get install libxtst-dev

Из двух этих примеров можно собрать программу, что принимает нажатия на пульте и выдаёт их за нажатия на клавиатуре.

Недостаток

Однако, есть проблема, про которую замечал в начале, YS-IRTM не принимает повторы клавиш, только начало нажатия. Для некоторых приложений, особенно игр, важна возможность удерживать клавишу нажатой. Поэтому придётся повторно тыкать на клавишу, или сделать клавишу переключателем (при первом нажатии клавиша считается нажатой, при следующем нажатии отпускается). Оба варианта мало подходят для игр, я попробовал. Так что остаётся только использовать для мультимедиа функций.

Через программируемые микроконтроллеры и ИК-приёмник (без чипа как в YS-IRTM) можно реализовать все протоколы и читать повторы, но придётся заморочиться куда больше.

© Habrahabr.ru