Разбираем протокол новых датчиков Noolite

    image

    Привет!

    На прошлой неделе компания Ноотехника выпустила два первых датчика — движения и температуры и влажности для своей линейки дистанционного управления светом Noolite.
    К сожалению, работа с датчиком температуры и влажности с помощью родного USB-приёмника от Ноотехники не поддерживается, только через их Ethernet-шлюз.

    В нашем контроллере для домашней автоматизации Wiren Board Smart Home есть встроенный универсальный приёмопередатчик на частоту 433MHz, с помощью которого можно работать с многими устройствами. Это значит, что WB Smart Home может работать с устройствами Noolite напрямую, без использования USB-приёмников и передатчиков Ноотехники.

    Впрочем, чтобы работать со сторонними устройствами, для начала обычно требуется реверс-инжиниринг (т.е. взлом) протокола радио-обмена.
    Протокол Noolite, используемый в блоках управления освещением, мы вскрыли и разобрали в одной из предыдущих статей.

    В этой статье мы расскажем про реверс-инжиниринг протокола датчика PT111, обновлённую информацию об устройстве протокола Noolite вообще, а также покажем, как работа с датчиками выглядит в WB Smart Home.



    Датчики



    image
    (как всегда, спасибо dima117 и thinking-home.ru за очень оперативно предоставленные устройства для экспериментов.)

    Как уже было сказано, Ноотехника выпустила два датчика: движения PM111 и температуры/влажности PT111. По слухам, к выходу готовится ещё датчик температуры.

    PM111


    Датчик движения PM111 имеет PIR-сенсор и датчик освещённости. Датчик работает аналогично пультам Noolite.
    После детектирования движения датчик отправляет команду на включение, через определённое время после отсутствия движения — команду на выключение.
    Чувствительность датчика, длительность включения и порог освещённости регулируются.

    Датчик движения со снятой задней крышкой и защитным колпачком (картинки кликабельны):



    Кстати, интересная трассировка платы.

    Датчик температуры и влажности PT111




    Внутри используется датчик Sensirion SHT10.
    Датчик SHT10 довольно дорогой: оптовая цена в России — больше 400 рублей, китайцы продают их по $6. Видимо этим частично и определяется довольно высокая цена итогового продукта.

    PT111 умеет работать в трёх режимах: собственно в режиме передачи показаний, а также в режимах термостата и гигростата.
    В двух последних режимах PT111 эмулирует обычный пульт Noolite, отправляя команды включения и выключения при переходе температуры или влажности через заданный порог.

    В режимах термостата и гигростатата функциональность отправки показаний зачем-то отключается.

    В отличие от распространённых погодных датчиков Oregon Scientific, PT111 отправляет пакет с данными только при изменении показаний температуры или влажности, но не чаще, чем примерно раз в минуту.

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

    Протокол PT111



    Реверс-инжиниринг протокола как всегда начинается со сбора дампов пакетов.
    Как сказано выше, у PT111 есть недокументированная особенность, очень полезная для отладки — датчик отправляет показания при нажатии на кнопку привязки.

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

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

    Вот так выглядят приходящие с датчика данные, в том виде, как их принимает демон радио-модуля Wiren Board Smart Home:

    $ mosquitto_sub  -v -t /events/wb-homa-rcd/protocols/+
    
    
    /events/wb-homa-rcd/protocols/raw raw=aaaaaaaaaaaaaaa85559aaa5569a66a6aaa6a960aab3554aad34cd4d554d52c055555555555555555550aaaaad34cd55554cb4a155555a699aaaaa99
    /events/wb-homa-rcd/protocols/noo raw=1111110100000011111001001010001000000010000110	fmt=1	cmd=15	flip=1	addr=149f
    
    


    Представленный здесь пакет — это пакет привязки пульта, отправленный при нажатии на кнопку на датчике.

    Пакет с данными выглядит так (здесь уже приведена расшифровка):

    /events/wb-homa-rcd/protocols/raw raw=aaaaaaaaaaaaaaa1999aaa9a9aa6a6569555555a699a95aa9a6a4333355535354d4cad2aaaaab4d3352b5534d480000000000000000000000101ffff
    /events/wb-homa-rcd/protocols/noo addr=149f	temp=27.2	  lowbat=0	fmt=7	cmd=21	flip=0	humidity=58	raw=10101010000000100010000100010111001111111111111001001010001110000010010001
    


    Исходный пакет
    aaaaaaaaaaaaaaa1999aaa9a9aa6a6569555555a699a95aa9a6a4333355535354d4cad2aaaaab4d3352b5534d480000000000000000000000101ffff
    

    после отрезания хвоста и преабмулы и преобразования из манчестерского кода выглядит так:

    10101010000000100010000100010111001111111111111001001010001110000010010001
    


    «Расширенный» формат пакета


    Разбиваем сообщение на октеты, начиная с конца — в пакетах Noolite формат и размер пакета кодируются в конце пакета.

    10 10101000 00001000 10000100 01011100 11111111
    ( адрес, 16 bit ) 11111001 00101000 
    (формат) 11100000
    (чексумма) 10010001
    


    Значения последних 4 байт мы знаем: это 2 байта адреса блока, байт кода формата и байт контрольной суммы.
    В отличие от ранее известных пакетов, этот имеет код формата fmt=7.

    От пакетов с командами обычных пультов есть и другое отличие: количество первых бит.

    Вот так выглядит пакет с обычной командой выключения:

    #ch:2 off_ch             110000                                              10011111 10100100 00000000 10000100  # fmt=0
    


    Здесь перед байтами идёт блок из 6 бит. Первый бит всегда единица, следующий бит (flip) инвертируется каждую передачу (чтобы отличить дубликаты команды от новой команды), затем идут 4 бита, задающие код команды.

    В «расширенном» формате первый блок имеет 10 бит. Значение первого бита непонятно, для датчика PT111 он всегда равен единице. Следующий бит — это flip-бит. Под команду в этом формате отведено уже 8 бит, а не 4. Формат пакета (расширенный или обычный) определяется кодом формата из предпоследнего байта. «Расширенный» формат, кроме собственно датчика PT111 (fmt=7), замечен также в командах «switch mode» и «switch color» USB-передатчика Noolite с кодом формата fmt=4.
    В «расширенном» формате не используются коды команды из обычного формата, т.е. 0-15 — влезающие в 4 бита; коды команд начинаются с 16.

    В зависимости от формата пакета по-разному считается и контрольная сумма (последний байт). Функция подсчёта — crc8_maxim — остаётся одинаковой в обоих случаях, однако по-разному обрабатывается первый блок, не кратный восьми битам:

    if cmd < 16:
        data = chr(((cmd << 1) | flip_bit) << 3)
    else:
        data = chr(flip_bit << 7) + chr(cmd)
    


    Итоговый парсер пакета выглядит так (весь код есть в репозитарии):
    парсер пакета
        def parsePacket(self, packet):
            if len(packet) < 38:
                return
            remainder =  (len(packet) - 6 ) % 4
            if remainder != 0:
                packet += '0'*(4-remainder)
    
    
    
            crc = int(packet[-8:][::-1], 2)
            fmt = int(packet[-16:-8][::-1], 2)
            addr_lo = int(packet[-32:-24][::-1], 2)
            addr_hi = int(packet[-24:-16][::-1], 2)
            addr = (addr_hi << 8)  + addr_lo
    
    
            if fmt < 4:
                sextet_1 = packet[:6]
                flip_bit = int(sextet_1[1], 2)
                cmd = int(sextet_1[2:][::-1], 2)
                args_data = packet[6:-32]
            else:
                dectet_1 = packet[:10]
                flip_bit = int(dectet_1[1], 2)
                cmd = int(dectet_1[2:][::-1], 2)
    
                args_data = packet[10:-32]
    
    
            #~ print "fmt=", fmt, len(args_data)
            #~ print args_data
            if fmt == 0:
                if len(args_data) != 0:
                   return
            elif fmt == 1:
                if len(args_data) != 8:
                   return
            elif fmt == 3:
                if len(args_data) != 32:
                   return
            elif fmt == 4:
                if len(args_data) != 0:
                   return
            elif fmt == 7:
                if len(args_data) != 32:
                   return
            else:
                return
    
            if args_data:
                args = [int(x[::-1], 2) for x in utils.batch_gen(args_data, 8, align_right=True)]
            else:
                args = []
    
            return flip_bit, cmd, addr, fmt, crc, args
    
    



    Извлекаем данные


    (флип-бит, код команда) 10 10101000 
    (данные) 00001000 10000100 01011100 11111111
    ( адрес, 16 bit ) 11111001 00101000 
    (формат) 11100000
    (чексумма) 10010001
    


    Как видно выше, под данные в пакете PT111 отведено 4 байта.
    Посмотрим, как меняются эти данные от посылки к посылке (4 байта данных выделены):

    10 10101000 { 00101000 10000100 01010100 11111111 } 11111001 00101000 11100000 10000000
    10 10101000 { 00001000 10000100 01001100 11111111 } 11111001 00101000 11100000 11101101
    11 10101000 { 11000100 10000100 10000010 11111111 } 11111001 00101000 11100000 01010110
    10 10101000 { 01011000 10000100 10111100 11111111 } 11111001 00101000 11100000 11001010
    11 10101000 { 00011000 10000100 01011100 11111111 } 11111001 00101000 11100000 10101011
    11 10101000 { 01101000 10000100 11110100 11111111 } 11111001 00101000 11100000 00100000
    


    Видим, что четвёртый байт данных во всех посылках равен 0xFF (0b11111111). Видимо он является зарезервированным.

    Что такое третий байт данных? С большой вероятностью это целое 8-битное значение влажности: раскодированные таким образом значения получаются адекватными. Более того, значение этого байта никогда не превышает 100 (0x64), что можно проверить выставив на датчике максимальную влажность (например подышать в сенсор пару минут).
    При сильном локальном нагреве воздуха паяльником, значение байта падает до 5-10, что похоже на правду.

    Первый байт постоянно меняется при различных манипуляциях с датчиком — видимо он каким-то образом отвечает за температуру.

    Второй байт кажется не меняется. Проверим, не кодируется ли здесь статус разряженной батарейки (судя по описанию датчик передаёт предупреждение об этом по радио).

    Замкнём контакты одной батарейки тонким проводом, чтобы понизить напряжение:

    ( было  ) 10 10101000 11000000 { 10000100 } 00111100 11111111 11111001 00101000 11100000 10010101
    ( стало ) 10 10101000 11100000 { 10000101 } 00100110 11111111 11111001 00101000 11100000 00111000
    


    Видим, что изменился младший бит этого байта. Он кодирует статус заряда батарейки.

    Что может быть в оставшихся битах второго байта? Например там может отдельно храниться знак температуры, как в датчиках Oregon Scientific.

    Охлаждаем сенсор:

    11 10101000 00110000 { 10000100 } 00001100 11111111 11111001 00101000 11100000 1110011 # 12, 33
    
    (первый бит поменялся)
    10 10101000 10011000 { 00000100 } 01101100 11111111 11111001 00101000 11100000 1100101 (25)
    10 10101000 00111000 { 00000100 } 11011100 11111111 11111001 00101000 11100000 0110111 (28)
    11 10101000 10111000 { 00000100 } 11110100 11111111 11111001 00101000 11100000 10100001
    
    (долго лежал при -18C, первые четыре бита стали 1111)
    11 10101000 01111010 { 11110100 } 11000010 11111111 11111001 00101000 11100000 1000100 (3934 = -16.2C)
    
    (достали из холодильника, датчик нагревается)
    10 10101000 00101010 { 11110100 } 11011100 11111111 11111001 00101000 11100000 00100110 (-17.2C)
    10 10101000 10110001 { 11110100 } 01111100 11111111 11111001 00101000 11100000 10001011 (-11.5C)
    10 10101000 01000011 { 11110100 } 11100010 11111111 11111001 00101000 11100000 01010011 (-6.2C)
    10 10101000 11000111 { 11110100 } 00101010 11111111 11111001 00101000 11100000 01010100 (-2.9C)
    11 10101000 00001111 { 11110100 } 11011010 11111111 11111001 00101000 11100000 00101010 (-1.6C)
    
    (переход через ноль? 1111 => 0000)
    11 10101000 11100100 { 00000100 } 01111010 11111111 11111001 00101000 11100000 01011101 (3.9C)
    10 10101000 11000010 { 00000100 } 10000110 11111111 11111001 00101000 11100000 00000100
    


    Дополнительно греем сенсор:
    10 10101000 11000101 { 01000100 } 11000000 11111111 11111001 00101000 11100000 1100011 # 67.5
    10 10101000 00100110 { 01000100 } 01000000 11111111 11111001 00101000 11100000 1011001 #
    


    Видим что первые 4 бита второго байта ведут себя следующим образом:

    • При уменьшении температуры значение меняется сначала с 0b0001 на 0b0000 (порядок бит — обратный)
    • После заморозки до -18, значение становится 0b1111
    • При нагреве в какой-то момент значение меняется с 0b1111 на 0b0000
    • При сильном нагреве выше комнатной температуры значение меняется с 0b0001 на 0b0002


    Итого, самое простое объяснение, которое приходит в голову — это то, что в первых четырёх битах хранится кусок signed-значения температуры.

    Склеиваем эти четыре бита с 8 битами из первого байта и получаем 12-бит signed значение. Результаты декодирования позволяют понять, что это 12-бит знаковое значение температуры в десяты долях градусов Цельсия.

    Пример декодирования:

    1)
    bytes 1-2 = 11100100 00000100
    
    0000 00100111 (bin) = 39 (dec) = 3.9C
    
    2)
    bytes 1-2 = 00101010 11110100
    
    1111 01010100  (bin) = 3924 (dec). 
    3924 - 0x1000 = -172 = -17.2C
    


    Итоговый код декодирования температуры:
    temp = ((args[1] & 0x0F) << 8) + args[0]
    if temp > 0x7ff:
        temp = temp - 0x1000
    temp = temp * 0.1
    



    Итого, формат данных PT111

     temp/hum:
         flip, 2 bit -->    10 10101000 11100000 10000101 00100110 11111111 11111001 00101000 11100000 00111000
         cmd 8 bit              ---^    |           | ^ ^    ^        ^     addr_lo  addr_hi    fmt      crc
         temperature, signed, 0.1C  --> |-- 12 bit -| | |    |        |
         unknown, 3bit, 0b010  ------->---------------- |    |        |
         bat low, 1bit   ------------->------------------    |        |
         humidity, 8 bit ------------->-----------------------        |
         unknown, 8 bit, always 0xFF so far -------->------------------
    
    


    WB Smart Home



    В нативном веб-интерфейсе WB Smart Home датчики выглядят как-то так:



    Итоговый код работы с Noolite в нашем репозитории.

    Реклама

    Контроллер WB Smart Home доступен в продаже у нас на сайте.

    А ещё мы ищем программистов C/C++/Python (Linux)
    (лучше сразу всё из списка), разрабатывать ПО для наших железок для домашней и промышленной автоматизации.
    Работа включает в том числе добавление поддержки в WB Smart Home новых внешних проводных и беспроводных устройств, вроде описанных в этой статье.

    География: Москва — Долгопрудный, удалённая работа и неполная занятость тоже рассматриваются.
    Писать можно на info+hr3@contactless.ru.

    Метки:
    Бесконтактные устройства 39,84
    Компания
    Поделиться публикацией
    Похожие публикации
    Комментарии 10
    • 0
      Правильно ли я понимаю, что для работы с устройствами noolite ваш контроллер использует свои средства? Т.е. тот usb контроллер не нужен?
      • +1
        В нашем контроллере для домашней автоматизации Wiren Board Smart Home есть встроенный универсальный приёмопередатчик на частоту 433MHz, с помощью которого можно работать с многими устройствами. Это значит, что WB Smart Home может работать с устройствами Noolite напрямую, без использования USB-приёмников и передатчиков Ноотехники.
      • –2
        Ну вот зачем noolite придумали какой-то свой стандарт? им что не хватило существующих?
        • 0
          думаю всё дело в попытке привязать клиента к одному вендору.
          вангую что когда выйдет умный дом от эппл, у ноолайта появится конвертер(аппаратный или софтварный в езернет-шлюзе) реализующий яблочное апи
          • +1
            Каких, например? Ты много знаешь стандартных протоколов поверх 433 Mhz?
            • –3
              Дык значит не надо использовать такую частоту
              • 0
                Приёмопередатчики для этой частоты очень дёшевы и просты в использовании. Это одна маленькая микруха. С каким-нибудь IEEE 802.15 всё устройство вышло бы вдвое дороже.
                • –2
                  Меня даже другое больше поражает — отсутствие нормальной экосистемы у них — что бы все устройства подключить и сразу везде всё работало, можно было управлять с телефона и писать сценарии не кодом.
          • 0
            Кстати, интересная трассировка платы


            TopoR? Уж очень похоже.
            • +1
              Если вменяемых аналогов не появилось, то точно он.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое