Реверс-инжиниринг протокола и контрольной суммы (CRC) для Bonaire Comfort Control

Статейка эта, по сути, сводится к паре довольно полезных утилит, которые я обнаружил в процессе разбора протокола связи, имеющегося у меня пульта дистанционного управления. Одна (reveng) из них позволяет, путем хитромудрого перебора подобрать подходящий алгоритм контрольной суммы. Другая (pycrc) генерирует готовый С код, реализующий конкретный алгоритм, задаваемый пользователем.

Но, обо всем по порядку. Есть у меня дома система центрального испарительного кондиционирования воздуха, весьма популярная в Австралии. Управлению ею производится с помощью пульта по радиочастоте. Пульт один, часто теряется. Кроме того, как я упоминал в своих статьях про умный дом, я постепенно делаю «умными» все «не-умные» устройства, для централизованного управления всем и вся через смартфоны. Настала очередь испарительного кондиционера.

Итак, есть вот такой вот симпатичный пульт Bonaire Comfort Control, которому ни много ни мало 20 лет.

Bonaire Comfort Control

Вопрос в том, как наладить взаимодействие между кондиционером и openHab’ом, который управляет домом. Путей, в принципе, существует три. Первый, и самый простой, это припаяться к кнопкам имеющегося пульта, и “нажимать” их цифровым, так сказать, способом. Но тогда пульту каюк. Второй путь, это не курочить имеющийся пульт, а купить еще один, и припаяться к нему. Но покупка еще одного обошлась бы по меньшей мере в 300-400 баксов. Эти пульты давно не выпускают, они в большом дефиците, поэтому и продаются весьма недешево.

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

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

Итак, попытавшись послушать дешевеньким SDR‘ом передачи на часто используемых частотах в 370 и 433 МГц, и не услышав там ничего, решено было вскрывать пульт и смотреть, что там к чему.

Bonaire Comfort Control

Вскрытие показало, что на борту имеется приемник RFM RX1100 с антенной в виде петли из провода, и передатчик RFM HX1002 с антенной, напечатанной прямо на плате. Ради интереса я отпаял антенну приемника, и, в последствии, даже перерезал дорожку питания. Ничего не изменилось, и пульт продолжил управлять кондиционером, как и прежде. Погуглив, стало ясно, что этот конкретный пульт – универсальный, и умеет общаться с целой кучей разных систем кондиционирования и отопления, одновременно. По всей видимости, общение с некоторыми системами двустороннее. Но не в моем случае. Это упрощало задачу, и означало, что пульт просто отсылает команды и надеется, что они будут выполнены.

Команд, кстати, было по большому счету три. Вкл-выкл, вентиляция или охлаждение, и интенсивность (читай скорость вентилятора). Команды вкл-выкл выполняются сразу после нажатия кнопки на пульте, а остальные лишь спустя 15 секунд после того, как было произведено последнее нажатие. Это наводило на банальную мысль, что, как и в случае с современными кондиционерами, пульт отправляет на устройство не последнюю введённую команду, а целый набор данных, полностью описывающих требуемый режим работы. Забегая вперед скажу, что так оно и оказалось.

Поскольку требовалось не только разобраться с протоколом, но и найти способ передавать радиосообщения со “своего”, самопального устройства, я взялся за чтение документации по передатчику. Упомянутый передатчик RFM HX1002 на борту пульта весьма серьезный, чуть ли не военный передатчик на 303.825 МГц, использующий простецкую “вкл-выкл” модуляцию (OOK, on-off keying).

Попытки найти такой-же или аналогичный передатчик с адекватной ценой не увенчались успехом. Поэтому, в закромах был найдет вот такой архи-простой и дешевый комплект радиосвязи для Arduino-подобных плат.

Приемник и передатчик для Arduino
Приемник (справа) и передатчик (слева) для Arduino на 433 МГц

На передатчике стоял кварц на 433 МГц. Не беда. За какие-то копейка заказал десятку кварцев на 303.825 МГц в аналогичном форм-факторе, и припаял один вместо родного 433тьего.

Затем отрезал, аккуратно, скальпелем дороги питания и сигнала на родной передатчик на пульте, и вместо него припаял «поддельный». И пульт работал. Отлично работал, без каких-либо проблем.

Франкен-пульт

Ну что-ж, замечательно. С железом все ясно. Настала пора разбираться с протоколом связи. Посмотрел осциллографом:

Осцилограмма передачи

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

Подключаем, и записываем как можно больше разных трансляций. Каждую запись я сохранял в отдельный .CSV файл и давал ему понятное имя, полностью описывающее посланную команду. Например, “переключение со скорости вентилятора 3 на 4 в режиме охлаждения” и т.п.

ГУЙ логического анализотора
ГУЙ логического анализатора

Дальнейшую обработку данных можно вести в любом удобном для вас пакете инструментов. Я предпочитаю MATLAB.

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

Все записанные данные разом
Все записанные данные разом

Далее я отбросил 4 последних повторения от каждой записи, оставив только первое, и опять отобразил все трансляции одну на другой. Стало очевидно, что часть пульсов общая для всех записей. По всей видимости это заголовок пакета.

Первое повторение данных каждого записанного вещания
Первое повторение данных каждого записанного вещания

Ближайшее визуальное рассмотрение индивидуальных трансляций перемешанное с сильным желанием увидеть 8ми-битные составляющие, позволило сделать вывод, что сообщение состоит из 6 байт. Первый, второй и пятый не меняются, второй и третий определяют режим работы кондиционера, и последний, шестой, по всей видимости является контрольной суммой. Кроме того, трансляция каждого байта начинается со стартового бита. Наконец, особый стартовый бит присутствует перед началом всего набора данных.

