Хабрахабр

Используем беспроводной выключатель на 433МГц для управления ПК

Привет geektimes habr.

У меня дома скопилось несколько беспроводных выключателей на 433МГц, стало интересно, можно ли их использовать для каких-либо задач, например для управления компьютером или для интегрирования в систему «умного дома».

Эти выключатели удобны своей дешевизной и стабильной работой, выглядят они примерно так:

Как это работает, и что с ними можно сделать (гусары молчать:), подробности под катом.

Теория

Скажу сразу — как работает такой выключатель, я не знаю, хотя и примерно догадываюсь. Значит нужно будет произвести небольшой reverse engineering.

Этот девайс ценой всего в 10$ позволяет принимать радиосигналы в диапазоне примерно от 50 до 1250МГц, для нас то что нужно. Первым делом сигнал нужно принять, для чего используем многим уже известный RTL-SDR приемник, у радиолюбителей часто называемый просто «свисток». Тема старая, но если кто не читал — https://habr.com/post/149698/.

Обнаруживаем что сзади на корпусе у него написано «Made in China» (кто бы мог подумать?) и, что более важно, указана частота 433МГц. Делаем первый шаг анализа — внимательно смотрим на выключатель. Теперь можно подключить SDR-приемник, запустить SDR# и убедиться, что данные действительно передаются.

Кстати справа виден более слабый «чужой» сигнал — их тоже можно принимать и декодировать, про них будет подробнее сказано отдельно. Симметрия сигнала на спектре подсказывает про наличие AM-модуляции. Записываем его в формате обычного WAV и нажимаем кнопки на пульте — для примера я нажал кнопки ON и OFF на канале «1». Впрочем, вернемся к сигналу.

Размещаем 2 сигнала c разных кнопок один над другим, чтобы увидеть разницу:
Открываем звуковой файл в любом аудиоредакторе, и воспользуемся для сравнения сигналов другим профессиональным инструментом аналитиков — программой Paint.

Пока кнопка нажата, выключатель просто циклически повторяет эту последовательность в эфир со скоростью 20 раз в секунду. Нетрудно видеть, что мы имеем обычную битовую последовательность, отличие в которой как раз в одном бите, соответствующем кнопке ON или OFF. Дешево и просто, даже если одна последовательность исказится при передаче, другая будет принята.

Такой выключатель или беспроводную розетку с таким выключателем не стоит использовать для каких-то ответственных функций, например для включения мощных обогревателей или тем более для открытия входной двери или гаража. Из этого кстати можно сделать один важный вывод — сигналы таких выключателей (речь о дешевых моделях) передаются в эфир «как есть», без какой-либо аутентификации, защиты или шифрования. По моему опыту использования, 2-3 раза за год выключатель таки включался «сам», то ли помеха, то ли действительно принимался далекий сигнал от такой же модели. Дело тут даже не в хакерах (шанс что кто-то будет взламывать мой дом беспроводным способом я оцениваю меньше чем шанс падения на мой дом МКС), а в том, что сосед может случайно купить такой же выключатель, и коды у них могут совпасть (впрочем на выключателе есть возможность выбора между 4 каналами).

Разумеется, это не относится к более сложным системам, таким как Lora или Philips Hue, там с шифрованием все впорядке.

Можно написать декодер таких сигналов самостоятельно, но к счастью, это уже сделали до нас, в проекте называемом «rtl_433». Впрочем, вернемся к нашей задаче. Изначально программа была создана для Linux, Windows-версию можно скачать по адресу https://cognito.me.uk/computers/rtl_433-windows-binary-32-bit/ Linux версию можно скачать с https://github.com/merbanan/rtl_433.

Запускаем программу из командной строки: «rtl_433.exe -F json»

Мы получили данные, осталось написать программу для их обработки.

Raspberry Pi

Первое, что интересно рассмотреть, это Raspberry Pi. Для установки rtl_433 на Raspbian распаковываем архив github.com/merbanan/rtl_433/archive/18.05.tar.gz и выполняем следующие команды:

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».

Popen и читает из него данные. Как можно видеть, программа запускает процесс с помощью subprocess. В данном примере, при нажатии кнопки «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)

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

Заключение

Как можно видеть, все довольно-таки просто, и есть место для экспериментов. Наконец, небольшой бонус для тех кто дочитал до сюда. На 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 %

67 °C
Humidity: 99 % 2018-01-10 21:15:28 : inFactory sensor
ID: 71
Temperature: 6.

2018-01-10 21:16:07 : Toyota : TPMS : 61511475 : 60e5006b : CRC

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:20:33 : Prologue sensor : 5 : 15
Channel: 1
Battery: OK
Button: 0
Temperature: 6.

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 с чьего-то датчика. Это позволяет например, выводить наружнюю температуру, если у соседей случайно окажется совместимая с этим протоколом метеостанция.

Всем удачных экспериментов.

Возможно, на aliexpress существуют готовые приемники за 1-2$, которые делают то же самое, с меньшей ценой и меньшим энергопотреблением. Disclaimer: Я понимаю, что использование SDR и цифровой обработки для чтения сигналов OOK-модуляции — это по сути, стрельба из пушки по воробьям. Если кто знает такие модели, напишите в комментариях.

Теги
Показать больше

Похожие статьи

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

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

Кнопка «Наверх»
Закрыть