997  Zigbee Lab: ESP32 Temperature Monitoring Network

997.1 Learning Objectives

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

  • Configure XBee Modules: Set up coordinator and end device roles using XCTU software
  • Build a Sensor Network: Connect DHT22 sensors to Arduino end devices
  • Implement API Mode Communication: Parse XBee API frames for reliable data transfer
  • Create a Central Coordinator: Use ESP32 to receive and aggregate sensor data
  • Monitor Network Health: Track RSSI, sensor counts, and alert on abnormal readings

What is this lab? A practical hands-on project building a Zigbee temperature and humidity monitoring network with real hardware.

When to use: - When learning Zigbee coordinator/end device communication - For understanding XBee API mode programming - Before deploying production sensor networks

Key Topics:

Topic Focus
XBee Configuration XCTU software, API mode
Coordinator Role ESP32 as central hub
End Device Role Arduino with sensors
Data Aggregation Parsing and displaying readings

Prerequisites: - Zigbee Fundamentals - Basic Arduino/ESP32 programming - Understanding of serial communication

997.2 Prerequisites

Before starting this lab, ensure familiarity with:

997.3 Hardware Requirements

To complete this lab, you will need:

  • ESP32 Development Board
  • XBee S2C module (configured as coordinator)
  • 2x Arduino Uno + XBee modules (end devices)
  • 2x DHT22 temperature/humidity sensors
  • XBee Explorer USB adapter
  • Jumper wires
  • Breadboard

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#7F8C8D', 'background': '#ffffff', 'mainBkg': '#2C3E50', 'secondBkg': '#16A085', 'tertiaryBkg': '#E67E22'}}}%%
flowchart TD
    subgraph Config[Configuration Station]
        XCTU[XCTU Software] <--> USB[XBee Explorer USB]
        USB <--> XCONF[XBee Modules<br/>Being Configured]
    end

    subgraph Coord[Coordinator Station]
        ESP[ESP32 Board<br/>GPIO 16/17 UART] <--> XC[XBee S2C Coordinator<br/>PAN ID: 0x1234<br/>Channel: 25]
        ESP -->|USB Serial<br/>115200 baud| MON[Serial Monitor]
    end

    subgraph ED1[End Device Station 1]
        ARD1[Arduino Uno<br/>SoftwareSerial 10/11] <--> XE1[XBee End Device]
        ARD1 --> DHT1[DHT22 Sensor<br/>Pin 2<br/>Temp & Humidity]
    end

    subgraph ED2[End Device Station 2]
        ARD2[Arduino Uno<br/>SoftwareSerial 10/11] <--> XE2[XBee End Device]
        ARD2 --> DHT2[DHT22 Sensor<br/>Pin 2<br/>Temp & Humidity]
    end

    XC <-.->|Zigbee Mesh<br/>2.4 GHz| XE1
    XC <-.->|Zigbee Mesh<br/>2.4 GHz| XE2

    style Config fill:#7F8C8D,stroke:#333
    style Coord fill:#E67E22,stroke:#2C3E50
    style ED1 fill:#16A085,stroke:#2C3E50
    style ED2 fill:#16A085,stroke:#2C3E50

Figure 997.1: Zigbee temperature monitoring lab setup showing hardware connections and data flow. Coordinator station (green) includes ESP32 development board connected via UART (GPIO 16/17) to XBee S2C coordinator module configured for PAN ID 0x1234 on channel 25, with USB serial monitor at 115200 baud for output. Two end device stations (orange) each contain Arduino Uno with SoftwareSerial (pins 10/11) connected to XBee end device modules and DHT22 temperature/humidity sensors on pin 2. Configuration section (navy) shows XCTU software used with XBee Explorer USB adapter to program XBee modules. Dotted lines represent wireless Zigbee mesh communication at 2.4 GHz between coordinator and end devices. Data flow sequence: DHT22 sensors measure temperature and humidity, Arduino formats data as β€œTEMP:22.5,HUMID:45” strings, XBee modules transmit to coordinator, ESP32 receives and parses data, network summary displays every 30 seconds showing all active sensors with their readings, battery levels, and RSSI signal strength.

997.4 Part 1: Configure XBee Coordinator

Use XCTU software to configure the XBee module as a coordinator:

  1. Connect XBee to computer via USB adapter
  2. Open XCTU software
  3. Configure as Coordinator:
ID (PAN ID): 1234
CE (Coordinator Enable): 1
AP (API Mode): 2
BD (Baud Rate): 9600
  1. Click β€œWrite” to save configuration
NoteAPI Mode 2

API mode 2 (AP=2) uses escaped characters for reliable binary data transfer. This prevents special bytes like 0x7E (start delimiter) from appearing in data payloads.

997.5 Part 2: ESP32 Coordinator Code

The ESP32 acts as the network coordinator, receiving data from all end devices:

