26 Lab: Zigbee Mesh Network
Interactive ESP32 simulation demonstrating Zigbee mesh networking concepts
Putting Numbers to It
Lab execution time can be estimated before starting runs:
\[ T_{\text{total}} = N_{\text{runs}} \times (t_{\text{setup}} + t_{\text{run}} + t_{\text{review}}) \]
Worked example: With 5 runs and per-run times of 4 min setup, 6 min execution, and 3 min review, total lab time is \(5\times(4+6+3)=65\) minutes. This prevents under-scoping and helps schedule complete experimental cycles.
26.1 Learning Objectives
By completing this lab, you will be able to:
- Configure peer-to-peer communication using ESP-NOW across multiple ESP32 nodes
- Construct message relay and forwarding logic to simulate multi-hop mesh routing
- Deploy a four-node mesh topology with coordinator, router, and end device roles
- Analyse self-healing behaviour by disabling routers and measuring reroute latency
- Diagnose wireless communication failures by interpreting serial monitor message flow
For Beginners: Zigbee Mesh Network Lab
In this lab, you will build a simulated Zigbee mesh network using ESP32 microcontrollers. You will create coordinator, router, and end device nodes, then watch them form a network and exchange messages. It is like watching a team organize itself – each device finds its role and starts cooperating automatically.
26.2 Introduction
This interactive lab uses the Wokwi ESP32 simulator to demonstrate mesh networking concepts that underpin Zigbee. Since Wokwi does not have native Zigbee support, we use ESP-NOW, a peer-to-peer protocol that mimics many mesh networking behaviors.
Why ESP-NOW for Learning Zigbee?
ESP-NOW and Zigbee share key characteristics:
| Feature | Zigbee | ESP-NOW |
|---|---|---|
| Frequency | 2.4 GHz | 2.4 GHz |
| Range | 10-100m | 10-250m |
| Low power | Yes | Yes |
| Peer-to-peer | Yes | Yes |
| Mesh capable | Native | Manual relay |
| Max peers | 65,000+ | 20 per device |
While Zigbee has sophisticated built-in mesh routing (AODV), ESP-NOW requires manual message relay - which is actually better for learning because you implement the routing logic yourself!
26.3 Lab Components
The simulation contains four ESP32 nodes representing a Zigbee mesh network:
| Node | Role | LED Behaviour | Function |
|---|---|---|---|
| ESP32 #1 | Coordinator | STATUS_LED stays ON | Network controller, message destination |
| ESP32 #2 | Router A | STATUS_LED blinks every 2 s | Primary relay path |
| ESP32 #3 | Router B | STATUS_LED blinks every 2 s | Backup relay path (self-healing) |
| ESP32 #4 | End Device | MSG_LED flashes on send | Sensor node, message source |
26.4 Embedded Wokwi Mesh Simulator
How to Use This Simulator
- Click “Start Simulation” to begin
- Watch the Serial Monitor for message flow logs
- LEDs blink when routing messages
- To test self-healing: Stop Router A by clicking the pause/stop button in its tab
- The network will automatically reroute through Router B
Simulator Setup Instructions
Since Wokwi doesn’t persist embedded projects, copy the complete mesh network code below into the simulator. The code creates a 4-node mesh network demonstrating all key Zigbee concepts.
26.5 Complete Mesh Network Code
Copy this code into each of 4 Wokwi tabs (change NODE_ROLE for each):
// ============================================================
// ESP-NOW Zigbee-Style Mesh Network Simulator
// Demonstrates: Coordinator, Router, End Device roles
// Multi-hop routing, Self-healing, Broadcast/Unicast
// ============================================================
#include <WiFi.h>
#include <esp_now.h>
// ============ CONFIGURATION - CHANGE FOR EACH NODE ============
// Set ONE of these to 1, others to 0:
#define IS_COORDINATOR 0 // Node 1: Set to 1
#define IS_ROUTER_A 0 // Node 2: Set to 1
#define IS_ROUTER_B 0 // Node 3: Set to 1
#define IS_END_DEVICE 1 // Node 4: Set to 1 (default)
// LED Pin Configuration
#define STATUS_LED 2 // Built-in LED
#define MSG_LED 4 // Message activity LED
// ============ NETWORK CONSTANTS ============
#define MAX_HOPS 5
#define HEARTBEAT_INTERVAL 5000
#define ROUTE_TIMEOUT 15000
#define MAX_NEIGHBORS 10
#define MSG_RETRY_COUNT 3
#define ACK_TIMEOUT 500
// Broadcast address
uint8_t broadcastMAC[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// ============ MESSAGE STRUCTURES ============
enum MessageType {
MSG_DATA = 0,
MSG_HEARTBEAT = 1,
MSG_ROUTE_REQUEST = 2,
MSG_ROUTE_REPLY = 3,
MSG_ACK = 4,
MSG_BROADCAST = 5
};
typedef struct __attribute__((packed)) {
uint8_t msgType;
uint8_t srcMAC[6];
uint8_t dstMAC[6];
uint8_t hopCount;
uint8_t maxHops;
uint32_t seqNum;
uint8_t payload[64];
uint8_t payloadLen;
} MeshMessage;
typedef struct {
uint8_t mac[6];
char role[12];
int8_t rssi;
uint32_t lastSeen;
bool isActive;
bool canRoute;
} Neighbor;
// ============ GLOBAL STATE ============
Neighbor neighbors[MAX_NEIGHBORS];
int neighborCount = 0;
uint32_t messageSeq = 0;
uint32_t lastHeartbeat = 0;
uint32_t messagesRouted = 0;
uint32_t messagesReceived = 0;
uint32_t lastDataSend = 0;
char myRole[12];
uint8_t myMAC[6];
bool awaitingAck = false;
uint32_t ackSeqNum = 0;
uint32_t ackSentTime = 0;
int ackRetries = 0;
// ============ LED FUNCTIONS ============
void blinkLED(int pin, int times, int delayMs) {
for (int i = 0; i < times; i++) {
digitalWrite(pin, HIGH);
delay(delayMs);
digitalWrite(pin, LOW);
delay(delayMs);
}
}
void flashMessageLED() {
digitalWrite(MSG_LED, HIGH);
delay(50);
digitalWrite(MSG_LED, LOW);
}
// ============ NEIGHBOR TABLE ============
int findNeighbor(const uint8_t* mac) {
for (int i = 0; i < neighborCount; i++) {
if (memcmp(neighbors[i].mac, mac, 6) == 0) return i;
}
return -1;
}
void updateNeighbor(const uint8_t* mac, const char* role, int8_t rssi) {
int idx = findNeighbor(mac);
if (idx >= 0) {
neighbors[idx].rssi = rssi;
neighbors[idx].lastSeen = millis();
neighbors[idx].isActive = true;
strncpy(neighbors[idx].role, role, 11);
neighbors[idx].canRoute = (strcmp(role, "ROUTER") == 0 ||
strcmp(role, "COORDINATOR") == 0);
} else if (neighborCount < MAX_NEIGHBORS) {
memcpy(neighbors[neighborCount].mac, mac, 6);
strncpy(neighbors[neighborCount].role, role, 11);
neighbors[neighborCount].rssi = rssi;
neighbors[neighborCount].lastSeen = millis();
neighbors[neighborCount].isActive = true;
neighbors[neighborCount].canRoute = (strcmp(role, "ROUTER") == 0 ||
strcmp(role, "COORDINATOR") == 0);
neighborCount++;
Serial.printf("[MESH] New neighbor: %02X:%02X:%02X:%02X:%02X:%02X (%s)\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], role);
}
}
void checkNeighborTimeouts() {
for (int i = 0; i < neighborCount; i++) {
if (neighbors[i].isActive &&
(millis() - neighbors[i].lastSeen > ROUTE_TIMEOUT)) {
neighbors[i].isActive = false;
Serial.printf("[MESH] Neighbor timeout: %02X:%02X:%02X:%02X:%02X:%02X\n",
neighbors[i].mac[0], neighbors[i].mac[1], neighbors[i].mac[2],
neighbors[i].mac[3], neighbors[i].mac[4], neighbors[i].mac[5]);
Serial.println("[MESH] >>> SELF-HEALING: Route may need update <<<");
}
}
}
void printNeighborTable() {
Serial.println("\n========== NEIGHBOR TABLE ==========");
for (int i = 0; i < neighborCount; i++) {
Serial.printf(" %d. %02X:%02X:%02X:%02X:%02X:%02X - %s (RSSI: %d) %s\n",
i + 1, neighbors[i].mac[0], neighbors[i].mac[1],
neighbors[i].mac[2], neighbors[i].mac[3],
neighbors[i].mac[4], neighbors[i].mac[5],
neighbors[i].role, neighbors[i].rssi,
neighbors[i].isActive ? "[ACTIVE]" : "[OFFLINE]");
}
Serial.println("=====================================\n");
}
// ============ ROUTING ============
int findBestRoute(const uint8_t* destMAC) {
int directIdx = findNeighbor(destMAC);
if (directIdx >= 0 && neighbors[directIdx].isActive) return directIdx;
int bestRouter = -1;
int8_t bestRSSI = -127;
for (int i = 0; i < neighborCount; i++) {
if (neighbors[i].isActive && neighbors[i].canRoute) {
if (neighbors[i].rssi > bestRSSI) {
bestRSSI = neighbors[i].rssi;
bestRouter = i;
}
}
}
if (bestRouter >= 0) {
Serial.printf("[ROUTE] Selected: %02X:%02X:%02X:%02X:%02X:%02X (RSSI: %d)\n",
neighbors[bestRouter].mac[0], neighbors[bestRouter].mac[1],
neighbors[bestRouter].mac[2], neighbors[bestRouter].mac[3],
neighbors[bestRouter].mac[4], neighbors[bestRouter].mac[5],
bestRSSI);
}
return bestRouter;
}
// ============ MESSAGE SENDING ============
void sendMessage(MeshMessage* msg, const uint8_t* nextHop) {
esp_err_t result = esp_now_send(nextHop, (uint8_t*)msg, sizeof(MeshMessage));
flashMessageLED();
if (result == ESP_OK) {
Serial.printf("[TX] Sent %s (seq: %lu, hops: %d)\n",
msg->msgType == MSG_DATA ? "DATA" :
msg->msgType == MSG_HEARTBEAT ? "HEARTBEAT" :
msg->msgType == MSG_ACK ? "ACK" : "OTHER",
msg->seqNum, msg->hopCount);
}
}
void sendHeartbeat() {
MeshMessage hb;
hb.msgType = MSG_HEARTBEAT;
memcpy(hb.srcMAC, myMAC, 6);
memset(hb.dstMAC, 0xFF, 6);
hb.hopCount = 0;
hb.maxHops = 1;
hb.seqNum = ++messageSeq;
snprintf((char*)hb.payload, sizeof(hb.payload), "%s", myRole);
hb.payloadLen = strlen(myRole);
sendMessage(&hb, broadcastMAC);
}
void sendSensorData() {
if (!IS_END_DEVICE) return;
MeshMessage data;
data.msgType = MSG_DATA;
memcpy(data.srcMAC, myMAC, 6);
memset(data.dstMAC, 0xFF, 6);
data.hopCount = 0;
data.maxHops = MAX_HOPS;
data.seqNum = ++messageSeq;
float temp = 20.0 + (random(0, 100) / 10.0);
float humidity = 40.0 + (random(0, 200) / 10.0);
snprintf((char*)data.payload, sizeof(data.payload),
"TEMP:%.1f,HUM:%.1f", temp, humidity);
data.payloadLen = strlen((char*)data.payload);
Serial.println("\n====== SENDING SENSOR DATA ======");
Serial.printf("Temperature: %.1f C, Humidity: %.1f%%\n", temp, humidity);
int routeIdx = findBestRoute(data.dstMAC);
if (routeIdx >= 0) {
sendMessage(&data, neighbors[routeIdx].mac);
awaitingAck = true;
ackSeqNum = data.seqNum;
ackSentTime = millis();
} else {
Serial.println("No route - broadcasting");
sendMessage(&data, broadcastMAC);
}
}
// ============ MESSAGE RECEIVING ============
void onDataSent(const uint8_t* mac, esp_now_send_status_t status) {
Serial.println(status == ESP_NOW_SEND_SUCCESS ?
"[DELIVERY] Confirmed" : "[DELIVERY] Failed");
}
void onDataReceived(const esp_now_recv_info_t* info, const uint8_t* data, int len) {
MeshMessage msg;
memcpy(&msg, data, sizeof(MeshMessage));
messagesReceived++;
flashMessageLED();
updateNeighbor(info->src_addr,
msg.msgType == MSG_HEARTBEAT ? (char*)msg.payload : "UNKNOWN",
info->rx_ctrl->rssi);
Serial.printf("\n[RX] %s from %02X:%02X:%02X:%02X:%02X:%02X\n",
msg.msgType == MSG_DATA ? "DATA" :
msg.msgType == MSG_HEARTBEAT ? "HEARTBEAT" :
msg.msgType == MSG_ACK ? "ACK" : "OTHER",
info->src_addr[0], info->src_addr[1], info->src_addr[2],
info->src_addr[3], info->src_addr[4], info->src_addr[5]);
if (msg.msgType == MSG_DATA) {
if (IS_COORDINATOR) {
Serial.println("\n****** DATA AT COORDINATOR ******");
Serial.printf("Hops: %d, Data: %s\n", msg.hopCount, (char*)msg.payload);
Serial.println("*********************************\n");
MeshMessage ack;
ack.msgType = MSG_ACK;
memcpy(ack.srcMAC, myMAC, 6);
memcpy(ack.dstMAC, msg.srcMAC, 6);
ack.seqNum = msg.seqNum;
ack.hopCount = 0;
ack.maxHops = MAX_HOPS;
sendMessage(&ack, info->src_addr);
blinkLED(STATUS_LED, 3, 100);
} else if (IS_ROUTER_A || IS_ROUTER_B) {
if (msg.hopCount < msg.maxHops) {
msg.hopCount++;
messagesRouted++;
Serial.printf("[RELAY] Forwarding (hop %d)\n", msg.hopCount);
int nextHopIdx = findBestRoute(msg.dstMAC);
if (nextHopIdx >= 0) {
sendMessage(&msg, neighbors[nextHopIdx].mac);
} else {
sendMessage(&msg, broadcastMAC);
}
blinkLED(MSG_LED, 2, 50);
}
}
} else if (msg.msgType == MSG_ACK) {
if (msg.seqNum == ackSeqNum && awaitingAck) {
awaitingAck = false;
Serial.printf("[ACK] Confirmed for seq %lu\n", msg.seqNum);
blinkLED(STATUS_LED, 2, 100);
} else if (IS_ROUTER_A || IS_ROUTER_B) {
if (msg.hopCount < msg.maxHops) {
msg.hopCount++;
int routeIdx = findNeighbor(msg.dstMAC);
if (routeIdx >= 0) sendMessage(&msg, neighbors[routeIdx].mac);
else sendMessage(&msg, broadcastMAC);
}
}
}
}
// ============ SETUP ============
void setup() {
Serial.begin(115200);
delay(1000);
pinMode(STATUS_LED, OUTPUT);
pinMode(MSG_LED, OUTPUT);
blinkLED(STATUS_LED, 3, 200);
if (IS_COORDINATOR) strcpy(myRole, "COORDINATOR");
else if (IS_ROUTER_A || IS_ROUTER_B) strcpy(myRole, "ROUTER");
else strcpy(myRole, "END_DEVICE");
Serial.println("\n========================================");
Serial.println(" Zigbee-Style Mesh Network Simulator");
Serial.printf(" Node Role: %s\n", myRole);
Serial.println("========================================\n");
WiFi.mode(WIFI_STA);
WiFi.disconnect();
WiFi.macAddress(myMAC);
Serial.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n\n",
myMAC[0], myMAC[1], myMAC[2], myMAC[3], myMAC[4], myMAC[5]);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed!");
return;
}
esp_now_register_send_cb(onDataSent);
esp_now_register_recv_cb(onDataReceived);
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, broadcastMAC, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
if (IS_COORDINATOR) digitalWrite(STATUS_LED, HIGH);
delay(random(100, 1000));
sendHeartbeat();
lastHeartbeat = millis();
Serial.println("Setup complete!\n");
}
// ============ MAIN LOOP ============
void loop() {
checkNeighborTimeouts();
if (millis() - lastHeartbeat > HEARTBEAT_INTERVAL) {
sendHeartbeat();
lastHeartbeat = millis();
static int heartbeatCount = 0;
if (++heartbeatCount % 3 == 0) {
printNeighborTable();
Serial.printf("[STATS] Received: %lu, Routed: %lu\n\n",
messagesReceived, messagesRouted);
}
}
if (IS_END_DEVICE && (millis() - lastDataSend > 10000)) {
sendSensorData();
lastDataSend = millis();
}
if (awaitingAck && (millis() - ackSentTime > ACK_TIMEOUT)) {
if (ackRetries < MSG_RETRY_COUNT) {
ackRetries++;
Serial.printf("[RETRY] %d/%d\n", ackRetries, MSG_RETRY_COUNT);
ackSentTime = millis();
} else {
Serial.println("[RETRY] Failed - triggering route rediscovery");
awaitingAck = false;
}
}
if ((IS_ROUTER_A || IS_ROUTER_B) && (millis() % 2000 < 100)) {
digitalWrite(STATUS_LED, HIGH);
} else if (IS_ROUTER_A || IS_ROUTER_B) {
digitalWrite(STATUS_LED, LOW);
}
delay(10);
}26.6 Step-by-Step Instructions
26.6.1 Step 1: Set Up Four Simulator Instances
- Open four browser tabs with Wokwi ESP32 simulators
- Copy the complete code into each tab
- Modify the configuration for each node:
| Tab | Configuration |
|---|---|
| Tab 1 | #define IS_COORDINATOR 1 (others 0) |
| Tab 2 | #define IS_ROUTER_A 1 (others 0) |
| Tab 3 | #define IS_ROUTER_B 1 (others 0) |
| Tab 4 | #define IS_END_DEVICE 1 (default) |
26.6.2 Step 2: Start the Network
- Start Coordinator first (Tab 1)
- Start both Routers (Tabs 2 and 3)
- Start End Device last (Tab 4)
- Watch Serial Monitor for network formation
26.6.3 Step 3: Observe Mesh Behavior
Watch for these behaviors:
- Network Formation: Heartbeat messages establish neighbor table
- Multi-Hop Routing: End Device → Router A → Coordinator
- ACK Flow: Coordinator → Router A → End Device
- Hop Counting: Each relay increments hop count
26.6.4 Step 4: Test Self-Healing
- With network running, stop Router A (click pause)
- Watch End Device detect timeout (15 seconds)
- Observe automatic reroute through Router B
- Data continues flowing via backup path
26.7 Key Concepts Demonstrated
26.7.1 Device Roles (Zigbee Device Types)
| Lab Role | Zigbee Equivalent | Behavior |
|---|---|---|
| Coordinator | ZC | Forms network, receives data |
| Router A/B | ZR | Relays messages, always on |
| End Device | ZED | Sends data, can sleep |
26.7.2 Neighbor Table (Like Zigbee Neighbor Table)
Each device maintains a table of nearby devices: - MAC address - Device role - Signal strength (RSSI) - Last seen timestamp - Active status
26.7.3 Self-Healing (AODV Concept)
When Router A fails: 1. End Device detects no ACK 2. Marks route as invalid 3. Discovers new route via Router B 4. Traffic resumes automatically
26.8 Exercises
26.8.1 Exercise 1: Measure Hop Latency
Add timestamps to measure per-hop delay:
// In sendMessage():
Serial.printf("[TX] Time: %lu ms\n", millis());
// In onDataReceived():
Serial.printf("[RX] Time: %lu ms\n", millis());26.8.2 Exercise 2: Add Second End Device
Create a 5th node as another End Device to see how the mesh handles multiple sources.
26.8.3 Exercise 3: Implement Route Caching
Modify the code to cache the last successful route and try it first before broadcasting.
Sensor Squad: Zigbee Mesh Lab
Sammy the Sensor (the End Device) reports: “I send temperature and humidity readings every 10 seconds to the Coordinator. But I can’t reach it directly – my message has to hop through Router A or Router B first!”
Max the Microcontroller (the Coordinator) explains: “I’m the boss of the network! I receive all the sensor data and display it. Each message comes with a hop count so I know how many devices it passed through to reach me.”
Lila the LED demonstrates: “Watch the LEDs during the experiment! Blue = Coordinator received data, green = Router A forwarded a message, yellow = Router B is the backup path. When I blink rapidly, it means a message is being relayed!”
Bella the Battery adds the best part: “Try unplugging Router A during the simulation. The End Device will notice its messages aren’t being acknowledged, and within seconds it discovers a new path through Router B. That’s mesh self-healing in action!”
Key ideas for kids:
- ESP-NOW = A simple wireless protocol we use to simulate Zigbee mesh behavior
- Heartbeat = A periodic ‘I’m alive’ message that devices send to neighbors
- Route discovery = Finding a new path when the old one breaks
- ACK timeout = Knowing a message failed because no confirmation came back
26.9 Knowledge Check
Q1: In the Wokwi mesh simulation, what triggers route rediscovery after a Router failure?
- The Coordinator periodically scans for offline devices
- The End Device fails to receive an ACK after multiple retry attempts
- Router B sends a notification that Router A is offline
- A timer automatically rotates routes every 30 seconds
Answer
B) The End Device fails to receive an ACK after multiple retry attempts – When the End Device sends a message through Router A and receives no acknowledgment (ACK) after the configured retry count (typically 3 attempts), it marks the route as broken and initiates a new route discovery. Option A is wrong because Zigbee uses reactive routing, not coordinator-initiated polling. Option C is wrong because Router B has no awareness of Router A’s state – there is no inter-router health notification mechanism. Option D is wrong because route rotation is not a Zigbee feature; routes remain stable until a failure is detected.
Worked Example: Optimizing Mesh Density for Reliability
Scenario: You’ve built the 4-node ESP-NOW mesh simulator (1 Coordinator, 2 Routers, 1 End Device) and notice that when Router A fails, the End Device takes 2-3 seconds to discover the alternate route through Router B. A production deployment requires <500ms recovery time.
Problem Analysis:
Current topology has only 1 alternate path. When Router A fails: - MAC ACK timeout: 500ms × 3 retries = 1500ms (as configured in the lab code) - Neighbor timeout detection: up to 15000ms (ROUTE_TIMEOUT) - Route table update: 50ms - Total: 1.5-15 seconds (far exceeds 500ms requirement)
Solution Steps:
Add Router C at position (1, 1) to create a denser mesh:
- Original: E → R1 → C or E → R2 → C (2 paths)
- Enhanced: E → R1 → C, E → R2 → C, E → R3 → C (3 paths)
Modify discovery parameters in the code:
#define ACK_TIMEOUT 80 // Reduce from 500ms #define MSG_RETRY_COUNT 2 // Reduce from 3 #define ROUTE_TIMEOUT 3000 // Reduce from 15000msPre-populate neighbor tables during initialization to avoid cold-start delays:
void setup() { // ... existing code ... discoverNeighbors(); buildRoutingTables(); // Pre-compute 2-hop paths }
Expected Improvement:
- ACK timeout: 80ms x 2 retries = 160ms (saved 1340ms vs original 1500ms)
- Route timeout: 3000ms reduced to allow faster neighbor table cleanup
- Pre-populated neighbors eliminate cold-start discovery delay
- New total: 210-400ms (meets <500ms requirement)
Real-World Application: Dense mesh deployments (1 router per 10-15m) provide multiple alternate paths, reducing worst-case recovery time from seconds to hundreds of milliseconds. Critical control applications (industrial safety, smart lighting) require 3-5× minimum router density for reliable sub-second recovery.
Key Insight: Mesh density trades cost (more routers) for reliability (faster recovery). Calculate worst-case failure scenarios during planning: if losing any single router must not exceed X ms recovery, deploy enough routers so every device has at least 2-3 viable paths.
Try It Yourself: Optimize Mesh Recovery Time
Challenge: The current simulation takes 2-3 seconds to recover when Router A fails. Reduce this to <500ms.
Modifications to try:
- Reduce ACK timeout from 500ms to 100ms (faster failure detection)
- Pre-populate neighbor tables during setup (skip discovery phase)
- Add third router (more alternate paths available)
- Implement cached backup route (store 2nd-best path proactively)
Expected improvements:
- 100ms ACK timeout × 3 retries = 300ms (saves 1.2 sec)
- Cached routes eliminate RREQ broadcast phase (saves 500ms)
- Third router provides immediate alternate without discovery
Learning objective: Understand trade-offs between network overhead (frequent updates) and recovery speed.
26.10 Concept Relationships
| Concept | Relationship to Lab | Hands-On Demonstration |
|---|---|---|
| ESP-NOW | Zigbee simulation protocol | Peer-to-peer messaging without WiFi infrastructure |
| Neighbor Tables | Route discovery foundation | Each device tracks nearby nodes with RSSI |
| Hop Counting | Multi-hop routing metric | Each relay increments hop count |
| ACK Timeout | Failure detection mechanism | 3 retries × 500ms = 1.5 sec failure detection |
| Self-Healing | Mesh resilience | Automatic route rediscovery after link failure |
26.11 See Also
- Zigbee Routing - AODV protocol implementation
- Zigbee Network Topologies - Mesh design patterns
- Zigbee Common Mistakes - Real-world deployment issues
- Zigbee Hands-On Labs - Additional practical exercises
Common Pitfalls
1. Not Checking RF Environment Before Lab
Zigbee lab exercises fail unexpectedly when conducted in offices with many competing 2.4 GHz networks. Check channel occupancy with a spectrum analyzer or Wi-Fi scanner and select a clear Zigbee channel before starting labs.
2. Using Old Firmware on Lab Hardware
Zigbee stack bugs fixed in firmware updates can cause erratic behavior in lab exercises. Update all lab hardware to the latest stable firmware before conducting exercises.
3. Not Documenting Unexpected Behaviors
Real hardware often produces unexpected behaviors (intermittent routing failures, association timeouts) that are highly instructive. Document unexpected behaviors with packet captures rather than dismissing them as ‘hardware issues’.
:
26.12 What’s Next
| Chapter | Focus |
|---|---|
| Zigbee Routing | AODV protocol details behind the self-healing behaviour observed in this lab |
| Zigbee Network Topologies | Star, tree, and mesh topology design patterns for production deployments |
| Zigbee Common Mistakes | Real-world deployment pitfalls including router placement and channel interference |
| Zigbee Hands-On Labs | Additional practical exercises building on the mesh concepts from this lab |
| Zigbee Security | AES-128 encryption and key management for securing mesh traffic |