Nerves system requirements

Copy Markdown View Source

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=y

BLUEZ5_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=y

bluez-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 (a nerves_system_br package) installs the Pi .hcd set with the per-board symlinks (e.g. BCM4345C0.raspberrypi,3-model-b-plus.hcd → BCM4345C0.hcd), so the kernel's btbcm picks the right patchram file from the device tree compatible string. (Trivia that bites raw-HCI stacks: LMP subversion 0x6119 is BCM4345C0 — Pi 3 B+/3 A+ — while 0x6606 is 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=y for the rtl_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=y

The 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=y

btusb 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/bluetooth symlink. bluetoothd persists adapter identity and link keys under /var/lib/bluetooth, which is read-only squashfs on Nerves. Bluez.prepare_runtime/0 creates /data/bluetooth (mode 0700) 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 a PropertiesChanged signal 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:

  1. ls /sys/class/bluetooth shows hci0 shortly after boot (serdev or btusb did its job).
  2. Bluez starts and Bluez.Client.adapters_info/0 lists the adapter with its MAC.
  3. Passive scanning delivers adverts to your on_advertisement: fun.
  4. With audio: true: bluealsad stays up (check the logs) and Bluez.BlueAlsa.pcms/0 answers [] (not an exit) with no headset connected.