-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
BGAdaptor sits between an old B&O Datalink deck and the modern networked world. Two independent protocol boundaries meet in the middle of the firmware:
Deck (Datalink UART, 320 baud)
│
▼
src/beogram.cpp ← transport-agnostic deck protocol (byte sequences ⇄ BeogramFeedback/BeogramCommand)
│
▼
src/transport.h + transport_ase.cpp / transport_moz.cpp ← ASE or Mozart
│
▼
B&O product over network
Everything else hangs off that spine:
-
src/state.h / state.cpp — the shared-globals module. All timers, connection flags, persisted settings, and peripheral objects (NeoPixel,
Preferences, HTTP client, WebSocket clients, web server) live here. Every other module reads/writes this state rather than passing data around directly — it's the seam where you can see how modules actually talk to each other. - src/discovery.cpp — mDNS scanning, used only to populate web-UI pickers and to power self-healing of a lost product IP. Actual runtime connections always use the stored IP.
- src/halo.cpp — Beoremote Halo WebSocket integration (tactile remote). Independent of ASE/Mozart; both transports call into it to reflect state.
- src/peer.cpp — links a second BGAdaptor so its deck appears as a second Halo page. One-way control (HTTP), state flows back over the peer's own UI-push WebSocket.
- src/ha_mqtt.cpp — Home Assistant MQTT auto-discovery entities.
-
src/webui.cpp / webpush.cpp / webpage.h — local HTTP config/control page, plus a WebSocket (
webpush) that pushes live state to that page (and to a linked peer). - src/led.cpp — single-NeoPixel connectivity status.
-
src/main.cpp — wires all of the above together in
setup()/loop().
setup(): bring up both serial ports, NeoPixel, WiFi (WiFiManager), mDNS, load all Preferences (including a migration path from older firmware that stored separate wsIP/sseIP instead of a unified productIP), wire up Mozart/Halo WebSocket callbacks, register HTTP routes, start the UI push socket, advertise mDNS services (_http._tcp, _bgadaptor._tcp with fn/id/deck/ver TXT records), and force-connect MQTT and (for ASE) SSE.
loop() is a round-robin: LED update → poll active transport → Halo connect/ping → product-recovery check → pending Beolink-expand check → read deck UART → webpushLoop() → peerLoop() → HTTP server → WiFi reconnect check → delayed-PLAY-after-digit → stopped-track timeout → Halo page/button updates → MQTT loop → tiny debug console.
B&O has shipped two generations of network API on their products:
- ASE — the legacy API (Server-Sent Events + BeoZone REST, port 8080). See Transport: ASE.
-
Mozart — the current API (dual WebSockets +
/api/v1REST, port 9339). See Transport: Mozart.
BGAdaptor auto-selects per installation (Platform setting, changeable in the web UI) so one firmware image supports both product generations. The deck-facing side (beogram.cpp) is identical either way — only the "talk to the product" side differs.
Setup
Daily use
Optional features
Other