Проще, чем MQTT? MQTT/UDP

    Хотел написать на эту тему подробную статью, но, очевидно, руки не доходят. Посему краткое сообщение. Я разработал и реализовал на нескольких языках в виде прототипного кода версию протокола MQTT под рабочим названием MQTT/UDP. Для нетерпеливых и тех, кому уже всё понятно и очевидно, код на Гитхабе

    Зачем

    Я живу в квартире, которая почти полностью управляется собственной системой умного дома несколько лет. Управление светом, климатом, датчики, лёгкая автоматизация, вот это всё.

    За это время я выяснил (да, впрочем, и до того понимал), что главное свойство систем УД — надёжность.

    Все системы с центральным узлом по определению ненадёжны. Отсюда — желание получить интерконнект компонент системы (а их в реальном умном доме много) без использования какого-либо центрального хаба.

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

    Логичный вывод — UDP бродкаст является идеальным инструментом.

    (Да, я предполагаю, что внутренняя сеть квартиры закрыта файрволлом.)

    Что в плюсах:

    Невероятно простая реализация. Минимальный микроконтроллер с крошечной памятью сумеет послать UDP-пакет. Для датчика даже приём UDP не нужен.

    Предельно низкая латентность. Нет форварда в центральном хабе, время доставки UDP пакета -практически минимальный квант времени в современной системе.

    Нет центральной точки отказа в хабе/брокере. Рассмотрим простую схему: два датчика температуры, контроллер тёплого пола, контроллер батарей отопления. С применением MQTT/UDP отказ любой части такой системы не приводит к отказу системы в целом.

    Низкий трафик в сети. Ниже некуда. TCP и подтверждения только добавляют трафика, но не добавляют надёжности. Отказавший датчик не заработает от получения TCP ACK. А отказ мы и так выявляем по отсутствию обновлений.

    Ненадёжность самого протокола UDP несущественна — датчики всё равно обновляют данные регулярно, и пропадание отсчёта совершенно незаметно, а пропадание команды с, например, выключателя визуально подтверждается несработкой целевой системы. Что делает человек? Щёлкает ещё раз. Тем не менее, это — главный ограничитель применимости протокола. Идеален для повторяющихся опросов.

    Не нужна конфигурация системы. Никому не нужно прописывать адрес брокера, хаба и т.п. Впрочем, всё равно нужна конфигурация датчика — требуется прописать идентификатор датчика. Но при переносе других частей системы на другие сервера переконфигурация ответных компонент не потребуется.

    Отдельно интересно, что эта модель обмена данными хорошо ложится на натурально бродкастные среды, такие как радиоканал или RS/485. Я с этим не экспериментировал, и протокол в таких средах нуждается в контроле. Логично здесь применить CRC16 от modbus, кстати.

    Минусы также очевидны: надежность доставки определяется только хардвером и трафиком в сети, ну и если в сеть пробрался враг, то протокол повержен сразу. Правда, будем откровенны, взлом иных типичных протоколов умного дома — дело секунд, так что это — спорный недостаток MQTT/UDP. Ещё один неочевидный минус — максимум один приёмник на IP-адрес.

    Что сделано и лежит в репозитории в исходниках:

    • Реализации клиента/сервера на нескольких языках. Есть Си, Питон и Ява. Я не осилил Lua (не смог поставить всё, что нужно, как вы в этом живёте?) и Codesys (не смог собрать пакет, кто придумал этот язык?).
    • Минимальный гейт в традиционный MQTT на Питоне
    • Примитивный инструмент для отображения трафика MQTT/UDP в сети

    Что бы я сделал, будь у меня ещё время:

    • Написал бы модуль для openHUB.
    • Сделал бы вариант протокола на JSON на другом порту и конвертор в основной формат и порт. Или гейт в обе стороны.
    • Сделал бы болванки реализации для основных платформ. Для Arduino я сделал подход к весу и реально протестировал код, но не оформил всё как следует. Для Малины годятся тестовые примеры на Питоне.
    • Сделал бы цифровую подпись и шифрование, но неясно, как.
    • Мультикаст.

    На сём благодарю, добро пожаловать в репозиторий

    PS: Аналогичный подход, но уже с мультикастом, описан в этой статье.
    Поделиться публикацией
    Комментарии 37
      0
      Все системы с центральным узлом по определению ненадёжны.

      Как-то самолетчики и автопроизводители об этом не в курсе. Сделать два хаба в избыточной схеме и получить заветные девятки надежности ИМХО проще, чем делать свой протокол.

        +1
        ТУ154: три гидросистемы, каждая со своим приводом, вся электрика троирована, причём каждый провод защищён автоматами с двух сторон. Ключевые приборы на панели дублированы, причём некоторые исполнены в вариантах с разными физическими принципами.

        Знают.

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

        Это всё решаемые проблемы, но, тупо, зачем? Предложенный метод проще.

        Я проектировал сложные протоколы и управлял сетью с избыточностью через топологию. Имею опыт. Хочется простого.

          0

          ТУ154 был разработан в 1968 году. Вы действительно думаете, что современный Боинг и Аирбас все еще исповедуют его принципы построения надежных систем управления?


          Взгляните хотя-бы на Fly-by-Wire — там стоит по три-четыре вычислителя с кворумом, каждый со своими датчиками и исполнительными устройствами. И везде за главную функцию отвечает центральный узел — вычислитель


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

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


          Тупо и решает всю проблему с дублированием.

            0
            — Описанная Вами схема отличается от ТУ только названием компонент. Единственного центрального узла всё равно нет.

            — Можно сделать дома холодный резерв. Но ЗАЧЕМ? И — кто будет следить за тем, что он готов и работает? И кто будет управлять реле? По какому признаку?

            И, главное, цель-то в чём такого усложнения?
              0
              — Описанная Вами схема отличается от ТУ только названием компонент. Единственного центрального узла всё равно нет.

              Как это нет? Там десятки, если не сотни датчиков, на основании данных от которых центральный вычислитель по сложному алгоритму определяет, что нужно делать исполнительным устройствам. Если этот вычислитель накрывается — весь канал (1 из четырех) считается неисправным. Если бы это была децентрализованная система, то датчики должны были бы непосредственно общаться с исполнительными устройствами, а такого в самолетах нет.


              Можно сделать дома холодный резерв. Но ЗАЧЕМ? И — кто будет следить за тем, что он готов и работает? И кто будет управлять реле? По какому признаку?

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


              Цель такого усложнения такая же, как и вашего — получить более надежную систему. И в моем предложении не надо придумывать никакие протоколы.

                0
                Реализуйте, посмотрим.
        +1
          0
          И такой еще линк может быть полезен: coap.technology
            0
            Нет. Спасибо, интересно. Тематика та же и подход к реализации сходный.
            +2
            Это всё хорошо когда датчики некритичные. И без обратного канала нет определения коллизий, могут наступать такие моменты синхронной работы датчиков что они будут очень долго находится в состоянии коллизий. С выключателем света очень наглядный пример. Сначала несрабатывание будет раздражать, потом просто начнёт бесить а с ростом количества датчиков и исполнителей всё будет становится хуже. Тут может помочь не центральный узел а какой-то простой арбитр, но для этого нужен хоть какой-то обратный канал. Например, выдавать в эфир сигнал синхронизации(передаёт номер секунды начиная с начала суток, например, день недели, дату, время дня день-ночь-сумерки и т.д.) каждую секунду, а датчики будут отсчитывать на какой секунде или даже миллисекунде внутри этой секунды им позволено отвечать(привязать это к IP-адресу, например). В итоге полная идиллия, всё работает четко и синхронно. Причем даже прослушивать эфир не нужно всё время — мы знаем что если получили синхронизирующий сигнал то следующий будет через секунду +-точность поддержания собственной частоты, приёмник включается за миллисекунду до предполагаемого времени и принимает синхронизирующий сигнал, корректируя собственный внутренний источник времени а первоначальный импульс можно и подождать секунду, или включаться с рандомной задержкой в надежде когда-нибудь поймать синхронизацию.
              0
              Можно ещё проще — каждый отправитель раз в N секунд отправляет апдейт своего состояния.

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

              А вот датчики температуры — есть потребность отдавать в несколько мест.
                0
                Это не решает проблемы. Будут коллизии, и чем больше датчиков и чем чаще опрос тем больше коллизий, поэтому нужна синхронизация или детектирование коллизий самим датчиком. А выключатели… их просто можно развести на другой частоте, датчики мешать не будут.
                  0
                  Нету коллизий в реальной сети. Не с чего им быть. Во-первых, потому что она давно не шина, а свитч, причём store/forward, во-вторых потому что для коллизий нужен довольно серьёзный трафик, которому в сегменте умного дома браться не откуда, и, наконец, потому что проведённые тесты вообще не показали пропаданий пакетов. На вполне живой сети в которой живут три десятка хостов, включая телевизоры, которые сигнал получают только через сеть, и никак иначе.
                    0
                    Что-то я уже запутался. У вас каждый датчик подключается по Ethernet используя для этого контроллеры с тактовой частотой ниже даже чем символьная скорость в линии?
                    Я почему-то думал что речь идёт о радиолинии, с общей средой. Для небольшого УД это логичней чем использовать Ethernet в каждом датчике и тащить к ним провода. Так-то да, если использовать обычные свичи то все будет работать. Но стоимость… Кстати в дешёвых свичах коллизии есть, просто клиентские Ethernet умеют детектировать коллизии и есть механизм повторной передачи так чтобы избегать повторной коллизии(ведь этим двум девайсам всё ещё надо передать свои данные после обнаружения коллизии), поэтому при маленьком трафике кажется что их нет. А свич с функцией store/forward уже больше похож на роутер, с соответствующим ценником.
                      0
                      По идее радиоканал кажется разумным. Реально самые дешёвые и технологичные варианты — витая пара (+ элементарный POE) и WiFi (ESP за копейки).
                        0
                        витая пара, да ещё с инжекторами PoE и свичи подерживающие PoE это не очень то и дёшево.
              • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Типовой масштаб сети — квартира. Для CAN это многовато. Да и локалка в квартире уже есть. И устройства с ethernet/wifi вполне массовые. И стоят уже.

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

                  Хранить на каждом передатчике карту получателей, собирать подтверждения, увеличить в разы трафик в сети, поднять требования к передатчику. Зачем? Датчик температуры всё равно отправит свою температуру через минуту снова.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      Ну да, поскольку там арбитраж на адресе, младший адрес побеждает и коллизии нет, это изящная схема. Но, опять, зачем?
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Даже при невероятной тысяче датчиков в квартире и отправке данных раз в секунду этот потолок недостижим. Так что, в целом, надеюсь выжить.

                          И ещё. При дроп рейте пакетов порядка нескольких процентов TCP встаёт колом и не работает. Для датчика температуры потеря каждого второго отсчёта (дроп рейт 50%) несущественна.

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

                          При перегрузке и потере пакетов любой TCP протокол ляжет, а UDP будет прорываться, хоть и с кровью.

                          Внезапно, UDP… надёжнее для этого применения.
                            0
                            Внезапно, UDP… надёжнее для этого применения.

                            Я чего-то не пойму ваше понятие надежности. Центральный узел априори не надежный, а потеря пакетов — фигня, жить можно?
                            Система связи в системах автоматизации должна быть надежна. Вашему датчику температуры, возможно и пофиг, а событийному выключателю далеко нет. Не дошло от него сообщение — свет не включился. Надежно?


                            При перегрузке и потере пакетов любой TCP протокол ляжет

                            И это правильное поведение. Вы должны получить понятное состояние системы, а не метание между "работает/не работает".


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

                            В реальной свитчованой сети нет приоритетов и ваш UDP трафик будет крутиться вместе с теми же более надежными TCP/IP пакетами. У вас домашняя сеть для УД изолирована от всего остального? Компьютеров, телевизоров, телефонов? Если нет — проведите тест — попробуйте перекинуть два больших файла по сети через один свитч и проверить надежность вашей MQTT/UDP. Странно, но TCP/IP не ложится при этом, правда?

                              0
                              Для детектирования отказа датчика необходимо и достаточно знать о том, что пакеты от него не приходят. TCP не привносит в этом плане ничего. Напротив — напомню, при 50% дроп рейте TCP ляжет насмерть, а UDP будет проходить с вероятностью 50%.

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

                              Ну и да, я пробовал. :) Я пока умозрительно не вижу ситуации, в которой бы UDP начал терять 100% пакетов, а TCP работал бы. Вижу ситуацию, в которой TCP лежит, а UDP доносит половину.

                              Ну и практические ситуации, когда DNS работает, а HTTP не бегает мне тоже вполне встречались. Именно что когда дроп рейт становится ощутим. Даже при 10% потери пакетов TCP не жилец в реальности.

                              Вы как-то религиозно упираетесь в «защищённость» TCP — у неё есть своя применимость и свой порог осмысленности. Для передачи периодической информации, когда следующий отсчёт обесценивает предыдущий, TCP тупо вреден.

                              Кстати, это хорошо понимают люди, которые разрабатывают голосовые и видео протоколы реального времени. Если кадр N+1 пришёл, то кадр N тупо не нужен, а следовательно не нужен и TCP.
                                0

                                Так у вас периодическая информация или нет? По вашей же статье вы пишете, что у вас куча сообщений имеет событийный характер, а не периодический.

                      +1
                      Массовость и фактическое наличие — это сильный аргумент. А вот с масштабом — нет, адресов просто завались — 11 бит или 29 бит.
                      И ещё, в CAN не арбитраж, а отсутствие коллизий — передатчик автоматически определяет, что он может помешать другому передатчику и перестаёт мешать. Исходное сообщение от первого передатчика — не портится.
                      Карту получателей на каждом передатчике хранить не нужно, подтверждения тоже не нужно. Нужно только озаботиться, чтобы адреса у разных передатчиков не совпадали и всё.
                  0
                  А какой контроллер тёплого пола используете? (интересуюсь тк выбираю сейчас как раз)
                    +1
                    Овен ПЛК 110-30. датчики температуры pt1000 через овеновский же аналоговый модуль (3 шт), управление полом и частью радиаторов.

                    PID-регуляторы в итоге писал сам, это оказалось проще, чем постичь штатные, да и легче было сделать связанный алгоритм руления полом и радиаторами.
                    0

                    Идея отличная.
                    Можете еще глянуть на эту статью https://habr.com/post/240053/ — там я описывал сходную идею на UDP multicast (не broadcast)

                      0
                      Спасибо, прочитаю.
                        0
                        Мы с Вами рассуждали совершенно идентично и пришли к почти идентичной модели. :) Мультикаст я тоже рассматривал, но пока отложил просто потому, что хотелось обеспечить реализацию для популярных в смарт хоум языков, а что там с мультикастом — навскидку неочевидно. Я вон в lua до обычного UDP-то не достучался. :)
                        +1

                        1) У Вас в любом случае есть центральный узел — роутер или иное устойство, которое проводную и/или беспроводную сеть поддерживает, разве нет? Упадёт он и никуда никакие UDP пакеты не дойдут.


                        1. Насколько вероятен отказ центрального хаба? Те же роутеры работают месяцами-годами. Допустим, что будет программный отказ (повиснет). Тогда достаточно вочдога, который его перезагрузит. А какая вероятность аппаратного отказа?
                          0
                          Вы правы. То есть, конечно, можно и сеть резервировать, но никто этого дома делать не будет. Тут плюсом является тот факт, что отказ локалки пользователь так и так заметит по другим признакам и ситуацию будет исправлять. А отказ брокера или компа под ним, поверьте, можно не видеть месяцами. Из практики.
                            0
                            То есть, конечно, можно и сеть резервировать, но никто этого дома делать не будет.

                            Сразу скажу, что проводную сеть резервировать очень дорого. С STP/RSTP в одноранговой домашней сети особо много не сделаешь, а полное дублирование по типу PRP/HSR требует соответствующего железа.


                            С этой точки зрения безпроводка — отличная штука. И мой подход выше тоже. На ту же переключаемую розетку вешаем еще и беспроводной роутер с теми же настройками и получаем резервную сеть.


                            У меня, правда, почти все датчики и устройства не на Wi-Fi, а на Z-wave, поэтому протоколами вообще не заморачиваюсь и резервирование центрального контроллера Z-wave вообще плевое дело.

                          0
                          Ещё проще было использовать готовый прoтокол для UDP MQTT-SN, он проще чем MQTT и изначально заточен под простые устройства с авто настройкой.

                          Как сoбственно мы и сделали в своём проекте Enviriot
                          Есть устройства ethernet/rs485/rf, сервер работающий под виндой либо линуксом.

                          На случай полного отказа центральной системы был прикручен маленький PLC в сами устройства. На сервере есть «большой» PLC с FBD либо JavaScript.

                          Гарантия доставки пакетов это часть протокола для QOS 1/2.
                          Даже по RF на 868/443MHz всё работает достойно.

                          Дублирование сервера то-же часть протокола. Устройства автоматически находят активный гейт/брокер.

                          А модификация протокола который изначально построен по принципу устройство — определенный гейт — это сова на глобусе.
                            –1
                            Он опять сложный. Причём — бессмысленно сложный.

                            «The CONNECT message is split into three messages. The two additional ones are optional and used to transfer the Will topic and the Will message to the server.»

                            Нафиг коннект! Есть имя топика в пакете? Есть пакет? Есть получатель, которому нужен этот топик? Отлично.

                            Никогда в жизни ещё не видел, чтобы IBM хоть что-то сделал просто. :)
                              0
                              Достаточно Connect/ConnAck, Will опциональная фаза. И предназначена только для того, чтобы те, кому это интересно, узнал о потере связи с нодой. Что порой бывает полезно.

                              И кстати, в самом MQTT фазу Connect опустить не возможно. И адрес гейта надо просто знать, если гейт упал, упс. Представьте как весело в сети с DHCP.

                              А в MQTT-SN можно, QOS — 1 в помощь.

                              Выглядит следующим образом:
                              1. Нода с пустой прошивкой шлет: Search Gateway.
                              2. Гейт отвечает: GWInfo
                              Если адрес гейта нам известен первые 2 пункта можно опустить.
                              3. Нода шлет Publish QOS-1, ответ на сообшение не требуется.

                              Обмен с CONNECT'ом
                              1. N. SearchGW
                              2 G. GwInfo
                              3. N. Connect, w/o will
                              4 G. Connack
                              4 N. Publish with QOS 1
                              5 G. PubAck
                              Если ноде что то надо от сервера, т.е. если сервер должен посылать ноде какие либо данные.
                              6. N. Subscribe *
                              7. G. SubAck

                              Всё, мы взяли ноду с пустой прошивкой, она САМА нашла к кому подключаться. И у нас уже работает двухсторонний обмен.

                              Собственно мы пилили сперва свой протокол, потом наткнулись на MQTT-S тогда ещё.
                              Оказалось что мы практически скоммуниздили один в один, и для совместимости обошлось практически только правкой дефайнов.

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

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