50  Thread & Matter Integration

In 60 Seconds

This chapter covers Thread application development using OpenThread SDK and Matter integration. Learn essential CLI commands for network diagnostics, how to configure device roles in code (Router vs SED), how Matter provides universal smart home interoperability on top of Thread networking, and how to avoid critical pitfalls like NAT64 prefix conflicts and incorrect link mode settings that destroy battery life.

Sammy the Sensor wanted to learn coding: “How do I tell Thread what kind of device I am?” Max the Microcontroller showed the code: “If you are plugged into the wall, we set ‘always listening’ to true – you become a mail carrier (Router). If you run on batteries like Bella, we set it to false so you can sleep and save energy!” Bella the Battery nodded: “If someone accidentally sets me to ‘always listening,’ I would run out of power in DAYS instead of YEARS!” Lila the LED was excited about Matter: “It is like a universal translator – before, Apple devices spoke Apple-ish and Google devices spoke Google-ese, but now with Matter, everyone speaks the same language and can be friends!”

50.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Diagnose Thread Networks via CLI: Execute OpenThread CLI commands to inspect device state, routing tables, and neighbor connectivity
  • Implement Device Role Configurations: Write Router and Sleepy End Device link mode settings in OpenThread C code, selecting correct flags for each power profile
  • Evaluate Matter Cluster Architecture: Analyze how Matter organizes device capabilities into standardized clusters running over the Thread network layer
  • Troubleshoot Common Pitfalls: Identify and resolve NAT64 prefix conflicts, commissioning window timeouts, and incorrect link mode settings that destroy battery life
  • Construct Thread Sensor Applications: Build complete SED sensor data transmission patterns using the OpenThread UDP API

50.2 Prerequisites

Before diving into this chapter, you should be familiar with:

Deep Dives:

Integration:

Development:

OpenThread is like the “operating system” for Thread networking—it’s free, open-source software that handles all the complex mesh networking, security, and device communication so you don’t have to write it from scratch.

Matter is the “universal language” for smart home devices. Just like how USB lets different devices plug into any computer, Matter lets smart home devices from different brands (Apple, Google, Amazon, Samsung) work together seamlessly.

How They Work Together:

  1. Your smart light bulb runs OpenThread to join the mesh network
  2. The bulb uses Matter to understand commands like “turn on” or “set brightness to 50%”
  3. Your phone app sends Matter commands over the Thread network
  4. The bulb receives the command via Thread, interprets it via Matter, and turns on

Why This Matters:

  • Before Matter: You needed separate apps for each brand (Philips Hue app, LIFX app, etc.)
  • After Matter: One app controls all Matter devices, regardless of manufacturer
  • Thread’s Role: Provides the reliable, low-power wireless mesh network that carries Matter commands

50.3 OpenThread Development

OpenThread is the open-source reference implementation of Thread. Here’s how to work with it.

50.3.1 OpenThread CLI Commands

The OpenThread CLI provides powerful diagnostic and configuration capabilities:

# Check device state
> state
router

# View network info
> netdata show
Prefixes:
fd12:3456::/64 paros med 4000

# View neighbor table
> neighbor list
0x4c01 0x6801 0x8001

# View routing table
> route
fd12:3456::/64 s med 4000

# Get device RLOC16
> rloc16
0x4c01

# Get mesh-local EID
> eid
fd12:3456:0:0:5e51:1d18:7c31:b80f

# Commissioning commands
> commissioner start
> commissioner joiner add * PSK123456

Common Diagnostic Commands:

Command Purpose Example Output
state Current device role router, leader, child
rloc16 16-bit routing locator 0x4c01
panid Network PAN ID 0x1a2b
channel Current 802.15.4 channel 15
networkname Thread network name HomeThread
leaderdata Leader information Partition ID, weighting
neighbor list Connected neighbors RLOC16 addresses
childtable Child devices (for routers) Child info with timeout
Try It: OpenThread CLI Command Explorer

