ble mesh development course Bluetooth Mesh: Heartbeat & Friendship

ble mesh development course Bluetooth Mesh: Heartbeat & Friendship
Transport Layer Deep Dive — Opcodes, Timing & Friend Negotiation
📡 Transport Layer
🤝 Friendship Feature
🔋 Low Power Nodes
💓 Heartbeat Messages

What Are We Learning Today?

In Bluetooth Mesh, nodes do not just send data — they also need to monitor the network health and save battery on constrained devices. Two key mechanisms handle this:

  • Heartbeat messages — periodic signals that let nodes discover the network topology.
  • Friendship feature — a power-saving handshake between a Low Power node and a Friend node.

Both live at the Transport Layer and use Transport Control (TC) messages.

Keywords

Transport Control Heartbeat InitTTL Features Field Friendship Low Power Node Friend Node ReceiveDelay ReceiveWindow PollTimeout Friend Poll Friend Request Friend Offer

💓 Section 1 — What Is a Heartbeat Message?

A Heartbeat is a Transport Control message that a node broadcasts periodically so that other nodes can figure out the shape of the network — which nodes are reachable and how many hops away they are.

Think of it like a ping in networking — except instead of checking if a server is alive, it helps the mesh understand its own topology.

Heartbeat Message Fields

The Heartbeat Transport Control message uses opcode 0x0A and carries three fields:

Field Size (bits) What It Means
RFU 1 bit Reserved for Future Use — ignore for now
InitTTL 7 bits Initial Time-To-Live when the message was sent (0x00–0x7F)
Features 16 bits Which mesh features this node currently has active

InitTTL — Why Does It Matter?

When a Heartbeat is received, the receiving node compares the InitTTL (what TTL the sender started with) against the current TTL (what’s left when it arrived). The difference tells you how many hops the message traveled — that’s the distance!

How Hop Count Is Calculated
Sender
InitTTL = 5
Relay
TTL = 4
Relay
TTL = 3
Receiver
TTL = 3
Hops = InitTTL − Received TTL = 5 − 3 = 2 hops

Features Field — Bit Layout

The 16-bit Features field tells other nodes which capabilities are currently active:

Bit Feature Meaning
Bit 0 Relay Node is currently acting as a relay
Bit 1 Proxy Node is currently acting as a GATT proxy
Bit 2 Friend Node is currently acting as a Friend node
Bit 3 Low Power Node is currently in Low Power mode
Bits 4–15 RFU Reserved, always 0
Opcode = 0x0A Total = 24 bits 1 RFU + 7 InitTTL + 16 Features

📋 Section 2 — Transport Control Opcodes Summary

Transport Control messages use a 7-bit opcode. Here is the full opcode table. These messages are not application data — they are internal mesh control signals.

Opcode Message Name Purpose
0x00 Reserved for lower transport layer
0x01 Friend Poll Low Power node asks Friend node for stored messages
0x02 Friend Update Friend node sends security updates to Low Power node
0x03 Friend Request Low Power node broadcasts to all-friends address looking for a Friend
0x04 Friend Offer Candidate Friend node offers friendship to Low Power node
0x05 Friend Clear Tells the old Friend node that friendship has ended
0x06 Friend Clear Confirm Old Friend acknowledges friendship removal
0x07 Friend Subscription List Add Add addresses to Friend’s subscription list
0x08 Friend Subscription List Remove Remove addresses from Friend’s subscription list
0x09 Friend Subscription List Confirm Friend acknowledges subscription list changes
0x0A Heartbeat Node announces its presence and active features for topology discovery
0x0B–0x7F RFU Reserved for future use

🤝 Section 3 — What Is Friendship in Bluetooth Mesh?

Battery-powered mesh nodes (sensors, switches) cannot stay awake all the time to receive messages. The Friendship feature solves this. A Low Power node (LPN) pairs with a always-on Friend node. The Friend stores incoming messages while the LPN sleeps. When the LPN wakes up, it polls the Friend — “any messages for me?” — and goes back to sleep.

🔋 Low Power Node
  • Sleeps most of the time
  • Wakes up periodically
  • Polls Friend for messages
  • Battery-operated devices
  • Example: temperature sensor
⚡ Friend Node
  • Always powered ON
  • Stores messages for the LPN
  • Delivers them on request
  • Manages security updates
  • Example: smart switch / hub

The Three Timing Parameters

Parameter What It Controls
ReceiveDelay Gap between LPN sending a request and then turning on its radio to listen. Gives the Friend time to prepare the response.
ReceiveWindow How long the LPN keeps its radio ON waiting for the response. If a message arrives, it can stop early.
PollTimeout Maximum time the Friend waits between polls from the LPN. If it expires with no poll, friendship is considered terminated.

Friendship Timing — Step by Step

Low Power Node
Friend Node

(sleeping)

sends Request
receives Request

← ReceiveDelay →
Prepares response…

🎙 Radio ON (ReceiveWindow starts)

receives Response
sends Response

(sleeping again)
↑ This cycle repeats. PollTimeout timer resets on each poll.

🔗 Section 4 — How Is Friendship Established?

Friendship is not automatic — the Low Power node must actively find and choose a Friend. Here is the full negotiation flow:

1

LPN broadcasts Friend Request

Sent to the all-friends fixed group address. All Friend-capable nodes in radio range receive it. The message includes minimum requirements the LPN needs (e.g. queue size, subscription list size).

