Bluetooth Mesh Networking
A warehouse needs 500 occupancy sensors to control lighting across 40,000 square meters. Wi-Fi APs every 15 meters is expensive. LoRaWAN is one-directional. Bluetooth Mesh puts a $3 BLE chip in every light fixture and sensor, and messages hop relay-to-relay until they reach the target. No infrastructure beyond the nodes themselves. It's not fast (messages take 10-50ms per hop) and it's not for streaming data. It's for "turn on group 7" and "occupancy detected in zone B3" — commands and state, not payloads.
Mesh topology
Provisioner (phone/gateway)
│
▼
┌─────────┐ relay ┌─────────┐ relay ┌─────────┐
│ Sensor │ ──────────► │ Light 1 │ ──────────► │ Light 2 │
│ (LPN) │ │ (relay) │ │ (relay) │
└────┬────┘ └─────────┘ └─────────┘
│ friend
▼
┌─────────┐
│ Friend │
│ node │
└─────────┘
- Provisioner — adds nodes to the network, assigns addresses and keys
- Relay nodes — mains-powered, retransmit messages (typically every light fixture)
- Low Power Nodes (LPN) — battery sensors, sleep most of the time
- Friend nodes — store messages for LPNs, LPN polls on a schedule
Provisioning
Nodes join the mesh through a provisioning process:
// Provisioner side (nRF Connect SDK)
static void prov_complete(uint16_t net_idx, uint16_t addr) {
LOG_INF("Node provisioned: addr 0x%04x on net_idx %u", addr, net_idx);
// Configure: add to groups, set publish/subscribe addresses
cfg_cli_mod_app_bind(net_idx, addr, 0, 0, 0); // bind Generic OnOff to AppKey
cfg_cli_sub_add(net_idx, addr, 0, GROUP_ADDR_LIGHTS_ZONE_B);
}
// Unprovisioned device advertises with UUID
// Provisioner scans, selects device, exchanges public keys (ECDH P-256)
// Device receives: NetKey, AppKey, unicast address
After provisioning, the node is part of the mesh with a unique unicast address and shared network key.
Models: the application layer
Mesh models define message types:
| Model | Messages | Use |
|---|---|---|
| Generic OnOff | OnOffSet, OnOffStatus | Simple on/off control |
| Generic Level | LevelSet, LevelStatus | Dimming (0-65535) |
| Light Lightness | LightLightnessSet | Light output level |
| Sensor | SensorStatus | Sensor data reporting |
| Generic Location | LocationGlobalSet | Asset position |
Models use publish/subscribe addressing:
// Sensor publishes occupancy to group 0xC001
struct bt_mesh_model_pub pub = {
.addr = 0xC001, // group address
.period = MSEC_PER_SEC * 5,
};
// Light subscribes to group 0xC001 — turns on when occupancy published
struct bt_mesh_elem elements[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, onoff_cli_ops, &pub, NULL),
};
One sensor publish turns on every light subscribed to that group. No central controller needed.
Message flow and relay
Messages flood through the network with controlled relay:
Sensor (addr 0x0005) publishes to group 0xC001
→ Light relay (0x0010) receives, retransmits (TTL-1)
→ Light relay (0x0015) receives, retransmits (TTL-1)
→ Light target (0x0020) receives, executes OnOffSet
TTL (Time To Live) limits hop count — typically 5-7 hops. Each relay decrements TTL. Network-wide messages use managed flooding with cache to prevent infinite loops.
Friend and Low Power Node
Battery sensors can't listen constantly. The friendship protocol solves this:
LPN (sensor): sleep ──wake──poll friend──sleep──wake──poll friend──sleep
Friend (light): always listening, stores messages for LPN
// LPN configuration
struct bt_mesh_lpn {
.pollTimeout = 300, // poll every 300 * 100ms = 30 seconds
.pollIntvl = 10, // poll interval steps
.retryIntvl = 10,
};
// Friend automatically established with nearest relay node
bt_mesh_lpn_set(true);
LPN battery life: 1-3 years on coin cell with 30-second poll interval, depending on message frequency.
Network sizing guidelines
| Parameter | Recommendation |
|---|---|
| Nodes per network | Up to 32,000 (spec limit), practical 500-2,000 |
| Relay nodes | Every 5-10 meters in indoor environments |
| Groups | Organize by zone/function (max 16,384 group addresses) |
| Subnets | Separate subnets for security zones (max 4,096 subnets) |
| Provisioner | One active provisioner; backup provisioner recommended |
nRF Connect SDK example
Nordic's SDK provides mesh samples:
# Build mesh light client
west build -b nrf52840dk_nrf52840 samples/bluetooth/mesh/light_lc
# Build mesh sensor server
west build -b nrf52840dk_nrf52840 samples/bluetooth/mesh/sensor_server
Flash provisioner on a DK, provision sensor and light nodes via nRF Mesh mobile app, then test group control.
Provision mesh networks with a known-good topology before scaling to 100+ nodes — reactive routing under load differs from lab mesh of five devices.
Security and provisioning
Mesh provisioning is the attack surface:
Provisioner → Provisioning bearer → Device joins network
Network key distributed encrypted during provisioning
Use Out-of-Band (OOB) authentication when possible — numeric comparison or QR code, not just "accept any provisioner." Rotate network keys on device RMA or security incident.
Message relay and hop limits
Default TTL is 5 hops — messages die after 5 relay nodes:
Node A → Relay B → Relay C → Relay D → Relay E → Node F (delivered)
Node A → ... → 6th hop (dropped)
Design topology so any node reaches gateway within 4 hops. Add relay nodes rather than increasing TTL — higher TTL increases network congestion exponentially.
Coexistence with BLE connections
Mesh and BLE GATT can coexist on same chip but share radio time:
- Mesh relay during connection events causes latency spikes
- Schedule provisioning when mesh traffic is low
- nRF52/nRF53 have coexistence protocols — enable in SDK config
Pair with Android BLE bluetooth low energy for phone-to-mesh gateway applications.
Common production mistakes
Teams get mesh networking bluetooth wrong in predictable ways:
- Skipping failure-mode rehearsal — run a game day or fault injection exercise before peak traffic, not after the first outage.
- Missing correlation context — every error path should carry request, trace, or tenant identifiers so incidents are debuggable.
- Optimizing for demo, not steady state — load tests, cache warm-up, and cold-start paths matter more than local dev latency.
- Undocumented trade-offs — if you chose speed over strict correctness (or vice versa), write that down for the next engineer.
IoT deployments of mesh networking bluetooth fail in the field when firmware assumes stable Wi-Fi, OTA rollback is untested, and device certificates expire without automated renewal.
Resources
- Bluetooth Mesh specification (Bluetooth SIG) — official protocol specification
- nRF Connect SDK Mesh docs — Nordic's mesh development guide
- Bluetooth Mesh Model Specification — application layer models and messages
- Silabs Bluetooth Mesh — alternative SDK with mesh support
Frequently asked questions
What is Bluetooth Mesh?
Bluetooth Mesh is a flooding-based mesh networking protocol built on BLE advertising. Nodes relay messages to extend range beyond a single radio hop. It supports thousands of nodes in a network with publish/subscribe messaging, making it suitable for commercial lighting, sensing, and asset tracking where Wi-Fi coverage is impractical.
How is Bluetooth Mesh different from BLE point-to-point?
Standard BLE is one-to-one (central connects to peripheral). Bluetooth Mesh has no central — every node can relay messages. Devices publish to group addresses and subscribers receive regardless of which relay path delivered the message. Range extends through relay nodes, not higher transmit power.
What are relay and friend nodes in Bluetooth Mesh?
Relay nodes retransmit messages to extend network range — typically mains-powered lights or dedicated routers. Friend nodes store messages for Low Power Nodes (LPNs) that sleep most of the time — the LPN polls its friend for pending messages. This lets battery sensors participate in a mesh without staying awake constantly.
Hiring a senior Android / Flutter engineer?
I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.
Get in touch →