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.

**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*)¢i_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*)¢i_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.