EN

BLE Device Gateway

Фокус
Backend / Edge / IoT
Стек
Go · gRPC · Protocol Buffers · BLE · BlueZ / D-Bus · Concurrency · Google Cloud Run · Docker · CI
Репозиторий на GitHub

Инженерная задача

Скрыть GATT UUID, структуру бинарных пакетов и байтовые команды конкретных устройств внутри gateway, но при этом поддержать потоковую телеметрию и удалённое управление, когда автомобиль находится за CGNAT мобильного оператора и не имеет стабильного входящего адреса.

Моя роль

Я спроектировал границы сервисов и контракты Protocol Buffers, реализовал BLE-адаптеры и разбор бинарного протокола, построил модель конкурентного доступа и переподключений и реализовал cloud / edge контур управления с развёртыванием облачной части в Google Cloud Run.

Модель работы

Телеметрия идёт от BLE-уведомлений к gRPC-потоку. Удалённые команды проходят в обратную сторону: HTTPS-клиент → Cloud Run → bidirectional gRPC stream → edge-agent в автодоме → локальный gateway → BLE-устройство.

Результат

Телеметрия JBD проверена на реальном оборудовании, путь управления реле покрыт тестами, есть Docker и CI, а удалённый end-to-end путь через Cloud Run проверен целиком. Финальный smoke test с физическим JDY-33 пока остаётся отдельным шагом.

BLE Device Gateway — аппаратный edge-слой RV Monitor. Он работает на Linux / Raspberry Pi, скрывает протоколы конкретных BLE-устройств за типизированным gRPC API, передаёт телеметрию JBD BMS, управляет реле JDY-33 и подключается к control service в Google Cloud Run через исходящую долгоживущую gRPC/TLS-сессию. Система объединяет реальное оборудование, конкурентные Go-сервисы, локальные gRPC API и удалённое управление через обычный мобильный интернет.

Архитектура gateway

Оборудование за типизированным API

Клиенты сервиса работают с предметными операциями GetSnapshot, StreamSnapshots и SetChannel вместо низкоуровневых ReadCharacteristic и WriteCharacteristic. BLE UUID, структура пакетов и кодирование команд остаются внутри gateway.

Внутренняя модель аккумулятора не зависит от сгенерированных типов Protocol Buffers. Это не даёт транспортному слою и особенностям оборудования просачиваться в остальную часть приложения.

От фрагментов BLE к состоянию устройства

Ответ JBD может быть разбит на несколько BLE-уведомлений. Сборщик кадров накапливает фрагменты до получения полного ответа, после чего gateway проверяет структуру пакета и контрольную сумму и разбирает напряжение, ток со знаком, уровень заряда, напряжения отдельных ячеек, температуры, состояния защит и другие параметры аккумулятора. Этот путь данных проверен на физическом BMS, установленном в автодоме.

JBD RS485 / RS232 / UART / Bluetooth communication protocol

Описание протокола, использованное для интеграции JBD

Потоковая передача без блокировки BLE

API поддерживает два режима получения телеметрии: разовый снимок текущего состояния и постоянный серверный поток обновлений. Для каждого подключённого клиента создаётся небольшой буферизированный канал, а публикация новых значений не блокирует обработку BLE. Если клиент не успевает читать данные, устаревшее ожидающее значение заменяется самым свежим: для мониторинга текущего состояния важнее получить актуальное показание, чем воспроизвести каждое промежуточное обновление. Доступ к общему состоянию синхронизирован, а поиск BLE-устройств выполняется последовательно, поскольку оба устройства используют один физический Bluetooth-адаптер.

Переподключение, отказоустойчивость и тестирование

Долгоживущие клиенты оборудования используют context-aware retry loops с экспоненциальной задержкой между попытками. При потере связи с BMS телеметрия помечается недоступной, поэтому API не выдаёт старый снимок как актуальный. Завершение процесса распространяется через context cancellation и ограниченную по времени корректную остановку gRPC-сервера. Для разработки без автомобиля есть mock-источник данных; интеграционные gRPC-тесты работают через bufconn, а CI запускает тесты, race detector, статический анализ и сборку Docker-образа.

Удалённое управление через мобильную сеть

Автодом не может рассчитывать на входящие соединения: адрес меняется, а мобильный оператор может держать устройство за CGNAT. Поэтому отдельный edge-agent сам открывает исходящий долгоживущий bidirectional gRPC stream к облачному control service. Облако предоставляет HTTPS API для удалённого клиента и передаёт команды через активную сессию автомобиля. В Google Cloud Run REST и gRPC работают через один порт контейнера: gRPC-запросы маршрутизируются в gRPC server по HTTP/2 и content type, а обычные HTTP-запросы — в control API. Публичная точка Cloud Run обеспечивает TLS, а edge-agent автоматически переподключается при разрыве потока.

Проверено end to end

Полный путь удалённой команды был проверен через публичный Cloud Run service: авторизованный HTTPS-запрос попадал в cloud control, проходил по живому gRPC/TLS stream к локальному edge-agent, превращался в RelayService.SetChannel для BLE Device Gateway и возвращал клиенту результат applied. Через тот же deployment проверялись online-статус устройства и heartbeat timestamps.

В прототипе реестр активных устройств намеренно хранится в памяти, а команды для отключённого автомобиля не ставятся в очередь. Поэтому Cloud Run в этой версии ограничен одним instance, а команда при offline-состоянии завершается ошибкой вместо отложенного выполнения после переподключения. Это осознанные ограничения MVP: они оставляют систему небольшой, но явно показывают границы масштабирования и будущей персистентности.