999 Zigbee Lab: Mesh Network Simulator
999.1 Learning Objectives
By the end of this chapter, you will be able to:
- Build Zigbee Mesh Networks: Implement coordinator, router, and end device roles in simulation
- Understand AODV Routing: Visualize route discovery and multi-hop message delivery
- Optimize Network Topology: Experiment with node placement and network density
- Test Fault Tolerance: Simulate node failures and observe self-healing mechanisms
- Analyze Power Consumption: Compare battery life strategies for end devices
What is this chapter? Browser-based Wokwi simulation lab for hands-on Zigbee mesh network experimentation without physical hardware.
When to use: - To visualize Zigbee routing and mesh behavior - For experimenting with network topology designs - To understand self-healing and fault tolerance
Key Topics:
| Topic | Focus |
|---|---|
| Device Roles | Coordinator, Router, End Device |
| AODV Routing | Route discovery visualization |
| Mesh Topology | Multi-hop network formation |
| Fault Tolerance | Self-healing mechanisms |
| Power Management | Battery optimization strategies |
Prerequisites: - Zigbee Fundamentals - Basic understanding of mesh routing - No physical hardware required
999.2 Prerequisites
Before diving into this chapter, you should be familiar with:
- Zigbee Fundamentals and Architecture: This simulation demonstrates core Zigbee concepts including device roles, AODV routing, and mesh formation
- Zigbee Routing Protocols (if available): Understanding AODV route discovery helps interpret simulation behavior
- Network Topologies Fundamentals: Mesh topology knowledge is essential for understanding multi-hop routing
999.3 What You Will Learn
In this hands-on lab, you will explore:
- Zigbee Device Roles: How Coordinators, Routers, and End Devices interact in a mesh network
- Network Formation: The process of forming a PAN and devices joining the network
- Message Routing: How messages hop through routers to reach their destination
- Self-Healing Mesh: How the network automatically reroutes when a node fails
- Hop Counting: Understanding route cost and path optimization
- Sleep Modes: How battery-powered end devices conserve energy
999.4 Interactive Wokwi Simulation
This simulation uses an ESP32 to model Zigbee mesh networking concepts. While ESP32 doesnβt natively support Zigbee (it uses Wi-Fi and Bluetooth), this simulation models Zigbee mesh behavior to help you understand the protocolβs key features.
Press the Play button to start the simulation and observe the network behavior in the Serial Monitor.
Copy and paste this code into the Wokwi editor to run the Zigbee mesh network simulation:
/**
* Zigbee Mesh Network Simulator for ESP32
* ========================================
*
* This simulation demonstrates core Zigbee mesh networking concepts:
* - Network formation with Coordinator, Routers, and End Devices
* - AODV-style route discovery and message routing
* - Self-healing mesh behavior when nodes fail
* - Hop counting and path optimization
* - Battery management for sleepy end devices
*
* Educational Purpose: Understand Zigbee mesh principles without
* requiring actual Zigbee hardware.
*
* Author: IoT Class Educational Simulation
* License: MIT
*/
#include <Arduino.h>
#include <vector>
#include <map>
#include <queue>
// =============================================================================
// CONFIGURATION CONSTANTS
// =============================================================================
#define MAX_NODES 12
#define MAX_NEIGHBORS 6
#define MAX_ROUTING_TABLE_SIZE 20
#define PAN_ID 0x1234
#define ZIGBEE_CHANNEL 25
#define NETWORK_KEY "ZigBeeAlliance09"
#define MAX_HOPS 7
#define ROUTE_DISCOVERY_TIMEOUT 3000
#define LINK_QUALITY_THRESHOLD 100
#define BATTERY_DRAIN_RATE 0.01
#define SLEEP_DURATION_MS 5000
#define SIMULATION_TICK_MS 100
// =============================================================================
// ZIGBEE DATA STRUCTURES
// =============================================================================
/**
* Device types in a Zigbee network
*/
enum DeviceType {
COORDINATOR, // Network founder, Trust Center, always on
ROUTER, // Full-function device, routes messages, mains-powered
END_DEVICE // Reduced-function device, can sleep, battery-powered
};
/**
* Device states for simulation
*/
enum DeviceState {
STATE_INIT,
STATE_SCANNING,
STATE_JOINING,
STATE_JOINED,
STATE_SLEEPING,
STATE_TRANSMITTING,
STATE_FAILED
};
/**
* Message types for Zigbee-like communication
*/
enum MessageType {
MSG_BEACON, // Network announcement
MSG_JOIN_REQUEST, // Device wants to join
MSG_JOIN_RESPONSE, // Join accepted/rejected
MSG_ROUTE_REQUEST, // RREQ in AODV
MSG_ROUTE_REPLY, // RREP in AODV
MSG_DATA, // Application data
MSG_ACK, // Acknowledgment
MSG_NETWORK_STATUS // Link status update
};
/**
* Routing table entry
*/
struct RouteEntry {
uint16_t destination;
uint16_t nextHop;
uint8_t hopCount;
uint8_t linkQuality;
unsigned long timestamp;
bool active;
};
/**
* Network message structure
*/
struct ZigbeeMessage {
MessageType type;
uint16_t source;
uint16_t destination;
uint16_t via; // For multi-hop routing
uint8_t seqNum;
uint8_t hopCount;
uint8_t maxHops;
String payload;
unsigned long timestamp;
};
/**
* Neighbor table entry
*/
struct Neighbor {
uint16_t address;
DeviceType type;
uint8_t linkQuality; // 0-255, higher is better
int8_t rssi; // Signal strength in dBm
bool canRoute;
unsigned long lastSeen;
};
/**
* Simulated Zigbee device
*/
struct ZigbeeDevice {
uint16_t networkAddress;
uint64_t ieeeAddress;
DeviceType type;
DeviceState state;
uint16_t parentAddress;
float batteryLevel; // 0.0 - 100.0
uint8_t txPower; // Transmission power level
std::vector<Neighbor> neighbors;
std::vector<RouteEntry> routingTable;
std::queue<ZigbeeMessage> messageQueue;
unsigned long lastActivity;
unsigned long sleepUntil;
uint32_t messagesSent;
uint32_t messagesReceived;
uint32_t messagesRouted;
// Position for simulation (grid-based)
int posX;
int posY;
};
// =============================================================================
// GLOBAL VARIABLES
// =============================================================================
ZigbeeDevice devices[MAX_NODES];
int deviceCount = 0;
uint8_t globalSeqNum = 0;
unsigned long simulationTime = 0;
bool networkFormed = false;
int failedNodeIndex = -1;
// Statistics
uint32_t totalMessagesSent = 0;
uint32_t totalMessagesDelivered = 0;
uint32_t totalRoutingDiscoveries = 0;
uint32_t totalSelfHealingEvents = 0;
// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================
/**
* Generate IEEE 802.15.4 extended address
*/
uint64_t generateIEEEAddress(int index) {
return 0x00158D0001000000ULL + index;
}
/**
* Convert device type to string
*/
const char* deviceTypeToString(DeviceType type) {
switch(type) {
case COORDINATOR: return "Coordinator";
case ROUTER: return "Router";
case END_DEVICE: return "EndDevice";
default: return "Unknown";
}
}
/**
* Convert device state to string
*/
const char* deviceStateToString(DeviceState state) {
switch(state) {
case STATE_INIT: return "Init";
case STATE_SCANNING: return "Scanning";
case STATE_JOINING: return "Joining";
case STATE_JOINED: return "Joined";
case STATE_SLEEPING: return "Sleeping";
case STATE_TRANSMITTING: return "Transmitting";
case STATE_FAILED: return "Failed";
default: return "Unknown";
}
}
/**
* Calculate simulated link quality based on distance
*/
uint8_t calculateLinkQuality(ZigbeeDevice* a, ZigbeeDevice* b) {
int dx = a->posX - b->posX;
int dy = a->posY - b->posY;
float distance = sqrt(dx*dx + dy*dy);
// Zigbee indoor range ~10-15 meters, grid units = 3 meters
// LQI decreases with distance: 255 at 0m, ~50 at max range
float maxRange = 5.0; // Grid units
if (distance > maxRange) return 0;
uint8_t lqi = (uint8_t)(255 * (1.0 - (distance / (maxRange * 1.2))));
// Add some randomness to simulate real-world conditions
lqi = max(0, min(255, lqi + random(-15, 15)));
return lqi;
}
/**
* Calculate RSSI from link quality
*/
int8_t lqiToRssi(uint8_t lqi) {
// RSSI typically ranges from -100 dBm (weak) to -20 dBm (strong)
return map(lqi, 0, 255, -95, -25);
}
/**
* Check if two devices can communicate directly
*/
bool canCommunicate(ZigbeeDevice* a, ZigbeeDevice* b) {
if (a->state == STATE_FAILED || b->state == STATE_FAILED) return false;
if (a->state == STATE_SLEEPING || b->state == STATE_SLEEPING) return false;
return calculateLinkQuality(a, b) >= LINK_QUALITY_THRESHOLD;
}
/**
* Print separator line
*/
void printSeparator(char c = '=', int len = 70) {
for (int i = 0; i < len; i++) Serial.print(c);
Serial.println();
}
/**
* Print hex address
*/
void printAddress(uint16_t addr) {
if (addr < 0x1000) Serial.print("0");
if (addr < 0x100) Serial.print("0");
if (addr < 0x10) Serial.print("0");
Serial.print(addr, HEX);
}
// =============================================================================
// NETWORK INITIALIZATION
// =============================================================================
/**
* Create the network topology
*
* Grid Layout (5x3):
* 0 1 2 3 4 (X)
* 0 [C] [R] [ ] [R] [E]
* 1 [R] [E] [R] [E] [R]
* 2 [E] [R] [E] [ ] [E]
* (Y)
*
* C = Coordinator (0x0000)
* R = Router (mains-powered)
* E = End Device (battery-powered)
*/
void initializeNetwork() {
Serial.println("\n========================================");
Serial.println(" ZIGBEE MESH NETWORK INITIALIZATION");
Serial.println("========================================\n");
Serial.print("PAN ID: 0x");
Serial.println(PAN_ID, HEX);
Serial.print("Channel: ");
Serial.println(ZIGBEE_CHANNEL);
Serial.println();
deviceCount = 0;
// Define network topology with positions
// Format: {type, posX, posY}
struct DeviceConfig {
DeviceType type;
int x;
int y;
};
DeviceConfig config[] = {
{COORDINATOR, 0, 0}, // Central coordinator
{ROUTER, 1, 0}, // Router R1
{ROUTER, 3, 0}, // Router R2
{ROUTER, 0, 1}, // Router R3
{ROUTER, 2, 1}, // Router R4
{ROUTER, 4, 1}, // Router R5
{ROUTER, 1, 2}, // Router R6
{END_DEVICE, 4, 0}, // End Device E1
{END_DEVICE, 1, 1}, // End Device E2
{END_DEVICE, 3, 1}, // End Device E3
{END_DEVICE, 0, 2}, // End Device E4
{END_DEVICE, 2, 2} // End Device E5
};
int numDevices = sizeof(config) / sizeof(config[0]);
for (int i = 0; i < numDevices && i < MAX_NODES; i++) {
devices[i].networkAddress = i; // Simplified addressing
devices[i].ieeeAddress = generateIEEEAddress(i);
devices[i].type = config[i].type;
devices[i].state = STATE_INIT;
devices[i].parentAddress = 0xFFFF;
devices[i].batteryLevel = (config[i].type == END_DEVICE) ? 100.0 : -1.0;
devices[i].txPower = 4;
devices[i].posX = config[i].x;
devices[i].posY = config[i].y;
devices[i].lastActivity = 0;
devices[i].sleepUntil = 0;
devices[i].messagesSent = 0;
devices[i].messagesReceived = 0;
devices[i].messagesRouted = 0;
devices[i].neighbors.clear();
devices[i].routingTable.clear();
deviceCount++;
Serial.print("Created device 0x");
printAddress(devices[i].networkAddress);
Serial.print(" [");
Serial.print(deviceTypeToString(devices[i].type));
Serial.print("] at position (");
Serial.print(devices[i].posX);
Serial.print(", ");
Serial.print(devices[i].posY);
Serial.println(")");
}
Serial.println();
Serial.print("Total devices created: ");
Serial.println(deviceCount);
}
// =============================================================================
// NEIGHBOR DISCOVERY
// =============================================================================
/**
* Discover neighbors for all devices
* Simulates the beacon scanning phase of network formation
*/
void discoverNeighbors() {
Serial.println("\n========================================");
Serial.println(" NEIGHBOR DISCOVERY PHASE");
Serial.println("========================================\n");
for (int i = 0; i < deviceCount; i++) {
devices[i].neighbors.clear();
for (int j = 0; j < deviceCount; j++) {
if (i == j) continue;
uint8_t lqi = calculateLinkQuality(&devices[i], &devices[j]);
if (lqi >= LINK_QUALITY_THRESHOLD) {
Neighbor neighbor;
neighbor.address = devices[j].networkAddress;
neighbor.type = devices[j].type;
neighbor.linkQuality = lqi;
neighbor.rssi = lqiToRssi(lqi);
neighbor.canRoute = (devices[j].type != END_DEVICE);
neighbor.lastSeen = millis();
devices[i].neighbors.push_back(neighbor);
}
}
Serial.print("Device 0x");
printAddress(devices[i].networkAddress);
Serial.print(" discovered ");
Serial.print(devices[i].neighbors.size());
Serial.print(" neighbors: ");
for (size_t n = 0; n < devices[i].neighbors.size(); n++) {
Serial.print("0x");
printAddress(devices[i].neighbors[n].address);
Serial.print("(LQI:");
Serial.print(devices[i].neighbors[n].linkQuality);
Serial.print(") ");
}
Serial.println();
}
}
// =============================================================================
// NETWORK FORMATION
// =============================================================================
/**
* Simulate network formation process
* 1. Coordinator starts network
* 2. Routers join and become part of mesh
* 3. End devices join via nearest router
*/
void formNetwork() {
Serial.println("\n========================================");
Serial.println(" NETWORK FORMATION");
Serial.println("========================================\n");
// Step 1: Coordinator forms the network
Serial.println("[Phase 1] Coordinator forming network...");
devices[0].state = STATE_JOINED;
devices[0].parentAddress = 0x0000; // Self
Serial.print(" Coordinator 0x0000 started PAN 0x");
Serial.print(PAN_ID, HEX);
Serial.print(" on channel ");
Serial.println(ZIGBEE_CHANNEL);
Serial.println(" Trust Center initialized, network key distributed");
delay(300);
// Step 2: Routers join in order of proximity to coordinator
Serial.println("\n[Phase 2] Routers joining network...");
for (int i = 1; i < deviceCount; i++) {
if (devices[i].type != ROUTER) continue;
devices[i].state = STATE_SCANNING;
delay(100);
// Find best parent (joined device with best LQI that can route)
uint16_t bestParent = 0xFFFF;
uint8_t bestLQI = 0;
for (size_t n = 0; n < devices[i].neighbors.size(); n++) {
Neighbor& neighbor = devices[i].neighbors[n];
int neighborIdx = neighbor.address;
if (devices[neighborIdx].state == STATE_JOINED &&
devices[neighborIdx].type != END_DEVICE &&
neighbor.linkQuality > bestLQI) {
bestParent = neighbor.address;
bestLQI = neighbor.linkQuality;
}
}
if (bestParent != 0xFFFF) {
devices[i].state = STATE_JOINING;
delay(100);
devices[i].state = STATE_JOINED;
devices[i].parentAddress = bestParent;
Serial.print(" Router 0x");
printAddress(devices[i].networkAddress);
Serial.print(" joined via parent 0x");
printAddress(bestParent);
Serial.print(" (LQI: ");
Serial.print(bestLQI);
Serial.println(")");
}
delay(200);
}
// Step 3: End devices join
Serial.println("\n[Phase 3] End devices joining network...");
for (int i = 1; i < deviceCount; i++) {
if (devices[i].type != END_DEVICE) continue;
devices[i].state = STATE_SCANNING;
delay(100);
// End devices prefer routers as parents (for polling)
uint16_t bestParent = 0xFFFF;
uint8_t bestLQI = 0;
for (size_t n = 0; n < devices[i].neighbors.size(); n++) {
Neighbor& neighbor = devices[i].neighbors[n];
int neighborIdx = neighbor.address;
if (devices[neighborIdx].state == STATE_JOINED &&
neighbor.canRoute &&
neighbor.linkQuality > bestLQI) {
bestParent = neighbor.address;
bestLQI = neighbor.linkQuality;
}
}
if (bestParent != 0xFFFF) {
devices[i].state = STATE_JOINED;
devices[i].parentAddress = bestParent;
Serial.print(" EndDevice 0x");
printAddress(devices[i].networkAddress);
Serial.print(" joined via parent 0x");
printAddress(bestParent);
Serial.print(" (LQI: ");
Serial.print(bestLQI);
Serial.print(", Battery: ");
Serial.print((int)devices[i].batteryLevel);
Serial.println("%)");
}
delay(150);
}
networkFormed = true;
Serial.println("\n[Complete] Network formation successful!");
}
// =============================================================================
// ROUTING TABLE MANAGEMENT
// =============================================================================
/**
* Add or update routing table entry
*/
void updateRouteTable(ZigbeeDevice* device, uint16_t dest, uint16_t nextHop, uint8_t hops, uint8_t lqi) {
// Check if route exists
for (size_t i = 0; i < device->routingTable.size(); i++) {
if (device->routingTable[i].destination == dest) {
// Update if better route
if (hops < device->routingTable[i].hopCount ||
(hops == device->routingTable[i].hopCount && lqi > device->routingTable[i].linkQuality)) {
device->routingTable[i].nextHop = nextHop;
device->routingTable[i].hopCount = hops;
device->routingTable[i].linkQuality = lqi;
device->routingTable[i].timestamp = millis();
device->routingTable[i].active = true;
}
return;
}
}
// Add new route
if (device->routingTable.size() < MAX_ROUTING_TABLE_SIZE) {
RouteEntry entry;
entry.destination = dest;
entry.nextHop = nextHop;
entry.hopCount = hops;
entry.linkQuality = lqi;
entry.timestamp = millis();
entry.active = true;
device->routingTable.push_back(entry);
}
}
/**
* Find route to destination
* Returns next hop address or 0xFFFF if no route
*/
uint16_t findRoute(ZigbeeDevice* device, uint16_t destination) {
// Direct neighbor?
for (size_t i = 0; i < device->neighbors.size(); i++) {
if (device->neighbors[i].address == destination &&
device->neighbors[i].linkQuality >= LINK_QUALITY_THRESHOLD) {
return destination;
}
}
// Check routing table
for (size_t i = 0; i < device->routingTable.size(); i++) {
if (device->routingTable[i].destination == destination &&
device->routingTable[i].active) {
return device->routingTable[i].nextHop;
}
}
return 0xFFFF; // No route found
}
/**
* Build initial routing tables using neighbor information
*/
void buildRoutingTables() {
Serial.println("\n========================================");
Serial.println(" BUILDING ROUTING TABLES");
Serial.println("========================================\n");
// Simple routing: each device routes through neighbors
// In real Zigbee, AODV discovers routes on-demand
for (int i = 0; i < deviceCount; i++) {
if (devices[i].type == END_DEVICE) continue; // End devices don't route
devices[i].routingTable.clear();
// Add direct routes to neighbors
for (size_t n = 0; n < devices[i].neighbors.size(); n++) {
Neighbor& neighbor = devices[i].neighbors[n];
updateRouteTable(&devices[i], neighbor.address, neighbor.address, 1, neighbor.linkQuality);
}
// Add routes via neighbors (2-hop)
for (size_t n = 0; n < devices[i].neighbors.size(); n++) {
Neighbor& neighbor = devices[i].neighbors[n];
int neighborIdx = neighbor.address;
if (devices[neighborIdx].type == END_DEVICE) continue;
for (size_t m = 0; m < devices[neighborIdx].neighbors.size(); m++) {
uint16_t remoteAddr = devices[neighborIdx].neighbors[m].address;
// Don't add route to self or direct neighbors
if (remoteAddr == devices[i].networkAddress) continue;
bool isDirect = false;
for (size_t d = 0; d < devices[i].neighbors.size(); d++) {
if (devices[i].neighbors[d].address == remoteAddr) {
isDirect = true;
break;
}
}
if (!isDirect) {
uint8_t combinedLQI = min(neighbor.linkQuality,
devices[neighborIdx].neighbors[m].linkQuality);
updateRouteTable(&devices[i], remoteAddr, neighbor.address, 2, combinedLQI);
}
}
}
Serial.print("Device 0x");
printAddress(devices[i].networkAddress);
Serial.print(" routing table (");
Serial.print(devices[i].routingTable.size());
Serial.println(" entries):");
for (size_t r = 0; r < devices[i].routingTable.size(); r++) {
Serial.print(" -> 0x");
printAddress(devices[i].routingTable[r].destination);
Serial.print(" via 0x");
printAddress(devices[i].routingTable[r].nextHop);
Serial.print(" (");
Serial.print(devices[i].routingTable[r].hopCount);
Serial.print(" hop");
if (devices[i].routingTable[r].hopCount > 1) Serial.print("s");
Serial.println(")");
}
Serial.println();
}
}
// =============================================================================
// MESSAGE ROUTING
// =============================================================================
/**
* Route a message through the mesh network
* Returns true if message reached destination
*/
bool routeMessage(uint16_t source, uint16_t destination, String payload, int depth = 0) {
if (depth > MAX_HOPS) {
Serial.println(" [ERROR] Max hops exceeded, dropping message");
return false;
}
ZigbeeDevice* srcDevice = &devices[source];
if (srcDevice->state == STATE_FAILED || srcDevice->state == STATE_SLEEPING) {
Serial.print(" [ERROR] Source device 0x");
printAddress(source);
Serial.println(" unavailable");
return false;
}
// Direct delivery?
if (source == destination) {
Serial.print(" [DELIVERED] Message arrived at 0x");
printAddress(destination);
Serial.println();
devices[destination].messagesReceived++;
totalMessagesDelivered++;
return true;
}
// Find next hop
uint16_t nextHop = findRoute(srcDevice, destination);
if (nextHop == 0xFFFF) {
Serial.print(" [ERROR] No route from 0x");
printAddress(source);
Serial.print(" to 0x");
printAddress(destination);
Serial.println();
return false;
}
// Check if next hop is available
if (devices[nextHop].state == STATE_FAILED) {
Serial.print(" [REROUTE] Next hop 0x");
printAddress(nextHop);
Serial.println(" is failed, finding alternate route");
// Mark route as inactive
for (size_t i = 0; i < srcDevice->routingTable.size(); i++) {
if (srcDevice->routingTable[i].nextHop == nextHop) {
srcDevice->routingTable[i].active = false;
}
}
// Try to find alternate route
nextHop = findRoute(srcDevice, destination);
if (nextHop == 0xFFFF || devices[nextHop].state == STATE_FAILED) {
Serial.println(" [ERROR] No alternate route available");
return false;
}
totalSelfHealingEvents++;
Serial.print(" [HEALED] Using alternate route via 0x");
printAddress(nextHop);
Serial.println();
}
// Log hop
Serial.print(" 0x");
printAddress(source);
Serial.print(" -> 0x");
printAddress(nextHop);
srcDevice->messagesSent++;
totalMessagesSent++;
if (nextHop == destination) {
Serial.println(" [FINAL HOP]");
} else {
Serial.println();
devices[source].messagesRouted++;
}
// Continue routing
return routeMessage(nextHop, destination, payload, depth + 1);
}
/**
* Send a message with visual output
*/
void sendMessage(uint16_t source, uint16_t destination, String payload) {
Serial.println();
printSeparator('-', 60);
Serial.print("MESSAGE: 0x");
printAddress(source);
Serial.print(" --> 0x");
printAddress(destination);
Serial.println();
Serial.print("Payload: \"");
Serial.print(payload);
Serial.println("\"");
printSeparator('-', 60);
Serial.println("Route trace:");
bool delivered = routeMessage(source, destination, payload);
if (delivered) {
Serial.println("Status: DELIVERED");
} else {
Serial.println("Status: FAILED");
}
printSeparator('-', 60);
}
// =============================================================================
// SELF-HEALING DEMONSTRATION
// =============================================================================
/**
* Simulate node failure and mesh self-healing
*/
void simulateNodeFailure(int nodeIndex) {
if (nodeIndex <= 0 || nodeIndex >= deviceCount) return;
if (devices[nodeIndex].type == COORDINATOR) return; // Can't fail coordinator
Serial.println("\n========================================");
Serial.println(" SIMULATING NODE FAILURE");
Serial.println("========================================\n");
Serial.print("[ALERT] Device 0x");
printAddress(devices[nodeIndex].networkAddress);
Serial.print(" (");
Serial.print(deviceTypeToString(devices[nodeIndex].type));
Serial.println(") has FAILED!");
devices[nodeIndex].state = STATE_FAILED;
failedNodeIndex = nodeIndex;
// Neighbors detect failure
Serial.println("\nNeighbors detecting failure...");
delay(500);
// Rebuild routes around failed node
Serial.println("Rebuilding routes around failed node...\n");
for (int i = 0; i < deviceCount; i++) {
if (devices[i].type == END_DEVICE) continue;
if (devices[i].state == STATE_FAILED) continue;
// Invalidate routes through failed node
for (size_t r = 0; r < devices[i].routingTable.size(); r++) {
if (devices[i].routingTable[r].nextHop == nodeIndex) {
devices[i].routingTable[r].active = false;
}
}
}
// Re-run neighbor discovery and routing
discoverNeighbors();
buildRoutingTables();
Serial.println("[COMPLETE] Network has self-healed around failed node");
}
/**
* Recover a failed node
*/
void recoverNode(int nodeIndex) {
if (nodeIndex <= 0 || nodeIndex >= deviceCount) return;
Serial.println("\n========================================");
Serial.println(" NODE RECOVERY");
Serial.println("========================================\n");
Serial.print("[RECOVERY] Device 0x");
printAddress(devices[nodeIndex].networkAddress);
Serial.println(" is coming back online...");
devices[nodeIndex].state = STATE_SCANNING;
delay(200);
devices[nodeIndex].state = STATE_JOINING;
delay(200);
devices[nodeIndex].state = STATE_JOINED;
Serial.println("[COMPLETE] Device has rejoined the network");
failedNodeIndex = -1;
// Rebuild network
discoverNeighbors();
buildRoutingTables();
}
// =============================================================================
// BATTERY AND SLEEP SIMULATION
// =============================================================================
/**
* Simulate battery drain for end devices
*/
void updateBatteryLevels() {
for (int i = 0; i < deviceCount; i++) {
if (devices[i].type == END_DEVICE && devices[i].batteryLevel > 0) {
// Drain battery based on activity
if (devices[i].state == STATE_SLEEPING) {
devices[i].batteryLevel -= BATTERY_DRAIN_RATE * 0.1; // Much less drain when sleeping
} else {
devices[i].batteryLevel -= BATTERY_DRAIN_RATE;
}
if (devices[i].batteryLevel < 0) devices[i].batteryLevel = 0;
// Critical battery warning
if (devices[i].batteryLevel > 0 && devices[i].batteryLevel < 10) {
Serial.print("[WARNING] Device 0x");
printAddress(devices[i].networkAddress);
Serial.print(" battery critical: ");
Serial.print((int)devices[i].batteryLevel);
Serial.println("%");
}
}
}
}
/**
* Simulate sleep cycle for end devices
*/
void simulateSleepCycle() {
for (int i = 0; i < deviceCount; i++) {
if (devices[i].type == END_DEVICE && devices[i].state == STATE_JOINED) {
// Random sleep pattern
if (random(100) < 30) { // 30% chance to go to sleep
devices[i].state = STATE_SLEEPING;
devices[i].sleepUntil = millis() + SLEEP_DURATION_MS;
}
}
// Wake up check
if (devices[i].state == STATE_SLEEPING && millis() > devices[i].sleepUntil) {
devices[i].state = STATE_JOINED;
}
}
}
// =============================================================================
// NETWORK VISUALIZATION
// =============================================================================
/**
* Print ASCII visualization of network topology
*/
void printNetworkTopology() {
Serial.println("\n========================================");
Serial.println(" NETWORK TOPOLOGY VISUALIZATION");
Serial.println("========================================\n");
Serial.println("Legend: [C]=Coordinator [R]=Router [E]=EndDevice [X]=Failed\n");
// Create grid
char grid[3][5];
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 5; x++) {
grid[y][x] = ' ';
}
}
// Place devices
for (int i = 0; i < deviceCount; i++) {
char symbol;
if (devices[i].state == STATE_FAILED) {
symbol = 'X';
} else {
switch (devices[i].type) {
case COORDINATOR: symbol = 'C'; break;
case ROUTER: symbol = 'R'; break;
case END_DEVICE: symbol = 'E'; break;
default: symbol = '?';
}
}
grid[devices[i].posY][devices[i].posX] = symbol;
}
// Print grid with connections
Serial.println(" 0 1 2 3 4");
Serial.println(" +---+---+---+---+---+");
for (int y = 0; y < 3; y++) {
Serial.print(" ");
Serial.print(y);
Serial.print(" |");
for (int x = 0; x < 5; x++) {
Serial.print(" ");
Serial.print(grid[y][x]);
Serial.print(" |");
}
Serial.println();
Serial.println(" +---+---+---+---+---+");
}
Serial.println("\nDevice Details:");
printSeparator('-', 70);
Serial.println("Addr Type State Parent Neighbors Battery");
printSeparator('-', 70);
for (int i = 0; i < deviceCount; i++) {
Serial.print("0x");
printAddress(devices[i].networkAddress);
Serial.print(" ");
// Type (padded)
Serial.print(deviceTypeToString(devices[i].type));
int typeLen = strlen(deviceTypeToString(devices[i].type));
for (int p = typeLen; p < 14; p++) Serial.print(" ");
// State (padded)
Serial.print(deviceStateToString(devices[i].state));
int stateLen = strlen(deviceStateToString(devices[i].state));
for (int p = stateLen; p < 11; p++) Serial.print(" ");
// Parent
if (devices[i].parentAddress == 0x0000 && devices[i].type == COORDINATOR) {
Serial.print("SELF ");
} else {
Serial.print("0x");
printAddress(devices[i].parentAddress);
Serial.print(" ");
}
// Neighbors
Serial.print(devices[i].neighbors.size());
Serial.print(" ");
// Battery
if (devices[i].batteryLevel >= 0) {
Serial.print((int)devices[i].batteryLevel);
Serial.print("%");
} else {
Serial.print("N/A");
}
Serial.println();
}
printSeparator('-', 70);
}
/**
* Print network statistics
*/
void printNetworkStats() {
Serial.println("\n========================================");
Serial.println(" NETWORK STATISTICS");
Serial.println("========================================\n");
int coordinators = 0, routers = 0, endDevices = 0, failed = 0;
float avgBattery = 0;
int batteryDevices = 0;
for (int i = 0; i < deviceCount; i++) {
if (devices[i].state == STATE_FAILED) {
failed++;
continue;
}
switch (devices[i].type) {
case COORDINATOR: coordinators++; break;
case ROUTER: routers++; break;
case END_DEVICE:
endDevices++;
if (devices[i].batteryLevel >= 0) {
avgBattery += devices[i].batteryLevel;
batteryDevices++;
}
break;
}
}
if (batteryDevices > 0) avgBattery /= batteryDevices;
Serial.print("Total Devices: ");
Serial.println(deviceCount);
Serial.print(" - Coordinators: ");
Serial.println(coordinators);
Serial.print(" - Routers: ");
Serial.println(routers);
Serial.print(" - End Devices: ");
Serial.println(endDevices);
Serial.print(" - Failed: ");
Serial.println(failed);
Serial.println();
Serial.print("Messages Sent: ");
Serial.println(totalMessagesSent);
Serial.print("Messages Delivered: ");
Serial.println(totalMessagesDelivered);
Serial.print("Self-Healing Events: ");
Serial.println(totalSelfHealingEvents);
Serial.println();
Serial.print("Avg Battery (EndDev): ");
Serial.print((int)avgBattery);
Serial.println("%");
}
// =============================================================================
// DEMONSTRATION SCENARIOS
// =============================================================================
/**
* Demonstrate multi-hop routing
*/
void demoMultiHopRouting() {
Serial.println("\n");
printSeparator('*', 70);
Serial.println(" DEMO 1: MULTI-HOP MESSAGE ROUTING");
printSeparator('*', 70);
Serial.println("\nSending sensor data from EndDevice to Coordinator...");
Serial.println("(EndDevice 0x000A at position (2,2) to Coordinator at (0,0))");
delay(500);
sendMessage(0x000A, 0x0000, "TEMP:22.5C,HUMID:45%");
delay(1000);
Serial.println("\nSending command from Coordinator to remote EndDevice...");
sendMessage(0x0000, 0x0007, "LED:ON,BRIGHTNESS:80");
}
/**
* Demonstrate self-healing mesh
*/
void demoSelfHealing() {
Serial.println("\n");
printSeparator('*', 70);
Serial.println(" DEMO 2: SELF-HEALING MESH NETWORK");
printSeparator('*', 70);
Serial.println("\nFirst, sending a message through Router 0x0004...");
delay(500);
sendMessage(0x000B, 0x0000, "STATUS:OK");
delay(1000);
// Fail a critical router
simulateNodeFailure(4); // Router R4 at (2,1)
delay(500);
Serial.println("\nNow trying to send same message with Router 0x0004 failed...");
sendMessage(0x000B, 0x0000, "STATUS:RETRYING");
delay(1000);
// Recover the node
recoverNode(4);
}
/**
* Demonstrate hop counting
*/
void demoHopCounting() {
Serial.println("\n");
printSeparator('*', 70);
Serial.println(" DEMO 3: HOP COUNTING AND PATH ANALYSIS");
printSeparator('*', 70);
Serial.println("\nAnalyzing paths from each EndDevice to Coordinator:\n");
int endDevices[] = {7, 8, 9, 10, 11}; // End device indices
for (int i = 0; i < 5; i++) {
int idx = endDevices[i];
Serial.print("EndDevice 0x");
printAddress(devices[idx].networkAddress);
Serial.print(" at (");
Serial.print(devices[idx].posX);
Serial.print(",");
Serial.print(devices[idx].posY);
Serial.println("):");
// Find path length
int hops = 0;
uint16_t current = devices[idx].parentAddress;
Serial.print(" Path: 0x");
printAddress(devices[idx].networkAddress);
while (current != 0x0000 && hops < MAX_HOPS) {
Serial.print(" -> 0x");
printAddress(current);
current = devices[current].parentAddress;
hops++;
}
if (current == 0x0000) {
Serial.print(" -> 0x0000");
hops++;
}
Serial.print("\n Total hops: ");
Serial.println(hops);
Serial.println();
delay(300);
}
}
/**
* Demonstrate battery and sleep behavior
*/
void demoBatteryAndSleep() {
Serial.println("\n");
printSeparator('*', 70);
Serial.println(" DEMO 4: BATTERY MANAGEMENT & SLEEP MODES");
printSeparator('*', 70);
Serial.println("\nEnd device sleep cycle simulation (5 cycles):\n");
for (int cycle = 0; cycle < 5; cycle++) {
Serial.print("Cycle ");
Serial.print(cycle + 1);
Serial.println(":");
for (int i = 0; i < deviceCount; i++) {
if (devices[i].type != END_DEVICE) continue;
// Simulate activity
if (random(100) < 40) {
// Wake and transmit
devices[i].state = STATE_JOINED;
devices[i].batteryLevel -= 0.5; // Transmit cost
Serial.print(" 0x");
printAddress(devices[i].networkAddress);
Serial.print(" AWAKE - Transmitting... Battery: ");
Serial.print((int)devices[i].batteryLevel);
Serial.println("%");
// Go back to sleep
devices[i].state = STATE_SLEEPING;
} else {
// Stay asleep
devices[i].batteryLevel -= 0.05; // Sleep cost
Serial.print(" 0x");
printAddress(devices[i].networkAddress);
Serial.print(" SLEEPING... Battery: ");
Serial.print((int)devices[i].batteryLevel);
Serial.println("%");
}
}
Serial.println();
delay(500);
}
Serial.println("Note: Sleeping devices use ~10x less power than active devices.");
}
// =============================================================================
// MAIN SETUP AND LOOP
// =============================================================================
void setup() {
Serial.begin(115200);
delay(2000);
randomSeed(analogRead(0));
Serial.println("\n\n");
printSeparator('=', 70);
Serial.println(" ZIGBEE MESH NETWORK SIMULATOR");
Serial.println(" ESP32 Educational Demonstration");
printSeparator('=', 70);
Serial.println("\nThis simulation demonstrates core Zigbee mesh concepts:");
Serial.println("- Network formation (Coordinator, Router, End Device roles)");
Serial.println("- Multi-hop message routing through mesh");
Serial.println("- Self-healing when nodes fail");
Serial.println("- Hop counting and path optimization");
Serial.println("- Battery management for sleepy end devices");
Serial.println();
// Initialize and form network
initializeNetwork();
delay(500);
discoverNeighbors();
delay(500);
formNetwork();
delay(500);
buildRoutingTables();
delay(500);
// Show network state
printNetworkTopology();
delay(1000);
// Run demonstrations
demoMultiHopRouting();
delay(1500);
demoHopCounting();
delay(1500);
demoSelfHealing();
delay(1500);
demoBatteryAndSleep();
delay(1500);
// Final statistics
printNetworkStats();
Serial.println("\n");
printSeparator('=', 70);
Serial.println(" SIMULATION COMPLETE");
printSeparator('=', 70);
Serial.println("\nKey Takeaways:");
Serial.println("1. Zigbee mesh networks self-organize with Coordinator, Router, and EndDevice roles");
Serial.println("2. Messages route through multiple hops via Routers to reach any device");
Serial.println("3. When a node fails, the mesh automatically reroutes around it");
Serial.println("4. End devices sleep most of the time to conserve battery");
Serial.println("5. Hop count affects latency - fewer hops = faster delivery");
Serial.println("\nExperiment Ideas:");
Serial.println("- Modify MAX_NODES to test larger networks");
Serial.println("- Change LINK_QUALITY_THRESHOLD to affect mesh density");
Serial.println("- Adjust device positions to create different topologies");
Serial.println("- Add more failure scenarios to test resilience");
}
void loop() {
// Simulation complete - just idle
delay(10000);
// Periodic network health check
Serial.println("\n[Heartbeat] Network status: HEALTHY");
Serial.print(" Active devices: ");
int active = 0;
for (int i = 0; i < deviceCount; i++) {
if (devices[i].state != STATE_FAILED) active++;
}
Serial.println(active);
}999.5 How to Use This Lab
- Open Wokwi: Click on the embedded simulator above or visit wokwi.com
- Create New Project: Select βESP32β as your board
- Copy Code: Expand the code section above and copy the entire simulation code
- Paste and Run: Paste the code into the editor and click the green βPlayβ button
- Observe Serial Monitor: Watch the network form and messages route through the mesh
999.6 Expected Output
When you run the simulation, you will see:
======================================================================
ZIGBEE MESH NETWORK SIMULATOR
ESP32 Educational Demonstration
======================================================================
This simulation demonstrates core Zigbee mesh concepts:
- Network formation (Coordinator, Router, End Device roles)
- Multi-hop message routing through mesh
- Self-healing when nodes fail
- Hop counting and path optimization
- Battery management for sleepy end devices
========================================
ZIGBEE MESH NETWORK INITIALIZATION
========================================
PAN ID: 0x1234
Channel: 25
Created device 0x0000 [Coordinator] at position (0, 0)
Created device 0x0001 [Router] at position (1, 0)
Created device 0x0002 [Router] at position (3, 0)
...
========================================
NETWORK FORMATION
========================================
[Phase 1] Coordinator forming network...
Coordinator 0x0000 started PAN 0x1234 on channel 25
Trust Center initialized, network key distributed
[Phase 2] Routers joining network...
Router 0x0001 joined via parent 0x0000 (LQI: 235)
Router 0x0002 joined via parent 0x0001 (LQI: 198)
...
[Phase 3] End devices joining network...
EndDevice 0x0007 joined via parent 0x0002 (LQI: 212, Battery: 100%)
...
========================================
NETWORK TOPOLOGY VISUALIZATION
========================================
Legend: [C]=Coordinator [R]=Router [E]=EndDevice [X]=Failed
0 1 2 3 4
+---+---+---+---+---+
0 | C | R | | R | E |
+---+---+---+---+---+
1 | R | E | R | E | R |
+---+---+---+---+---+
2 | E | R | E | | E |
+---+---+---+---+---+
------------------------------------------------------------
MESSAGE: 0x000A --> 0x0000
Payload: "TEMP:22.5C,HUMID:45%"
------------------------------------------------------------
Route trace:
0x000A -> 0x0006
0x0006 -> 0x0003
0x0003 -> 0x0000 [FINAL HOP]
[DELIVERED] Message arrived at 0x0000
Status: DELIVERED
------------------------------------------------------------
999.7 Challenge Exercises
999.8 Key Concepts Demonstrated
| Concept | What the Simulation Shows |
|---|---|
| Device Roles | Coordinator (0x0000) starts network, Routers extend range, End Devices conserve power |
| Network Formation | Devices join in phases: Coordinator first, then Routers, then End Devices |
| Mesh Routing | Messages hop through multiple Routers to reach destination |
| Self-Healing | When Router fails, network automatically finds alternate paths |
| Hop Counting | Each relay adds one hop; fewer hops = lower latency |
| Sleep Modes | End Devices sleep 90%+ of time, waking only to transmit |
999.9 Connection to Real Zigbee Networks
While this ESP32 simulation uses software to model Zigbee behavior, real Zigbee networks use:
- IEEE 802.15.4 radios operating at 2.4 GHz
- AODV routing protocol for on-demand route discovery
- AES-128 encryption for network security
- Zigbee Cluster Library (ZCL) for device interoperability
- 15-bit PAN IDs and 16-bit network addresses
The principles demonstrated here (mesh topology, self-healing, device roles, hop-based routing) directly apply to production Zigbee deployments with hardware like:
- XBee S2C modules (Digi)
- CC2530/CC2652 (Texas Instruments)
- EFR32MG (Silicon Labs)
- Philips Hue Bridge (consumer example)
999.10 Visual Reference Gallery
These AI-generated figures provide alternative visual perspectives on Zigbee concepts.
Zigbee Network Topology:
Zigbee Cluster Library:
Zigbee Protocol Stack:
Zigbee in IoT Landscape:
999.11 Summary
This interactive simulation demonstrated core Zigbee mesh networking concepts:
- Network Formation: Coordinator starts the PAN, routers join and extend coverage, end devices connect through nearest router
- Multi-Hop Routing: Messages traverse multiple nodes to reach distant destinations, with hop counting for path optimization
- Self-Healing Mesh: When nodes fail, the network automatically discovers alternate routes
- Power Management: End devices use sleep modes to conserve battery, waking only to transmit
- Topology Visualization: ASCII grid shows device placement and connectivity
999.12 Whatβs Next
Return to Zigbee Wokwi Simulation Lab for an overview of all Zigbee labs, or explore related chapters:
Deep Dives: - Zigbee Fundamentals and Architecture - Protocol stack and mesh routing - Zigbee Comprehensive Review - Complete protocol analysis - IEEE 802.15.4 Fundamentals - Underlying MAC/PHY layer
Comparisons: - Thread vs Zigbee - IPv6-based mesh comparison - Bluetooth Mesh - Alternative mesh approach - Matter Integration - Smart home protocol evolution
Learning: - Quizzes Hub - Test your Zigbee knowledge - Simulations Hub - More network simulators