Skip to content

Feature: Bluetooth / BLE compatibility (Bluetooth microphones + mobile remote control) #67

@andrescera

Description

@andrescera

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 (see apps/backend/src/modules/streaming/audio.ts and apps/backend/src/rpc/procedures/status.procedure.ts).
  • Audio pipelines currently assume GStreamer alsasrc with 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).

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:

  1. Device pairing/connection (BlueZ), and
  2. 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.ts
    • packages/rpc/src/contracts/bluetooth.contract.ts
    • Backend procedures under apps/backend/src/rpc/procedures/
    • Add to status broadcasts (e.g., status.bluetooth), and keep status.asrcs updated.

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 rfkill

5. Armbian BT service checklist

  • systemctl status bluetooth — is bluetoothd running?
  • 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 btattach for UART-attached BT.
  • HFP/HSP microphone not working: May need ofono or hsphfpd for 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 pulsesrc or PipeWire pipeline variant if needed
  • 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 bluetooth in 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) ⚠️ Android-only 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 ⚠️ Fallback Works; being phased out
BlueALSA ⚠️ Fallback 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 ⚠️ Fallback 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)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions