Skip to content

5.1 应用层对 ZCL API 的调用 ​

一句话理解

应用层用 ZCL,只需做三件事:注册命令回调(接命令)→ 注册属性列表(挂属性)→ 注册消息接收(收基础命令)。本节的初始化代码会反复出现在后续每个实验里。

一. 基本调用模式 ​

打开 zcl_samplesw.c 的应用层初始化函数 zclSampleSw_Init(),可以看到这些注册代码:

c
// 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 个函数各管什么 ​

📞
zclGeneral_RegisterCmdCallbacks()
注册"命令处理函数列表"(回调表)。设备收到命令时,就到这个列表里找对应的处理函数
🔄
zclSampleSw_ResetAttributesToDefaultValues()
把一些属性恢复为默认值(如设备上电初始化)
📋
zcl_registerAttrList()
注册属性列表 zclSampleSw_Attrs(定义在 zcl_samplesw_data.c)
📬
zcl_registerForMsg()
告诉协议栈:基础命令(读/写/上报等)也发给我处理。收到后产生 ZCL_INCOMING_MSG 事件

类比

开一家"命令餐厅":回调表 = 员工分工表(谁负责做菜、谁负责收银),属性列表 = 库存清单(店里有什么货),消息注册 = 对外公布电话(顾客能打电话进来下单)。

二. zclSampleSw_CmdCallbacks:命令处理函数列表 ​

在 zcl_samplesw.c 中定义:

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(),可以看到对应处理:

c
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() 的处理逻辑 ​

c
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 分) ​

  1. 在 zclSampleSw_Init() 中,用于注册 ZCL 命令处理回调表的函数是(A. zcl_registerAttrList() B. zclGeneral_RegisterCmdCallbacks() C. zcl_registerForMsg() D. zclSampleSw_ResetAttributesToDefaultValues()) 答案:B

  2. zcl_registerForMsg(zclSampleSw_TaskID) 的作用是(A. 注册本设备拥有的属性列表 B. 把属性恢复为默认值 C. 告诉协议栈把未处理的基础命令/响应消息发给本任务,收到后产生 ZCL_INCOMING_MSG 事件 D. 注册 AF 层数据回调) 答案:C

  3. 在 zclSampleSw_CmdCallbacks 回调表中,某个命令位置填 NULL 表示(A. 该命令仍会被默认函数处理 B. 该命令不处理 C. 编译报错 D. 该命令由协议栈自动应答) 答案:B

  4. 设备收到 AF 层数据时,按照 AF 章节的回忆,会产生哪个事件(A. ZCL_INCOMING_MSG B. AF_INCOMING_MSG_CMD C. ZDO_CB_MSG D. SYS_EVENT_MSG) 答案:B

  5. ZCL_INCOMING_MSG 事件表示(A. 有 AF 层数据包到达 B. 有 ZCL 基础命令或响应消息需要处理(读命令、写命令、上报、默认响应等) C. 设备入网广播 D. 串口收到数据) 答案:B

  6. zclSampleSw_ProcessIncomingMsg() 内部按哪个字段对 incoming 消息分流处理(A. pInMsg->clusterId B. pInMsg->dstAddr C. pInMsg->zclHdr.commandID D. pInMsg->endPoint) 答案:C

  7. 工程中要使用 ZCL"读"命令,必须在预编译宏中开启(A. ZCL_WRITE B. ZCL_READ C. ZCL_ON_OFF D. ZCL_BASIC) 答案:B

  8. 关于簇命令与基础命令的接收路径,下列说法正确的是(A. 两者都走 ZCL_INCOMING_MSG 事件 B. 簇命令走回调表,基础命令走 ZCL_INCOMING_MSG 事件 C. 两者都走回调表 D. 簇命令走 ZCL_INCOMING_MSG,基础命令走回调表) 答案:B

  9. 页面指出,对应 ZCL 宏没有开启时(A. 运行时才会报错 B. 对应的 case 代码不会被编译进来 C. 命令仍可正常收发 D. 属性列表不会注册) 答案:B

  10. zclSampleSw_ResetAttributesToDefaultValues() 的作用是(A. 注册属性列表 B. 把一些属性恢复为默认值(如设备上电初始化) C. 向网络发送复位命令 D. 释放消息内存) 答案:B

二、问答题(每题 20 分,共 60 分) ​

  1. 题干:请按顺序列出应用层在 zclSampleSw_Init() 中使用 ZCL 的初始化四步,并简述每一步的作用。 参考答案要点:
  • 第一步:zclGeneral_RegisterCmdCallbacks(),注册命令处理回调表(回调表=员工分工表),设备收到簇命令时据此查找处理函数;
  • 第二步:zclSampleSw_ResetAttributesToDefaultValues(),把属性恢复为默认值(设备上电初始化);
  • 第三步:zcl_registerAttrList(),注册属性列表 zclSampleSw_Attrs(属性列表=库存清单/档案柜);
  • 第四步:zcl_registerForMsg(),注册接收未处理的基础命令/响应消息,收到后产生 ZCL_INCOMING_MSG 事件(相当于对外公布电话)。
  1. 题干:设备收到 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 内存。
  1. 题干:为什么使用 ZCL 基础命令前必须开启对应预编译宏?以"读命令"为例说明宏的开启位置与不开宏的后果。 参考答案要点:
  • 原因:相关处理代码都包裹在 #ifdef ZCL_READ / #ifdef ZCL_WRITE 等条件编译中,宏不开,对应的 case 代码不会被编译进来;
  • 开启位置:工程 Options → C/C++ Compiler → Preprocessor → Defined symbols,例如添加 ZCL_READ、ZCL_WRITE、ZCL_BASIC、ZCL_DISCOVER 等;
  • 不开宏的后果:发送/处理该类基础命令的代码缺失,收不到对应的响应处理逻辑,功能表现为"发了命令却没有响应处理分支"。

aioteer · 物联网专业课程群

💬AI 伴学配置