💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
>[success] 导师视频讲解:[**去听课**](https://www.bilibili.com/video/BV1k34y1D7Vz?p=36) >[success] **技术支持说明:** >**1**.一般以自主学习为主 > **2**.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/zigbee) > **3**.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> 前述章节讲解了如何使用AF层通信API,但是现在用得更多的是基于ZCL的数据通信方式。ZCL的全称是ZigBee Cluster Library,中文意思是ZigBee集群库。ZCL与AF的关系如图所示: ![](https://img.kancloud.cn/b5/2c/b52ca643c0af792c291c855b9dafaabf_434x526.png =200x) ## 图中共有3个层次,分别是AF、ZCL和应用层,分别详细讲解一下。 <br/> ## **AF层** 在前述章节已经详细讲解了AF层,可以调用AF层的数据通信API实现ZigBee设备之间的数据发送与接收。然而,直接使用AF层通信API的最大问题是不同公司开发的设备难以做到互联互通。 <br/> ## **应用层** 开发者此层次中专注于开发设备的各个功能。在讲解AF通信的章节中,我们就是在应用层中直接调用AF层的通信API来发送和接收数据的。 <br/> ## **ZCL层简介** ZigBee联盟在AF层与应用层之间构建了ZCL层,其最大的作用就是实现了各个ZigBee设备的互联互通。ZCL定义了ZigBee设备的各种应用领域(Profile)、设备类型(Device)、集群(Cluster)、设备属性和命令,这些定义均由ZigBee联盟统一定制。各个厂商在开发ZigBee设备时都遵循这些定义,便实现了互联互通了。 <br/> ## **ZCL层的架构** ZCL的大部分内容在Z-Stack 3.0 中的以下目录中: ``` ...\\Z-Stack 3.0.1\\Components\\stack\\zcl ``` 它的部分内容如图所示。 ![](https://img.kancloud.cn/3e/3a/3e3a892e04f37a3d02933d150faf45e0_897x804.png =500x) ### 一个设备一般只会使用ZCL中的部分内容,例如本书一直讲解的这个SampleSwitch工程使用了这些内容,如图所示。 ![](https://img.kancloud.cn/9b/6c/9b6c17f576895e15efc8c73df30068fd_312x626.png =250x) <br/> **代码分析** 我们对ZCL中的代码简单分析一下,打开zcl.h文件,可以找到ZCL层数据发送函数zcl\_SendCommand(),代码如下: ``` /********************************************************************* * @fn zcl_SendCommand * * @brief Used to send Profile and Cluster Specific Command messages. * * NOTE: The calling application is responsible for incrementing * the Sequence Number. * * @param srcEp - source endpoint * @param destAddr - destination address * @param clusterID - cluster ID * @param cmd - command ID * @param specific - whether the command is Cluster Specific * @param direction - client/server direction of the command * @param disableDefaultRsp - disable Default Response command * @param manuCode - manufacturer code for proprietary extensions to a profile * @param seqNumber - identification number for the transaction * @param cmdFormatLen - length of the command to be sent * @param cmdFormat - command to be sent * * @return ZSuccess if OK */ 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 ); // EMBEDDED RETURN } #if defined ( INTER_PAN ) if ( StubAPS_InterPan( destAddr->panId, destAddr->endPoint ) ) { options = AF_TX_OPTIONS_NONE; } else #endif { options = zclGetClusterOption( srcEP, clusterID ); // The cluster might not have been defined to use security but if this message // is in response to another message that was using APS security this message // will be sent with APS security if ( !( options & AF_EN_SECURITY ) ) { afIncomingMSGPacket_t *origPkt = zcl_getRawAFMsg(); if ( ( origPkt != NULL ) && ( origPkt->SecurityUse == TRUE ) ) { options |= AF_EN_SECURITY; } } } zcl_memset( &hdr, 0, sizeof( zclFrameHdr_t ) ); // Not Profile wide command (like READ, WRITE) if ( specific ) { hdr.fc.type = ZCL_FRAME_TYPE_SPECIFIC_CMD; } else { hdr.fc.type = ZCL_FRAME_TYPE_PROFILE_CMD; } if ( ( epDesc->simpleDesc == NULL ) || ( zcl_DeviceOperational( srcEP, clusterID, hdr.fc.type, cmd, epDesc->simpleDesc->AppProfId ) == FALSE ) ) { return ( ZFailure ); // EMBEDDED RETURN } // Fill in the Maufacturer Code if ( manuCode != 0 ) { hdr.fc.manuSpecific = 1; hdr.manuCode = manuCode; } // Set the Command Direction if ( direction ) { hdr.fc.direction = ZCL_FRAME_SERVER_CLIENT_DIR; } else { hdr.fc.direction = ZCL_FRAME_CLIENT_SERVER_DIR; } // Set the Disable Default Response field if ( disableDefaultRsp ) { hdr.fc.disableDefaultRsp = 1; } else { hdr.fc.disableDefaultRsp = 0; } // Fill in the Transaction Sequence Number hdr.transSeqNum = seqNum; // Fill in the command hdr.commandID = cmd; // calculate the needed buffer size msgLen = zclCalcHdrSize( &hdr ); msgLen += cmdFormatLen; // Allocate the buffer needed msgBuf = zcl_mem_alloc( msgLen ); if ( msgBuf != NULL ) { // Fill in the ZCL Header pBuf = zclBuildHdr( &hdr, msgBuf ); // Fill in the command frame zcl_memcpy( pBuf, cmdFormat, cmdFormatLen ); status = AF_DataRequest( destAddr, epDesc, clusterID, msgLen, msgBuf, &zcl_TransID, options, AF_DEFAULT_RADIUS ); zcl_mem_free ( msgBuf ); } else { status = ZMemError; } return ( status ); } ``` 可以看到zcl\_SendCommand()函数中,其实是调用AF层的数据发送函数AF_DataRequest()来发送数据的。 ### 在zcl.c文件中还有一个名为zcl\_event\_loop()的函数。在这个函数中,我们可以看到AF层数据的接收和处理,代码如图所示。 ![](https://img.kancloud.cn/5b/f8/5bf8562956a76e53b2a77d016afdbb2e_1680x1007.png =500x) ### 以上的分析也印正了ZCL层的底层就是AF层。 <br/> <br/> ## **项目定制** * 如需项目定制开发,可扫码添加项目经理好友(注明“**项目定制**”) * 定制范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等IoT技术方案 * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](https://img.kancloud.cn/ca/73/ca739f92cab220a3059378642e3bd502_430x430.png =200x) * 非项目定制**勿扰**,此处**非**技术支持