Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Моя задача сейчас — научиться отправлять команды кондиционерам и другим устройствам в доме. Исходно эти устройства имеют только IR remote control. Для решения этой задачи у меня есть Raspberry Pi и IR transceiver shield. В статье можно найти конфиги, команды, советы и немного теории. Из софта будут LIRC (Linux Infrared Remote Control) и Python.
LIRC я нашел с помощью Гугла. В процессе исследования выяснил, что LIRC работает как с передатчиками, так и с приемниками IR сигналов, может декодировать принятый сигнал и выполнять какие-то действия в связи с этим. Сейчас прием сигналов мне не нужен, но в будущем может оказаться полезным. Если будете возиться с LIRC сами, прочтение LIRC Configuration Guide крайне рекомендуется.
Конфигурация
apt-get update
apt-get install lirc
# /etc/modules (добавить в конец)
lirc_dev
lirc_rpi gpio_in_pin=18 gpio_out_pin=17
# /etc/lirc/hardware.conf (по умолчанию файла нет, надо создать)
LIRCD_ARGS="--uinput --listen"
LOAD_MODULES=true
DRIVER="default"
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"
# /boot/config.txt (в конце уже есть заготовка секции про lirc, исходно пустая)
dtoverlay=lirc-rpi,gpio_in_pin=18,gpio_out_pin=17
# /etc/lirc/lirc_options.conf (найти и заменить эти параметры)
driver = default
device = /dev/lirc0
reboot
sudo /etc/init.d/lircd status
Запись
Сначала необходимо создать конфигурационный файл с последовательностями данных для всех необходимых команд. Я дальше называю этот файл lircd.conf
, но на самом для каждого устройства создается свой файл my_device_name.lircd.conf
в каталоге /etc/lirc/lircd.conf.d
.
Формат файла описан здесь. Если есть пульт, то можно записать передаваенмые им сигналы в файл с помощью утилиты irrecord.
/etc/init.d/lircd stop
irrecord -d /dev/lirc0 ~/my_device.lircd.conf
mv ~/my_device.lircd.conf /etc/lirc/lircd.conf.d/
irrecord
анализирует последовательности и пытается определить протокол и временные параметры. В некоторых случаях irrecord
терпит неудачу в анализе, поэтому есть возможность сохранить последовательность как она была принята, в "сыром" (raw) виде, для этого есть ключ --force
.
Но даже с --force
irrecord
пытается что-то анализировать, и тоже может потерпеть неудачу. Тогда можно записать последовательности с помощью mode2
и создать файл самостоятельно.
mode2
печатает последовательно длительности наличия и отсутствия сигнала, из которых и складываются передаваемые данные. Длительность измеряется в микросекундах (1e-6 секунды). В raw формате в lircd.conf указыватся эти же длительности, начиная с 'pulse' (ведущий 'space' не нужен). Сответственно, всегда должно быть нечетное количество чисел (начинается и заканчивается наличием сигнала — 'pulse').
Для автоматизации я сделал скрипт для записи, который спрашивает имя команды, запускает mode2
на 5 секунд, запоминает и в конце печатает результат в формате, готовом для lircd.conf (см. под спойлером).
#!/usr/bin/env python3
import io
import sys
import os
r = """begin remote
name DEVICE_NAME_CHANGE_ME
flags RAW_CODES
eps 30
aeps 100
gap 19037
begin raw_codes
"""
while True:
print()
print("enter the command name, or just press Enter to finish")
a = sys.stdin.readline().rstrip("\n\r")
if not a:
break
first_space = True
n = 0
r += " name " + a + "\n"
for line in os.popen('timeout 5s mode2 -d /dev/lirc0 2> /dev/null').read().split('\n'):
words = line.split()
if len(words) < 2:
continue
if words[0] != 'space' and words[0] != 'pulse':
continue
if first_space and words[0] == 'space':
first_space = False
continue
if n % 4 == 0:
r += "\n "
r += words[1] + " "
n = n+1
if n > 5:
print ("got", n, "values, looks good")
else:
print ("no signal, something went wrong")
r += "\n\n"
r += """ end raw_codes
end remote
"""
print()
print(r)
print()
Созданный файл можно еще раз попытаться "распознать" с помощью irrecord --analyse
. Не всегда это проходит удачно, не спешите выбрасывать старый файл. Моя статистика такая: пульт от телевизора LG был понят с легкостью, все кондиционеры и пылесос потребовали ручного создания, пылесос был потом обработан --analyse
.
Просто как пример: вот так выглядит файл для моего пылесоса.
Стандартные имена команд
LIRC по своему прямому назначению должен принятый и распознанный IR сигнал превратить в событие Linux input. Поэтому по умолчанию от нас требуют, чтобы имена команд в lircd.conf были из стандартного списка. Можно посмотреть список допустимых имен:
irrecord --list-namespace
Кондиционеры под этот шаблон не попадают. Требование к именам можно выключить, добавив при записи параметр:
irrecord --disable-namespace ....
Отладка
Проверить приемник помогает утилита mode2
, которая печатает все видимые сигналы.
/etc/init.d/lircd stop
mode2 -d /dev/lirc0
Проверить передачу проще всего, если есть другой приемник и запущенная на нем mode2
. В особо безнадежных случаях можно менять значение на выходе GPIO и проверять тестером или осцилографом, куда доходит сигнал. Команда gpio
входит в состав пакета wiringpi.
while sleep 1; do
gpio -g toggle 17
done
Посмотреть на логи можно с помощью journalctl
, в частности это позволяет увидеть ошибки в файле конфигурации:
journalctl -b 0 /usr/sbin/lircd
Отправка команд
Для передачи записанных команд есть утилита irsend
. Она же может показать список известных устройств, и список известных команд для каждого устройства. Обратите внимание на "пустые аргументы" в примере ниже, они там нужны.
irsend
— это клиент для lircd
, поэтому если что-то пошло не так, заглядывайте в логи (см. выше).
# список устройств
irsend LIST "" ""
# список команд для устройства LG_TV
irsend LIST LG_TV ""
# отправить команду ON
irsend SEND_ONCE LG_TV ON
Теоретически, есть еще одна возможность — отправлять команды в lircd через его сокет. Не разбирался.
Вызов из Python
Почти все библиотеки являются всего лишь обвесом над irsend
. Единственная найденная мной библиотека, которая компилирует клиента для API через сокет, не работает на Raspberry (нужна другая версия lircd). Поэтому смысла в них мало, команду вызвать я могу сам:
import subprocess
subprocess.call(["irsend", "send_once", "BEDROOM_AC", "OFF"])
Hardware
Я использую готовую плату, их много на Amazon и AliExpress. Гуглить можно как "Raspberry infrared sheild". Она использует GPIO 17 на выход, и GPIO 18 на вход, что видно из конфигов выше.
На плате есть место для второго (дополнительного) светодиода D2, который по умолчанию не установлен. При использовании двух светодиодов, они подключаются последовательно. Поэтому при отсутствии светодиода D2 нужно замкнуть перемычку SJ1. С удивлением обнаружил, что на всех моих платах перемычка исходно была разомкнута. Пришлось доработать паяльником.
Итоги
Работает: сигнал передается, устройства его видят и правильно на него реагируют.
Многое зависит от положения диода-излучателя, он должен быть точно направлен на приемник. Один неподвижный передатчик не может управлять всеми устройствами в комнате. Клонировать решение на базе Raspberry Pi для каждого гаджета дорого, нужно или доработать излучатель для "покрытия большей площади", или найти более дешевую платформу.
LIRC исходно создавался для преобразования IR сигналов в стандарнтные линуксовые события устройств ввода. Поэтому для него ествественно, что одна кнопка — один код. Для некоторых устройств (болшинства Air conditioner) это не так: при нажатии на любую кнопку пульт передает пакет данных, содержащий полное состояние устройства (включено, режим работы, температура, режимы работы вентилятора, время, таймер и т.д.). Возможности собирать многокомпонентный пакет на основе нескольких параметров в LIRC нет, поэтому как быстрый инструмент "из коробки" он помогает, но дальше придется искать что-то еще. Хотя для большинства случаев записанных данных с привычными настройками вентилятора и без экзотических режимов вполне достаточно.