Explore common OpenThread CLI commands and see what information they reveal about your Thread network. Select a command category and specific command to view its purpose, typical output, and when to use it.

50.3.2 Device Configuration for Different Roles

Router (Mains-Powered Light/Switch):

// OpenThread configuration for always-on router
#include <openthread/thread.h>

void configure_as_router(otInstance *instance) {
    // Enable router-eligible mode
    otThreadSetRouterEligible(instance, true);

    // Set link mode: Router, RxOnWhenIdle, FullThread
    otLinkModeConfig mode = {
        .mRxOnWhenIdle = true,
        .mDeviceType = true,  // Full Thread Device
        .mNetworkData = true  // Full Network Data
    };
    otThreadSetLinkMode(instance, mode);

    // Start Thread protocol
    otThreadSetEnabled(instance, true);
}

The power difference between Router and SED modes is dramatic. For a Thread device with RxOnWhenIdle enabled (Router mode), the radio draws approximately 15-20 mA continuously. For battery life calculation:

\[ \text{Battery Life (hours)} = \frac{\text{Battery Capacity (mAh)}}{\text{Average Current (mA)}} \]

Router example (CR2032 coin cell, 225 mAh): \[ \text{Life} = \frac{225 \text{ mAh}}{18 \text{ mA}} = 12.5 \text{ hours} \]

SED example (same battery, 60-second poll interval, 5 µA sleep, 15 mA active for 10ms per poll): \[ \text{Average} = 0.005 \text{ mA (sleep)} + \frac{(15 \text{ mA} \times 10 \text{ ms})}{60,000 \text{ ms}} = 0.0075 \text{ mA} \] \[ \text{Life} = \frac{225 \text{ mAh}}{0.0075 \text{ mA}} = 30,000 \text{ hours} \approx 3.4 \text{ years} \]

This 2,400× difference in battery life (12 hours vs 3.4 years) shows why mRxOnWhenIdle configuration is critical.

Key Router Configuration:

  • mRxOnWhenIdle = true: Radio always listening (required for routing)
  • mDeviceType = true: Full Thread Device (can become router)
  • mNetworkData = true: Receives complete network topology data
  • otThreadSetRouterEligible(true): Allows promotion to router when needed

Sleepy End Device (Battery Sensor):

// OpenThread configuration for battery-powered sensor
void configure_as_sed(otInstance *instance, uint32_t poll_ms) {
    // Set link mode: Sleepy, minimal network data
    otLinkModeConfig mode = {
        .mRxOnWhenIdle = false,  // Sleep between polls
        .mDeviceType = false,    // Minimal Thread Device
        .mNetworkData = false    // Stable Network Data only
    };
    otThreadSetLinkMode(instance, mode);

    // Configure polling interval (e.g., 60000ms = 1 minute)
    otLinkSetPollPeriod(instance, poll_ms);

    // Start Thread protocol
    otThreadSetEnabled(instance, true);
}

Key SED Configuration:

  • mRxOnWhenIdle = false: Radio sleeps between polls (saves power)
  • mDeviceType = false: Minimal Thread Device (cannot route)
  • mNetworkData = false: Only receives stable network data (less overhead)
  • otLinkSetPollPeriod(): Time between parent polls (trade-off: latency vs battery)
Try It: Thread Battery Life Calculator

Configure a Thread device’s link mode and battery parameters to see how different settings affect battery life. This demonstrates why mRxOnWhenIdle is the most critical configuration choice for battery-powered devices.

50.3.3 Sensor Data Transmission Pattern

// Complete SED sensor application pattern
#include <openthread/thread.h>
#include <openthread/udp.h>

typedef struct {
    otInstance *instance;
    otUdpSocket socket;
    uint16_t poll_period_ms;
} ThreadSensor;

