Меня зовут Андрей Соловьев. Я - технический директор в компании КЕДР Solutions. Наша команда специализируется на разработке электроники и встроенного ПО для решений Интернета вещей. К числу таковых относятся и системы “умный дом”. Здесь я поделюсь нашим опытом разработки хаба для системы с BLE-датчиками, расскажу о том, с какими проблемами мы столкнулись и как проект менялся в ходе работы.
Запрос заказчика
К нам обратился клиент с задачей разработать хаб для системы умного дома, использующей BLE-датчики. В своей основе система довольно стандартна: датчики передают данные по BLE на хаб, тот обрабатывает информацию и отсылает данные на бэкенд (сервер AWS). В качестве фронтенда выступает мобильное приложение.
Но решение предназначалось не для обычного жилья, а для домов на колесах. Это потребовало ряда решений, нетипичных для “умных домов”, о чем я скажу позже.
Первичная архитектура устройства
Сбор данных с датчиков
Системы “умный дом” можно создать на базе Raspberry Pi. Однако решение предназначалось для коммерческой реализации и на базе “Малинки” было бы слишком дорогим. В то же время хаб должен был отвечать за связь с сервером, за связь с периферией, а также за другой функционал. Чтобы снизить нагрузку, было решено разделить функции между двумя контроллерами.
В качестве “мозга” системы мы использовали ESP32S3. Он отвечает за общение с сервером, контроль и анализ данных, реакцию на данные и управление исполнительными устройствами. За общение с BLE-датчиками отвечает nRF52840. Изначально заказчик пожелал использовать несколько достаточно простых BLE-датчиков. Вся передаваемая ими информация помещалась в широковещательные пакеты (ADV-пакеты), поэтому хабу не требовалось устанавливать соединение с датчиками. nRF52 просто фильтрует получаемые от сенсоров ADV-пакеты по MAC-адресам, пакует данные во внутренний формат согласно протоколу (о нем будет сказано ниже) и отправляет их на ESP32, который их парсит.
Добавление BLE-датчиков
Чтобы хаб не получал данные со случайных BLE-маячков, устройства фильтруются по MAC-адресам. Список адресов содержится в мобильном приложении, которое управляет системой. При включении и настройке приложение через сервер отправляет команду на ESP32 добавить список MAC-адресов. Контроллер передает список на nRF52, чтобы тот мог фильтровать все входящие ADV-пакеты.
Хранение и передача данных на сервер
Чтобы на стороне ESP32 не аккумулировалась очередь из пакетов, старые данные заменяются новыми. Таким образом, на контроллере хранятся только актуальные данные с временной меткой их получения. Информация с разных сенсоров передается с разной периодичностью: с одних – раз в секунду, с других – раз в 30 секунд (в зависимости от типа датчика).
Данные с хаба на сервер передаются в формате JSON. Для сериализации данных из ADV-пакета в JSON на ESP32 используется библиотека nlohmann/json.
Сетевые интерфейсы
Хаб может подключаться к серверу двумя способами – через Wi-Fi или через LTE. Для подключения к Wi-Fi в хаб нужно прописать credentials сети. Для настройки Wi-Fi Provisioning для ESP32 производитель контроллера сделал специальную утилиту. Однако для удобства пользователей это API было добавлено в мобильное приложение, которое управляет хабом.
Связь по LTE и получение координат GPS
Поскольку данная система создавалась для использования в домах на колесах, заказчик пожелал, чтобы хаб также умел определять свое местоположение по GPS. Реализовать эту функцию мы решили с помощью модема SIM7080G, т.к., согласно документации, он обеспечивал как получение координат по GPS, так и обмен данными через LTE. Однако на испытаниях выяснилось, что у этой модели GPS и LTE работают через один и тот же ВЧ-тракт и выполнять обе функции одновременно он не может. В документации к устройству открыто об этой особенности не заявлялось.
Впрочем, мы смогли реализовать псевдосовместную работу GPS и LTE. Модем периодически включал GPS приемник для получения координат. На это время подключение к серверу терялось. После получения координат модем вновь переподключался к серверу и продолжал штатную работу.
На этом этапе загрузка хаба была небольшой, и такое переключение режимов было некритично.
Протоколы для общения с сервером и между контроллерами
Мы разработали для устройства два протокола. Один отвечает за упаковку данных в формат JSON и общение между ESP32 и сервером. Протокол поддерживает топики для команд, ответов на команды и отправки репортов. Ниже приведены общие структуры команд и ответов:
Общая структура репортов выглядит так:
Протокол поддерживает:
Добавление и удаление BLE-устройств, отправку MAC-адресов;
Передачу данных от датчиков;
Переключение режимов работы хаба – stay, sleep, away, off;
Включение и выключение звукового сигнала.
Другой кастомный протокол отвечает за общение между ESP32 и nRF52. Поскольку обмен данными с периферией на тот момент был довольно прост, протокол мы сделали синхронным. Протокол поддерживает:
Обмен командами;
Запуск сканирования;
Остановку сканирования;
Установку списка поддерживаемых MAC-адресов;
Обмен данными;
Процесс обновления прошивки nRF52.
Обновление прошивки
Обновление встроенного ПО было реализовано на базе сервера AWS. Первичная реализация была синхронной: работа хаба блокируется, и на ESP32 загружается образ, который содержит прошивку и для ESP32, и для nRF52. Сначала обновляется ESP32. После перезагрузки он проверяет версию текущей прошивки на nRF52 и, если в загруженном образе содержится более свежая версия, обновляет прошивку.
Дополнительное аппаратное обеспечение
Кроме контроллеров, на хабе также были установлены датчики влажности и температуры, которые общаются с ESP32 напрямую. Эти данные выводятся через сервер на мобильное приложение, чтобы информировать пользователя о состоянии устройства.
Изначальное расположение датчика температуры оказалось не слишком удачным. Он сильно нагревался от чипов и меди проводников. Пришлось вынести датчик на отдельный “островок” и убрать от него всю медь, кроме самых необходимых дорожек.
Все антенны (для связи по Wi-Fi, LTE и BLE) были разведены на печатной плате хаба.
Устройство может питаться от литий-ионного аккумулятора емкостью 2000 мА·ч, от внешнего источника или через USB-разъем.
Наконец, клиент попросил установить на хаб динамик, который должен был включаться для предупреждения пользователя об опасности – например, когда срабатывает датчик на открытие двери. В зависимости от режима работы хаба (активный, спящий и режим ожидания) динамик проигрывает разные типы сирен.
Такова была архитектура первой итерации разрабатываемого решения. После демонстрации и некоторых испытаний заказчик решил внести несколько изменений, из-за чего решение пришлось переработать.
Вторая итерация устройства
Пожалуй, главное изменение заключалось в том, что заказчик решил дополнить систему сложными BLE-датчиками. Изначально архитектура решения была рассчитана на BLE-маячки, способные передавать всю нужную информацию по первичным широковещательным каналам (адвертайзинг). Теперь же клиент пожелал добавить устройства, к которым необходимо подключиться, чтобы извлечь нужные данные. Такие датчики рассылают широковещательные пакеты только для того, чтобы центральное устройство могло их обнаружить, а для обмена данными требовалось соединение. Некоторые сенсоры просто устроены так, что в ADV-пакетах содержится только служебная информация, а полезные данные можно передать только через соединение.
Передача информации у некоторых таких устройств осуществлялась по неизвестным нам протоколам, и в этом заключалась первая проблема.
Анализ BLE-протоколов новых датчиков
Чтобы хаб мог поддерживать новые BLE-сенсоры, необходимо было понимать их протоколы. Часть производителей предоставили нам нужные спецификации, но протоколы нескольких датчиков пришлось анализировать с помощью снифферинга. Здесь мы использовали devkit от nRF52840, для которого есть готовая прошивка снифферинга
Используя приложение nRF Connect, мы определяем, из каких сервисов и дескрипторов состоит устройство. Затем датчик подключается к приложению, разработанному производителем устройства, а мы при помощи сниффера слушаем и анализируем общение между ними. Если общение между устройствами не шифруется, можно сопоставить интересующие нас данные с пересылаемыми сообщениями. Далее, используя эту информацию, мы добавляем поддержку данного устройства в нашем хабе.
Механизм установления соединения и эмуляция ADV-пакетов
Вторая проблема заключалась в способе передачи данных. Напомню, что в первом варианте устройства nRF52 принимает ADV-пакеты от BLE-датчиков и передает их на ESP32. Однако новые сенсоры, во-первых, требовали установления соединения, а во-вторых, передавали данные по вторичным широковещательным каналам. Нам требовалось научить nRF52 общаться с такими устройствами и каким-то образом передавать данные на главный контроллер.
Для решения первой проблемы мы реализовали на стороне nRF52 механизм взаимодействия с такими BLE-датчиками (“Процесс”). Он использует адаптеры ADV-пакетов для опроса датчика и получения нужных данных через установление соединения.
При этом инициатором подключения здесь выступает не nRF52, а ESP32. Реализованный на нем “Менеджер процессов” определяет, от какого датчика пришел пакет; если это сенсор, требующий подключения, ESP32 запускает на стороне nRF52 “Процесс”, который устанавливает соединение. Такой “Процесс” может быть как периодическим, так и одноразовым (для включения, переключения и отключения некоторых исполнительных устройств).
Чтобы нам не пришлось кардинально менять способ общения между двумя контроллерами, мы поступили следующим образом. Когда nRF52 получает данные с новых BLE-сенсоров, он пакует их в виртуальные ADV-пакеты и передает их на ESP32, где они обрабатываются как обычно.
Доработка протоколов общения между контроллерами
Изначально для общения между контроллерами использовался синхронный протокол, но на данном этапе у нас появилась потребность в асинхронной работе. В частности, в системе появились процессы, которые отправляют результат выполнения асинхронно.
Кроме того, чтобы повысить надежность системы, мы также дополнили протокол двумя командами для контроля за nRF:
Оповещение со стороны nRF52 о начале работы;
Периодический опрос контроллера nRF52 о его состоянии.
Введение порогов для экономии трафика
Первоначально данные с датчиков регулярно отправлялись на сервер как только обновлялись в памяти хаба. Пока мы имели дело всего с несколькими маячками, это не вызывало проблем. Однако теперь датчиков стало больше, объем данных вырос, а с ним вырос и расход трафика. Большой счет за интернет едва ли обрадовал бы покупателей такой системы. Чтобы уменьшить ее аппетиты, решено было ввести пороги для детектирования изменения значений.
В новой итерации хаба хранение и отправка данных были устроены следующим образом. Данные с датчиков хранятся во временном хранилище. Когда приходят новые данные, они сравниваются с предыдущими значениями. Если изменение значения превышает установленный для данного параметра порог, то хаб отправляет информацию на сервер. Если же изменение значения не превышает порог, данные не передаются. Например, если измерения датчика температуры упали на 0,2°C (при установленном пороге в 1°C), то на сервер эта информация не отправляется.
При этом, если новое значение не превышает установленный порог, оно не заменяет предыдущее в хранилище данных. Все последующие значения сравниваются с хранящимся в памяти, пока одно из них не превысит установленный порог.
Пороги для каждого типа датчиков настраиваются на сервере и хранятся на ESP32.
Введение heartbeat-пакетов и списка подключенных устройств
Из-за минимизации данных от хаба стало невозможно точно определить, исправно ли устройство, если данные с него не поступают. Чтобы актуальное состояние хаба отображалось в мобильном приложении, мы запрограммировали устройство раз в минуту отправлять на сервер heartbeat-пакет с информацией о подключенном типе питания, текущем сигнале у сетевого интерфейса и т.д. Так сервер видит, что устройство функционирует.
Сервер также должен был получать информацию о состоянии подключенных датчиков. Однако когда мы ввели пороговые значения, данные с сенсоров стали поступать на сервер нерегулярно. Соответственно, система уже не могла понять, почему не приходят данные, – потому что значение не изменилось или потому что сенсор не работает.
Поэтому мы запрограммировали хаб формировать список подключенных устройств на основе данных с датчиков. В случае устройств, требующих соединения, сигналом о прекращении работы служит разрыв подключения, которое может зафиксировать nRF52.
В случае BLE-маячков, которые общаются с хабом только с помощью адвертайзинга, таким сигналом служит отсутствие ADV-пакетов в течение установленного времени. ESP32 периодически опрашивает провайдер информации – сущность, которая отдает список подключенных устройств. В списке данные от каждого датчика сопровождаются временной меткой. Она обновляется при каждом получении ADV-пакета – даже если значение параметров сенсора не превысило пороговое значение. Если данные с какого-либо датчика не поступают, временная метка не обновляется, и ESP32 понимает, что датчик неисправен.
Когда один или несколько датчиков перестают работать, контроллер формирует обновленный список подключенных устройств и отправляет его на сервер.
Отказ от GPS-позиционирования
По мере роста функционала хаба механизм переключения перестал справляться. Мы предложили клиенту заменить текущий модем на аналог, который поддерживает одновременную передачу данных по LTE и координат GPS, однако тот посчитал такую замену слишком дорогой. Кроме того, пришлось бы переделывать разводку платы и заказывать на фабрике новый прототип, что тоже потребовало бы расходов и времени. В итоге заказчик решил отказаться от функций GPS в пользу стабильного LTE-соединения.
Тем не менее в новой итерации платы мы заложили возможность установки модема Quectel EG915.
Переделали обновление по воздуху
Разработанное нами обновление по воздуху для первого варианта устройства мы не тестировали в “боевых” условиях. Плата всегда прошивалась на столе у разработчика, а тестировщики использовали скрипты. На втором этапе работ обновлять прошивку пришлось чаще, и здесь выяснилось, что обновление по LTE занимает порядка 20 минут. И все это время хаб должен оставаться выключенным, поскольку обновление происходит в синхронном режиме. Для второй итерации хаба было решено переделать обновление на асинхронный режим, чтобы хаб продолжал работать во время загрузки образа со встроенным ПО.
Заменили динамик и добавили разъем для CAN-шины
Наконец, на испытаниях в корпусе было выявлено, что динамик устройства слишком тихий. Поэтому мы заменили бузер на более громкий.
Кроме того, поскольку разрабатываемое решение предполагалось устанавливать в домах на колесах, заказчик пожелал добавить в хаб возможность получать данные по CAN-шине. Мы вывели разъем и проверили его работу на CAN-устройстве, однако разработка соответствующего ПО пока не началась.
Тестирование на производстве
Для первичного тестирования на производстве был разработан скрипт, который посредством CLI-команд и отладочного интерфейса мог провести функциональное тестирование платы непосредственно на производстве: проверить работу с сетью через разные интерфейсы, получение данных по BLE и прочие функции. Отчет о тестировании приходит на почту.
Заключение и планы
Данный проект пережил довольно много изменений. Большая их часть продиктована тем, что заказчик выдвинул к устройству новые требования уже после создания первого прототипа. Менять архитектуру хаба в этот момент означало бы начинать работу практически заново. Чтобы не тратить деньги заказчика, мы предложили более гибкий вариант – внести множество изменений, но в рамках исходной архитектуры.
Проект изначально создавался под периферийные устройства, использующие Bluetooth Low Energy. Однако разделение функций устройства между двумя контроллерами (один общается с периферией, другой – с сервером) обеспечило системе гибкость с точки зрения выбора технологии связи. В частности, такая архитектура позволяет легко перевести хаб на работу с ZigBee или OpenThread без замены аппаратной части. Достаточно будет лишь написать соответствующее встроенное ПО и обновить прошивку контроллеров. На данный момент заказчик решил остаться на Bluetooth Low Energy, но потенциальную совместимость устройства с другими технологиями связи оценил.