2

Friend Nodes reply with Friend Offer

Each qualifying Friend node responds with its capabilities (RSSI, queue size, etc.). Multiple offers may arrive — the LPN picks the best one.

3

LPN sends Friend Poll to chosen Friend

This is effectively the LPN saying “I choose you.” It also starts the PollTimeout timer.

4

Friend responds with Friend Update — Friendship is established! ✅

The Friend Update contains security information (IV Index, Flags). From this point the LPN can sleep and the Friend stores messages on its behalf.

What Terminates Friendship?

If the LPN goes silent long enough that the PollTimeout timer expires on the Friend node, the Friend treats the friendship as terminated. It stops storing messages for that LPN. The LPN would need to run the Friend Request process again to find a new Friend.

⏱ PollTimeout Timer
LPN sends poll
→ timer resets ✅
LPN sends poll
→ timer resets ✅
LPN silent…
Timer expires ❌
→ Friendship terminated

🐧 Section 5 — Observing This in BlueZ (Linux)

BlueZ includes mesh-cfgclient and a mesh daemon (bluetooth-meshd) that implement the full Bluetooth Mesh stack including Heartbeat subscriptions and Friendship. Here are some practical examples using BlueZ tools.

1. Start the Mesh Daemon

# Start the BlueZ mesh daemon (it listens on D-Bus)
sudo bluetooth-meshd --nodetach --debug

# In another terminal, start the config client
mesh-cfgclient

2. Subscribe a Node to Heartbeat Messages

To receive Heartbeat messages, a node must be configured with a Heartbeat Subscription. This is done via the Configuration Model using the mesh-cfgclient tool:

# Inside mesh-cfgclient interactive shell:
# Connect to target node (replace with actual unicast address)
connect 0001

# Set heartbeat subscription
# dst = group address, period = 0x11 (approx 17 seconds), count = 0x11
hb-sub 0001 0x0001 0xC000 0x11 0x11 0x7F 0x00

3. Parsing the Heartbeat Features Field in C (BlueZ Source Style)

#include <stdint.h>
#include <stdio.h>

/* Heartbeat message payload (after opcode) */
struct hb_msg {
    uint8_t  rfu_and_init_ttl;  /* bit7 = RFU, bits 6:0 = InitTTL */
    uint16_t features;          /* little-endian, 16-bit features field */
} __attribute__((packed));

#define HB_FEAT_RELAY      (1 << 0)
#define HB_FEAT_PROXY      (1 << 1)
#define HB_FEAT_FRIEND     (1 << 2)
#define HB_FEAT_LOW_POWER  (1 << 3)

void parse_heartbeat(const uint8_t *pdu, uint8_t len)
{
    if (len < 3)
        return;

    uint8_t  init_ttl = pdu[0] & 0x7F;       /* mask out RFU bit */
    uint16_t features = pdu[1] | (pdu[2] << 8); /* little-endian */

    printf("InitTTL  : %u\n", init_ttl);
    printf("Relay    : %s\n", (features & HB_FEAT_RELAY)     ? "yes" : "no");
    printf("Proxy    : %s\n", (features & HB_FEAT_PROXY)     ? "yes" : "no");
    printf("Friend   : %s\n", (features & HB_FEAT_FRIEND)    ? "yes" : "no");
    printf("LowPower : %s\n", (features & HB_FEAT_LOW_POWER) ? "yes" : "no");
}

4. Hop Count Calculation

/*
 * When a Heartbeat is received, the mesh stack gives us:
 *   - init_ttl : from the Heartbeat payload
 *   - rx_ttl   : the TTL value in the Network PDU when received
 *
 * hop_count = init_ttl - rx_ttl
 * (tells us how many relay hops the message took)
 */
uint8_t calc_hops(uint8_t init_ttl, uint8_t rx_ttl)
{
    if (rx_ttl > init_ttl)
        return 0;           /* should not happen, guard clause */
    return init_ttl - rx_ttl;
}

5. Monitoring Mesh Traffic with btmon

# btmon captures HCI-level traffic — useful to see raw mesh PDUs
sudo btmon -w mesh_capture.btsnoop

# In another terminal, run your mesh application
# Then Ctrl+C btmon and open the file in Wireshark
# Filter: btle.advertising_header.pdu_type == 0x02  (non-connectable adv)
# Mesh PDUs ride inside advertising bearer PDUs
bluetooth-meshd mesh-cfgclient btmon + Wireshark C bitfield parsing

✅ Section 6 — Quick Summary
Concept One-line Meaning
Heartbeat Node broadcasts InitTTL + active features so others can learn network shape
InitTTL Starting TTL; difference with received TTL = hop count
Features field 16-bit mask: Relay (bit0), Proxy (bit1), Friend (bit2), Low Power (bit3)
Friendship Low Power node + Friend node pairing to save battery via store-and-poll
ReceiveDelay Wait before turning on radio — gives Friend time to prepare
ReceiveWindow How long LPN keeps radio ON listening for Friend’s reply
PollTimeout Max silence allowed; expiry = friendship terminated
Friendship Setup Request → Offer(s) → Poll → Update = friendship live

Keep Exploring Bluetooth Mesh

Next up: Mesh Network Layer — how messages are routed across the mesh using unicast, group, and virtual addresses.

Leave a Reply

Your email address will not be published. Required fields are marked *