// Initialize sensor as SED
void sensor_init(ThreadSensor *sensor, otInstance *instance) {
    sensor->instance = instance;
    sensor->poll_period_ms = 60000;  // 60 second poll

    // Configure as SED
    otLinkModeConfig mode = {
        .mRxOnWhenIdle = false,
        .mDeviceType = false,
        .mNetworkData = false
    };
    otThreadSetLinkMode(instance, mode);
    otLinkSetPollPeriod(instance, sensor->poll_period_ms);

    // Open UDP socket for sensor data
    otUdpOpen(instance, &sensor->socket, NULL, NULL);
}

// Send sensor reading (call this from sensor interrupt or timer)
void sensor_send_reading(ThreadSensor *sensor, int16_t temperature) {
    // Prepare message
    otMessage *message = otUdpNewMessage(sensor->instance, NULL);
    if (message == NULL) return;

    // Add temperature data (simple format)
    uint8_t payload[4] = {
        (temperature >> 8) & 0xFF,  // High byte
        temperature & 0xFF,          // Low byte
        0x00, 0x01                   // Sensor ID
    };
    otMessageAppend(message, payload, sizeof(payload));

    // Set destination (border router or cloud gateway)
    otMessageInfo info;
    memset(&info, 0, sizeof(info));
    otIp6AddressFromString("fd12:3456::1", &info.mPeerAddr);
    info.mPeerPort = 5683;  // CoAP port

    // Send message
    otUdpSend(sensor->instance, &sensor->socket, message, &info);
}

50.4 Thread + Matter Integration

Thread serves as the primary network layer for Matter smart home devices:

Matter and Thread integration stack showing Matter application layer (device types, clusters, commands) running over Thread network layer (IPv6, mesh routing, border router) on IEEE 802.15.4 physical layer
Figure 50.1: Matter over Thread Protocol Stack Architecture

50.4.1 Matter Device Structure

// Simplified Matter + Thread device structure
typedef struct {
    // Thread configuration
    uint16_t rloc16;
    uint8_t extended_pan_id[8];
    char network_name[17];

    // Matter configuration
    uint16_t vendor_id;
    uint16_t product_id;
    uint32_t fabric_id;

    // Device clusters
    bool on_off_state;
    uint8_t level_value;
    int16_t temperature_value;
} MatterThreadDevice;

// Matter command handler
void handle_on_off_command(MatterThreadDevice *device, bool new_state) {
    device->on_off_state = new_state;

    // Update hardware
    if (new_state) {
        turn_on_light();
    } else {
        turn_off_light();
    }

    // Report attribute change to fabric
    matter_report_attribute_change(
        device->fabric_id,
        CLUSTER_ON_OFF,
        ATTRIBUTE_ON_OFF,
        &device->on_off_state
    );
}

50.4.2 Matter Cluster Model

Matter organizes device functionality into clusters—standardized groups of commands and attributes:

Cluster Purpose Example Attributes Example Commands
OnOff Binary control OnOff (bool) On(), Off(), Toggle()
LevelControl Brightness/position CurrentLevel (0-254) MoveToLevel(level)
ColorControl Color management Hue, Saturation MoveToHue(hue)
TemperatureMeasurement Sensor reading MeasuredValue (°C×100) (read-only)
DoorLock Lock control LockState LockDoor(), UnlockDoor()

Why This Matters:

  • All Matter lights implement the same OnOff cluster
  • Any Matter controller can send OnOff.Toggle() to any Matter light
  • Brand interoperability is guaranteed by the standard
Try It: Matter Cluster Explorer

Explore how Matter organizes device functionality into standardized clusters. Select a device type to see which clusters it implements and experiment with sending commands.

50.5 Common Pitfalls

Pitfall: Misconfiguring Border Router NAT64 Prefix

The Mistake: Developers manually configure the NAT64 prefix on the Border Router (e.g., setting 64:ff9b::/96) without verifying that the prefix doesn’t conflict with the Thread network’s mesh-local prefix or other network infrastructure, causing IPv6-to-IPv4 translation failures.

Why It Happens: NAT64 allows Thread devices to reach IPv4 cloud services, but the synthesized IPv6 addresses must use a dedicated prefix that doesn’t overlap with any existing IPv6 addressing in the network. Many developers copy example configurations without understanding that the 64:ff9b::/96 well-known prefix requires proper upstream routing, or they use custom prefixes without advertising them correctly via Thread Network Data.

