Используем беспроводной выключатель на 433МГц для управления ПК
У меня дома скопилось несколько беспроводных выключателей на 433МГц, стало интересно, можно ли их использовать для каких-либо задач, например для управления компьютером или для интегрирования в систему «умного дома».
Эти выключатели удобны своей дешевизной и стабильной работой, выглядят они примерно так:
Как это работает, и что с ними можно сделать (гусары молчать:), подробности под катом.
Теория
Скажу сразу — как работает такой выключатель, я не знаю, хотя и примерно догадываюсь. Значит нужно будет произвести небольшой reverse engineering.
Первым делом сигнал нужно принять, для чего используем многим уже известный RTL-SDR приемник, у радиолюбителей часто называемый просто «свисток». Этот девайс ценой всего в 10$ позволяет принимать радиосигналы в диапазоне примерно от 50 до 1250МГц, для нас то что нужно. Тема старая, но если кто не читал — читайте.
Делаем первый шаг анализа — внимательно смотрим на выключатель. Обнаруживаем что сзади на корпусе у него написано «Made in China» (кто бы мог подумать?) и, что более важно, указана частота 433МГц. Теперь можно подключить SDR-приемник, запустить SDR# и убедиться, что данные действительно передаются.
Симметрия сигнала на спектре подсказывает про наличие AM-модуляции. Кстати справа виден более слабый «чужой» сигнал — их тоже можно принимать и декодировать, про них будет подробнее сказано отдельно. Впрочем, вернемся к сигналу. Записываем его в формате обычного WAV и нажимаем кнопки на пульте — для примера я нажал кнопки ON и OFF на канале «1».
Открываем звуковой файл в любом аудиоредакторе, и воспользуемся для сравнения сигналов другим профессиональным инструментом аналитиков — программой Paint. Размещаем 2 сигнала c разных кнопок один над другим, чтобы увидеть разницу:
Нетрудно видеть, что мы имеем обычную битовую последовательность, отличие в которой как раз в одном бите, соответствующем кнопке ON или OFF. Пока кнопка нажата, выключатель просто циклически повторяет эту последовательность в эфир со скоростью 20 раз в секунду. Дешево и просто, даже если одна последовательность исказится при передаче, другая будет принята.
Из этого кстати можно сделать один важный вывод — сигналы таких выключателей (речь о дешевых моделях) передаются в эфир «как есть», без какой-либо аутентификации, защиты или шифрования. Такой выключатель или беспроводную розетку с таким выключателем не стоит использовать для каких-то ответственных функций, например для включения мощных обогревателей или тем более для открытия входной двери или гаража. Дело тут даже не в хакерах (шанс что кто-то будет взламывать мой дом беспроводным способом я оцениваю меньше чем шанс падения на мой дом МКС), а в том, что сосед может случайно купить такой же выключатель, и коды у них могут совпасть (впрочем на выключателе есть возможность выбора между 4 каналами). По моему опыту использования, 2-3 раза за год выключатель таки включался «сам», то ли помеха, то ли действительно принимался далекий сигнал от такой же модели.
Разумеется, это не относится к более сложным системам, таким как Lora или Philips Hue, там с шифрованием все впорядке.
Впрочем, вернемся к нашей задаче. Можно написать декодер таких сигналов самостоятельно, но к счастью, это уже сделали до нас, в проекте называемом «rtl_433». Изначально программа была создана для Linux, Windows-версию можно скачать по адресу Linux версию можно скачать с GitHub.
Запускаем программу из командной строки: «rtl_433.exe -F json»
Мы получили данные, осталось написать программу для их обработки.
Raspberry Pi
Первое, что интересно рассмотреть, это Raspberry Pi. Для установки rtl_433 на Raspbian распаковываем архив и выполняем следующие команды.
sudo apt-get install libtool libusb-1.0.0-dev librtlsdr-dev rtl-sdr build-essential autoconf cmake pkg-config cd rtl_433/ autoreconf --install ./configure make make install
Вторым шагом, напишем программу которая будет получать эти данные, и в зависимости от них, выполнять нужные действия. Код на Python весьма несложный:
from __future__ import print_function import os, sys, io import json import subprocess print("RTLSDR listening started") transmitter_name = "Waveman Switch Transmitter" transmitter_channel = 1 proc = subprocess.Popen(["rtl_433 -F json"], stdout=subprocess.PIPE, shell=True) while True: try: line = proc.stdout.readline().encode('ascii','ignore') proc.poll() data = json.loads(line) print(data) m,st,ch,btn= data['model'],data['state'],data['channel'],data['button'] if m==transmitter_name and ch==transmitter_channel and btn==1 and st=='on': print("ON") elif m==transmitter_name and ch==transmitter_channel and btn==1 and st=='off': print("OFF") except KeyboardInterrupt: break except: pass print("RTLSDR listening done")
Чтобы запустить код, нужно сохранить его в файле (например rtl_listen.py) и запустить командой «python rtl_listen.py».
Как можно видеть, программа запускает процесс с помощью subprocess.Popen и читает из него данные. Дальше все просто, код вполне читабельный, и внести изменения будет не сложно. В данном примере, при нажатии кнопки «1» выводится сообщение print(«ON»), вместо этого можно делать что-то другое, например, активировать пин GPIO, включать реле, посылать данные на сервер и пр. Перед использованием будет необходимо заодно поменять имя transmitter_name на название той модели пульта, который будет использоваться.
Кстати, сам RTL-SDR-приемник по сравнению с Raspberry Pi выглядит так:
Windows
К сожалению, под Windows 10 вышеприведенный код не заработал. Но как подсказал поиск на github, работает асинхронное чтение данных из отдельного потока. Почему так, выяснять было лень, просто приведу под спойлером работающий код.
from __future__ import print_function import os, sys import subprocess import time import threading import Queue import json class AsynchronousFileReader(threading.Thread): # Helper class to implement asynchronous reading def __init__(self, fd, queue): assert isinstance(queue, Queue.Queue) assert callable(fd.readline) threading.Thread.__init__(self) self._fd = fd self._queue = queue def run(self): # The body of the tread: read lines and put them on the queue. for line in iter(self._fd.readline, ''): self._queue.put(line) def eof(self): # Check whether there is no more content to expect return not self.is_alive() and self._queue.empty() def replace(string): while ' ' in string: string = string.replace(' ', ' ') return string def read_rtl_data(): process = subprocess.Popen(["rtl_433.exe", "-F", "json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Launch the asynchronous readers of stdout and stderr. stdout_queue = Queue.Queue() stdout_reader = AsynchronousFileReader(process.stdout, stdout_queue) stdout_reader.start() stderr_queue = Queue.Queue() stderr_reader = AsynchronousFileReader(process.stderr, stderr_queue) stderr_reader.start() transmitter_name = "Waveman Switch Transmitter" transmitter_channel = 1 # Check the queues if we received some output while not stdout_reader.eof() or not stderr_reader.eof(): # Show what we received from standard output. while not stdout_queue.empty(): line = stdout_queue.get() print("Line1:", repr(line)) data = json.loads(line) # print("Data:", repr(line)) m,st,ch,btn= data['model'],data['state'],data['channel'],data['button'] if m==transmitter_name and ch==transmitter_channel and btn==1 and st=='on': print("ON") elif m==transmitter_name and ch==transmitter_channel and btn==1 and st=='off': print("OFF") # Show what we received from standard error. while not stderr_queue.empty(): line = replace(stderr_queue.get()) print("Line2:", line) # Sleep a bit before asking the readers again. time.sleep(0.1) stdout_reader.join() stderr_reader.join() # Close subprocess' file descriptors. process.stdout.close() process.stderr.close() if __name__ == '__main__': print("RTLSDR listening started") read_rtl_data() print("RTLSDR listening done")
С этим кодом мы можем использовать любые действия в обработчике, логика такая же как и в коде на Raspberry Pi.
Пример: допустим, у нас есть компьютер, выделенный под домашний кинотеатр, и мы хотим выключать его нажатием кнопки с пульта. Заменяем код ‘print(«OFF»)’ на
os.system('shutdown -s') sys.exit(0)
После чего, компьютер будет выключаться по нажатию соответствующей кнопки. Разумеется, кроме «shutdown -s» можно использовать любую другую команду Windows, стоит лишь учитывать что команды будут посылаться многократно, пока кнопка пульта нажата, чтобы избежать такого дублирования, нужно усовершенствовать код.
Заключение
Как можно видеть, все довольно-таки просто, и есть место для экспериментов. Наконец, небольшой бонус для тех кто дочитал до сюда. На 433МГц работает большое количество разных устройств, которые rtl_433 может декодировать, можно просто оставить программу работать несколько часов, и посмотреть что «поймается». Под спойлером пример такого лога, записанного ранее:
2018-01-10 21:15:17 : Prologue sensor : 5 : 15
Channel: 1
Battery: OK
Button: 0
Temperature: 6.00 C
Humidity: 11 %
2018-01-10 21:15:28 : inFactory sensor
ID: 71
Temperature: 6.67 °C
Humidity: 99 %
2018-01-10 21:16:07 : Toyota : TPMS : 61511475 : 60e5006b : CRC
2018-01-10 21:20:33 : Prologue sensor : 5 : 15
Channel: 1
Battery: OK
Button: 0
Temperature: 6.00 C
Humidity: 11 %
: Waveman Switch Transmitter
id: A
channel: 2
button: 1
state: on
: Waveman Switch Transmitter
id: A
channel: 2
button: 1
state: on
: Waveman Switch Transmitter
id: A
channel: 2
button: 1
state: on
2018-01-10 21:21:21 : Akhan 100F14 remote keyless entry
ID (20bit): 0x41
Data (4bit): 0x4 (Mute)
: Waveman Switch Transmitter
id: A
channel: 2
button: 1
state: off
2018-01-10 21:32:31 : Ford : TPMS : 00268b1f : a34a0e : CHECKSUM
2018-01-10 21:32:32 : Ford : TPMS : 00268a5c : 9c440e : CHECKSUM
2018-01-10 21:32:37 : Ford : TPMS : 016dbfce : 99430e : CHECKSUM
2018-01-10 21:32:39 : Ford : TPMS : 002671a0 : 9c4a0e : CHECKSUM
Есть интересные данные, например давление в шинах у соседского автомобиля (TPMS, tire-pressure monitoring system), или наружняя температура +6 с чьего-то датчика. Это позволяет например, выводить наружнюю температуру, если у соседей случайно окажется совместимая с этим протоколом метеостанция.
Всем удачных экспериментов.
Disclaimer: Очевидно, что использование SDR и цифровой обработки для чтения сигналов OOK-модуляции — это по сути, стрельба из пушки по воробьям. Возможно, на aliexpress существуют готовые приемники за 1-2$, которые делают то же самое, с меньшей ценой и меньшим энергопотреблением. Если кто знает такие модели, напишите в комментариях.
Записки склерозника
Этот выключатель позволяет управлять дистанционно включением-выключением нагрузки в сети 220 вольт (освещение, ворота, шлагбаум, сирена и пр. устройства мощностью до 2 кВт).
На вход подается 220 вольт (контакт N — ноль; L — фаза). При срабатывании реле на выходе (контакт L) появляется фаза. Ноль со входа на выход подается без коммутации.
ВАЖНО! Приемник подключается к сети 220В напрямую, ему не требуется для работы отдельного блока питания — это очень удобно и значительно упрощает его использование.
1. Режим «Звонок». Реле замкнуто, пока нажата кнопка пульта ДУ. Если отпустить кнопку – реле разомкнется. Используется одна кнопка пульта.
2. Режим «Однокнопочный выключатель». Нажатие кнопки пульта ДУ замыкает реле, повторное нажатие — размыкает. Используется одна кнопка пульта.
Выбор режима работы.
Каждый пульт ДУ имеет свой индивидуальный цифровой код. Для того чтобы приемник записал код пульта необходимо выполнить «обучение» приемника. Для этого предназначена кнопка на плате приемника (она только одна).
1. Обучение для режима «Звонок»:
Нажмите кнопку обучения приемника 1 раз, светодиод моргнет 1 раз, и перейдет в режим постоянного свечения — это признак готовности приемника принять код пульта. Нажмите на пульте ДУ любую кнопку 1 раз (эта кнопка в дальнейшем и будет работать), светодиод приемника моргнет 3 раза и потухнет, это значит код пульта успешно принят и приемник и пульт готовы к совместной работе в этом режиме.
2. Обучение для режима «Однокнопочный выключатель»:
Нажмите кнопку обучения приемника 2 раза, светодиод моргнет 2 раза, и перейдет в режим постоянного свечения — это признак готовности приемника принять код пульта. Нажмите на пульте ДУ любую кнопку 1 раз (эта кнопка в дальнейшем и будет работать), светодиод приемника моргнет 3 раза и потухнет, это значит код пульта успешно принят и приемник и пульт готовы к совместной работе в этом режиме.
3. Обучение для режима «Двухкнопочный выключатель»:
Нажмите кнопку обучения приемника 3 раза, светодиод моргнет 3 раза, и перейдет в режим постоянного свечения — это признак готовности приемника принять коды пульта.
Поскольку в этом режиме используются 2 кнопки, первой в приемник записывается кнопка включения, второй — выключения. Какие для этого будут выбраны кнопки на пульте — зависит от вашего предпочтения. Запись сигналов кнопок пульта в приемник записывается так же, как и в предыдущих случаях.
Сброс приемника:
Чтобы очистить приемник от записанных кодов нужно нажать кнопку обучения 8 раз. После этого все коды в приемнике будут стерты.