RU

BLE Device Gateway

Focus
Backend / Edge / IoT
Year
2026
Stack
Go · gRPC · Protocol Buffers · BLE · BlueZ / D-Bus · Concurrency · Docker · CI
GitHub repository

Engineering problem

Keep vendor-specific GATT UUIDs, packet layouts and command bytes out of application services while presenting stable domain-oriented APIs.

My role

Go / Systems Engineer. I designed the service boundary, protobuf contracts, BLE adapters, binary protocol handling, concurrency model, streaming path, retry lifecycle, tests and container setup.

Telemetry path

BLE notification → frame assembler → checksum / protocol parser → thread-safe battery state → gRPC unary snapshot or server stream

Outcome

A runnable gateway with hardware-tested JBD telemetry, a test-covered JDY relay command path, mock mode, Docker, integration tests and CI.

BLE Device Gateway is the hardware-facing service behind RV Monitor. It runs on Linux or Raspberry Pi and turns device-specific BLE communication into a small typed gRPC boundary. The current implementation reads a JBD battery-management system, exposes the latest state and a live server stream, and implements control of a JDY-33 relay. The interesting part is not CRUD but the edge conditions around real hardware: fragmented binary packets, a shared Bluetooth adapter, concurrent consumers, cancellation, stale state and reconnects.

Gateway architecture

Hardware behind a typed API

Consumers work with operations such as GetSnapshot, StreamSnapshots and SetChannel instead of ReadCharacteristic or WriteCharacteristic. BLE UUIDs, vendor packet layouts and command encoding stay inside the gateway, while the internal battery model remains independent from generated protobuf types. That keeps hardware concerns from leaking into the application layer.

From fragmented BLE frames to domain state

JBD responses can arrive split across multiple BLE notifications. A frame assembler buffers fragments until a complete response is available, after which the gateway validates framing and checksum data and parses voltage, signed current, state of charge, cell voltages, temperatures, protection flags and other battery state. The telemetry path has been tested against the physical BMS used in the vehicle.

Streaming without blocking device I/O

The API supports both a unary current snapshot and server-streaming telemetry. Each subscriber gets a small buffered channel and publication is non-blocking: when a client falls behind, stale pending data is replaced by the newest snapshot because current device state matters more than replaying every intermediate value. Shared state is protected with synchronization, and BLE discovery is serialized because both adapters use the same physical Bluetooth controller.

Failure recovery and testability

Long-running hardware clients use context-aware retry loops with exponential backoff, and a lost BMS connection marks telemetry unavailable instead of serving stale values as current. Shutdown propagates through context cancellation and bounded gRPC graceful stop. A hardware-free mock source, Docker / Compose, bufconn gRPC integration tests, race-detector checks and GitHub Actions make the same public API easy to run and verify without the vehicle.