The Fix: Use the Border Router’s automatic NAT64 prefix advertisement. In OpenThread Border Router (OTBR):

# Check current NAT64 prefix
> nat64 prefix
64:ff9b::/96 (active)

# Verify prefix is advertised in network data
> netdata show
Prefixes:
64:ff9b::/96 paros med 4000  # NAT64 prefix advertised

# For custom prefix, ensure it's unique and properly routed
> nat64 prefix 2001:db8:1:ffff::/96

Always verify with netdata show that the prefix appears with the paros flag (Published, Active, Router, On-mesh, Stable) and test with ping 64:ff9b::8.8.8.8 from a Thread device.

Pitfall: Commissioning Window Timeout During Multi-Device Setup

The Mistake: When commissioning multiple Thread devices in sequence, developers open the commissioning window once and assume it stays open indefinitely. After 15 minutes (900 seconds default), the window closes automatically, causing subsequent devices to fail joining with “No network found” or “Authentication failed” errors.

Why It Happens: Thread’s commissioning window has a security timeout to prevent prolonged exposure to unauthorized joining attempts. The default OPENTHREAD_CONFIG_COMMISSIONER_JOINER_TIMEOUT is 120 seconds per joiner, and the overall window timeout is typically 900 seconds. Developers batch-commissioning many devices often exceed this limit without realizing the window closed silently.

The Fix: Implement commissioning workflow with explicit window management:

// Before each batch of devices, open/extend window
otCommissionerStart(instance);
otCommissionerAddJoiner(instance, NULL, "PSK123456", 120); // 120s per joiner

// Check window status periodically
if (otCommissionerGetState(instance) != OT_COMMISSIONER_STATE_ACTIVE) {
    // Window closed - reopen for next batch
    otCommissionerStart(instance);
}

// For large deployments, use longer per-joiner timeout
otCommissionerAddJoiner(instance, NULL, pskd, 300); // 5 minutes per device

In production, use Matter’s enhanced commissioning with explicit window control: OpenCommissioningWindow(timeout=1800) for 30-minute windows during bulk provisioning.

Pitfall: Incorrect Link Mode for Battery Devices

The Mistake: Setting mRxOnWhenIdle = true for battery-powered sensors, causing the radio to stay on continuously and draining the battery in days instead of years.

Why It Happens: Developers copy router configuration examples without understanding that mRxOnWhenIdle controls whether the radio sleeps. For routers, this must be true (always listening to route traffic). For battery sensors, this must be false (sleep between polls).

The Fix: Always verify link mode matches device power source:

// Battery device - MUST be false
otLinkModeConfig sed_mode = {
    .mRxOnWhenIdle = false,  // Critical for battery life
    .mDeviceType = false,
    .mNetworkData = false
};

// Mains-powered device - must be true
otLinkModeConfig router_mode = {
    .mRxOnWhenIdle = true,   // Required for routing
    .mDeviceType = true,
    .mNetworkData = true
};

Verify with mode CLI command - output should show r (rxOnWhenIdle) only for mains-powered devices.

50.6 Worked Example: Thread Device Commissioning Sequence

Worked Example: Thread Device Commissioning Sequence

Scenario: You are commissioning a new Eve Door & Window sensor (battery-powered SED) onto an existing Thread network using the Apple Home app on your iPhone. The Thread network already has a HomePod Mini as Border Router and 6 smart bulbs as routers.

Given:

  • Existing Thread network: PAN ID 0x1A2B, Channel 15
  • Network name: “HomeThread”
  • Eve sensor PSKd (from QR code): “A1B2C3D4E5F6”
  • Commissioner: iPhone with Apple Home app
  • HomePod Mini: Border Router + Leader
  • Target parent router: Hallway smart bulb (strongest signal)

