Skip to content

4.1 ZCL 简介 ​

一句话理解

ZCL 是夹在应用层和 AF 层之间的"通用语言层"。它的使命只有一个:让不同厂商的 ZigBee 设备能互相听懂。

为什么需要 ZCL? ​

前面第3章我们直接用 AF 层 API 发数据——这没问题,但有个大坑:不同公司开发的设备难以互联互通。A 公司把"开灯"编成 0x01,B 公司编成 0x02,网关收到数据一脸懵。

ZigBee 联盟的解决办法:在 AF 层之上加一个 ZCL 层,把设备功能统一标准化。

📱
应用层
开发者专注设备功能:如智能插座、温湿度传感器
↓ 数据往下传 ↑ 数据往上送
💡
ZCL 层
统一标准:定义 Profile / Device / Cluster / 属性 / 命令
↓↑
📦
AF 层
底层数据收发(上一章的主角)

通俗类比

想象全世界的餐厅都使用同一套菜单模板:菜名编号、口味标准、价格单位都统一。客人不管走进哪家店都能点菜,厨师不管在哪家店都能做菜——ZCL 就是物联网世界的"统一菜单模板"。

ZCL 层的架构 ​

ZCL 的大部分内容位于 Z-Stack 3.0 的以下目录中:

...\Z-Stack 3.0.1\Components\stack\zcl

该目录下有大量以 zcl_ 开头的文件,每个文件对应一类标准功能(如 zcl_general.c 通用功能、zcl_lighting.c 照明、zcl_hvac.c 暖通空调等)。

一个设备一般只会用到 ZCL 中的部分内容。例如本书一直用的 SampleSwitch 工程,只引用了这些 ZCL 文件:

📄
zcl.c / zcl.h
ZCL 核心:命令收发、事件处理
📄
zcl_general.c / .h
通用簇:On/Off、Level Control 等
📄
zcl_ha.c / .h
ZHA 家居自动化 Profile 定义
📄
zcl_diagnostic.c / .h
诊断簇(设备自检信息)
📄
zcl_green_power.c / .h
Green Power(绿色能源)支持
📄
zcl_ota.c / .h
OTA 在线升级支持

代码分析:ZCL 怎么发数据? ​

ZCL 层的数据发送,最终还是会调用 AF 层的 AF_DataRequest()——ZCL 的底层就是 AF 层。

打开 zcl.h,找到 ZCL 层数据发送函数 zcl_SendCommand():

c
ZStatus_t zcl_SendCommand( uint8 srcEP, afAddrType_t *destAddr,
 uint16 clusterID, uint8 cmd, uint8 specific, uint8 direction,
 uint8 disableDefaultRsp, uint16 manuCode, uint8 seqNum,
 uint16 cmdFormatLen, uint8 *cmdFormat )
{
  endPointDesc_t *epDesc;
  zclFrameHdr_t hdr;
  uint8 *msgBuf;
  uint16 msgLen;
  uint8 *pBuf;
  uint8 options;
  ZStatus_t status;

  epDesc = afFindEndPointDesc( srcEP );
  if ( epDesc == NULL )
  {
    return ( ZInvalidParameter );
  }

  options = zclGetClusterOption( srcEP, clusterID );
  ...
  // 组装 ZCL 报文头
  zcl_memset( &hdr, 0, sizeof( zclFrameHdr_t ) );
  hdr.fc.type = ...;          // 命令类型:通用命令 / 簇限定命令
  hdr.fc.direction = ...;     // 通信方向:Client→Server 或反向
  hdr.transSeqNum = seqNum;   // 事务序号
  hdr.commandID = cmd;        // 命令 ID

  // 计算并分配发送缓冲区
  msgLen = zclCalcHdrSize( &hdr );
  msgLen += cmdFormatLen;
  msgBuf = zcl_mem_alloc( msgLen );
  ...
  pBuf = zclBuildHdr( &hdr, msgBuf );        // 填 ZCL 头
  zcl_memcpy( pBuf, cmdFormat, cmdFormatLen ); // 填命令数据

  // ← 关键:最终还是调用 AF 层发送函数
  status = AF_DataRequest( destAddr, epDesc, clusterID, msgLen, msgBuf,
                           &zcl_TransID, options, AF_DEFAULT_RADIUS );
  ...
  return ( status );
}

可以看到 zcl_SendCommand() 内部最终调用的是 AF_DataRequest() ——印证了 ZCL 层是"站在 AF 层肩膀上"的。

代码分析:ZCL 怎么收数据? ​

在 zcl.c 中有 zcl_event_loop() 函数,负责 ZCL 层的数据接收和处理:

c
uint16 zcl_event_loop( uint8 task_id, uint16 events )
{
  uint8 *msgPtr;
  ...
  if ( events & SYS_EVENT_MSG )
  {
    msgPtr = osal_msg_receive( zcl_TaskID );
    while ( msgPtr != NULL )
    {
      if ( *msgPtr == AF_INCOMING_MSG_CMD )   // AF 层数据到达
      {
        zcl_ProcessMessageMSG( (afIncomingMSGPacket_t *)msgPtr ); // ZCL 层处理
      }
      else
      {
        uint8 taskID = zcl_getExternalFoundationHandler(
                           (afIncomingMSGPacket_t *)msgPtr );
        if ( taskID != TASK_NO_TASK )
        {
          // 交给其他任务处理
        }
      }
      ...
    }
  }
  ...
}