#include <HardwareSerial.h>

// XBee connected to Serial2
#define XBEE_RX 16
#define XBEE_TX 17

HardwareSerial XBeeSerial(2);

struct SensorReading {
  uint16_t nodeAddress;
  float temperature;
  float humidity;
  unsigned long timestamp;
  int rssi;
};

SensorReading sensorData[10];
int sensorCount = 0;

void setup() {
  Serial.begin(115200);
  XBeeSerial.begin(9600, SERIAL_8N1, XBEE_RX, XBEE_TX);

  Serial.println("\n=== Zigbee Coordinator - Temperature Network ===");
  Serial.println("Waiting for sensor data...\n");

  delay(2000);  // Wait for XBee initialization
}

void loop() {
  // Check for incoming data from Zigbee network
  if (XBeeSerial.available()) {
    processZigbeePacket();
  }

  // Print network summary every 30 seconds
  static unsigned long lastSummary = 0;
  if (millis() - lastSummary > 30000) {
    printNetworkSummary();
    lastSummary = millis();
  }
}

void processZigbeePacket() {
  /**
   * Process XBee API frame (API mode 2).
   * Format: 7E [Length] [Frame Type] [Data] [Checksum]
   */

  if (XBeeSerial.read() != 0x7E) return;  // Start delimiter

  // Read length (2 bytes)
  uint16_t length = XBeeSerial.read() << 8;
  length |= XBeeSerial.read();

  uint8_t frameType = XBeeSerial.read();

  if (frameType == 0x90) {  // RX packet
    // Read 64-bit source address
    uint64_t sourceAddr64 = 0;
    for (int i = 0; i < 8; i++) {
      sourceAddr64 = (sourceAddr64 << 8) | XBeeSerial.read();
    }

    // Read 16-bit network address
    uint16_t sourceAddr16 = XBeeSerial.read() << 8;
    sourceAddr16 |= XBeeSerial.read();

    // Skip receive options
    XBeeSerial.read();

    // Read RF data (temperature and humidity)
    String data = "";
    for (int i = 0; i < length - 12; i++) {
      char c = XBeeSerial.read();
      if (c != 0xFF) data += c;
    }

    // Skip checksum
    XBeeSerial.read();

    // Parse sensor data
    parseSensorData(sourceAddr16, data);
  }
}

void parseSensorData(uint16_t nodeAddr, String data) {
  /**
   * Parse sensor data format: "TEMP:22.5,HUMID:45.2,RSSI:-45"
   */

  Serial.print("\n[Received] Node 0x");
  Serial.print(nodeAddr, HEX);
  Serial.print(": ");
  Serial.println(data);

  float temp = 0, humid = 0;
  int rssi = 0;

  // Extract temperature
  int tempIdx = data.indexOf("TEMP:");
  if (tempIdx >= 0) {
    temp = data.substring(tempIdx + 5, data.indexOf(",", tempIdx)).toFloat();
  }

  // Extract humidity
  int humidIdx = data.indexOf("HUMID:");
  if (humidIdx >= 0) {
    humid = data.substring(humidIdx + 6, data.indexOf(",", humidIdx)).toFloat();
  }

  // Extract RSSI
  int rssiIdx = data.indexOf("RSSI:");
  if (rssiIdx >= 0) {
    rssi = data.substring(rssiIdx + 5).toInt();
  }

  // Store/update sensor reading
  storeSensorReading(nodeAddr, temp, humid, rssi);

  // Display formatted data
  Serial.println("====================================");
  Serial.print("  Temperature: ");
  Serial.print(temp, 1);
  Serial.println(" C");
  Serial.print("  Humidity:    ");
  Serial.print(humid, 1);
  Serial.println(" %");
  Serial.print("  RSSI:        ");
  Serial.print(rssi);
  Serial.println(" dBm");
  Serial.println("====================================");

  // Alert on abnormal readings
  if (temp > 30.0) {
    Serial.println("[ALERT] HIGH TEMPERATURE!");
  }
  if (temp < 10.0) {
    Serial.println("[ALERT] LOW TEMPERATURE!");
  }
  if (humid > 70.0) {
    Serial.println("[ALERT] HIGH HUMIDITY!");
  }
}

void storeSensorReading(uint16_t nodeAddr, float temp, float humid, int rssi) {
  /**
   * Store sensor reading in memory
   */

  // Find existing sensor or add new one
  int idx = -1;
  for (int i = 0; i < sensorCount; i++) {
    if (sensorData[i].nodeAddress == nodeAddr) {
      idx = i;
      break;
    }
  }

  if (idx == -1 && sensorCount < 10) {
    idx = sensorCount++;
  }

  if (idx >= 0) {
    sensorData[idx].nodeAddress = nodeAddr;
    sensorData[idx].temperature = temp;
    sensorData[idx].humidity = humid;
    sensorData[idx].timestamp = millis();
    sensorData[idx].rssi = rssi;
  }
}