Steps:

  1. Discovery phase (0-5 seconds):
    • Eve sensor powers on and enters commissioning mode
    • Scans 802.15.4 channels 11-26 for Thread networks
    • Receives Beacon from HomePod Mini on channel 15
    • Network info: PAN ID 0x1A2B, Extended PAN ID, Network Name “HomeThread”
  2. Commissioner authentication (5-15 seconds):
    • User scans Eve sensor QR code with iPhone camera
    • iPhone extracts PSKd: “A1B2C3D4E5F6”
    • iPhone (Commissioner) connects to HomePod Mini via Wi-Fi
    • Initiates commissioning session with Leader
  3. DTLS session establishment (15-25 seconds):
    • Commissioner sends Joiner PSKd to Leader
    • Leader advertises commissioning availability
    • Eve sensor initiates DTLS handshake with Leader using PSKd
    • DTLS 1.2 session established (encrypted channel)
    • Session key: ECDH-derived 128-bit AES key
  4. Credential transfer (25-30 seconds):
    • Leader sends Network Master Key (128-bit) over DTLS
    • Transfers: Channel 15, PAN ID 0x1A2B, Extended PAN ID
    • Eve sensor stores credentials in secure flash
    • DTLS session closes
  5. Mesh attachment (30-45 seconds):
    • Eve sensor sends MLE Parent Request (broadcast)
    • Hallway bulb responds as best parent (strongest RSSI)
    • MLE Child ID Request → Leader assigns Child ID
    • Eve sensor attaches to Hallway bulb as parent
    • Assigned RLOC16 address (e.g., 0x7C01)

Result: Eve Door sensor successfully commissioned in 45 seconds. Device appears in Apple Home app as “Eve Door Sensor”. Parent router: Hallway bulb. RLOC16: 0x7C01. Poll interval: 30 seconds (SED mode). First status report sent to cloud via HomePod Mini Border Router.

Key Insight: Thread commissioning uses out-of-band authentication (QR code provides PSKd) combined with DTLS encryption for credential transfer. The PSKd is never transmitted over the air; it’s used only to derive session keys. This provides significantly stronger security than Wi-Fi WPA2-PSK because each device has unique credentials, and network keys cannot be captured by simply sniffing commissioning traffic.

Try It: Thread Commissioning Timeline Simulator

Simulate the Thread secure commissioning process step by step. Adjust network conditions to see how they affect commissioning time and observe the security mechanisms at each stage.

50.7 Understanding Check: Thread + Matter Integration

Scenario: You’re building a smart home using Matter devices from different brands: Philips Hue lights, Eve door sensors, and Nanoleaf panels. Your Apple HomePod Mini is the Thread Border Router. You also have some Wi-Fi-based Matter devices (cameras, speakers).

Think about:

  1. How does Matter use Thread differently for battery sensors vs mains-powered lights?
  2. What happens when you control a Thread light from your iPhone while away from home?
  3. Why can devices from different brands work together seamlessly?

Key Insight:

  • Matter uses Thread for mesh, Wi-Fi for bandwidth: Battery sensors use Thread (low power, mesh reliability). Cameras/speakers use Wi-Fi (high bandwidth for video/audio). Matter application layer works over both.
  • Message path (remote control): iPhone (cellular) → Cloud → Wi-Fi router → HomePod Mini (Border Router) → Thread mesh → Light bulb. Border Router bridges Wi-Fi ↔︎ Thread.
  • Cross-brand compatibility: Matter defines standard device types (light, sensor, lock) and control commands (on/off, brightness, lock/unlock). Thread provides the reliable network layer. Brands implement Matter spec, so all devices speak the same language.
  • Thread’s role: Provides IPv6 networking and mesh routing. Matter sits on top, handling application-level device control and interoperability.

Real-world example: When you say “Hey Siri, turn off bedroom lights”: 1. Siri (on HomePod Mini) sends Matter command over Thread mesh to Philips Hue bulb 2. Bulb receives Matter OnOff command via Thread multi-hop routing 3. Bulb turns off and sends status update back via Thread mesh 4. No Philips Hue bridge needed - HomePod Mini Border Router handles Thread ↔︎ Wi-Fi

