5.1 应用层对 ZCL API 的调用
一句话理解
应用层用 ZCL,只需做三件事:注册命令回调(接命令)→ 注册属性列表(挂属性)→ 注册消息接收(收基础命令)。本节的初始化代码会反复出现在后续每个实验里。
一. 基本调用模式
打开 zcl_samplesw.c 的应用层初始化函数 zclSampleSw_Init(),可以看到这些注册代码:
// 1. 注册 ZCL 命令处理回调(命令来了找谁处理)
zclGeneral_RegisterCmdCallbacks( SAMPLESW_ENDPOINT,
&zclSampleSw_CmdCallbacks );
// 2. 把属性设置为默认值
zclSampleSw_ResetAttributesToDefaultValues();
// 3. 注册属性列表(设备有哪些"档案项")
zcl_registerAttrList( SAMPLESW_ENDPOINT,
zclSampleSw_NumAttributes,
zclSampleSw_Attrs );
// 4. 注册接收未处理的基础命令/响应消息
zcl_registerForMsg( zclSampleSw_TaskID );这 4 个函数各管什么
类比
开一家"命令餐厅":回调表 = 员工分工表(谁负责做菜、谁负责收银),属性列表 = 库存清单(店里有什么货),消息注册 = 对外公布电话(顾客能打电话进来下单)。
二. zclSampleSw_CmdCallbacks:命令处理函数列表
在 zcl_samplesw.c 中定义:
static zclGeneral_AppCallbacks_t zclSampleSw_CmdCallbacks =
{
zclSampleSw_BasicResetCB, // Basic Cluster Reset 命令处理
NULL, // Identify Trigger Effect 命令(不处理)
NULL, // On/Off cluster 命令 ①
NULL, // On/Off cluster 命令 ②
NULL, // On/Off cluster 命令 ③
NULL, // On/Off cluster 命令 ④
NULL, // On/Off cluster 命令 ⑤
#ifdef ZCL_LEVEL_CTRL
NULL, // Level Control 命令
NULL,
NULL,
NULL,
#endif
#ifdef ZCL_GROUPS
NULL, // Group Response
#endif
#ifdef ZCL_SCENES
...
#endif
};这个列表就是"员工分工表":每个元素代表某个特定命令的处理函数。
- 值为
NULL:该命令不处理; - 想要处理某个命令:在对应位置填上你的处理函数。
以 On/Off Cluster 为例:把对应位置的 NULL 改成 zclSampleSw_OnOffCB,设备就能接收并处理 On/Off 命令(下一节实验会演示)。
三. ZCL_INCOMING_MSG 事件
回忆 AF 章节:设备收到 AF 层数据 → 产生 AF_INCOMING_MSG_CMD 事件。
ZCL 类似:发生 ZCL_INCOMING_MSG 事件 = 有基础命令或消息需要处理(读命令、写命令、上报命令、默认响应等)。
打开 zcl_samplesw.c 的 zclSampleSw_event_loop(),可以看到对应处理:
uint16 zclSampleSw_event_loop( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
...
if ( events & SYS_EVENT_MSG )
{
while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( zclSampleSw_TaskID )) )
{
switch ( MSGpkt->hdr.event )
{
case ZCL_INCOMING_MSG: // ZCL 基础命令/响应消息
// Incoming ZCL Foundation command/response messages
zclSampleSw_ProcessIncomingMsg( (zclIncomingMsg_t *)MSGpkt );
break;
...
}
}
...
}
...
}zclSampleSw_ProcessIncomingMsg() 的处理逻辑
static void zclSampleSw_ProcessIncomingMsg( zclIncomingMsg_t *pInMsg )
{
switch ( pInMsg->zclHdr.commandID ) // 根据命令 ID 分类处理
{
#ifdef ZCL_READ
case ZCL_CMD_READ_RSP: // 读命令响应
zclSampleSw_ProcessInReadRspCmd( pInMsg );
break;
#endif
#ifdef ZCL_WRITE
case ZCL_CMD_WRITE_RSP: // 写命令响应
zclSampleSw_ProcessInWriteRspCmd( pInMsg );
break;
#endif
#ifdef ZCL_REPORT
case ZCL_CMD_CONFIG_REPORT: // 配置上报
case ZCL_CMD_REPORT: // 上报
...
break;
#endif
case ZCL_CMD_DEFAULT_RSP: // 默认响应
zclSampleSw_ProcessInDefaultRspCmd( pInMsg );
break;
default:
break;
}
if ( pInMsg->attrCmd )
osal_mem_free( pInMsg->attrCmd ); // 释放消息内存
}结构很清楚:根据 commandID 分流,各自处理。暂时只需要大致了解,后续实验会逐个展开。
注意
使用 ZCL 基础命令前,需要开启对应宏!例如用"读"命令要开 ZCL_READ。开启位置:工程 Options → C/C++ Compiler → Preprocessor → Defined symbols,例如:
ZCL_READ
ZCL_DISCOVER
ZCL_WRITE
ZCL_BASIC宏没开,对应的 case 代码不会被编译进来。
小结
- 初始化四步:注册回调表 → 重置属性 → 注册属性列表 → 注册消息接收;
- 收命令两条路:簇命令走回调表(如 On/Off),基础命令走 ZCL_INCOMING_MSG 事件(读/写/上报)。
内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第四部分·5.1 应用层对 ZCL API 的调用,原站链接
本章自测
一、单项选择题(每题 4 分,共 40 分)
在
zclSampleSw_Init()中,用于注册 ZCL 命令处理回调表的函数是(A.zcl_registerAttrList()B.zclGeneral_RegisterCmdCallbacks()C.zcl_registerForMsg()D.zclSampleSw_ResetAttributesToDefaultValues()) 答案:Bzcl_registerForMsg(zclSampleSw_TaskID)的作用是(A. 注册本设备拥有的属性列表 B. 把属性恢复为默认值 C. 告诉协议栈把未处理的基础命令/响应消息发给本任务,收到后产生ZCL_INCOMING_MSG事件 D. 注册 AF 层数据回调) 答案:C在
zclSampleSw_CmdCallbacks回调表中,某个命令位置填NULL表示(A. 该命令仍会被默认函数处理 B. 该命令不处理 C. 编译报错 D. 该命令由协议栈自动应答) 答案:B设备收到 AF 层数据时,按照 AF 章节的回忆,会产生哪个事件(A.
ZCL_INCOMING_MSGB.AF_INCOMING_MSG_CMDC.ZDO_CB_MSGD.SYS_EVENT_MSG) 答案:BZCL_INCOMING_MSG事件表示(A. 有 AF 层数据包到达 B. 有 ZCL 基础命令或响应消息需要处理(读命令、写命令、上报、默认响应等) C. 设备入网广播 D. 串口收到数据) 答案:BzclSampleSw_ProcessIncomingMsg()内部按哪个字段对 incoming 消息分流处理(A.pInMsg->clusterIdB.pInMsg->dstAddrC.pInMsg->zclHdr.commandIDD.pInMsg->endPoint) 答案:C工程中要使用 ZCL"读"命令,必须在预编译宏中开启(A.
ZCL_WRITEB.ZCL_READC.ZCL_ON_OFFD.ZCL_BASIC) 答案:B关于簇命令与基础命令的接收路径,下列说法正确的是(A. 两者都走
ZCL_INCOMING_MSG事件 B. 簇命令走回调表,基础命令走ZCL_INCOMING_MSG事件 C. 两者都走回调表 D. 簇命令走ZCL_INCOMING_MSG,基础命令走回调表) 答案:B页面指出,对应 ZCL 宏没有开启时(A. 运行时才会报错 B. 对应的
case代码不会被编译进来 C. 命令仍可正常收发 D. 属性列表不会注册) 答案:BzclSampleSw_ResetAttributesToDefaultValues()的作用是(A. 注册属性列表 B. 把一些属性恢复为默认值(如设备上电初始化) C. 向网络发送复位命令 D. 释放消息内存) 答案:B
二、问答题(每题 20 分,共 60 分)
- 题干:请按顺序列出应用层在
zclSampleSw_Init()中使用 ZCL 的初始化四步,并简述每一步的作用。 参考答案要点:
- 第一步:
zclGeneral_RegisterCmdCallbacks(),注册命令处理回调表(回调表=员工分工表),设备收到簇命令时据此查找处理函数; - 第二步:
zclSampleSw_ResetAttributesToDefaultValues(),把属性恢复为默认值(设备上电初始化); - 第三步:
zcl_registerAttrList(),注册属性列表zclSampleSw_Attrs(属性列表=库存清单/档案柜); - 第四步:
zcl_registerForMsg(),注册接收未处理的基础命令/响应消息,收到后产生ZCL_INCOMING_MSG事件(相当于对外公布电话)。
- 题干:设备收到 ZCL 消息后有哪两条处理路径?它们分别处理哪类命令? 参考答案要点:
- 路径一:簇限定命令走回调表
zclSampleSw_CmdCallbacks,例如 On/Off 命令在对应位置填上zclSampleSw_OnOffCB即可接收处理;表中位置为NULL则不处理; - 路径二:基础命令/响应走
ZCL_INCOMING_MSG事件,在zclSampleSw_event_loop()中收到事件后调用zclSampleSw_ProcessIncomingMsg(); zclSampleSw_ProcessIncomingMsg()再按zclHdr.commandID分流:ZCL_CMD_READ_RSP(读响应)、ZCL_CMD_WRITE_RSP(写响应)、ZCL_CMD_CONFIG_REPORT/ZCL_CMD_REPORT(上报)、ZCL_CMD_DEFAULT_RSP(默认响应)等;处理完后用osal_mem_free()释放pInMsg->attrCmd内存。
- 题干:为什么使用 ZCL 基础命令前必须开启对应预编译宏?以"读命令"为例说明宏的开启位置与不开宏的后果。 参考答案要点:
- 原因:相关处理代码都包裹在
#ifdef ZCL_READ/#ifdef ZCL_WRITE等条件编译中,宏不开,对应的case代码不会被编译进来; - 开启位置:工程 Options → C/C++ Compiler → Preprocessor → Defined symbols,例如添加
ZCL_READ、ZCL_WRITE、ZCL_BASIC、ZCL_DISCOVER等; - 不开宏的后果:发送/处理该类基础命令的代码缺失,收不到对应的响应处理逻辑,功能表现为"发了命令却没有响应处理分支"。