同样的套路:AF 层收到数据 → 产生 AF_INCOMING_MSG_CMD 事件 → ZCL 层的 event_loop 接住并分发。

小结

ZCL 不是替代 AF,而是在 AF 之上加一层标准:

  • 发数据:zcl_SendCommand() → 内部调用 AF_DataRequest()
  • 收数据:zcl_event_loop() → 接住 AF 层事件并处理

理解了这条链,下一节我们就可以看 ZCL 具体定义了哪些"标准零件"。

内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第四部分·4.1 ZCL 简介,原站链接


本章自测 ​

一、单项选择题(每题 4 分,共 40 分) ​

  1. ZCL 在 ZigBee 协议栈中的位置是(A. 在 AF 层之下、APS 之上 B. 夹在应用层和 AF 层之间,是"通用语言层" C. 等同于 PHY 层 D. 替代 AF 层) 答案:B

  2. 引入 ZCL 的最主要目的是(A. 提高无线发射功率 B. 让不同厂商的 ZigBee 设备能互相听懂、实现互联互通 C. 减少设备功耗 D. 加快组网速度) 答案:B

  3. 书中用"全世界餐厅使用同一套菜单模板"来类比 ZCL,这一比喻主要说明 ZCL 的什么特性(A. ZCL 规定了设备功能的统一标准描述 B. ZCL 负责无线电收发 C. ZCL 是一种编程语言 D. ZCL 只适用于餐饮行业) 答案:A

  4. ZCL 的大部分内容在 Z-Stack 3.0 中的存放目录是(A. Components\stack\zcl B. Profile C. App D. Tools) 答案:A

  5. 下列文件中,负责 On/Off、Level Control 等通用簇的是(A. zcl_ota.c B. zcl_green_power.c C. zcl_general.c D. zcl_diagnostic.c) 答案:C

  6. 关于 ZCL 层的数据发送,下列说法正确的是(A. ZCL 绕开 AF,直接操作无线电 B. zcl_SendCommand() 内部最终还是调用 AF 层的 AF_DataRequest() C. ZCL 不能发送数据 D. ZCL 发送时不需要 Cluster ID) 答案:B

  7. 在 zcl_SendCommand() 组装 ZCL 报文头时,hdr.fc.direction 字段表示(A. 事务序号 B. 通信方向(Client→Server 或反向) C. 命令 ID D. 缓冲区长度) 答案:B

  8. ZCL 层接收数据时,AF 层数据到达会产生的事件是(A. AF_INCOMING_MSG_CMD B. SAMPLEAPP_P2P_EVT C. KEY_EVT D. SYS_EVENT_MSG 中的自定义事件) 答案:A

  9. zcl_event_loop() 收到 AF_INCOMING_MSG_CMD 后,主要调用哪个函数进行 ZCL 层处理(A. AF_DataRequest() B. zcl_ProcessMessageMSG() C. afRegister() D. bdb_StartCommissioning()) 答案:B

  10. 关于 ZCL 与 AF 的关系,下列说法正确的是(A. ZCL 完全替代了 AF B. ZCL 不是替代 AF,而是在 AF 之上加一层标准,发数据走 zcl_SendCommand()→AF_DataRequest(),收数据由 zcl_event_loop() 接住 AF 事件 C. ZCL 与 AF 互不通信 D. AF 位于 ZCL 之上) 答案:B

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

  1. 题干:为什么直接用 AF 层 API 发数据会有"不同公司设备难以互联互通"的问题?ZCL 是如何解决的? 参考答案要点:
  • 直接用 AF 发数据时,"开灯"等业务含义由各公司自行编码(如 A 公司编 0x01、B 公司编 0x02),网关收到数据无法理解。
  • ZigBee 联盟在 AF 层之上加一个 ZCL 层,把设备功能统一标准化(统一 Profile / Device / Cluster / 属性 / 命令的编号和含义)。
  • 这样不同厂商按同一套标准描述功能,设备之间就能互相听懂。
  1. 题干:一个设备通常不会用到 ZCL 的全部内容。请列举书中 SampleSwitch 工程引用的几类 ZCL 文件及其作用。 参考答案要点:
  • zcl.c / zcl.h:ZCL 核心,负责命令收发、事件处理。
  • zcl_general.c / .h:通用簇(On/Off、Level Control 等)。
  • zcl_ha.c / .h:ZHA 家居自动化 Profile 定义。
  • 此外还可能用到 zcl_diagnostic(诊断簇)、zcl_green_power(绿色能源)、zcl_ota(在线升级)等。
  1. 题干:简述 ZCL 层"发数据"和"收数据"的完整调用链。 参考答案要点:
  • 发送:应用调用 zcl_SendCommand() → 组装 ZCL 报文头(命令类型、方向、事务序号、命令 ID)→ 分配缓冲区并填入头和命令数据 → 最终调用 AF 层 AF_DataRequest() 发出。
  • 接收:AF 层收到数据 → 产生 AF_INCOMING_MSG_CMD 事件 → ZCL 层 zcl_event_loop() 接住 → 调用 zcl_ProcessMessageMSG() 处理,无法处理的可转交给其他任务。
  • 一句话:ZCL 站在 AF 层肩膀上,发收两端最终都落到 AF 的收发机制上。

aioteer · 物联网专业课程群

💬AI 伴学配置