EmbeddedPathashala · BLE Mesh Series
How messages travel across a mesh network, how loops are prevented, and how to get BlueZ mesh running on your Linux machine.
Key Concepts in This Post
How Does a Mesh Message Actually Travel?
In a Bluetooth Mesh network, there is no fixed routing table like in TCP/IP. Instead, it uses a technique called managed-flood networking: when a node broadcasts a message, every nearby node that hears it will re-broadcast it. The message floods through the network until every node has received it (or the TTL runs out).
This sounds chaotic, but two control mechanisms keep it orderly: the Network Message Cache (prevent re-relaying the same message twice) and the TTL counter (limit how many hops a message can travel).
🎉 Managed-Flood Relay in Action
Message from Node A to Node E — relayed hop by hop
Sender
TTL = 5
Relay
TTL = 4
Relay
TTL = 3
Relay
TTL = 2
Destination
TTL = 1
It looks it up in its message cache. The message is already there (SRC+SEQ match), so Node B silently drops it — no re-relay. This prevents broadcast storms.
⏳ Time To Live (TTL)
Every mesh message carries a 7-bit TTL field. Each relay node decrements TTL by 1 before re-broadcasting. When TTL reaches 1, the message is delivered but not relayed further. TTL = 0 means the message was never meant to be relayed at all.
| TTL Value | Meaning | Relay? |
|---|---|---|
| 0 | Do not relay — local delivery only | ❌ No |
| 1 | Received and processed, not relayed | ❌ No (final hop) |
| 2–126 | Relay and decrement TTL by 1 | ✅ Yes |
| 127 | Default TTL — relay up to 126 times | ✅ Yes (max) |
TTL Countdown: Message Sent with TTL = 4
4
3
2
1
dropped
☓
At Hop 3 the TTL is 1 — node processes the message but does NOT relay it. Hop 4 never happens.
📁 Network Message Cache
Without the cache, a single message in a 100-node network could generate millions of re-transmissions. The cache stops this by remembering recently seen messages.
Key = Source Address + SEQ Number
Message already seen
Do not relay
New message
Process + Relay
Each node in the mesh has a 24-bit sequence number that increments with every message. This combination is unique per message, so the cache can reliably identify duplicates. The cache size is implementation-defined — when full, oldest entries are evicted.
🏠 Networks & Subnets
A large mesh network can be divided into subnets. Each subnet is defined by a shared Network Key (NetKey). Only nodes that know a particular NetKey can participate in that subnet’s traffic — they can relay and receive messages, but cannot decrypt application-layer data without the AppKey.
Example: Hotel Building Mesh Network
Room 101 nodes cannot decode Room 102 traffic — they hold different NetKeys. Reception nodes may hold all NetKeys to manage the full building.
Every mesh network shares 4 common resources:
Identify source & destination of messages
Secure network layer traffic
Secure access layer payload
32-bit value extending network lifetime
🔧 Setting Up BlueZ Mesh on Ubuntu
# Update and install BlueZ (includes bluetooth-meshd from 5.50+)
sudo apt update
sudo apt install bluetooth bluez bluez-meshd
# Verify version (mesh support requires 5.50+)
bluetoothctl --version
# Expected output: bluetoothctl: 5.xx
# Check mesh daemon binary exists
which bluetooth-meshd
# Expected: /usr/libexec/bluetooth/bluetooth-meshd
# or: /usr/sbin/bluetooth-meshd
# Stop the default Bluetooth daemon first (both cannot run at once)
sudo systemctl stop bluetooth
# Start the mesh daemon in foreground (debug mode)
sudo bluetooth-meshd --nodetach --debug
# In a second terminal — verify it is running
dbus-send --system --print-reply --dest=org.bluez.mesh /org/bluez/mesh org.freedesktop.DBus.Introspectable.Introspect
# Expected: long XML output describing the mesh D-Bus interface
# Launch meshctl (BlueZ mesh provisioner CLI)
meshctl
# Inside the meshctl interactive shell:
[meshctl]# discover-unprovisioned on
# Starts scanning for unprovisioned device beacons
# Look for output like:
# Unprovisioned device: UUID=aabbccddeeff00112233445566778899
[meshctl]# discover-unprovisioned off
# Stop scanning
[meshctl]# node-list
# List all provisioned nodes in the network
[meshctl]# export /tmp/mesh-network.json
# Export current network configuration to a JSON file
[meshctl]# quit
The IV Index (Initialization Vector Index) is a 32-bit network-wide counter that extends the lifetime of the network. It is used in message encryption nonces. Because each source node has a 24-bit SEQ counter — once it exhausts all 16 million values, the IV Index is incremented (the IV Update procedure). This resets the SEQ and allows the network to keep operating indefinitely. The primary subnet handles IV Index updates and propagates them to all subnets.
Next: Devices, Nodes & Provisioning
Part 4 covers the full provisioning flow — how an unprovisioned device becomes a mesh node — with a complete BlueZ meshctl + mesh-cfgclient walkthrough.
