---
# System prepended metadata

title: How to add a temperature sensor to my zigbee code?
tags: [Sensor]

---

Here’s a platform-agnostic recipe to add a [temperature sensor](https://www.onzuu.com/category/temperature-sensors) to your Zigbee device firmware, plus ready-to-copy snippets for Silicon Labs (EmberZNet/GSDK) and TI Z-Stack.
![ESP32-DS18B20-Temperature-Sensor-Arduino-IDE-Web-Server](https://hackmd.io/_uploads/HJ7z20v2ge.jpg)

**1) What Zigbee expects (ZCL)**

Use the Temperature Measurement cluster (0x0402) on your endpoint.

* MeasuredValue (0x0000): int16, units = 0.01 °C
* e.g., 25.34 °C → 2534; −5.2 °C → -520
* MinMeasuredValue (0x0001) and MaxMeasuredValue (0x0002): optional, int16 (0.01 °C).
* Tolerance (0x0003): optional, uint16 (0.01 °C).
* Invalid value (if sensor not ready): 0x8000 (int16 min).

You’ll also want reporting so coordinators (Home Assistant, Zigbee2MQTT, etc.) get updates automatically.

**2) Hardware & driver**

Pick a [sensor](https://www.ampheo.com/c/sensors) and read it in your main loop or a periodic timer:

I²C ([TMP117](https://www.ampheo.com/search/TMP117), [TMP102](https://www.ampheo.com/search/TMP102), SHTC3…), 1-Wire ([DS18B20](https://www.ampheo.com/product/ds18b20-26808200)), Analog ([LM35](https://www.ampheo.com/search/LM35_page6)/NTC via ADC), or on-chip temp sensor if available.
Convert to °C, then scale to centi-degrees (×100) for ZCL.

Example (pseudo, I²C TMP117):

```
float t_c = tmp117_read_celsius(i2c0);         // e.g., 25.34
int16_t zcl_temp = (int16_t)lrintf(t_c * 100); // 2534
```

**3) Push the value into ZCL & enable reporting**

Workflow:

1. Read sensor → convert to int16 centi-°C.
2. Write MeasuredValue attribute on your endpoint.
3. Configure attribute reporting (min/max intervals + reportable change), or accept Configure Reporting from the coordinator.

Typical reporting policy:

minInterval = 30 s, maxInterval = 300 s, reportableChange = 50 (i.e., 0.5 °C).

**4) [Silicon Labs](https://www.ampheo.com/manufacturer/silicon-labs) (EmberZNet / GSDK) example**

a) **Add the cluster (server) to your endpoint** in the .isc/.slcp (Temperature Measurement cluster, server side). Also include Basic/Identify and (optional) Power Configuration if battery-powered.

**b) Periodic read & update**

```
#include "app/framework/include/af.h"

#define EP 1
static int16_t lastReported = 0x8000;

static void report_temperature(int16_t centi_c) {
  emberAfWriteServerAttribute(EP,
    ZCL_TEMPERATURE_MEASUREMENT_CLUSTER_ID,
    ZCL_TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE_ID,
    (uint8_t*)&centi_c,
    ZCL_INT16S_ATTRIBUTE_TYPE);
  // Notify reporting plugin that value changed
  emberAfReportingAttributeChangeCallback(EP,
    ZCL_TEMPERATURE_MEASUREMENT_CLUSTER_ID,
    ZCL_TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE_ID,
    ZCL_INT16S_ATTRIBUTE_TYPE,
    (uint8_t*)&centi_c);
}

void sl_zigbee_app_tick(void) {
  float t_c = tmp117_read_celsius(I2C0);
  int16_t val = (int16_t)lrintf(t_c * 100.0f);
  if (val != lastReported) {
    report_temperature(val);
    lastReported = val;
  }
}
```


**c) Configure default reporting (startup)**

