The official Nerves systems ship neither BlueZ nor dbus, so this
library needs a customized system. This guide lists exactly what that
system must add — every item below was hardware-found bringing the stack
up on Raspberry Pis (a known-good reference implementation for eight
targets lives at
bbangert/nerves_systems_universal_proxy).
Buildroot packages (nerves_defconfig)
BR2_PACKAGE_DBUS=y
BR2_PACKAGE_BLUEZ5_UTILS=yBLUEZ5_UTILS installs bluetoothd at
/usr/libexec/bluetooth/bluetoothd — the library's default
bluetoothd_path:. BlueZ must be ≥ 5.66 for the
AdvertisementMonitorManager1 passive-scanning API (the library always
starts the daemon with -E).
For audio: true, additionally:
BR2_PACKAGE_BLUEZ_ALSA=ybluez-alsa's PCM is a userspace ALSA plugin — no kernel audio config
is needed (unlike, say, snd-usb-audio). Buildroot ships bluez-alsa 4.x,
whose daemon is named bluealsad; the library probes both the v4 and v3
binary names. The stock package builds SBC only — fine for A2DP; codec
extras (AAC etc.) have licensing implications and are your call.
Radio firmware blobs
- Raspberry Pi onboard radios:
BR2_PACKAGE_RPI_DISTRO_BLUEZ_FIRMWARE=y(anerves_system_brpackage) installs the Pi.hcdset with the per-board symlinks (e.g.BCM4345C0.raspberrypi,3-model-b-plus.hcd → BCM4345C0.hcd), so the kernel'sbtbcmpicks the right patchram file from the device tree compatible string. (Trivia that bites raw-HCI stacks: LMP subversion0x6119is BCM4345C0 — Pi 3 B+/3 A+ — while0x6606is C5, Pi 400/CM4. The kernel path gets this right by itself.) - Realtek USB dongles (RTL8761B/BU — most cheap "BT 5.0" dongles):
BR2_PACKAGE_LINUX_FIRMWARE=y+BR2_PACKAGE_LINUX_FIRMWARE_RTL_87XX_BT=yfor thertl_bt/blobs.
Kernel fragment
For a UART-attached radio (Pi onboard chips) the kernel must auto-attach
hci0 via serdev:
CONFIG_BT=y
CONFIG_BT_BREDR=y
CONFIG_BT_LE=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_SERDEV=y
CONFIG_BT_HCIUART_BCM=y
# THE GOTCHA — see below:
CONFIG_SERIAL_DEV_BUS=y
CONFIG_SERIAL_DEV_CTRL_TTYPORT=yThe gotcha: CONFIG_BT_HCIUART_SERDEV only depends on
SERIAL_DEV_BUS — it does not select it. If SERIAL_DEV_BUS is in
neither your fragment nor the base defconfig, make olddefconfig
silently drops BT_HCIUART_SERDEV back to n: the firmware boots,
bluetoothd runs, the .hcd files are present, the device tree is
correct — and /sys/class/bluetooth/ stays empty with zero BT lines in
dmesg. When writing kernel fragments, list every depends on
prerequisite explicitly; olddefconfig only auto-resolves selected
symbols.
For USB dongles (also the only path on boards without an onboard radio):
CONFIG_BT_HCIBTUSB=y
CONFIG_BT_HCIBTUSB_BCM=y
CONFIG_BT_HCIBTUSB_RTL=y
CONFIG_BT_HCIBTUSB_MTK=ybtusb probes on USB enumeration — no attach step, independent of the
serdev path above. With both configured, onboard and USB radios coexist
and desired_adapter: picks which one this library drives.
Device tree (Raspberry Pi)
The Pi systems must keep the Bluetooth child node under the mini-UART
(the miniuart-bt arrangement the upstream Nerves Pi systems already
use — BT on /dev/ttyS0, leaving the PL011 for the console). The serdev
kernel path binds that node and auto-attaches the chip; nothing in
userspace touches the UART.
Known boot-timing caveat on the Pi 3 B+: the serdev probe can race
rootfs availability, so the onboard radio occasionally enumerates
seconds late. See the adapter-selection notes in the
architecture guide for how that interacts with
desired_adapter:.
App-side rootfs overlay
These live in the application's rootfs_overlay (not the system),
since they're policy, not plumbing:
/var/lib/bluetooth → /data/bluetoothsymlink.bluetoothdpersists adapter identity and link keys under/var/lib/bluetooth, which is read-only squashfs on Nerves.Bluez.prepare_runtime/0creates/data/bluetooth(mode0700) at start; the symlink makes the daemon land there.rootfs_overlay/var/lib/bluetooth -> /data/bluetooth/etc/bluetooth/main.conf(optional but recommended for passive scanning): BlueZ's default AdvMonitor scan duty cycle is aggressive (~50%), and every received advert becomes aPropertiesChangedsignal your BEAM must D-Bus-decode — at ~50% duty this can dominate CPU on a small device. A ~10% duty cycle cuts the advert rate ~5× with no practical loss for sensor scanning:[LE] ScanIntervalAdvMonitor=480 # 300 ms ScanWindowAdvMonitor=48 # 30 ms(bluetoothd 5.79 logs a cosmetic "Unknown key" warning for these — a key-validation whitelist typo upstream; the values are applied, and the warning disappears on later BlueZ versions.)
What the library handles itself
No system/overlay work is needed for: /run/dbus + the bus socket +
machine-id (created/cleaned by Bluez.prepare_runtime/0), daemon
supervision (MuonTrap, no init scripts — do not enable the
buildroot dbus/bluetoothd init scripts), or agent/monitor registration.
Checklist
A new target is ready when, on hardware:
ls /sys/class/bluetoothshowshci0shortly after boot (serdev or btusb did its job).Bluezstarts andBluez.Client.adapters_info/0lists the adapter with its MAC.- Passive scanning delivers adverts to your
on_advertisement:fun. - With
audio: true:bluealsadstays up (check the logs) andBluez.BlueAlsa.pcms/0answers[](not an exit) with no headset connected.