-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Summary
Add Bluetooth capabilities to CeraUI/CeraLive devices to support:
- Connecting Bluetooth audio devices (especially microphones/headsets) and selecting them as an audio input for streaming.
- Remote control via mobile using BLE and/or Bluetooth (start/stop stream, status, and other selected controls), including "no local WiFi" scenarios.
Why
- Many users already own Bluetooth microphones/headsets and want to use them as an input without extra USB hardware.
- BLE can provide a reliable "nearby" control channel from a phone even when the device is not on the same network.
Current state (relevant repo context)
- Audio input selection is backend-driven and exposed to the UI via
status.asrcs(seeapps/backend/src/modules/streaming/audio.tsandapps/backend/src/rpc/procedures/status.procedure.ts). - Audio pipelines currently assume GStreamer
alsasrcwith an ALSA device (e.g.alsasrc device="hw:<cardId>"). - Device management modules exist and follow a clear pattern (e.g. WiFi via
nmcli+ UI selector), which Bluetooth can mirror. - There is already remote control over the network/cloud; this issue targets Bluetooth/BLE as an additional local/nearby option.
Goals
- Bluetooth adapter management: show adapter state, enable/disable, discoverable mode, scanning.
- Pair/connect flows: scan → pair/bond → connect → reconnect on boot (configurable).
- Bluetooth microphone input:
- A connected BT mic/headset becomes selectable in the Audio Source UI.
- Streaming uses the selected BT input reliably.
- Clear UI feedback when the BT mic disconnects (and a safe fallback).
- BLE mobile remote control (MVP):
- A phone can connect over BLE and issue a small set of commands (below).
- Backend enforces pairing/bonding and a minimal auth model (no unauthenticated control).
- Operational robustness: works on target devices (ARM64 SBCs + AMD64) and is resilient to transient disconnects.
Control interfaces (UX requirements)
We need clear operator interfaces for controlling Bluetooth and BLE features:
-
CeraUI (on-device web UI):
- Bluetooth manager UI (enable/disable, scan, pair, connect/disconnect, forget/unpair, trust, "allow pairing for N minutes").
- Audio integration UI (BT mic/headset appears in audio source dropdown, with availability/connection status).
- BLE remote UI (enable/disable advertising, show paired phones, revoke access, show connected phone).
-
Mobile interface (phone):
- A simple control surface to send commands and show status (start/stop stream, status snapshot, etc.).
- Implementation options:
- Native app (most compatible, especially for iOS):
- Lynx (ByteDance) — lightweight, React-like, good performance, native BLE via plugins.
- React Native — mature ecosystem, large community, BLE libraries available.
- Flutter — strong cross-platform, good BLE support via
flutter_blue_plus.
- Web/PWA + Web Bluetooth (limited platform support; Android/Chromium-only, no iOS Safari).
- Native app (most compatible, especially for iOS):
Non-goals (initially)
- Bluetooth LE Audio (LC3) support (can be revisited later).
- Building a full native mobile app (unless needed); MVP can target a simple companion UX later.
- Supporting every Bluetooth profile under the sun; focus on the profiles needed for microphones/remote.
Technical approach (high-level)
Part A — Bluetooth audio input (microphones/headsets)
We need two pieces:
- Device pairing/connection (BlueZ), and
- A reliable audio capture interface that GStreamer can ingest.
Open design question (to decide during implementation):
- Option A (keep
alsasrc): make the BT microphone appear as a usable ALSA device (e.g., via BlueALSA or a system audio layer that exposes capture as ALSA). - Option B (add pipeline support for Pulse/PipeWire sources): if BT mic capture is not stable as ALSA across targets, extend pipelines to support
pulsesrc/ PipeWire capture paths and switch based on the selected source type.
Deliverable expectation for MVP:
- At least one supported profile path for microphone input on the target OS image (e.g. HFP/HSP capture).
Part B — BLE remote control (mobile nearby control)
Implement a BLE GATT server on the device:
- Advertise a custom "Cera Remote" service UUID.
- Expose characteristics for:
- commands (write)
- status (notify/read)
- (optional) pairing/session info (read)
MVP command set (proposed):
- Start/Stop stream
- Fetch status snapshot
- Select pipeline (optional, if safe)
- Select audio source (optional, if safe)
- Toggle bitrate overlay (optional)
Security baseline:
- Only accept commands from bonded/paired devices.
- Provide a UI flow for passkey confirmation if required (or explicit "allow pairing for N minutes" mode).
Backend work (proposed)
- Add new module:
apps/backend/src/modules/bluetooth/- Adapter state (powered/discoverable/scanning)
- Device list (known/paired + scan results)
- Actions: scan start/stop, pair, connect, disconnect, forget/unpair, trust/untrust
- Audio-device mapping (when a BT mic becomes a selectable audio source)
- BLE GATT server for remote control (if chosen in MVP)
- Add RPC:
packages/rpc/src/schemas/bluetooth.schema.tspackages/rpc/src/contracts/bluetooth.contract.ts- Backend procedures under
apps/backend/src/rpc/procedures/ - Add to status broadcasts (e.g.,
status.bluetooth), and keepstatus.asrcsupdated.
Frontend work (proposed)
- Add a Bluetooth UI entry (likely a card in General/Advanced, matching existing patterns):
- Adapter status + "Enable Bluetooth"
- Scan list with signal/name/address, and actions (Pair/Connect/Disconnect/Forget)
- Device details (paired/connected, last seen, battery if available)
- Audio UX:
- If BT mic is selected and disconnects: show toast + mark source "not available" (similar to existing handling for unavailable audio sources).
- BLE remote UX:
- Show "BLE remote connected" and the connected phone name (if available)
- Provide an "Allow pairing for N minutes" toggle if needed
System dependencies / packaging
Likely requirements (exact choices depend on target image):
- BlueZ (
bluetoothd) enabled/started - Tools/libs for control:
- Prefer DBus integration for reliability; avoid fragile parsing of interactive CLIs where possible
- Audio path dependency:
- Either BlueALSA (if using ALSA capture), or
- PipeWire/PulseAudio capture (if using
pulsesrc/PipeWire)
Armbian-specific considerations
CeraUI target devices run Armbian (Debian/Ubuntu-based for ARM SBCs). This affects Bluetooth support:
1. Bluetooth stack on Armbian
| Component | Armbian default | Notes |
|---|---|---|
| BlueZ | ✅ Usually installed (bluez package) |
Version varies (5.55–5.66+); ensure ≥5.50 for stable BLE GATT |
| bluetoothd | May need manual enable | sudo systemctl enable --now bluetooth |
| rfkill | Present | Check rfkill list for soft/hard blocks on Bluetooth |
| Firmware | Board-dependent | Some SBCs need armbian-firmware or vendor blobs for BT |
2. Audio stack on Armbian
| Stack | Armbian status | Recommendation |
|---|---|---|
| PipeWire | Default on newer images (Armbian 23.11+, Bookworm) | Recommended — best BT audio support, handles HFP/HSP/A2DP well |
| PulseAudio | Older images (Bullseye, Jammy server) | Works; can use pulsesrc in GStreamer |
| BlueALSA | Not installed by default | Fallback if we must stay pure-ALSA; apt install bluez-alsa-utils |
Recommendation: Target PipeWire as the primary path. For older images, document a migration path or require PipeWire.
3. SBC-specific Bluetooth quirks
| Board | Bluetooth | Known issues / notes |
|---|---|---|
| Orange Pi 5 / 5+ | Onboard BT (AP6275P combo) | Generally works; may need sp-rtk-driver or firmware from vendor |
| Rock 5B / 5B+ | Onboard BT (RTL8852BE or similar) | Realtek BT can be flaky; ensure firmware-realtek installed |
| Raspberry Pi 4/5 | Onboard BT (BCM) | Usually stable on Armbian; pi-bluetooth package may help |
| Jetson Nano/Orin | No onboard BT (usually) | Requires USB BT dongle |
| Generic USB dongle | Varies | CSR/Cambridge Silicon Radio dongles well-supported; avoid cheap clones |
4. Required packages (Armbian)
# Core Bluetooth
sudo apt install bluez bluetooth
# For D-Bus control from backend
sudo apt install libdbus-1-dev # if building native bindings
# Audio stack (choose one)
sudo apt install pipewire pipewire-pulse wireplumber # PipeWire path
# or
sudo apt install pulseaudio pulseaudio-module-bluetooth # PulseAudio path
# or
sudo apt install bluez-alsa-utils # BlueALSA path (ALSA-only)
# Useful tools
sudo apt install bluetoothctl rfkill5. Armbian BT service checklist
-
systemctl status bluetooth— isbluetoothdrunning? -
rfkill list bluetooth— is BT unblocked? -
bluetoothctl show— does the adapter appear? -
hciconfig -a— legacy check for adapter state - Firmware present in
/lib/firmware/brcm/or/lib/firmware/rtl_bt/(board-dependent)
6. Known Armbian issues
- BT not showing up after reboot: Often a race condition; add a small delay or use
btattachfor UART-attached BT. - HFP/HSP microphone not working: May need
ofonoorhsphfpdfor full HFP support on PulseAudio; PipeWire handles it natively. - Adapter disappears after suspend: Armbian SBCs often don't suspend, but if they do, BT may not recover. Workaround: restart
bluetooth.service.
Acceptance criteria
- Scan + pair + connect works from the UI on target hardware.
- A paired BT device can be set to auto-reconnect (configurable).
- A connected BT microphone can be selected and used as the streaming audio input.
- Disconnection handling is sane (user sees feedback; streaming doesn't silently degrade).
- BLE remote control MVP works from a phone:
- Can connect over BLE
- Can start/stop stream and read status
- Requires pairing/bonding (no unauthenticated control)
Implementation checklist (suggested)
Phase 0 — QR-code hotspot join (quick win, no BLE)
- Generate QR code in CeraUI with hotspot SSID + password + WebSocket URL
- Phone scans → auto-joins WiFi → opens CeraUI in browser
- Works on iOS + Android without app
Phase 1 — Bluetooth audio (microphones)
- Verify Armbian Bluetooth stack on target boards (BlueZ, firmware, rfkill)
- Decide audio capture strategy: PipeWire (recommended) vs PulseAudio vs BlueALSA
- Implement backend Bluetooth module (
apps/backend/src/modules/bluetooth/)- Adapter state (powered, discoverable, scanning)
- Device list (paired, connected, scan results)
- Actions: scan, pair, connect, disconnect, forget, trust
- Add RPC schemas/contracts (
packages/rpc/src/schemas/bluetooth.schema.ts) - Add frontend Bluetooth UI (card in General/Advanced tab)
- Adapter status + enable/disable
- Scan list with pair/connect/forget actions
- Integrate BT mic into audio source selection (
status.asrcs)- Extend
updateAudioDevices()to include BT capture sources - Add
pulsesrcor PipeWire pipeline variant if needed
- Extend
- Handle BT mic disconnect gracefully (toast, fallback source)
- Add mocks for Bluetooth module (scan results, pairing errors)
Phase 2 — BLE remote control (optional, if offline control is priority)
- Implement BLE GATT server (D-Bus + BlueZ GATT API)
- Custom "Cera Remote" service UUID
- Command characteristic (write)
- Status characteristic (notify/read)
- Add "Allow pairing for N minutes" UI toggle
- Add UI for BLE remote status (connected phone name, revoke access)
- Build native companion app for iOS + Android (Lynx preferred; React Native / Flutter as alternatives)
- Security: require BLE bonding; no unauthenticated commands
Armbian-specific tasks
- Document required packages in installation guide
- Test on target boards: Orange Pi 5, Rock 5B, (USB dongle fallback)
- Handle firmware loading issues (vendor blobs,
armbian-firmware) - Verify
systemctl enable bluetoothin setup/deployment scripts
Alternatives considered
Mobile control interface
| Option | Verdict | Notes |
|---|---|---|
| BLE GATT | ✅ Proposed for MVP | Works offline; needs native app for iOS |
| Existing cloud/WebSocket | Already exists | Requires internet; doesn't help offline use |
| Local WiFi + mDNS | ✅ Phase 0 candidate | Device hotspot + phone connects; no new protocol |
| WiFi Direct / P2P | ❌ Skip | Complex; poor cross-platform support |
| QR-code bootstrap | ✅ Nice UX addition | Phone scans QR → joins hotspot → opens CeraUI |
| Native companion app (Lynx / RN / Flutter) | ✅ Best for BLE on iOS | More dev effort; Lynx is lightweight option |
| Web Bluetooth (PWA) | iOS Safari doesn't support Web Bluetooth |
Recommendation: Start with QR-code → hotspot → CeraUI (Phase 0). Add BLE + native app later if offline control is critical.
Note on Lynx: Lynx is ByteDance's cross-platform framework (open-sourced 2025). It's lighter than React Native, uses a React-like API, and can integrate native BLE plugins. Good fit for a simple companion app.
Bluetooth audio capture
| Option | Verdict | Notes |
|---|---|---|
| PipeWire | ✅ Recommended | Modern; default on Armbian Bookworm+; good BT audio |
| PulseAudio | Works; being phased out | |
| BlueALSA | Pure ALSA; less maintained |
Recommendation: Target PipeWire with pulsesrc (PipeWire provides Pulse compatibility). Document PulseAudio fallback for older images.
BLE GATT server implementation
| Option | Verdict | Notes |
|---|---|---|
| D-Bus + BlueZ GATT API | ✅ Recommended | Native Linux way; reliable |
| Node.js bleno | ❌ Avoid | Unmaintained; compatibility issues |
| Rust/Go sidecar | Clean boundary if D-Bus is painful |
Security / auth for BLE
| Option | Verdict | Notes |
|---|---|---|
| BLE bonding only | ✅ MVP | Simple; relies on BLE pairing security |
| PIN confirmation | Nice-to-have | Device shows PIN, phone enters it |
| QR pairing | Nice-to-have | One-time token in QR |
| Time-limited pairing window | ✅ MVP | "Allow new devices for 2 min" button |
Open questions (to answer before/during implementation)
- Armbian version: Which Armbian release / branch are we targeting (Bookworm, Jammy, etc.)? This determines PipeWire vs PulseAudio default.
- Audio stack decision: Commit to PipeWire-only, or support both PipeWire and PulseAudio paths?
- Bluetooth profiles: Which profiles are required for "microphone" support (HFP, HSP, A2DP sink, or all)?
- BLE advertising: Should the device advertise BLE by default, or only when explicitly enabled in settings?
- BLE remote feature flag: Should BLE remote control be a separate toggle in settings (for security/resource reasons)?
- Firmware bundling: Do we bundle BT firmware in the CeraUI image, or rely on Armbian's
armbian-firmware? - USB dongle fallback: Should we officially support USB BT dongles for boards without onboard BT (e.g., Jetson)?