```
#include "reporting.h"

void app_init(void) {
  EmberAfPluginReportingEntry cfg = {
    .endpoint = EP,
    .clusterId = ZCL_TEMPERATURE_MEASUREMENT_CLUSTER_ID,
    .attributeId = ZCL_TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE_ID,
    .mask = EMBER_ZCL_ATTRIBUTE_MASK_CLIENT | EMBER_ZCL_ATTRIBUTE_MASK_SERVER, // server attr
    .manufacturerCode = EMBER_AF_NULL_MANUFACTURER_CODE,
    .data.reportingDirection = EMBER_ZCL_REPORTING_DIRECTION_REPORTED,
    .data.reported.minInterval = 30,
    .data.reported.maxInterval = 300,
    .data.reported.reportableChange = 50  // 0.5°C
  };
  emberAfPluginReportingConfigureReportedAttribute(&cfg);
}
```

**5) [TI](https://www.ampheo.com/manufacturer/texas-instruments) Z-Stack (Z-Stack 3.x) example**

**a) Simple Descriptor**: add Temperature Measurement (0x0402) to your endpoint’s server cluster list.

**b) Attribute table:**

```
#include "zcl.h"
#include "zcl_general.h"
#include "zcl_ha.h"
#include "zcl_ms.h"

int16 MeasuredValue = 0x8000; // invalid at boot
CONST zclAttrRec_t tempAttrList[] = {
  { ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT,
    { ATTRID_MS_TEMPERATURE_MEASURED_VALUE, ZCL_DATATYPE_INT16, ACCESS_CONTROL_READ | ACCESS_REPORTABLE, (void *)&MeasuredValue } },
  // Optional:
  // { ... MinMeasuredValue }, { ... MaxMeasuredValue }, { ... Tolerance }
};
```


**c) Periodic sensor read & update:**

```
void Temp_ReadAndUpdate(void) {
  float t_c = tmp117_read_celsius(I2C0);
  int16 newVal = (int16)lrintf(t_c * 100.0f);
  if (newVal != MeasuredValue) {
    MeasuredValue = newVal;
    zcl_setAttrValue(ENDPOINT, ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT,
                     ATTRID_MS_TEMPERATURE_MEASURED_VALUE, &MeasuredValue, sizeof(MeasuredValue));
    // Tell BDB/reporting the value changed
    bdb_RepChangedAttrValue(ENDPOINT, ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT,
                            ATTRID_MS_TEMPERATURE_MEASURED_VALUE);
  }
}
```


**d) Reporting (default on boot or via Configure Reporting from coordinator):**

```
zclReportCmd_t rep;
rep.numAttr = 1;
rep.attrList[0].attrID = ATTRID_MS_TEMPERATURE_MEASURED_VALUE;
rep.attrList[0].dataType = ZCL_DATATYPE_INT16;
rep.attrList[0].minReportInt = 30;
rep.attrList[0].maxReportInt = 300;
rep.attrList[0].reportableChange = 50; // 0.5°C
zcl_SendConfigReportCmd(ENDPOINT, &dstAddr, ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT,
                        &rep, ZCL_FRAME_SERVER_CLIENT_DIR, true, bdb_getZCLFrameCounter());
```

**6) Discovery & binding**

* Ensure your endpoint exposes Basic (0x0000) and Identify (0x0003) so pairing is smooth.
* During commissioning, the coordinator will discover the Temperature Measurement cluster and may send Configure Reporting. Your code above already supports that.

**7) Testing checklist**

* Join the device, open Zigbee2MQTT logs or a sniffer (Wireshark) and verify:

  * Read Attribute for 0x0402/0x0000 returns your value (e.g., 2534).
  * You receive Report Attributes when temperature changes or the max interval elapses.

* Heat/cool the [sensor](https://www.ampheoelec.de/c/sensors) (touch with finger / freeze spray) and watch updates.
* Verify negative temps format (e.g., −2.35 °C → -235).

**8) Common pitfalls (quick fixes)**

* Wrong units: remember centi-°C; coordinators won’t auto-scale raw °C floats.
* No reports: forgot to configure reporting or to call the “attribute changed” hook.
* I²C/ADC timing: don’t block Zigbee stack; read in a timer/task and keep handlers short.
* Battery devices: throttle reporting (bigger minInterval, larger reportableChange) to save power; also expose Power Configuration (0x0001) for voltage/percent.