Objective: Thread uses UDP over IPv6 for device communication. This lab demonstrates the same UDP messaging pattern on ESP32 Wi-Fi, helping you understand the data transmission flow before working with actual Thread hardware.

Code to Try:

#include <WiFi.h>
#include <WiFiUdp.h>

const char* ssid = "Wokwi-GUEST";
const char* password = "";

WiFiUDP udp;
const int localPort = 5683;  // CoAP port, common in Thread/IoT
unsigned long lastSend = 0;
int messageCount = 0;

// Simulated Thread device roles
enum DeviceRole { LEADER, ROUTER, SED };
DeviceRole myRole = ROUTER;

const char* roleToString(DeviceRole role) {
  switch(role) {
    case LEADER: return "Leader";
    case ROUTER: return "Router";
    case SED:    return "Sleepy End Device";
    default:     return "Unknown";
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("=== Thread-Style UDP Communication Demo ===");

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nNetwork joined: " + WiFi.localIP().toString());

  udp.begin(localPort);
  Serial.printf("UDP socket open on port %d\n", localPort);
  Serial.printf("Device role: %s\n", roleToString(myRole));

  // Simulated Thread network info
  Serial.println("\n--- Thread Network Info (Simulated) ---");
  Serial.println("Network Name: HomeThread");
  Serial.println("PAN ID:       0x1A2B");
  Serial.printf("RLOC16:       0x%04X\n", random(0x0400, 0xFC00));
  Serial.println("Channel:      15 (802.15.4)");
}

void loop() {
  // Check for incoming UDP messages
  int packetSize = udp.parsePacket();
  if (packetSize) {
    char buffer[255];
    int len = udp.read(buffer, 255);
    buffer[len] = 0;
    Serial.printf("[RX] From %s:%d -> %s\n",
      udp.remoteIP().toString().c_str(), udp.remotePort(), buffer);
  }

  // Send sensor data every 5 seconds (like Thread SED poll)
  if (millis() - lastSend > 5000) {
    lastSend = millis();
    messageCount++;

    float temp = 22.0 + random(-20, 20) / 10.0;
    char msg[64];
    snprintf(msg, sizeof(msg), "{\"id\":%d,\"temp\":%.1f,\"role\":\"%s\"}",
      messageCount, temp, roleToString(myRole));

    // In Thread: this would be sent to the Border Router via mesh
    udp.beginPacket("255.255.255.255", localPort);
    udp.write((uint8_t*)msg, strlen(msg));
    udp.endPacket();

    Serial.printf("[TX] Msg #%d: %s (UDP broadcast)\n", messageCount, msg);
  }
}

What to Observe:

  1. The UDP communication pattern mirrors Thread’s data transmission: send small JSON payloads via UDP
  2. Thread uses IPv6 multicast for mesh flooding; this demo uses UDP broadcast as an analogy
  3. The simulated RLOC16 shows how Thread assigns 16-bit routing addresses to each device
  4. In real Thread, the SED would poll its parent router periodically rather than sending continuously

50.9 How It Works: Thread Secure Commissioning Process

How It Works: DTLS Commissioning with PSKd

Thread uses a secure commissioning process to add new devices without exposing long-term network credentials:

Step 1: Device Powers On (Joiner Role)

  • New device enters “detached” state (not part of any Thread network)
  • Device knows only its PSKd (Pre-Shared Key for Devices) - printed on QR code or label
  • PSKd is device-specific, not the network key (single-use credential)

Step 2: User Initiates Commissioning (Commissioner Role)

  • User scans device QR code in Matter/Thread app (e.g., Apple Home, Google Home)
  • App sends PSKd to Commissioner (Border Router or dedicated commissioning device)
  • Commissioner authorizes the PSKd for 120 seconds (time window for joining)

Step 3: Joiner Discovery

  • Device broadcasts MLE Discovery Request on all 16 Thread channels (11-26)
  • Commissioner (or Joiner Routers acting as proxies) responds with MLE Discovery Response
  • Response includes: network name, PAN ID, channel, Commissioner UDP port

Step 4: DTLS Handshake (Encrypted Channel Establishment)

  • Device initiates DTLS handshake to Commissioner using PSKd as shared secret
  • DTLS provides end-to-end encryption for commissioning (even over multi-hop mesh)
  • Mutual authentication: Device proves it has valid PSKd, Commissioner proves it’s authorized

Step 5: Credential Distribution

  • Commissioner sends Network Master Key over DTLS-encrypted channel
  • Additional parameters distributed: PAN ID, extended PAN ID, mesh-local prefix, network name
  • Device stores credentials in non-volatile memory (survives power cycles)

Step 6: Network Attachment

  • Device uses Network Master Key to authenticate to Thread network
  • Follows standard MLE Parent Request/Response process
  • Router assigns RLOC16, device configures IPv6 addresses
  • Device enters “child” or “router” state (operational)

Step 7: PSKd Invalidation

  • Commissioner invalidates PSKd after successful join (single-use credential)
  • Device can no longer use PSKd (must be factory reset to rejoin)
  • Prevents replay attacks (stolen QR code can’t join network)

Key Security Properties:

  • PSKd never transmitted in plaintext: Used only as DTLS pre-shared key
  • Network key never exposed: Distributed only over DTLS tunnel
  • Time-limited authorization: 120-second window reduces attack surface
  • Out-of-band authentication: Physical QR code scan proves device proximity
  • Forward secrecy: Compromised PSKd after join doesn’t expose network key

50.10 Try It Yourself: Thread Development with OpenThread CLI

Scenario: You’re developing a Thread network using OpenThread CLI to understand network formation, device roles, and diagnostics before writing production firmware.

Tasks:

  1. Form a Thread Network (Border Router):
# Initialize Thread stack
> dataset init new
Done

# Configure network parameters
> dataset networkname MyThreadNet
Done
> dataset channel 15
Done
> dataset panid 0xABCD
Done
> dataset commit active
Done

# Start Thread interface
> ifconfig up
Done
> thread start
Done

# Verify Border Router role
> state
leader
> rloc16
0x0000

# Check assigned addresses
> ipaddr
fd12:3456:789a:1::ff:fe00:0     (Mesh-Local EID)
fe80::a0b:c0d:e0f:1234           (Link-Local)
  1. Add a Router Device:
# On second device
> dataset networkname MyThreadNet
Done
> dataset channel 15
Done
> dataset panid 0xABCD
Done
> dataset masterkey 00112233445566778899aabbccddeeff
Done
> dataset commit active
Done

> ifconfig up
> thread start

# Wait 15-30 seconds, then check role
> state
router

> rloc16
0x4800    # Router ID 18 (0x12 in hex)

# Check parent (should show Border Router)
> parent
Ext Addr: a0bc0de0f1234567
Rloc: 0x0000
Link Quality In: 3
Link Quality Out: 3
  1. Add a Sleepy End Device:
# On third device (battery-powered sensor)
# Join network (same dataset as router)
> dataset networkname MyThreadNet
> dataset channel 15
> dataset panid 0xABCD
> dataset masterkey 00112233445566778899aabbccddeeff
> dataset commit active

# Configure as SED (critical! NO 'r' flag)
> mode -    # No flags: RxOnWhenIdle OFF, MTD, stable data only
Done

# Start Thread
> ifconfig up
> thread start

# Verify SED role
> state
child

> childip max
1   # Maximum 1 IP address (minimal)

# Check poll interval
> pollperiod
3000   # 3000ms = 3 seconds (configure longer for battery)

# Set longer poll interval
> pollperiod 60000   # 60 seconds
Done
  1. Network Diagnostics:
# On Border Router, check neighbor table
> neighbor table
| Role | RLOC16 | Age | Avg RSSI | Last RSSI |
|------|--------|-----|----------|-----------|
|  R   | 0x4800 | 23  |   -45    |    -42    |
|  C   | 0x4801 | 45  |   -58    |    -60    |

# Check child table
> child table
| ID | RLOC16 | Timeout | Age | LQ | RSSI |
|----|--------|---------|-----|----|----- |
|  1 | 0x0401 | 240     | 45  | 3  | -58  |

# Check router table
> router table
| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out |
|----|--------|----------|-----------|-------|--------|
|  0 | 0x0000 | -        | 0         | 3     | 3      |
| 18 | 0x4800 | 0        | 1         | 3     | 3      |

What to Observe:

  • Border Router becomes Leader (state = leader, RLOC16 = 0x0000)
  • Router joins and gets RLOC16 with non-zero Router ID
  • SED joins as child with mode - (no r flag means RxOnWhenIdle OFF)
  • Poll period affects how often SED wakes (battery life trade-off)
  • Neighbor/child/router tables show network topology

Common Pitfalls to Avoid:

  • Including the r flag for SEDs: The r flag enables RxOnWhenIdle (radio always on), wasting battery – use mode s or mode - for SEDs
  • Mismatched masterkey: Devices won’t join if network key differs
  • Wrong channel: Devices won’t see each other if on different channels
  • thread start before ifconfig up: Must enable interface first

50.11 Concept Check

50.12 Concept Relationships

Concept Relationship Connected Concept
PSKd (Pre-Shared Key for Devices) Secures DTLS commissioning handshake (single-use credential)
mRxOnWhenIdle Flag Determines Router/FED (true, ~20 mA) vs SED (false, ~5 µA) power mode
OpenThread CLI Provides Network diagnostics (state, rloc16, neighbor table, child table)
Matter Clusters Defines Application-layer device capabilities (On/Off, LevelControl, etc.)
NAT64 Prefix Enables IPv4 internet connectivity for IPv6-only Thread devices

50.13 See Also

50.14 Summary

This chapter covered Thread development and Matter integration:

  • OpenThread CLI: Diagnostic commands (state, rloc16, neighbor list) provide visibility into network health and topology
  • Device Role Configuration: Routers require mRxOnWhenIdle = true for continuous listening; SEDs require mRxOnWhenIdle = false for battery life
  • Matter Integration: Matter provides application-layer device interoperability (clusters, commands) while Thread provides the mesh network layer
  • Common Pitfalls: NAT64 prefix conflicts cause IPv4 connectivity failures; commissioning timeouts cause batch provisioning failures; incorrect link mode destroys battery life
  • Commissioning: Secure device onboarding uses QR codes (PSKd) for out-of-band authentication combined with DTLS encryption

50.15 Knowledge Check

::

::

Key Concepts

  • OpenThread: An open-source implementation of the Thread networking protocol by Google/Nest, widely used in ESP32-H2, nRF52840, and other IoT chips.
  • ot-cli: The OpenThread command-line interface for managing Thread network configuration, viewing network state, and performing commissioning operations.
  • FreeRTOS + OpenThread: A common embedded integration pairing OpenThread’s networking stack with FreeRTOS’s real-time task scheduling for Thread-enabled IoT devices.
  • Thread Demo App: A reference application demonstrating Thread network formation, CoAP communication, and device role management on target hardware.
  • RCP (Radio Co-Processor): An architecture where the IEEE 802.15.4 radio runs OpenThread minimal host on a co-processor, with the full Thread stack running on a separate application processor.

50.16 What’s Next

Topic Chapter What You Will Learn
Thread deployment Thread Deployment Guide Border Router configuration, multi-network design, and production troubleshooting
Thread security Thread Security and Matter Cryptographic foundations, key rotation, and Matter security architecture
Matter protocol Matter Overview Matter application layer, device types, and multi-admin fabric model
Thread fundamentals Thread Network Architecture Device roles, mesh topology, and addressing architecture
Thread operations Thread Network Operations Network formation, self-healing mechanisms, and power management
Hands-on labs Hands-On Labs Hub Interactive ESP32 simulations for Thread and mesh networking concepts