Hands-On BLE Mesh Provisioning with Zephyr: A Step-by-Step C and Python Tutorial for Self-Healing Networks

Bluetooth Low Energy (BLE) Mesh is a transformative technology for large-scale, reliable, and self-healing wireless networks. Unlike traditional point-to-point BLE connections, BLE Mesh enables many-to-many communication, making it ideal for smart home, industrial automation, and healthcare applications—such as the smart pill box described in the reference material, which relies on BLE for connectivity. In this tutorial, we will walk through the process of provisioning a BLE Mesh node using the Zephyr RTOS, with C for the embedded device and Python for the provisioning client. We will emphasize the self-healing nature of the network and provide hands-on code examples.

1. Understanding BLE Mesh Provisioning

Provisioning is the process of adding an unprovisioned device (e.g., a sensor or actuator) to a BLE Mesh network. The provisioner (typically a smartphone, gateway, or dedicated tool) authenticates the device, assigns it a unicast address, and provides network keys. After provisioning, the device becomes a node capable of sending and receiving mesh messages.

The BLE Mesh protocol stack, as defined by the Bluetooth SIG (see the Mesh Model specification v1.1.1), includes foundation models like the Configuration Server and Health Server, which are essential for network management. Self-healing is achieved through the use of relay nodes, friendship, and message retransmission. If a node fails or moves out of range, messages are rerouted via alternate paths.

2. Prerequisites and Development Environment

  • Hardware: Two or more nRF52840 DK boards (or other Zephyr-supported BLE boards).
  • Software: Zephyr RTOS v3.5 or later, Nordic nRF Connect SDK, Python 3.8+ with BLE Mesh libraries (e.g., bleak and mesh).
  • Tools: West build system, a serial terminal (minicom, PuTTY), and optionally a BLE sniffer.

Install Zephyr and the nRF Connect SDK following the official documentation. Ensure you have the west tool and can compile samples.

3. Step 1: Building the Zephyr BLE Mesh Node (C)

We will use Zephyr’s samples/bluetooth/mesh/light_switch as a starting point. This sample demonstrates a simple on/off switch node that can be provisioned and controlled. We will modify it to include health server support for self-healing diagnostics.

First, create a new project directory and copy the sample:

mkdir ble_mesh_switch
cd ble_mesh_switch
cp -r $ZEPHYR_BASE/samples/bluetooth/mesh/light_switch/* .

Modify the prj.conf file to enable health server and relay functionality:

CONFIG_BT_MESH=y
CONFIG_BT_MESH_RELAY=y
CONFIG_BT_MESH_HEALTH_SRV=y
CONFIG_BT_MESH_CFG_SRV=y
CONFIG_BT_MESH_PROV=y
CONFIG_BT_MESH_PB_ADV=y
CONFIG_BT_MESH_GATT_PROXY=y
CONFIG_BT_MESH_FRIEND=y
CONFIG_BT_MESH_LOW_POWER=y
CONFIG_BT_MESH_LPN_AUTO=y
CONFIG_BT_MESH_LPN_ESTABLISHMENT=y

Now edit src/main.c to add a health server callback that logs faults:

#include <zephyr/bluetooth/mesh.h>

static void health_fault_cb(uint16_t addr, uint8_t test_id, uint16_t company_id,
                            const struct bt_mesh_health_fault *faults)
{
    printk("Health fault detected on node 0x%04x: test_id %d, company_id 0x%04x\n",
           addr, test_id, company_id);
}

static struct bt_mesh_health_srv_cb health_cb = {
    .fault = health_fault_cb,
};

struct bt_mesh_health_srv health_srv = {
    .cb = &health_cb,
};

/* Add to the element array */
static struct bt_mesh_elem elements[] = {
    BT_MESH_ELEM(0, model_list, &health_srv),
};

Build and flash the firmware to two boards:

west build -b nrf52840dk_nrf52840 -t flash

4. Step 2: Provisioning Using a Python Client

We will write a Python script that acts as a provisioner using the bleak library and the BLE Mesh provisioning protocol. The script will scan for unprovisioned devices, authenticate them, and assign addresses.

Install dependencies:

pip install bleak asyncio

Create provisioner.py:

import asyncio
import struct
from bleak import BleakScanner, BleakClient

# BLE Mesh provisioning service UUID (PB-ADV)
PROVISIONING_SERVICE_UUID = "00001827-0000-1000-8000-00805f9b34fb"

async def scan_and_provision():
    # Scan for unprovisioned devices
    print("Scanning for unprovisioned BLE Mesh devices...")
    devices = await BleakScanner.discover(timeout=10)
    target = None
    for d in devices:
        if d.name and "Mesh" in d.name:
            target = d
            break
    
    if not target:
        print("No unprovisioned device found.")
        return
    
    print(f"Found: {target.name} ({target.address})")
    
    # Connect and provision (simplified - real implementation requires PB-ADV or PB-GATT)
    async with BleakClient(target.address) as client:
        # This is a placeholder; actual provisioning involves multiple steps:
        # 1. Provisioning invite
        # 2. Provisioning capabilities exchange
        # 3. Provisioning start (OOB authentication)
        # 4. Provisioning data exchange (network key, IV index, unicast address)
        # 5. Provisioning confirmation
        # For brevity, we simulate using a hardcoded network key and address
        net_key = bytes([0x01]*16)  # Example network key
        unicast_addr = 0x0001       # First node address
        
        # Send provisioning data (simplified - use mesh library for production)
        print(f"Provisioning node with address 0x{unicast_addr:04x}")
        # In real code, you would use bt_mesh_provision() on the Zephyr side
        # and send appropriate provisioning PDUs over BLE.
        
    print("Provisioning complete.")

