A Raspberry Pi Bluetooth mesh network uses BlueZ and its meshctl tool to provision and manage BLE Mesh nodes over the Bluetooth 5.0 interface. This guide covers what Bluetooth mesh actually is, when it makes sense over Wi-Fi or Zigbee, hardware requirements, installing BlueZ with mesh support on Bookworm, provisioning nodes, and sending publish/subscribe messages across the mesh. This is an advanced networking project. The BlueZ mesh stack is functional but sparsely documented, and the provisioning workflow requires careful attention to key management.
Last tested: Raspberry Pi OS Bookworm Lite 64-bit | May 3, 2026 | Raspberry Pi 4 Model B (4GB) | BlueZ 5.66 | meshctl 5.66
Key Takeaways
- Bluetooth mesh is not the same as Bluetooth pairing or BLE beacons. It is a separate Bluetooth SIG specification (Mesh Profile 1.0) that creates a many-to-many network using a publish/subscribe model and managed flooding. The Pi acts as a provisioner and gateway, not a client connecting to individual devices.
- BlueZ mesh support requires building BlueZ from source or using a version that includes the mesh daemon (
bluetooth-meshd). The APT package on Bookworm includes mesh support in BlueZ 5.66, but you must enable and startbluetooth-meshdseparately from the mainbluetoothddaemon. Running both simultaneously causes conflicts. - This guide uses the Pi as the provisioner only. Actual mesh sensor nodes require dedicated BLE Mesh-compatible hardware such as the Nordic nRF52840 or Silicon Labs EFR32. The Pi’s onboard Bluetooth chip can act as a node in testing scenarios, but real deployments use microcontroller-based nodes running BLE Mesh firmware.
Raspberry Pi Bluetooth Mesh: What It Is and When to Use It
Bluetooth mesh is a network topology defined in the Bluetooth SIG Mesh Profile 1.0 specification. It uses BLE advertising channels to flood messages across a network of nodes, each of which can relay messages to its neighbours. The result is a self-healing, infrastructure-free radio network that does not require a central router or access point.
Use Bluetooth mesh when: your sensors and actuators already use BLE hardware, you need a low-power network that works without Wi-Fi infrastructure, or you are integrating with the commercial BLE mesh ecosystem (Bluetooth mesh lighting is now common in commercial buildings). Bluetooth mesh is a poor fit when: you need high data rates, you are already using Zigbee or Z-Wave, or your nodes are spread across more than a few hundred metres. Zigbee and Z-Wave have larger installed bases, better tooling, and more stable firmware ecosystems for DIY deployments. See the FAQ for a direct comparison.
The Pi runs as the provisioner. Its role is to add nodes to the network by generating and distributing the cryptographic keys that allow nodes to communicate. Once provisioned, nodes can relay messages without the Pi being present. The Pi also runs a proxy node to bridge between the mesh and standard GATT clients like phones.
Hardware Requirements
The Pi’s built-in Bluetooth chip (BCM43455 on Pi 4, RP2040 BT coexistence on Pi 5) supports BLE Mesh provisioning. For production deployments, an external USB Bluetooth 5.0 adapter with a chipset that has known good BlueZ support is more reliable. The TP-Link UB500 (Realtek RTL8761B) works without additional firmware on Bookworm.
For the mesh nodes themselves, you need BLE Mesh-compatible microcontroller hardware. The two most common options:
- Nordic nRF52840: The reference platform for BLE Mesh. Supported by the Zephyr RTOS mesh stack and Nordic’s nRF5 SDK. The nRF52840 DK development board or a Seeed XIAO nRF52840 both work for testing.
- Espressif ESP32: Supports BLE Mesh via the ESP-IDF
esp_ble_meshcomponent. Cheaper than nRF hardware. The ESP32-DevKitC works out of the box with the Espressif BLE Mesh examples.
You need at least two BLE Mesh nodes to test the provisioning workflow. One node can be simulated on the Pi itself using BlueZ’s meshctl demo mode, but real mesh relay and publish/subscribe testing requires physical nodes.
Installing BlueZ with Mesh Support
Bookworm ships BlueZ 5.66 via APT. This version includes bluetooth-meshd. Install it:
sudo apt update && sudo apt full-upgrade -y
sudo apt install bluez bluez-meshd bluez-tools python3-dbus -y
Verify the mesh daemon binary is present:
which bluetooth-meshd
# Expected: /usr/sbin/bluetooth-meshd
bluetooth-meshd --version
# Expected: BlueZ 5.66
Important: The standard bluetoothd daemon and bluetooth-meshd cannot run simultaneously on the same adapter. Stop and disable bluetoothd before starting the mesh daemon:
sudo systemctl stop bluetooth
sudo systemctl disable bluetooth
Create the mesh daemon configuration directory and start it:
sudo mkdir -p /var/lib/bluetooth/mesh
sudo bluetooth-meshd --nodetach --debug 2>&1 | head -20
Expected result: The daemon starts and prints initialisation messages including the HCI adapter index (typically hci0). If it prints Failed to open HCI device, the standard bluetooth daemon is still running or the adapter is busy. Run sudo hciconfig hci0 down to release it and retry.
To run the mesh daemon as a service:
sudo nano /etc/systemd/system/bluetooth-mesh.service
[Unit]
Description=BlueZ Mesh Daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/bluetooth-meshd --nodetach
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now bluetooth-mesh
sudo systemctl status bluetooth-mesh
Provisioning Nodes with meshctl
meshctl is the interactive shell for the BlueZ mesh stack. It connects to the mesh daemon over D-Bus and provides commands for network creation, node discovery, and provisioning. Launch it in a separate terminal while the mesh daemon is running:
meshctl
Create a new mesh network. This generates the network key, IV index, and provisioner address:
[meshctl]# create
Expected result: meshctl prints a token (a 16-character hex string). Save this token. It is the only way to re-attach to this network if meshctl restarts. The network configuration is stored in ~/.config/meshctl/.
Scan for unprovisioned nodes. Power on your BLE Mesh hardware now:
[meshctl]# discover-unprovisioned on
Unprovisioned nodes broadcast a beacon on the advertising channel. meshctl lists each one with a UUID. When your node appears, provision it:
[meshctl]# provision <UUID>
The provisioning exchange involves: capability exchange, public key exchange (P-256 ECDH), OOB authentication if configured, and distribution of the network key and unicast address. The process takes 5 to 15 seconds. On success, meshctl prints the node’s assigned unicast address.
Expected result: meshctl prints something like Provisioning node 0100 followed by Node 0100 is provisioned. The node is now part of the mesh and will relay messages.
Retrieve the node’s composition data (what models it supports):
[meshctl]# menu config
[meshctl]# target 0100
[meshctl]# get-composition 0