Визуальная оценка строения пакета данных
Визуальная оценка строения пакета данных

Далее, путем оценки длительности отдельных битов специальным скриптом, все записанные сообщения были переведены в цифровой вид. Анализ, теперь уже точных данных, подтвердил предположения о назначении каждого байта. Стало понятно, как именно формируются байты установки скорости вентилятора и режима работы кондиционера.

А именно: первый, второй и пятый байты всегда 0x45, 0x4E и 0x60 соответственно. Байты я считаю слева направо, в порядке вещания, начиная от 1 и до 6. Третий байт 0x08 для охлаждения, 0x04 для вентиляции и 0x0C для выключения установки.

Четвертый байт определяет скорость вентилятора. Всего 11 скоростей, 0x0A, 0x8A , 0x4A , 0xCA , 0x2A , 0xAA, 0x6A, 0xEA, 0x1A, 0x9A и 0x5A от самой низкой до самой высокой.

Кстати, я полагаю что один из неменяющихся байтов, на самом деле может меняться, и несет в себе информацию о “канале” вещания пульта. Инструкция говорит, что в редких случаях, когда в соседних домах оказывается установлена одинаковая система, и пульты начинают управлять обеими, можно переткнуть джамперы на пульте и на кондиционере и перейти на другой “канал”. Мне было лень заниматься этой эквилибристикой, поэтому у меня три байта постоянны.

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

Я попробовал конечно, вручную поковыряться, в надежде, что алгоритм окажется очень простым, вроде банального XOR побайтно, или что-то подобное. Но ничего не сработало. Стало ясно, что алгоритм может и не архисложный, но вот так, за 5 минут его не подобрать.

Разумеется, решил погуглить информацию по теме, и в момент нашел утилиту для командной строки, разрабатываемую энтузиастом на протяжении последних, по меньшей мере 15ти лет. Утилита reveng путем хитрого перебора находит алгоритм для вычисления известной контрольной суммы для известного пакета данных. Шансы на успех существенно увеличиваются, если скормить утилите несколько пакетов с контрольными суммами сразу, а также указать другие параметры, такие как размер контрольной суммы и т.п.

В данном случае я вызвал утилиту следующей командой:

reveng -w 8 -s 454E080A6066 454E0CC06002 454E040A6087

Где “-w 8” указывает на длину контрольной суммы в 8 бит, а остальное это известные пакеты данных с правильной суммой в них.

Результат утилита выдала такой:

width=8 poly=0xfd init=0xcc refin=true refout=true xorout=0x00 check=0x80 residue=0x00 name=(none)

Нужно сказать, что для утилиты существует неплохой help на сайте. Но написан он профессионалом для профессионалов, и у меня заняло немало времени, чтобы хотя-бы поверхностно разобраться что к чему. Настолько поверхностно, что я не был уверен, что точно интерпретирую результат работы утилиты, пытаять преобразовать его в код. Т.е. мне было понятно, что уже со второй попытки скармливания пакетов утилите, она нашла решение. Но вот точно понять, как реализовать его в коде было ясно не полностью.

Тем не менее, опираясь на результат работы reveng, я накатал пару функций в MATLAB’е, параллельно гугля и пытаясь понять, где-же я ошибся, и почему контрольная получавшаяся контрольная сумма ошибочна. Как выяснилось в дальнейшем, я был очень близок, но это неважно. Потому что пока гуглил, нашел еще одну утилиту pycrc, которая просто генерирует готовый С код, вычисляющий контрольную сумму по заданным параметрам. Причем входные параметры pycrc практически идентичны тем, что выдает в качестве результата reveng. Что на самом деле логично, поскольку данный способ является стандартным способом описания алгоритмов вычисления контрольных сумм.

Вызов, заставляющий pycrc вычислить контрольную сумму для сообщения 454E042A60(2F), где 2F и есть контрольная сумма:

pycrc.py --width 8 --poly 0xfd --reflect-in True --xor-in 0xcc --reflect-out True --xor-out 0x00 --algorithm bbf --check-hexstring=454E042A60

Ответ:

0x2f

То есть pycrc все понял и сделал верно. Теперь заставим его сгеренировать код:

pycrc.py --width 8 --poly 0xfd --reflect-in True --xor-in 0xcc --reflect-out True --xor-out 0x00 --algorithm bbf --generate h -o crc.h

pycrc.py --width 8 --poly 0xfd --reflect-in True --xor-in 0xcc --reflect-out True --xor-out 0x00 --algorithm bbf --generate c -o crc.c

Получаем 2 файла, crc.h и crc.c. В них и код и пояснения что к чему, и в каком порядке и зачем вызывать функции. Я быстренько накидал код на С и проверил сгенерированные файлы – все работает, попадание в яблочко.

Утилита, кстати, довольно умная, и позволяет задать желаемый стиль вычисления контрольной суммы в генерируемом коде. К примеру это может быть математический способ. Он наглядный, но медленный. Наиболее быстрый способ основывается на таблицах поиска. Курите мануал, товарищи.

А мне осталось дело за малым – написать код для микроконтроллера, который будет принимать команды от openHab’а через MQTT, составлять соответствующие пакеты данных для кондиционера и отправлять их на него, просто дергая ножкой управляющей моим рудиментарным передатчиком.

Я надеюсь с этим закончить в ближайшие пару-тройку недель, и следом опубликую материал.

На всякий случай прикладываю архив с данными, кодом и описанием протокола.



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