if __name__ == "__main__":
    asyncio.run(scan_and_provision())

This script is a simplified demonstration. A full implementation would require implementing the provisioning bearer layer (PB-ADV or PB-GATT) and handling the provisioning protocol state machine. For production, consider using the mesh Python library from Nordic Semiconductor.

5. Step 3: Testing Self-Healing Behavior

After provisioning two nodes (e.g., switch and light), we can test self-healing. The BLE Mesh stack in Zephyr supports relay and friendship features. If one node is turned off, messages will be rerouted through other relay nodes.

To observe this:

  1. Provision three nodes: Node A (switch), Node B (relay/light), Node C (relay).
  2. Configure Node B and C as relays using the configuration client.
  3. Turn off Node B. Send an on/off message from Node A to the light (Node C). The message should still reach Node C via Node C’s own relay or directly.
  4. Use the health server to monitor fault status. If a node fails, the health server will report a fault.

The self-healing is automatic: Zephyr’s mesh stack implements a message cache and retransmission mechanism. If a message is not acknowledged, the source node may retransmit or use an alternate path (if configured).

6. Performance Analysis and Protocol Details

BLE Mesh performance depends on several factors:

  • Relay count: Each relay adds latency. A typical mesh network can handle 3-5 hops without significant delay.
  • Message TTL: Default TTL is 7, meaning messages can traverse up to 7 hops.
  • Friendship: Low-power nodes (LPN) use friendship to reduce duty cycle. This introduces additional latency (up to 100 ms per friend poll).
  • Provisioning time: With OOB authentication, provisioning takes about 1-2 seconds per device.

The Mesh Model specification (v1.1.1) defines generic models (e.g., Generic OnOff Server) and health models. The health server can report faults such as low battery, communication failure, or sensor errors. In our smart pill box scenario, a health fault could indicate a stuck compartment or a failed sensor.

7. Conclusion

This tutorial provided a practical guide to BLE Mesh provisioning using Zephyr and Python. We built a self-healing network with relay support and health monitoring. The code examples can be extended to support more complex models, such as lighting control or sensor data collection, as seen in the smart pill box application. BLE Mesh’s self-healing capability ensures reliability in critical IoT deployments, making it a robust choice for modern wireless communication.

For further reading, refer to the Bluetooth SIG Mesh Model specification v1.1.1 and Zephyr’s BLE Mesh documentation.

常见问题解答

问: What is the role of the provisioner in BLE Mesh provisioning, and how does it interact with an unprovisioned device?

答: The provisioner is a device (e.g., a smartphone, gateway, or dedicated tool) that authenticates an unprovisioned BLE device, assigns it a unicast address, and provides network keys. This process integrates the device into the mesh network as a node, enabling it to send and receive mesh messages. The interaction typically involves a provisioning protocol over BLE advertising channels, where the provisioner sends provisioning data and the device confirms receipt.

问: How does BLE Mesh achieve self-healing in a network, and what components are involved?

答: Self-healing in BLE Mesh is achieved through relay nodes, friendship, and message retransmission. Relay nodes forward messages to extend range and provide alternate paths if a node fails or moves out of range. Friendship allows low-power nodes to rely on friend nodes for message buffering. The Health Server model monitors node health and reports faults, enabling the network to reroute messages dynamically. This ensures reliability and fault tolerance without manual intervention.

问: What are the key hardware and software prerequisites for following this tutorial?

答: The tutorial requires two or more nRF52840 DK boards (or other Zephyr-supported BLE boards) as hardware. Software prerequisites include Zephyr RTOS v3.5 or later, Nordic nRF Connect SDK, Python 3.8+ with BLE Mesh libraries like 'bleak' and 'mesh', and tools such as the West build system and a serial terminal (e.g., minicom or PuTTY). A BLE sniffer is optional for debugging.

问: How do you modify the Zephyr light_switch sample to support self-healing diagnostics?

答: To add self-healing diagnostics, modify the 'prj.conf' file to enable the Health Server and relay functionality. Specifically, set CONFIG_BT_MESH_HEALTH_SRV=y to activate the Health Server model, which monitors node health and reports faults, and CONFIG_BT_MESH_RELAY=y to enable relay nodes for message rerouting. This allows the node to participate in self-healing by detecting issues and providing alternate communication paths.

问: What is the significance of the Configuration Server and Health Server models in BLE Mesh provisioning?

答: The Configuration Server model is essential for network management, as it handles provisioning and configuration of nodes, such as setting network keys and addresses. The Health Server model provides diagnostics and fault reporting, which are critical for self-healing networks. Together, they enable reliable operation by allowing the network to monitor node status and automatically recover from failures, as defined in the Bluetooth Mesh Model specification v1.1.1.

💬 欢迎到论坛参与讨论: 点击这里分享您的见解或提问

Login

Bluetoothchina Wechat Official Accounts

qrcode for gh 84b6e62cdd92 258