ZigBee Alarms集群开发指南:物联网设备告警系统原理与NXP ZCL实现

📅 2026/6/17 20:28:04
ZigBee Alarms集群开发指南:物联网设备告警系统原理与NXP ZCL实现
1. ZigBee Alarms集群物联网设备的“哨兵”与“记事本”在智能家居或者工业物联网项目中设备出问题了怎么办是让用户对着一个不亮的灯泡干瞪眼还是让工厂的工程师逐个排查上百个传感器一个健壮的告警系统就是解决这个问题的关键。它就像给每个设备配备了一个尽职的“哨兵”一旦发现异常不仅能立刻“吹哨”通知管理员还能把“哨声”的时间、原因都记录在“记事本”里方便事后复盘。ZigBee协议栈中的Alarms集群正是这样一个标准化的“哨兵”与“记事本”机制。它不属于某个具体设备比如灯或传感器而是一个公共服务。任何遵循ZigBee Cluster Library规范的设备当自身或其他集群如温度传感器集群、门磁集群检测到异常条件如温度超限、门被非法打开时都可以通过Alarms集群这个统一的接口向网络中的其他设备如网关、中控屏发送告警通知。接收方则可以根据告警代码和集群ID精确判断是哪个设备、出了什么问题。这种设计的好处是解耦和标准化。设备功能如测量温度和告警功能是分离的。温度传感器集群只负责定义“温度过高”这个告警条件比如ALARM_CODE_OVER_TEMP 0x01而具体的告警发送、日志记录、远程复位等脏活累活都交给Alarms集群来处理。这极大简化了应用开发你不需要在每个设备里都写一遍网络通信和日志管理代码。NXP恩智浦提供的ZigBee 3.0 ZCL实现将这套理论模型变成了可以直接调用的API函数和数据结构。本文将深入剖析Alarms集群的工作原理并结合NXP ZCL的代码实现手把手带你完成从集群启用、告警触发、日志管理到事件处理的完整开发流程。无论你是正在开发智能安防主机还是需要为传感器添加远程诊断功能这篇文章都能提供直接的参考。2. 核心概念与架构拆解服务器、客户端与告警表在深入代码之前必须厘清Alarms集群中三个核心角色的关系这是理解其工作流的基础。2.1 服务器与客户端的角色定位在ZigBee的集群模型中一个集群实例要么是服务器Server要么是客户端Client这个概念和传统的网络服务器/客户端略有不同更接近于“数据提供者”和“数据消费者”。Alarms集群服务器这是告警的“源头”和“档案管理员”。它运行在可能产生告警的设备上。例如一个温湿度传感器设备。它的核心职责有两个告警日志记录维护一个本地的“告警表”每当有告警产生就自动或由应用添加一条包含告警代码、集群ID和时间戳的记录。告警通知发送当告警条件触发时调用API向指定的客户端设备发送告警通知报文。Alarms集群客户端这是告警的“接收者”和“指挥者”。它通常运行在需要感知和处理告警的设备上比如智能家居网关、触摸面板或手机App。它的核心职责也有两个告警通知接收监听网络中的告警通知收到后通过回调函数通知上层应用以便进行声光提示、推送消息等操作。告警远程管理可以向服务器发送命令请求复位清除单个告警、全部告警甚至清空整个告警日志。一个设备可以同时包含Alarms集群的服务器和客户端吗理论上可以但并不常见。更常见的场景是一个设备如多功能传感器上的不同功能集群如温度、湿度作为告警源它们共享同一个Alarms服务器实例来发送告警而这个设备本身可能不需要接收其他设备的告警因此可以不包含客户端。2.2 告警表日志记录的核心告警表是服务器端最重要的数据结构它是一个先进先出的队列用于存储历史告警事件。NXP ZCL的实现中其大小可以在编译时配置。每条记录包含三个关键信息告警代码一个8位的无符号整数用于标识告警的具体类型。这是集群特定的也就是说0x01在温度集群中可能代表“高温告警”在门磁集群中可能代表“门被打开告警”。定义告警代码是具体功能集群开发者的责任。集群ID一个16位的无符号整数指明是哪个集群产生的告警。例如温度传感器的集群ID是0x0402。结合集群ID和告警代码就能唯一确定告警的含义。时间戳一个32位的UTC时间戳记录告警发生的精确时间。这要求设备必须支持并运行时间集群否则时间戳可能无效填充为0xFFFFFFFF。这个表不仅用于历史查询还直接支持“获取最早告警”命令。当客户端发送Get Alarm命令时服务器就是从这张表里取出时间最早的那条记录返回并在返回后将其删除。2.3 工作流程全景图让我们通过一个典型的烟雾报警场景把上述角色串联起来告警产生烟雾传感器中的“烟雾浓度检测集群”检测到浓度超标判定告警条件满足。调用服务器API传感器的应用程序调用eCLD_AlarmsSignalAlarm()函数传入告警代码如SMOKE_DETECTED和集群ID烟雾集群的ID。服务器执行Alarms服务器实例执行两个动作a) 将此次告警记录到本地告警表b) 构造一个ZCL“告警通知”命令并通过ZigBee网络发送给预设的客户端地址通常是网关。网络传输ZigBee PRO协议栈负责将此命令可靠地传输到网关设备。客户端接收网关设备上的Alarms集群客户端收到该命令。事件回调ZCL框架生成一个E_CLD_ALARMS_CMD_ALARM事件并调用网关应用注册的回调函数将告警详情传递给应用层。应用处理网关应用根据收到的集群ID和告警代码触发相应的动作向用户手机发送推送通知、启动本地蜂鸣器、甚至联动打开排风扇。告警复位用户处理完警情后通过手机App点击“复位”。App通过网关的Alarms客户端向传感器发送一个Reset Alarm或Reset All Alarms命令。传感器收到命令后从告警表中移除相应条目并可能重置传感器状态。3. 工程实现从配置到代码的完整指南理解了原理我们来看如何在NXP的ZCL框架中实际使用Alarms集群。整个过程可以分为配置、初始化和应用开发三个部分。3.1 编译时配置启用与定制一切始于配置文件zcl_options.h。你需要在这里决定是否启用Alarms集群以及以何种角色启用。// 在 zcl_options.h 文件中添加以下定义 /* 启用 Alarms 集群功能 */ #define CLD_ALARMS /* 定义本设备上 Alarms 集群的角色 */ // 如果本设备需要发送或记录告警则启用 SERVER #define ALARMS_SERVER // 如果本设备需要接收和处理告警则启用 CLIENT #define ALARMS_CLIENT /* 配置告警表的大小仅对 SERVER 有效 */ // 定义告警表可以存储的最大条目数例如 10 条 #define CLD_ALARMS_MAX_ALARMS 10 /* 启用可选的告警计数属性 */ // 这个属性会报告当前告警表中的条目数便于客户端查询 #define CLD_ALARMS_ATTR_ALARM_COUNT配置解析与选型建议CLD_ALARMS是总开关必须定义。ALARMS_SERVER和ALARMS_CLIENT可以根据设备功能独立选择。一个智能网关通常只需要CLIENT一个传感器通只需要SERVER一个复杂的多功能设备如带屏的温控器可能需要两者都启用。CLD_ALARMS_MAX_ALARMS需要根据设备内存和实际需求权衡。设得太小历史告警可能被快速覆盖设得太大浪费内存。对于电池供电的传感器5-10条通常足够。对于网关可能需要更多如50条。CLD_ALARMS_ATTR_ALARM_COUNT是一个便利属性启用后客户端可以直接读取u16AlarmCount属性来了解当前未处理的告警数量无需发送Get Alarm命令试探。3.2 数据结构与初始化创建集群实例配置好后需要在应用程序中创建并初始化Alarms集群实例。这通常在你的设备端点初始化函数中完成。// 在你的设备应用初始化文件中例如 app_sensor.c #include Alarms.h // 1. 定义集群实例和共享结构体 tsZCL_ClusterInstance sAlarmsClusterInstance; tsCLD_Alarms sAlarmsClusterServerData; // 用于存储属性如告警计数 tsCLD_AlarmsCustomDataStructure sAlarmsCustomData; // 内部使用 uint8 au8AlarmsAttributeControlBits[1]; // 属性控制位数组长度取决于属性数量 // 2. 在端点初始化函数中调用创建函数 void vApp_InitEndpoint(void) { teZCL_Status eStatus; tsZCL_EndPointDefinition sEndPointDefinition; tsZCL_ClusterDefinition sClusterDefinition; // ... 其他初始化代码例如填充 sEndPointDefinition ... // 设置集群定义指向预定义的Alarms集群结构 sClusterDefinition sCLD_Alarms; // 创建Alarms集群服务器实例 eStatus eCLD_AlarmsCreateAlarms( sAlarmsClusterInstance, // 集群实例指针 TRUE, // bIsServer: TRUE 表示创建服务器 sClusterDefinition, // 集群定义 sAlarmsClusterServerData, // 属性存储结构指针 au8AlarmsAttributeControlBits, // 属性控制位 sAlarmsCustomData // 自定义数据结构 ); if(eStatus ! E_ZCL_SUCCESS) { // 处理创建失败错误 DBG_vPrintf(TRACE_ALARMS, “Alarms cluster creation failed: %d\n”, eStatus); } // 将创建的集群实例关联到端点定义中 sEndPointDefinition.u8ClusterCount 1; sEndPointDefinition.psClusterInstance sAlarmsClusterInstance; // ... 注册端点 ... }关键点解析tsCLD_Alarms sAlarmsClusterServerData这个结构体包含了Alarms集群的所有属性。对于服务器最重要的就是可选的u16AlarmCount。这个结构体必须在整个生命周期内有效通常是全局变量或静态变量。tsCLD_AlarmsCustomDataStructure sAlarmsCustomData这是ZCL库内部用于管理状态如当前正在处理的命令、事务序列号等的数据结构。应用层不需要访问其内部字段但必须提供其存储空间。eCLD_AlarmsCreateAlarms的第二个参数bIsServer决定了创建的是服务器还是客户端。一个端点只能创建一个Alarms实例要么是服务器要么是客户端。如果你需要同时具备两种角色理论上需要创建两个不同的端点。3.3 告警的触发与发送服务器端实战当你的设备检测到需要告警的条件时比如温度传感器读数超过阈值就需要触发告警。以下是完整的触发流程。// 假设在温度传感器应用中检测到高温 void vTempSensor_CheckAlarm(int16 i16MeasuredTemp) { teZCL_Status eStatus; uint8 u8Tsn; tsZCL_Address sDestinationAddress; const int16 i16HighThreshold 3500; // 阈值 35.00°C if(i16MeasuredTemp i16HighThreshold) { // 1. 设置目标地址例如网关的地址 sDestinationAddress.eAddressType E_ZCL_AM_SHORT; // 使用短地址 sDestinationAddress.uAddress.u16DestinationAddress 0x0000; // 网关短地址实际应从绑定表或配置获取 // 2. 调用告警信号函数 // 假设温度集群的ID是 0x0402自定义高温告警代码是 0x01 eStatus eCLD_AlarmsSignalAlarm( APP_TEMP_SENSOR_ENDPOINT, // 本设备端点 GATEWAY_ALARMS_ENDPOINT, // 目标设备端点需事先知晓或通过发现获得 sDestinationAddress, u8Tsn, // 函数会填充事务序列号 0x01, // u8AlarmCode: 高温告警 0x0402 // u16ClusterId: 温度测量集群 ); if(eStatus E_ZCL_SUCCESS) { DBG_vPrintf(TRACE_ALARMS, “High temp alarm signaled. TSN: %d\n”, u8Tsn); // 此时告警已自动添加到本地日志表并已发送通知 } else { DBG_vPrintf(TRACE_ALARMS, “Failed to signal alarm: %d\n”, eStatus); // 处理发送失败可能需要重试或仅记录本地日志 } } }注意事项与陷阱地址管理sDestinationAddress的设置是关键。在生产环境中更可靠的方式是使用绑定。在设备入网时就将网关的Alarms客户端端点与传感器的Alarms服务器端点绑定。这样在调用eCLD_AlarmsSignalAlarm时可以使用地址类型E_ZCL_AM_BOUND库函数会自动从绑定表中查找目标地址无需硬编码。告警防抖在真实场景中传感器读数可能瞬间波动。直接使用瞬时值触发告警会导致误报。通常需要实现一个简单的防抖逻辑例如“连续3次采样超过阈值才触发告警”。告警自动复位有些告警条件可能是瞬时的比如瞬时电压浪涌。当条件消失后你可能希望自动复位告警。这需要应用层在检测到条件恢复正常时调用eCLD_AlarmsResetAlarmLog或相关函数来清除对应的告警日志条目。否则告警将一直存在于日志中直到被手动清除。错误处理eCLD_AlarmsSignalAlarm的返回值需要仔细处理。E_ZCL_ERR_ZTRANSMIT_FAIL通常意味着网络问题目标设备离线、路由失败等。对于关键告警应用层应实现重试机制并可能将发送失败的告警在非易失性存储器中暂存待网络恢复后重发。3.4 告警的接收与处理客户端实战在网关或中控设备上你需要设置回调函数来接收和处理告警通知。// 在网关应用的回调函数中处理Alarms事件 teZCL_Status eApp_AlarmsClientCallback( tsZCL_CallBackEvent *psEvent ) { tsCLD_AlarmsCallBackMessage *psAlarmMessage; tsCLD_AlarmsAlarmCommandPayload *psAlarmPayload; // 1. 检查事件类型是否为集群自定义命令 if(psEvent-eEventType ! E_ZCL_CBET_CLUSTER_CUSTOM) { return E_ZCL_ERR_UNDEFINED; } // 2. 安全地将自定义数据指针转换为告警回调消息结构 psAlarmMessage (tsCLD_AlarmsCallBackMessage*)psEvent-uMessage.sClusterCustomMessage.pvCustomData; // 3. 根据命令ID进行分发处理 switch(psAlarmMessage-u8CommandId) { case E_CLD_ALARMS_CMD_ALARM: // 收到告警通知 psAlarmPayload psAlarmMessage-uMessage.psAlarmCommandPayload; DBG_vPrintf(TRACE_ALARMS, “Alarm Received! Cluster: 0x%04X, Code: 0x%02X\n”, psAlarmPayload-u16ClusterId, psAlarmPayload-u8AlarmCode); // 4. 根据集群ID和告警代码执行具体操作 vHandleAlarmNotification(psAlarmPayload-u16ClusterId, psAlarmPayload-u8AlarmCode); break; case E_CLD_ALARMS_CMD_GET_ALARM_RESPONSE: // 处理Get Alarm命令的响应如果发送了该请求 DBG_vPrintf(TRACE_ALARMS, “Get Alarm Response received.\n”); vHandleGetAlarmResponse(psAlarmMessage-uMessage.psGetAlarmResponsePayload); break; default: DBG_vPrintf(TRACE_ALARMS, “Unknown Alarms command: %d\n”, psAlarmMessage-u8CommandId); break; } return E_ZCL_SUCCESS; } // 具体的告警处理函数示例 void vHandleAlarmNotification(uint16 u16ClusterId, uint8 u8AlarmCode) { char szMsg[100]; switch(u16ClusterId) { case 0x0402: // 温度测量集群 if(u8AlarmCode 0x01) { snprintf(szMsg, sizeof(szMsg), “温度传感器告警检测到高温”); // 触发动作发送推送、记录数据库、点亮LED等 vSendPushNotification(szMsg); vTurnOnWarningLED(LED_RED); } break; case 0x0500: // IAS Zone 集群入侵报警 if(u8AlarmCode 0x01) { // 假设0x01为入侵告警 snprintf(szMsg, sizeof(szMsg), “安防告警检测到入侵”); vTriggerSiren(TRUE); vRecordSecurityEvent(SECURITY_BREACH); } break; default: DBG_vPrintf(TRACE_ALARMS, “Unhandled alarm from cluster 0x%04X\n”, u16ClusterId); break; } }回调机制深度解析事件传递链当ZigBee协议栈收到一个发往本设备、本端点的Alarms集群命令时ZCL层会解析该命令然后通过你注册的端点回调函数本例中是eApp_AlarmsClientCallback将事件抛给应用层。psEvent参数包含了所有上下文信息。命令ID分发tsCLD_AlarmsCallBackMessage结构体中的u8CommandId是分发的关键。对于客户端主要处理E_CLD_ALARMS_CMD_ALARM告警通知和E_CLD_ALARMS_CMD_GET_ALARM_RESPONSE查询响应。对于服务器端则需要处理E_CLD_ALARMS_CMD_RESET_ALARM等复位命令。负载数据获取命令的具体数据如告警代码、集群ID存储在uMessage联合体中。对于ALARM命令需要通过psAlarmCommandPayload指针来访问tsCLD_AlarmsAlarmCommandPayload结构体。这里必须注意类型转换的安全性确保pvCustomData确实指向的是告警消息。3.5 告警的远程管理客户端发起控制客户端不仅可以被动接收告警还可以主动管理服务器端的告警状态。这是实现远程复位、日志查询的基础。// 网关应用收到用户“复位所有告警”的指令后 void vGateway_ResetAllAlarms(uint16 u16TargetShortAddr, uint8 u8TargetEndpoint) { teZCL_Status eStatus; uint8 u8Tsn; tsZCL_Address sDestAddr; // 设置目标地址产生告警的传感器 sDestAddr.eAddressType E_ZCL_AM_SHORT; sDestAddr.uAddress.u16DestinationAddress u16TargetShortAddr; // 发送 Reset All Alarms 命令 eStatus eCLD_AlarmsCommandResetAllAlarmsCommandSend( GATEWAY_ENDPOINT, // 本地客户端端点 u8TargetEndpoint, // 远程服务器端点 sDestAddr, u8Tsn ); if(eStatus E_ZCL_SUCCESS) { DBG_vPrintf(TRACE_ALARMS, “ResetAllAlarms command sent. TSN: %d\n”, u8Tsn); // 可以在这里启动一个定时器等待服务器的响应事件如果需要确认 } else { DBG_vPrintf(TRACE_ALARMS, “Failed to send ResetAllAlarms: %d\n”, eStatus); } } // 查询特定设备上最早的告警用于诊断 void vGateway_GetEarliestAlarm(uint16 u16TargetShortAddr, uint8 u8TargetEndpoint) { teZCL_Status eStatus; uint8 u8Tsn; tsZCL_Address sDestAddr; sDestAddr.eAddressType E_ZCL_AM_SHORT; sDestAddr.uAddress.u16DestinationAddress u16TargetShortAddr; eStatus eCLD_AlarmsCommandGetAlarmCommandSend( GATEWAY_ENDPOINT, u8TargetEndpoint, sDestAddr, u8Tsn ); if(eStatus E_ZCL_SUCCESS) { DBG_vPrintf(TRACE_ALARMS, “GetAlarm command sent. TSN: %d\n”, u8Tsn); // 响应将通过 E_CLD_ALARMS_CMD_GET_ALARM_RESPONSE 事件在回调函数中接收 } }管理命令的交互逻辑Reset All Alarms和Reset Alarm Log的区别前者是请求服务器复位所有告警条件逻辑复位后者是请求清空告警日志表物理删除记录。具体实现取决于服务器应用。通常Reset All Alarms会触发服务器应用去检查各个集群的告警条件是否已消除然后从日志中移除对应条目而Reset Alarm Log是强制清空日志不管告警条件是否还存在。事务序列号每个发送的命令都会有一个唯一的TSN。当服务器回复响应时对于Get Alarm命令响应的TSN会与请求的TSN匹配。这在一个客户端同时向多个服务器发送请求时用于正确匹配请求和响应。虽然Alarms集群的标准命令中只有Get Alarm有响应但TSN机制是ZCL的通用设计。无响应命令Reset Alarm、Reset All Alarms、Reset Alarm Log命令都是单向的服务器不会发送标准的ZCL响应。服务器端的应用在收到这些命令的事件后执行相应操作即可。如果客户端需要确认需要在应用层设计额外的确认机制例如客户端发送复位命令后再主动发送一个Get Alarm查询看日志是否已空。4. 高级应用与避坑指南掌握了基础操作后我们来看几个在实际项目中容易遇到的问题和进阶用法。4.1 告警代码的定义与管理避免“魔法数字”直接在代码中写0x01这样的告警代码是极不推荐的这被称为“魔法数字”会严重降低代码的可读性和可维护性。最佳实践是为每个使用告警的集群在其头文件中明确定义告警代码枚举。// 在温度传感器集群的头文件例如 TemperatureCluster.h 中定义 typedef enum { E_TEMP_ALARM_UNDEFINED 0x00, E_TEMP_ALARM_OVER_TEMPERATURE 0x01, E_TEMP_ALARM_UNDER_TEMPERATURE 0x02, E_TEMP_ALARM_SENSOR_FAULT 0x03, } teTempAlarmCode; // 在烟雾传感器集群的头文件 SmokeCluster.h 中定义 typedef enum { E_SMOKE_ALARM_UNDEFINED 0x00, E_SMOKE_ALARM_SMOKE_DETECTED 0x01, E_SMOKE_ALARM_SENSOR_DIRTY 0x02, } teSmokeAlarmCode;这样在触发和解析告警时代码意图就非常清晰// 触发告警 eCLD_AlarmsSignalAlarm(..., E_TEMP_ALARM_OVER_TEMPERATURE, TEMPERATURE_MEASUREMENT_CLUSTER_ID, ...); // 解析告警 if(u16ClusterId TEMPERATURE_MEASUREMENT_CLUSTER_ID) { switch(u8AlarmCode) { case E_TEMP_ALARM_OVER_TEMPERATURE: // 处理高温 break; case E_TEMP_ALARM_SENSOR_FAULT: // 处理传感器故障 break; } }4.2 时间戳的依赖与处理Alarms集群的日志条目包含一个UTC时间戳字段。这是一个非常有用的功能可以让你知道告警发生的具体时间。然而它引入了一个关键的依赖项时间同步。依赖Time集群要使时间戳有效设备必须实现并运行ZigBee的Time集群。该集群负责从网络协调器或网关同步UTC时间。如果设备没有时间同步eCLD_AlarmsAddAlarmToLog函数或SignalAlarm内部调用生成的时间戳将是0xFFFFFFFF无效值。实践建议对于电池设备持续运行Time集群进行时间同步可能耗电。一种折中方案是只在设备唤醒并连接网络后尝试同步一次时间。如果同步成功则后续告警使用有效时间戳如果失败则记录无效时间戳。在客户端处理时需要能容忍无效时间戳。对于关键系统如果告警时间对于故障诊断至关重要如工业场景应考虑使用有持续电源的设备作告警服务器或确保网络中有可靠的时间源。客户端显示在网关或客户端界面上显示告警时间时务必检查时间戳是否为0xFFFFFFFF。如果是应显示“时间无效”或“设备未同步时间”而不是显示一个荒谬的日期。4.3 内存管理与告警表溢出告警表是一个大小固定的队列。当表满后新的告警条目该如何处理NXP ZCL的实现通常有两种策略你需要查阅具体版本的文档或源码来确认丢弃最旧条目新的条目覆盖时间最早的条目。这是比较常见的FIFO队列行为。丢弃新条目表满后不再添加新条目eCLD_AlarmsAddAlarmToLog可能返回一个错误码如E_ZCL_FAIL。你需要在自己的应用层处理这种边界情况如果策略是覆盖你需要评估丢失最早告警的影响。对于需要完整审计日志的场景这可能不可接受。一个更健壮的方案是在调用eCLD_AlarmsSignalAlarm后检查返回值。如果是因为表满而失败你可以将告警信息存储到外部的非易失性存储器中或者至少记录一条系统日志表明有告警因日志满而丢失。可以通过定期查询u16AlarmCount属性如果启用来监控告警表的填充情况并在达到阈值如80%时提前进行清理或通知用户。4.4 网络可靠性考量与重试机制ZigBee是无线网络传输可能失败。eCLD_AlarmsSignalAlarm发送的告警通知是一个单播或广播消息可能因为目标设备离线、路由错误、信道干扰等原因发送失败。对于关键告警如火灾、入侵必须实现应用层的重试和确认机制简单的重试如果发送失败启动一个定时器在几秒后重试最多重试3-5次。带确认的发送ZigBee APS层有ACK机制但这只是数据链路层的确认。更上层的确认可以通过自定义一个“告警已接收”的集群命令来实现。当客户端收到告警后反向发送一个确认命令给服务器。服务器只有在收到确认后才认为告警已成功送达。否则进入重试循环。本地持久化在发送前先将告警事件记录到设备的Flash中。发送成功后标记为“已发送”。这样即使设备在发送过程中断电重启上电后也能重新读取未发送的告警并进行补发。这需要设计一个简单的非易失性存储队列。4.5 与具体功能集群的集成示例Alarms集群本身是通用的它需要与具体的功能集群配合工作。以“温度过高告警”为例完整的集成流程如下在温度集群中定义告警条件在温度传感器的应用代码中你需要定义一个阈值并定期检查当前温度。#define TEMP_HIGH_THRESHOLD 3500 // 35.00°C #define TEMP_ALARM_HYSTERESIS 100 // 1.00°C 迟滞防止在阈值附近频繁触发 static bool_t bHighTempAlarmActive FALSE; void vTempCluster_PeriodicCheck(int16 i16CurrentTemp) { if(!bHighTempAlarmActive (i16CurrentTemp TEMP_HIGH_THRESHOLD)) { // 条件满足触发告警 vTriggerAlarm(E_TEMP_ALARM_OVER_TEMPERATURE); bHighTempAlarmActive TRUE; } else if (bHighTempAlarmActive (i16CurrentTemp (TEMP_HIGH_THRESHOLD - TEMP_ALARM_HYSTERESIS))) { // 条件解除复位告警可选也可等远程复位 vClearAlarm(E_TEMP_ALARM_OVER_TEMPERATURE); bHighTempAlarmActive FALSE; } }告警触发函数这个函数封装了对Alarms集群的调用。void vTriggerAlarm(teTempAlarmCode eAlarmCode) { // ... 设置目标地址等 ... eCLD_AlarmsSignalAlarm(..., (uint8)eAlarmCode, TEMPERATURE_MEASUREMENT_CLUSTER_ID, ...); // 可以在这里增加本地指示如闪烁LED } void vClearAlarm(teTempAlarmCode eAlarmCode) { // 当告警条件消失可以选择本地清除日志条目。 // 注意这需要知道告警在日志表中的具体位置通常更简单的做法是等待远程复位命令。 // 或者如果告警是瞬时性的也可以不处理等客户端来查询或复位。 }处理复位命令在温度传感器的Alarms服务器回调函数中当收到E_CLD_ALARMS_CMD_RESET_ALARM事件时你需要检查请求复位的集群ID和告警代码是否匹配。case E_CLD_ALARMS_CMD_RESET_ALARM: psResetPayload psAlarmMessage-uMessage.psResetAlarmCommandPayload; if(psResetPayload-u16ClusterId TEMPERATURE_MEASUREMENT_CLUSTER_ID) { if(psResetPayload-u8AlarmCode E_TEMP_ALARM_OVER_TEMPERATURE) { // 复位高温告警状态 bHighTempAlarmActive FALSE; DBG_vPrintf(TRACE_ALARMS, “Over-temperature alarm reset remotely.\n”); } } // 注意收到复位命令后通常还需要调用 eCLD_AlarmsGetAlarmFromLog 等函数 // 来从日志表中移除对应的条目。具体取决于ZCL库的实现有些可能自动处理。 break;通过这样的集成温度集群负责定义“什么情况下算告警”而Alarms集群负责“如何把告警消息发出去、记下来、并管理它”。两者各司其职共同构成了一个清晰、可维护的告警子系统。