Breadboard
docs
Example project · ESP32

Zigbee temperature sensor + hub

The reporting direction of Zigbee: a sealed battery-style temperature sensor joins the thermostat hub and reports over the standard temperature-measurement cluster. The trim pot on its probe (0–3.3 V = −10…+50 °C) is the temperature dial — drag it and watch the hub’s serial tab print each received report. Both firmware sketches are in the Code tab.

The Zigbee temperature sensor + hub circuit as rendered by the simulator

How it works

Zigbee's reporting direction. The sealed temperature sensor's C6 reads its probe pin with the real ADC (0–3.3 V maps to −10…+50 °C) and carries the value on the standard temperature-measurement cluster. The hub DevKit runs the thermostat coordinator firmware: it forms the network, waits for the sensor to bind, requests the sensor's configuration, and subscribes to fast reporting — from then on, every temperature change lands as a genuine attribute report the hub's callback prints. The trim pot on the probe is the temperature dial.

What's on the bench

  • 2× Battery
  • ESP32-C6
  • Trim Pot
  • Zigbee Temp Sensor

How it's wired

  • Battery · posESP32-C6 · vin
  • Battery · negESP32-C6 · gnd
  • Battery · posZigbee Temp Sensor · vcc
  • Battery · negZigbee Temp Sensor · gnd
  • Battery · negESP32-C6 · gnd2
  • Trim Pot · aESP32-C6 · gnd2
  • Trim Pot · bESP32-C6 · 3v3
  • Trim Pot · wiperZigbee Temp Sensor · probe

The code

The exact Arduino C++ sketch(es) the bundled firmware was compiled from — shown in the simulator’s Code tab, where you can edit them and press Compile & upload to rebuild the board’s firmware.

Thermostat hub (coordinator)

// Zigbee thermostat hub — the coordinator for the temperature sensor.
//
// Forms the network, waits for the sensor to join and bind, asks it for its
// configuration and a fast reporting schedule (no typing needed), and then
// narrates every temperature report it receives over the air.

#include <Arduino.h>
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif

#include "Zigbee.h"

#define THERMOSTAT_ENDPOINT 1

ZigbeeThermostat zbThermostat = ZigbeeThermostat(THERMOSTAT_ENDPOINT);

static float lastTemp = -1000;

void receiveSensorTemp(float temperature) {
  lastTemp = temperature;
  Serial.printf("Sensor reports %.1f C\r\n", temperature);
}

void receiveSensorConfig(float min_temp, float max_temp, float tolerance) {
  Serial.printf("Sensor config: %.1f..%.1f C, tolerance %.1f C\r\n", min_temp, max_temp, tolerance);
}

void setup() {
  Serial.begin(115200);

  zbThermostat.onTempReceive(receiveSensorTemp);
  zbThermostat.onTempConfigReceive(receiveSensorConfig);
  zbThermostat.setManufacturerAndModel("Breadboard", "ThermostatHub");

  Zigbee.addEndpoint(&zbThermostat);
  Zigbee.setRebootOpenNetwork(180);

  if (!Zigbee.begin(ZIGBEE_COORDINATOR)) {
    Serial.println("Zigbee failed to start!");
    Serial.println("Rebooting...");
    ESP.restart();
  }

  Serial.println("Network formed and open. Waiting for the temperature sensor to bind");
  while (!zbThermostat.bound()) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.println("Sensor bound.");

  // Ask the sensor for its settings and a fast reporting schedule — the
  // stock example does this on a button press; the hub just does it.
  std::list<zb_device_params_t *> boundSensors = zbThermostat.getBoundDevices();
  for (const auto &device : boundSensors) {
    if (device->short_addr == 0x0000 || device->short_addr == 0xFFFF) {
      zbThermostat.getTemperatureSettings(device->endpoint, device->ieee_addr);
    } else {
      Serial.printf("Sensor on endpoint %u, short address 0x%04x\r\n", device->endpoint, device->short_addr);
      zbThermostat.getTemperatureSettings(device->endpoint, device->short_addr);
    }
  }
  delay(500);
  zbThermostat.setTemperatureReporting(0, 10, 0.5);
  Serial.println("Reporting requested: every temperature change arrives here.");
}

void loop() {
  static uint32_t lastPrint = 0;
  if (millis() - lastPrint > 10000) {
    lastPrint = millis();
    if (lastTemp > -1000) {
      Serial.printf("Latest sensor temperature: %.1f C\r\n", lastTemp);
    }
  }
  delay(100);
}

Temp sensor (end device)

// Zigbee temperature sensor — a battery-style end device that measures on
// its analog probe (GPIO2 / ADC1_CH2) and reports over the air.
//
// The probe voltage maps 0..3.3 V onto -10..+50 °C, so a potentiometer
// wiper on the probe pin sweeps the reported temperature. Reports ride the
// standard temperature-measurement cluster: the coordinator (e.g. the
// thermostat hub) receives genuine attribute reports.

#include <Arduino.h>
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif

#include "Zigbee.h"

#define ZIGBEE_TEMP_ENDPOINT 10
const uint8_t probe = 2;  // GPIO2 = ADC1_CH2

ZigbeeTempSensor zbTemp = ZigbeeTempSensor(ZIGBEE_TEMP_ENDPOINT);

static float readProbeC() {
  int raw = analogRead(probe);  // 0..4095 across 0..3.3 V
  return (raw / 4095.0f) * 60.0f - 10.0f;
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);

  zbTemp.setManufacturerAndModel("Breadboard", "TempSensor");
  zbTemp.setMinMaxValue(-10, 50);
  zbTemp.setTolerance(0.5);

  Serial.println("Adding ZigbeeTempSensor endpoint to Zigbee Core");
  Zigbee.addEndpoint(&zbTemp);

  if (!Zigbee.begin()) {
    Serial.println("Zigbee failed to start!");
    Serial.println("Rebooting...");
    ESP.restart();
  }
  Serial.println("Connecting to network");
  while (!Zigbee.connected()) {
    Serial.print(".");
    delay(100);
  }
  Serial.println();

  // Report at most every second, at least every 10 s, or on a 0.5 °C move.
  zbTemp.setReporting(1, 10, 0.5);
  Serial.println("Joined. Reporting probe temperature (GPIO2, 0-3.3V = -10..50C).");
}

void loop() {
  static uint32_t lastRead = 0;
  if (millis() - lastRead >= 2000) {
    lastRead = millis();
    float t = readProbeC();
    zbTemp.setTemperature(t);
    Serial.printf("probe %.1f C\r\n", t);
  }
  delay(100);
}

Try this

  • Watch the hub's serial tab: network forms, sensor binds, config arrives, then reports stream in.
  • Drag the pot — the sensor’s own serial prints each probe reading, and the hub prints each received report.
  • Click the temp sensor: the Inspector shows the live probe temperature.
  • Open the Code tab: both real sketches (hub + sensor), exactly what the firmware was built from.

Related projects