Sending Messages Across the Mesh
BLE Mesh uses a publish/subscribe model. Nodes publish messages to group addresses. Other nodes subscribed to the same group address receive them. This is different from direct unicast addressing, though unicast is also supported for configuration messages.
Bind an application key to the node’s Generic OnOff model (element 0, model 0x1000), then set a publish address:
[meshctl]# app-key-add 0x0000 0x0000 0x0000
[meshctl]# bind 0100 0 0x0000 0x1000
[meshctl]# pub-set 0100 0 0xC000 0 5 0x1000
The address 0xC000 is a group address. Any node subscribed to 0xC000 receives messages published there. Subscribe a second node:
[meshctl]# sub-add 0200 0 0xC000 0x1000
Send a Generic OnOff Set message (turn on):
[meshctl]# menu onoff
[meshctl]# target 0xC000
[meshctl]# set 1
Expected result: Any node subscribed to 0xC000 with a Generic OnOff Server model receives the message. On nRF hardware running the Zephyr mesh sample, the board LED toggles. On ESP32 running the ESP-IDF OnOff Server example, the GPIO bound to the model changes state.
Troubleshooting
Mesh daemon fails to start
The most common cause is bluetoothd still holding the adapter. Check with sudo systemctl status bluetooth. If it is running, stop it and bring the adapter down with sudo hciconfig hci0 down before starting bluetooth-meshd.
No unprovisioned devices found
Confirm the node firmware is running and advertising. On nRF hardware, a solid LED usually means the node is provisioned already. A flashing LED means it is unprovisioned and advertising. Use nRF Connect on a phone to confirm the beacon is visible before blaming meshctl. If the phone sees it and meshctl does not, the adapter on the Pi may not be scanning. Run sudo hciconfig -a to confirm the adapter is up and PSCAN is enabled.
Provisioning fails mid-exchange
BLE Mesh provisioning uses an unacknowledged protocol over advertising channels. Interference, distance, or a node reset mid-exchange all cause failures. Move the node within 1 metre of the Pi for initial provisioning. Once provisioned, nodes can operate at normal range. If provisioning repeatedly fails, check whether the node requires OOB authentication and whether meshctl is configured to match (see the BlueZ mesh documentation for OOB configuration).
Messages not received by subscribed nodes
Confirm the application key is bound on both the publishing and subscribing nodes. Composition data retrieval (get-composition) shows which models are present. Each model must have an app key bound before it can send or receive. Confirm the group address matches exactly on both publish and subscribe commands. A mismatch of even one bit in the address means the messages go to different groups.
FAQ
How does Bluetooth mesh compare to Zigbee?
Both use mesh topology with relay nodes, publish/subscribe messaging, and AES-128 encryption. Zigbee uses the 802.15.4 radio at 2.4GHz with a dedicated radio chip. Bluetooth mesh uses the Bluetooth radio, which is already present in the Pi and most smartphones. The practical difference: Zigbee has a larger ecosystem of sensors and switches, better DIY tooling (Zigbee2MQTT, Home Assistant ZHA), and more reliable firmware. Bluetooth mesh has the advantage that smartphones can directly connect as proxy clients without a hub. For most home automation builds, Zigbee is the better choice. See Home Automation with Raspberry Pi for the Zigbee approach.
Can the Pi itself be a mesh node?
Yes, with limitations. The BlueZ mesh daemon can configure the Pi as a node using its onboard Bluetooth chip. In practice the Pi is better suited as the provisioner and gateway. Running the Pi as a relay node alongside meshctl provisioning tasks causes timing conflicts on the single adapter. For lab testing with no other hardware available, the Pi-as-node works well enough to verify the provisioning flow.
How many nodes can a BLE mesh support?
The Bluetooth Mesh specification supports up to 32,767 unicast addresses per network. Practical limits are set by message propagation time and radio congestion. A well-designed network with proper relay node spacing handles hundreds of nodes comfortably. The flooding mechanism means every relay node re-broadcasts every message it receives, so dense networks generate significant radio traffic. Time-to-live (TTL) limits on messages prevent infinite flooding. Most deployments set TTL to 3 to 5 hops.
What happens if the Pi provisioner goes offline?
Nothing. Once nodes are provisioned and have their network keys, they communicate independently. The provisioner does not need to be present for the mesh to function. Messages route through whatever relay nodes are active. The Pi is only needed to add new nodes, change configuration, or monitor the network via meshctl.
Can I integrate a BLE mesh with Home Assistant?
Yes, via MQTT bridging. The Pi runs a Python script or a purpose-built bridge daemon that subscribes to mesh model states via the BlueZ mesh D-Bus API and publishes them to an MQTT broker. Home Assistant reads the MQTT topics. This is a custom integration, not a supported Home Assistant component. For a simpler path to BLE device integration with Home Assistant, the Home Assistant Connect ZBT-2 handles Zigbee and Thread natively without custom scripting. See Home Automation with Raspberry Pi.
References
- https://www.bluetooth.com/specifications/specs/mesh-profile-1-0-1/
- https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/mesh-api.txt
- https://docs.zephyrproject.org/latest/connectivity/bluetooth/bluetooth-mesh.html
- https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/esp-ble-mesh/ble-mesh-index.html
About the Author
Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.
Last tested hardware: Raspberry Pi 4 Model B (4GB). Last tested OS: Raspberry Pi OS Bookworm Lite 64-bit. BlueZ 5.66, meshctl 5.66.