void printNetworkSummary() {
  /**
   * Print summary of all active sensors
   */

  Serial.println("\n======================================================================");
  Serial.println("NETWORK SUMMARY");
  Serial.println("======================================================================");
  Serial.print("Active Sensors: ");
  Serial.println(sensorCount);
  Serial.println("----------------------------------------------------------------------");
  Serial.println("Node      Temperature    Humidity    RSSI      Last Update");
  Serial.println("----------------------------------------------------------------------");

  unsigned long now = millis();

  for (int i = 0; i < sensorCount; i++) {
    unsigned long age_sec = (now - sensorData[i].timestamp) / 1000;

    Serial.print("0x");
    Serial.print(sensorData[i].nodeAddress, HEX);
    Serial.print("    ");
    Serial.print(sensorData[i].temperature, 1);
    Serial.print(" C        ");
    Serial.print(sensorData[i].humidity, 1);
    Serial.print("%       ");
    Serial.print(sensorData[i].rssi);
    Serial.print("dBm    ");
    Serial.print(age_sec);
    Serial.println("s ago");
  }

  Serial.println("======================================================================\n");
}

997.6 Part 3: Arduino End Device Code

Each end device reads sensors and transmits to the coordinator:

#include <SoftwareSerial.h>
#include <DHT.h>

#define DHTPIN 2
#define DHTTYPE DHT22

SoftwareSerial xbee(10, 11);  // RX, TX
DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  xbee.begin(9600);
  dht.begin();

  Serial.println("Zigbee End Device - Temperature Sensor");

  // Wait for network join
  delay(5000);
  Serial.println("[OK] Joined Zigbee network");
}

void loop() {
  // Read sensor
  float temp = dht.readTemperature();
  float humid = dht.readHumidity();

  if (isnan(temp) || isnan(humid)) {
    Serial.println("[ERROR] Failed to read sensor");
    delay(10000);
    return;
  }

  // Read RSSI (signal strength)
  int rssi = getRSSI();

  // Format and send data
  String data = "TEMP:" + String(temp, 1) +
                ",HUMID:" + String(humid, 1) +
                ",RSSI:" + String(rssi);

  xbee.print(data);

  Serial.print("Sent: ");
  Serial.println(data);

  // Sleep for 10 seconds
  delay(10000);
}

int getRSSI() {
  // Query XBee for last RSSI (simplified)
  // In real implementation, send AT command "ATDB"
  return -random(40, 70);  // Simulate RSSI between -40 and -70 dBm
}

997.7 Expected Output

When the network is running, the coordinator displays:

=== Zigbee Coordinator - Temperature Network ===
Waiting for sensor data...

[Received] Node 0x1: TEMP:22.3,HUMID:45.8,RSSI:-52
====================================
  Temperature: 22.3 C
  Humidity:    45.8 %
  RSSI:        -52 dBm
====================================

[Received] Node 0x2: TEMP:23.1,HUMID:48.2,RSSI:-58
====================================
  Temperature: 23.1 C
  Humidity:    48.2 %
  RSSI:        -58 dBm
====================================

======================================================================
NETWORK SUMMARY
======================================================================
Active Sensors: 2
----------------------------------------------------------------------
Node      Temperature    Humidity    RSSI      Last Update
----------------------------------------------------------------------
0x1       22.3 C         45.8%       -52dBm    5s ago
0x2       23.1 C         48.2%       -58dBm    3s ago
======================================================================

997.8 Troubleshooting

WarningCommon Issues
Problem Solution
No data received Verify PAN ID matches on all devices
Garbled data Check baud rate settings (9600)
Only partial packets Ensure API mode 2 on coordinator
Sensors not joining Check coordinator CE=1 setting
Weak RSSI Move devices closer or add routers

997.9 Summary

This lab demonstrated building a practical Zigbee temperature monitoring network:

  • XBee Configuration: Used XCTU to set up coordinator and end device roles with matching PAN ID
  • API Mode Communication: Implemented frame parsing for reliable binary data transfer
  • Sensor Integration: Connected DHT22 sensors to Arduino end devices
  • Data Aggregation: ESP32 coordinator collects and displays readings from multiple sensors
  • Network Monitoring: Tracked RSSI and timing for all connected devices

997.10 What’s Next

Continue with Zigbee Lab: Network Analyzer to learn how to visualize network topology and device health using Python and Zigbee2MQTT, or explore the Zigbee Mesh Simulator for hands-on routing experiments.

NoteRelated Chapters

Prerequisites: - Zigbee Fundamentals and Architecture - Device roles and protocol stack

Next Steps: - Zigbee Lab: Network Analyzer - Python monitoring tools - Zigbee Lab: Mesh Simulator - Interactive Wokwi simulation

Reference: - Zigbee Comprehensive Review - Protocol deep dive