У нашего местного дискаунтера «ALDI» я купил несколько розеток модели «Easy Home» с дистанционным управлением. В комплект входит 4 розетки плюс (всего лишь) один пульт. Кроме того, в интернете я поискал подходящий передатчик с USB-разъемом, который бы смог управлять розетками, и нашел очень дешевую модель «Home Easy». Вы, вероятно, заметили, что словосочетания «Easy Home» и «Home Easy» несколько отличаются. Я тоже обратил внимание на это различие, но все же надеялся, что передатчик будет работать без дополнительных танцев с бубном.
Автор: Sven Tantau
Home easy или easy home?
У нашего местного дискаунтера «ALDI» я купил несколько розеток модели «Easy Home» с дистанционным управлением. В комплект входит 4 розетки плюс (всего лишь) один пульт. Кроме того, в интернете я поискал подходящий передатчик с USB-разъемом, который бы смог управлять розетками, и нашел очень дешевую модель «Home Easy». Вы, вероятно, заметили, что словосочетания «Easy Home» и «Home Easy» несколько отличаются. Я тоже обратил внимание на это различие, но все же надеялся, что передатчик будет работать без дополнительных танцев с бубном.
Я ошибался.
Оборудование для экспериментов
Рисунок: Перечень оборудования
Easy Home (Розетки + Пульт)
10-ти кнопочный пульт позволяет включать/выключать розетки. Еще существует так называемый мастер-режим для выключения/выключения одновременно всех 4-х розеток. Во время подачи питания розетки находятся в «режиме обучения». В течение 30 секунд розетка ожидает сигнал на включение, который затем считается корректным, и запоминает некоторый идентификатор. Если в течение 30 секунд никаких сигналов не поступало, розетка использует сохраненный ранее идентификатор. Розетку можно «научить» принимать сигналы включения/отключения от нескольких кнопок пульта. Если во время режима обучения поступил сигнал выключения, эта кнопка больше не сможет управлять данной розеткой. Если во время режима обучения используется мастер-режим на выключение, удаляются все сохраненные ранее идентификаторы.
Производитель: Globaltronics GmbH & Co KG
Модель розетки: GT-FSI-07
Модель пульта: GT-9000
RTL-SDR (USB DVB-T приемник)
DVB-T приемник с USB-разъемом и тюнером Elonics E4000 (TV-приемник), функционирующий как программно-определяемая радиосистема, предназначен для анализа сигнала. Программное обеспечение с открытым исходным кодом позволяет устанавливать частоту, полосу пропускания, периодичность выборки и на выходе получать I/Q сигнал.
Home Easy (USB-RF-передатчик)
USB-передатчик, работающий на частоте 433.92 МГц.
Поставщик: ROOS Electronics bv.
Модель: ELRO AB600USB
idVendor=04d9, idProduct=1357
Производитель: ABLOCK
Размышления, догадки и немного удачи
Я не знал, на какой частоте работают электрические розетки, но предположил, что значение может быть 433.92 МГц, поскольку большинство дешевых устройств (например, оборудование для метеостанций) используют именно эту частоту.
В своих исследованиях я использовал утилиту baudline в связке с rtl_sdr:
sudo rtl_sdr -s 2e6 -f 433.92e6 - | baudline -reset -stdin
# This invokes rtl_sdr
# -s 2e6 sets the sample rate to 2000000 samples per second
# -f 433.92e6 sets the frequency to 433.92 MHz
# and then pipes the output into baudline
Рисунок 1: Выходной сигнал при нажатии кнопки на пульте
Мои предположения подтвердились. Как только я нажал на кнопку пульта, то увидел «содержимое» на диаграмме частот в baudline.
Подсказка: вы можете остановить обработку новых данных, если кликнете на правую кнопку мыши и выберете «Pause».
В baudline я обнаружил следующую опцию:
-quadrature Анализ комплексного I/Q сигнала.
Поскольку на выходе утилиты I/Q сигнал, я решил попробовать опцию –quadrature. Также я решил установить входной формат как беззнаковый целый, а затем увеличил периодичность выборки до 1e6.
sudo rtl_sdr -s 2e6 -f 433.92e6 - | baudline -reset -samplerate 1000000 -format u8 -quadrature -stdin
# This invokes rtl_sdr
# -s 2e6 (sets the sample rate to 2000000 samples per second)
# -f 433.92e6 (sets the frequency to 433.92 MHz)
# and then pipes the output into baudline
# -reset (use defaults)
# -samplerate 1000000 (guess what)
# -format u8 (unsigned integer as input)
# -quadrature (I/Q data is coming)
# -stdin (take input from stdin)
Рисунок 2: В анализаторе изменен формат входного сигнала и периодичность выборки
Чтобы лучше рассмотреть сигнал, я переключился в режим waveform (кликните правой кнопкой мыши, выберите displays и далее waveform). Комбинация клавиш ALT+(Левая/Правая стрелка) позволяет масштабировать время и найти искомую информацию.
Рисунок 3: Сигнал без масштабирования
Рисунок 4: Масштабированный сигнал
Глядя на сигнал в масштабированном виде, я предположил, что передаются бинарные последовательности. Предполагая, что при помощи длинного импульса передается единица, а при помощи короткого – ноль, я смог выписать бинарные последовательности, передаваемые при нажатии кнопок на моем пульте.
Помимо сигналов, отвечающих за передачу нулей и единиц, существуют еще более длинные импульсы (на картинках выше эти сигналы не видны), завершающие передачу каждой бинарной последовательности.
Одна из последовательностей, которую я обнаружил: 110100000111010000110000.
На данном этапе я не стал вдаваться в подробности и выяснять, что означает каждый конкретный бит. Я лишь выяснил, что везде присутствует префикс 1101, возможно кодирующий модель розетки, и что последние 4 бита кодируют один из 5 наборов кнопок пульта (каждый набор состоит из пары кнопок включения/выключения розетки). Также я заметил, что существуют многократно повторяемые коды для одной и той же кнопки.
Я не знал, какое количество кодов используется для каждой из 10 кнопок, и не стал далее анализировать сигнал вручную. Намного интереснее было поиграться с передатчиком (модель Home Easy).
Я сделал некоторые пометки относительно длительности сигналов, поскольку эта информация могла пригодиться в дальнейшем.
Рисунок 5: Еще один участок масштабированного сигнала
Еще одна подсказка: не используйте функцию копирования+вставки в baudline. Я не вдавался в подробности, но, по-видимому, существует различие между реальными данными и тем, что хранит baudline. Если хотите поработать с выборками, сохраните всю информацию в файл и используйте утилиту dd для того, чтобы отрезать нужные вам части.
Изучение, тестирование и первые успехи
Не удивительно, что программное обеспечение, идущее в комплекте с передатчиком, не работало с моими розетками. На мою удачу, я нашел утилиту he853-remote, написанную на C++, которая позволила мне отослать некоторые первоначальные сигналы при помощи передатчика.
Код утилиты не особо сложен для понимания.
Используется библиотека 'HIDAPI' (libusb). Вначале происходит инициализация обработчика устройства на основе имени поставщика и идентификатора продукта, а затем отсылаются байты передатчику.
Первоначальный код заполняет массив uint8_t rfCmdBuf[32] информацией, а потом происходит передача порциями по 8 байт контроллеру HE853Controller::sendOutputReport(uint8_t* buf). Контроллер добавляет еще один пустой байт, и далее 9 байт отсылаются передатчику. После пересылки 4 блоков, код отсылает еще один специальный контрольный блок (также состоящий из 9 байт), и устройство начинает передачу данных.
Изучив документацию и внеся небольшие изменения на основе того, что я наблюдал в baudline, мне удалось сделать новую версию rfCmdBuf, успешно управляющую моими розетками.
// Frame number 1
rfCmdBuf[0*8+0] = 0x01;
// StartBit_HTime
rfCmdBuf[0*8+1] = (uint8_t) ((420 / 10) >> 8);
rfCmdBuf[0*8+2] = (uint8_t) (420 / 10);
// StartBit_LTime
rfCmdBuf[0*8+3] = (uint8_t) ((2200 / 10) >> 8);
rfCmdBuf[0*8+4] = (uint8_t) (2200 / 10);
// EndBit_HTime
rfCmdBuf[0*8+5] = (uint8_t) ((6200 / 10) >> 8 ); // set to 0 for learning mode
rfCmdBuf[0*8+6] = (uint8_t) (6200 / 10); // set to 0 for learning mode
// EndBit_LTime
rfCmdBuf[0*8+7] = 0x00;
// Frame number 2
rfCmdBuf[1*8+0] = 0x02;
// EndBit_LTime
rfCmdBuf[1*8+1] = 0x00;
// DataBit0_HTime
rfCmdBuf[1*8+2] = (uint8_t) (330 / 10); // 2250
// DataBit0_LTime
rfCmdBuf[1*8+3] = (uint8_t) (1190 / 10); // 750
// DataBit1_HTime
rfCmdBuf[1*8+4] = (uint8_t) (1190 / 10); // 750
// DataBit1_LTime
rfCmdBuf[1*8+5] = (uint8_t) (330 / 10); // 2250
// DataBit_Count
rfCmdBuf[1*8+6] = (uint8_t) 24; // how many bits to send
// Frame_Count
rfCmdBuf[1*8+7] = (uint8_t) 4;
// Frame number 3
rfCmdBuf[2*8+0] = 0x03;
// payload: 110100000111010000110100
rfCmdBuf[2*8+1] = 0xd0; // 11010000
rfCmdBuf[2*8+2] = 0x74; // 01110100
rfCmdBuf[2*8+3] = 0x34; // 00110000
rfCmdBuf[2*8+4] = 0x00;
rfCmdBuf[2*8+5] = 0x00;
rfCmdBuf[2*8+6] = 0x00;
rfCmdBuf[2*8+7] = 0x00;
// Frame number 4
rfCmdBuf[3*8+0] = 0x04;
rfCmdBuf[3*8+1] = 0x00;
rfCmdBuf[3*8+2] = 0x00;
rfCmdBuf[3*8+3] = 0x00;
rfCmdBuf[3*8+4] = 0x00;
rfCmdBuf[3*8+5] = 0x00;
rfCmdBuf[3*8+6] = 0x00;
rfCmdBuf[3*8+7] = 0x00;
В итоге получилась последовательность из 5*9 байтов:
00 01 00 2A 00 DC 02 6C 00
00 02 00 21 77 77 21 18 01
00 03 D0 74 34 00 00 00 00
00 04 00 00 00 00 00 00 00
00 05 00 00 00 00 00 00 00
Первоначальная задача решена. Теперь я могу управлять розетками при помощи устройства от стороннего поставщика. Однако решение не очень красивое, поскольку каждый раз приходится искать коды управления вручную. Я стал изучать, как в GNU Radio создаются новые модули.
Создание декодера сигнала от пульта
GNU Radio представляет собой набор библиотек для создания радиосистем под конкретные нужды. Для построения блок-схемы будущей системы предусмотрен очень удобный графический интерфейс GNU Radio Companion (GRC). После создания схемы происходит преобразование в код на python, который потом при необходимости можно доработать.
Существует множество руководств по установке и настройке gnuradion, большинство из которых рекомендуют использовать самую последнюю версию. Я бы порекомендовал вам то же самое. Компилируйте исходники. Поскольку вам нужна рабочая среда для написания собственных модулей, крайне важно иметь «нормальную» версию.
После того, как я потратил некоторое время на изучение документации, должен сказать, что мне не был понятен функционал большинства компонентов, поскольку я не знаком с основами радиотехники.
Однако моя страсть к экспериментам не ослабла.
Вначале я изучил возможности отдельных компонентов при помощи различных измерительных приборов (например, осциллографа). Получая результаты измерений и графики я примерно понимал, что ожидать от того или иного блока.
После некоторых экспериментов мне удалось собрать простейшую работоспособную версию.
Рисунок 6: Первая работоспособная версия простейшей радиосистемы
Значение N равное 30 я поставил случайно. Во всех последующих версиях модуля число N также было в районе 30 (плюс минус).
Как было сказано ранее, измерительные приборы очень помогали мне при изучении компонентов.
Ниже показан осциллограф, подключенный к радиосистеме:
Рисунок 7: Показания осциллографа, подключенного ко всей радиосистеме
Осциллограф, подключенный к компоненту Complex to Mag^2:
Рисунок 8: Показания осциллографа, подключенного к компоненту Complex to Mag^2
Осциллограф, подключенный к компоненту Low Pass Filter (низкочастотный фильтр):
Рисунок 9: Показания осциллографа, подключенного к компоненту Low Pass Filter
Осциллограф, подключенный к компоненту Keep 1 of N:
Рисунок 10: Показания осциллографа, подключенного к компоненту Keep 1 of N
Код моего модуля socket_dump_f, написанного на Python, считает «потоки» нулей и единиц. Огромное множество единиц означает импульс, разделяющий битовые последовательности (обозначается символом «X»).
Множество единиц представляют собой бинарную единицу в исходящем сигнале.
Несколько единиц представляют собой бинарный ноль в исходящем сигнале.
В итоге получается строка следующего формата: бинарный код X бинарный код X бинарный код …
Если детектируется длинная последовательность поступающих нулей, предполагается, что пакет завершен (если это был пакет).
Если в итоговой строке есть какие-либо данные, я выбираю коды между символами «X». Не слишком красивое, но рабочее решение.
Код модуля:
#!/usr/bin/env python
import numpy
from gnuradio import gr
class socket_dump_f(gr.sync_block):
"""
docstring for block socket_dump_f
"""
def __init__(self):
self.zero_counter = 0
self.one_counter = 0
self.last = 0
self.output_buffer = ""
gr.sync_block.__init__(self,
name="socket_dump_f",
in_sig=[numpy.float32],
out_sig=None)
def work(self, input_items, output_items):
in0 = input_items[0]
for item in in0 :
if item < 1 :
if self.last >= 1 :
if self.one_counter > 50 :
self.output_buffer += "X"
elif self.one_counter > 20 :
self.output_buffer += "1"
else :
self.output_buffer += "0"
self.one_counter = 0
self.zero_counter += 1
if self.zero_counter > 1000 and len(self.output_buffer) > 10:
a = self.output_buffer.split('X')
if len(a) >= 2 :
print "Received Code: " + a[1]
self.output_buffer = ""
self.zero_counter = 500
else :
self.zero_counter = 0
self.one_counter += 1
self.last = item
return len(input_items[0])
Если захотите импортировать код в GNU Radio Companion, вам понадобится описание проекта в формате XML.
sockethack_socket_dump_f.xml:
<!--?xml version="1.0"?-->
<block>
<name>socket_dump_f</name>
<key>sockethack_socket_dump_f</key>
<category>sockethack</category>
<import>import sockethack</import>
<make>sockethack.socket_dump_f()</make>
<sink>
<name>in0</name>
<type>float</type>
</sink>
</block>
Последние штрихи и оставшиеся вопросы
Теперь я могу легко выгружать все коды, передаваемые моим пультом.
Каждая порция передаваемой битовой последовательности состоит из 24 байт:
В байтах [0-3] закодирован идентификатор продукта (эти байты всегда неизменны).
В байтах [4-19] передается непонятное содержимое.
В байтах [20-23] закодирован идентификатор набора кнопок (эти байты одинаковые для каждого набора кнопок включения/выключения розетки).
Как только найдется немного свободного времени, я продолжу свои эксперименты, чтобы приоткрыть тайну загадочных битовых последовательностей.
Первое — находим постоянно, второе — ждем вас