下面我们分析一下telink泰凌微的zigbee SDK中的sampleLight这个demo的代码流程。
1、
我们从main函数开始看
apps/common/main.c
main()
-> user_init()
-> user_app_init()
-> zcl_register(SAMPLE_LIGHT_ENDPOINT, SAMPLELIGHT_CB_CLUSTER_NUM, (zcl_specClusterInfo_t *)g_sampleLightClusterList)
从命名可以看出,g_sampleLightClusterList是light设备所有cluster的一个列表
apps/sampleLight/sampleLightEpCfg.c
/**
* @brief Definition for simple light ZCL specific cluster
*/
const zcl_specClusterInfo_t g_sampleLightClusterList[] =
{
{ZCL_CLUSTER_GEN_BASIC, ZCL_BASIC_ATTR_NUM, basic_attrTbl, zcl_basic_register, sampleLight_basicCb},
{ZCL_CLUSTER_GEN_IDENTIFY, ZCL_IDENTIFY_ATTR_NUM, identify_attrTbl, zcl_identify_register, sampleLight_identifyCb},
#ifdef ZCL_GROUP
{ZCL_CLUSTER_GEN_GROUPS, ZCL_GROUP_ATTR_NUM, group_attrTbl, zcl_group_register, NULL},
#endif
#ifdef ZCL_SCENE
{ZCL_CLUSTER_GEN_SCENES, ZCL_SCENE_ATTR_NUM, scene_attrTbl, zcl_scene_register, sampleLight_sceneCb},
#endif
#ifdef ZCL_ON_OFF
{ZCL_CLUSTER_GEN_ON_OFF, ZCL_ONOFF_ATTR_NUM, onOff_attrTbl, zcl_onOff_register, sampleLight_onOffCb},
#endif
#ifdef ZCL_LEVEL_CTRL
{ZCL_CLUSTER_GEN_LEVEL_CONTROL, ZCL_LEVEL_ATTR_NUM, level_attrTbl, zcl_level_register, sampleLight_levelCb},
#endif
#ifdef ZCL_LIGHT_COLOR_CONTROL
{ZCL_CLUSTER_LIGHTING_COLOR_CONTROL, ZCL_COLOR_ATTR_NUM, lightColorCtrl_attrTbl, zcl_lightColorCtrl_register, sampleLight_colorCtrlCb},
#endif
};
u8 SAMPLELIGHT_CB_CLUSTER_NUM = (sizeof(g_sampleLightClusterList)/sizeof(g_sampleLightClusterList[0]));
而zcl_register()函数实际是调用g_sampleLightClusterList里面各项的clusterRegisterFunc
zigbee/zcl/zcl.c
_CODE_ZCL_ void zcl_register(u8 endpoint, u8 clusterNum, zcl_specClusterInfo_t *info)
{
zcl_specClusterInfo_t *p = info;
for(u32 i = 0; i < clusterNum; i++){
if(p->clusterRegisterFunc){
if(p->clusterRegisterFunc(endpoint, p->attrNum, p->attrTbl, p->clusterAppCb) == ZCL_STA_INSUFFICIENT_SPACE){
return;
}
}
p++;
}
}
比如ZCL_CLUSTER_GEN_ON_OFF这个cluster的zcl_onOff_register:
zigbee/zcl/general/zcl_onoff.c
_CODE_ZCL_ status_t zcl_onOff_register(u8 endpoint, u8 attrNum, const zclAttrInfo_t attrTbl[], cluster_forAppCb_t cb)
{
u8 status = ZCL_STA_SUCCESS;
status = zcl_registerCluster(endpoint, ZCL_CLUSTER_GEN_ON_OFF, attrNum, attrTbl, zcl_onOff_cmdHandler, cb);
if(status == ZCL_STA_SUCCESS){
zcl_onOff_startUpOnOff(endpoint);
}
return status;
}
然后调用到zcl层的公共接口
zigbee/zcl/zcl.c
/*********************************************************************
* @fn zcl_registerCluster
*
* @brief Register the cluster to ZCL
*
* @param endpoint Specified endpoint
* @param clusterId Specified cluster ID
* @param attrNum Specified attribute number in the cluster
* @param pAttrTbl Specified attributes
*
* @return ZCL Status @ref zcl_error_codes
*/
_CODE_ZCL_ status_t zcl_registerCluster(u8 endpoint, u16 clusterId, u8 attrNum, const zclAttrInfo_t *pAttrTbl, cluster_cmdHdlr_t cmdHdlrFn, cluster_forAppCb_t cb)
{
if(zcl_vars.clusterNum >= ZCL_CLUSTER_NUM_MAX){
return ZCL_STA_INSUFFICIENT_SPACE;
}
if(zcl_findCluster(endpoint, clusterId)){
return ZCL_STA_DUPLICATE_EXISTS;
}
zcl_vars.clusterList[zcl_vars.clusterNum].endpoint = endpoint;
zcl_vars.clusterList[zcl_vars.clusterNum].clusterID = clusterId;
zcl_vars.clusterList[zcl_vars.clusterNum].attrNum = attrNum;
zcl_vars.clusterList[zcl_vars.clusterNum].cmdHandlerFunc = cmdHdlrFn;
zcl_vars.clusterList[zcl_vars.clusterNum].clusterAppCb = cb;
zcl_vars.clusterList[zcl_vars.clusterNum++].attrTable = pAttrTbl;
return ZCL_STA_SUCCESS;
}
这里重点关注以下这两项赋值
zcl_vars.clusterList[zcl_vars.clusterNum].cmdHandlerFunc = cmdHdlrFn; //zcl_onOff_cmdHandler
zcl_vars.clusterList[zcl_vars.clusterNum].clusterAppCb = cb; //sampleLight_onOffCb
1、从函数命名来看,cmdHandler就是处理各种命令的函数,每个cluster都有很多的command,这里就是处理收到的各种command
zigbee/zcl/general/zcl_onoff.c
_CODE_ZCL_ static status_t zcl_onOff_clientCmdHandler(zclIncoming_t *pInMsg)
{
u8 status = ZCL_STA_SUCCESS;
apsdeDataInd_t *pApsdeInd = (apsdeDataInd_t*)pInMsg->msg;
u8 *pData = pInMsg->pData;
zcl_onoff_cmdPayload_t cmdPayload;
memset((u8 *)&cmdPayload, 0, sizeof(zcl_onoff_cmdPayload_t));
switch(pInMsg->hdr.cmd)
{
case ZCL_CMD_ONOFF_OFF:
case ZCL_CMD_ONOFF_ON:
case ZCL_CMD_ONOFF_TOGGLE:
case ZCL_CMD_ON_WITH_RECALL_GLOBAL_SCENE:
break;
case ZCL_CMD_OFF_WITH_EFFECT:
cmdPayload.offWithEffect.effectId = pData[0];
cmdPayload.offWithEffect.effectVariant = pData[1];
break;
case ZCL_CMD_ON_WITH_TIMED_OFF:
cmdPayload.onWithTimeOff.onOffCtrl.onOffCtrl = *pData++;
cmdPayload.onWithTimeOff.onTime = BUILD_U16(pData[0], pData[1]);
pData += 2;
cmdPayload.onWithTimeOff.offWaitTime = BUILD_U16(pData[0], pData[1]);
pData += 2;
break;
default:
status = ZCL_STA_UNSUP_CLUSTER_COMMAND;
break;
}
if(status == ZCL_STA_SUCCESS){
if(pInMsg->clusterAppCb){
zclIncomingAddrInfo_t addrInfo;
addrInfo.dirCluster = pInMsg->hdr.frmCtrl.bf.dir;
addrInfo.profileId = pApsdeInd->indInfo.profile_id;
addrInfo.srcAddr = pApsdeInd->indInfo.src_short_addr;
addrInfo.dstAddr = pApsdeInd->indInfo.dst_addr;
addrInfo.srcEp = pApsdeInd->indInfo.src_ep;
addrInfo.dstEp = pApsdeInd->indInfo.dst_ep;
pInMsg->clusterAppCb(&addrInfo, pInMsg->hdr.cmd, &cmdPayload);
#ifdef ZCL_SCENE
u16 attrLen = 0;
u8 sceneValid = 0;
if(zcl_getAttrVal(pInMsg->msg->indInfo.dst_ep, ZCL_CLUSTER_GEN_SCENES, ZCL_ATTRID_SCENE_SCENE_VALID, &attrLen, (u8 *)&sceneValid) == ZCL_STA_SUCCESS){
sceneValid = 0;
zcl_setAttrVal(pInMsg->msg->indInfo.dst_ep, ZCL_CLUSTER_GEN_SCENES, ZCL_ATTRID_SCENE_SCENE_VALID, (u8 *)&sceneValid);
}
#endif
}
}
return status;
}
...
_CODE_ZCL_ static status_t zcl_onOff_cmdHandler(zclIncoming_t *pInMsg)
{
if(pInMsg->hdr.frmCtrl.bf.dir == ZCL_FRAME_CLIENT_SERVER_DIR){
return zcl_onOff_clientCmdHandler(pInMsg);
}else{
return ZCL_STA_UNSUP_CLUSTER_COMMAND;
}
}
上面最终调用了pInMsg->clusterAppCb(&addrInfo, pInMsg->hdr.cmd, &cmdPayload),其实就是回调了前面赋值的cb函数
zcl_vars.clusterList[zcl_vars.clusterNum].clusterAppCb = cb; //sampleLight_onOffCb
apps/sampleLight/zcl_onOffCb.c
/*********************************************************************
* @fn sampleLight_onOffCb
*
* @brief Handler for ZCL ONOFF command. This function will set ONOFF attribute first.
*
* @param pAddrInfo
* @param cmdId - onoff cluster command id
* @param cmdPayload
*
* @return status_t
*/
status_t sampleLight_onOffCb(zclIncomingAddrInfo_t *pAddrInfo, u8 cmdId, void *cmdPayload)
{
zcl_onOffAttr_t *pOnOff = zcl_onoffAttrGet();
if(pAddrInfo->dstEp == SAMPLE_LIGHT_ENDPOINT){
switch(cmdId){
case ZCL_CMD_ONOFF_ON:
case ZCL_CMD_ONOFF_OFF:
case ZCL_CMD_ONOFF_TOGGLE:
sampleLight_onoff(cmdId);
break;
case ZCL_CMD_OFF_WITH_EFFECT:
if(pOnOff->globalSceneControl == TRUE){
/* TODO: store its settings in its global scene */
pOnOff->globalSceneControl = FALSE;
}
sampleLight_onoff_offWithEffectProcess((zcl_onoff_offWithEffectCmd_t *)cmdPayload);
break;
case ZCL_CMD_ON_WITH_RECALL_GLOBAL_SCENE:
if(pOnOff->globalSceneControl == FALSE){
sampleLight_onoff_onWithRecallGlobalSceneProcess();
pOnOff->globalSceneControl = TRUE;
}
break;
case ZCL_CMD_ON_WITH_TIMED_OFF:
sampleLight_onoff_onWithTimedOffProcess((zcl_onoff_onWithTimeOffCmd_t *)cmdPayload);
break;
default:
break;
}
}
return ZCL_STA_SUCCESS;
}
上面的函数比较简单,不同的command调用不同的接口去点灯,这里就不深究了,就是操作一些GPIO。
2、
上面的分析是针对收到消息后的处理流程,不过在这之前,我们还需要注册endpoint到AF层,还是回到user_app_init()函数:
apps/sampleLight/sampleLight.c
void user_app_init(void)
{
...
/* Register endPoint */
af_endpointRegister(SAMPLE_LIGHT_ENDPOINT, (af_simple_descriptor_t *)&sampleLight_simpleDesc, zcl_rx_handler, NULL);
...
/* Register ZCL specific cluster information */
zcl_register(SAMPLE_LIGHT_ENDPOINT, SAMPLELIGHT_CB_CLUSTER_NUM, (zcl_specClusterInfo_t *)g_sampleLightClusterList);
...
}
zigbee/zdo/app_framework.c
/****************************************************************************************************
* @brief AF layer endpoint descriptor register interface, call this function to add ep descriptor to the AF layer
*
* @param ep: end point number
* simple_desc: af_simple_descriptor_t
* cb: call back function
*
* @return TRUE: add success
* FALSE: endpoint full, no space, add fail
*/
_CODE_ZDO_ bool af_endpointRegister(u8 ep, af_simple_descriptor_t *simple_desc, af_endpoint_cb_t rx_cb, af_dataCnf_cb_t cnfCb)
{
if(ep == ZDO_EP){
zdo_epDesc.cb_cnf = cnfCb;
zdo_epDesc.cb_rx = rx_cb;
zdo_epDesc.correspond_simple_desc = (af_simple_descriptor_t *)simple_desc;
return TRUE;
}
for(u8 i = 0; i < MAX_ACTIVE_EP_NUMBER; i++){
if(aed[i].ep == ep){
return FALSE;
}
}
if (available_active_ep_num < MAX_ACTIVE_EP_NUMBER) {
aed[available_active_ep_num].ep = ep;
aed[available_active_ep_num].correspond_simple_desc = (af_simple_descriptor_t *)simple_desc;
aed[available_active_ep_num].cb_cnf = cnfCb;
aed[available_active_ep_num++].cb_rx = rx_cb;
}else{
return FALSE;
}
return TRUE;
}
最终就是将zcl_rx_handler函数注册进AF层,后面用来处理下层传上来的command消息
zigbee/zcl/zcl.c
/*********************************************************************
* @fn zcl_cmdHandler
*
* @brief ZCL command handler
*
* @param pCmd
*
* @return None
*/
_CODE_ZCL_ void zcl_cmdHandler(u8 *pCmd)
{
apsdeDataInd_t *pApsdeInd = (apsdeDataInd_t*)pCmd;
u8 status = ZCL_STA_SUCCESS;
u8 toAppFlg = 0;
printf("ljm : zcl_cmdHandler\n\r");
zclIncoming_t inMsg;
TL_SETSTRUCTCONTENT(inMsg,0);
inMsg.msg = pApsdeInd;
/* Parse Header */
if(pApsdeInd->asdu[0] & ZCL_FRAME_CONTROL_MANU_SPECIFIC){
if(pApsdeInd->asduLen >= 5){
inMsg.hdr.frmCtrl.byte = pApsdeInd->asdu[0];
inMsg.hdr.manufCode = BUILD_U16(pApsdeInd->asdu[1], pApsdeInd->asdu[2]);
inMsg.hdr.seqNum = pApsdeInd->asdu[3];
inMsg.hdr.cmd = pApsdeInd->asdu[4];
inMsg.pData = &pApsdeInd->asdu[5];
inMsg.dataLen = pApsdeInd->asduLen - 5;
}else{
status = ZCL_STA_FAILURE;
}
}else{
if(pApsdeInd->asduLen >= 3){
inMsg.hdr.frmCtrl.byte = pApsdeInd->asdu[0];
inMsg.hdr.manufCode = 0;
inMsg.hdr.seqNum = pApsdeInd->asdu[1];
inMsg.hdr.cmd = pApsdeInd->asdu[2];
inMsg.pData = &pApsdeInd->asdu[3];
inMsg.dataLen = pApsdeInd->asduLen - 3;
}else{
status = ZCL_STA_FAILURE;
}
}
if(status == ZCL_STA_FAILURE){
ev_buf_free(pCmd);
return;
}
u16 devEnableAttrLen = 0;
bool devEnable = TRUE;
/* Command dispatch */
if(inMsg.hdr.frmCtrl.bf.type == ZCL_FRAME_TYPE_PROFILE_CMD){
/* Foundation type message */
if(inMsg.hdr.frmCtrl.bf.manufSpec){
// We don't support any manufacturer specific command
status = ZCL_STA_UNSUP_MANU_GENERAL_COMMAND;
}else if(inMsg.hdr.cmd > ZCL_CMD_MAX){
// Unsupported message
status = ZCL_STA_UNSUP_GENERAL_COMMAND;
}else{
status = zcl_foundationCmdHandler(&inMsg);
if((status != ZCL_STA_SUCCESS) && (status != ZCL_STA_CMD_HAS_RESP)){
status = ZCL_STA_FAILURE;
}
toAppFlg = 1;
}
}else{
/* Cluster specific command */
if(inMsg.hdr.frmCtrl.bf.manufSpec){
// We don't support any manufacturer specific command
status = ZCL_STA_UNSUP_MANU_CLUSTER_COMMAND;
}else{
clusterInfo_t *pCluster = zcl_findCluster(pApsdeInd->indInfo.dst_ep, pApsdeInd->indInfo.cluster_id);
if(!pCluster){
status = ZCL_STA_UNSUP_CLUSTER_COMMAND;
}else{
/* Check if basic device enable support */
zcl_getAttrVal(pApsdeInd->indInfo.dst_ep, ZCL_CLUSTER_GEN_BASIC, ZCL_ATTRID_BASIC_DEV_ENABLED, &devEnableAttrLen, &devEnable);
if(devEnable || (pCluster->clusterID == ZCL_CLUSTER_GEN_IDENTIFY)){
inMsg.clusterAppCb = pCluster->clusterAppCb;
status = pCluster->cmdHandlerFunc(&inMsg);
devEnable = TRUE;
}
}
}
}
if(devEnable){
if((inMsg.hdr.frmCtrl.bf.disDefResp == 0 || status != ZCL_STA_SUCCESS) && UNICAST_MSG(inMsg.msg) && (status != ZCL_STA_CMD_HAS_RESP)){
/* send default response */
zcl_sendDfltRsp(&inMsg, inMsg.hdr.cmd, status);
}
}
if(zcl_vars.hookFn && toAppFlg && inMsg.attrCmd){
zcl_vars.hookFn(&inMsg);
ev_buf_free(inMsg.attrCmd);
}
ev_buf_free(pCmd);
}
/*********************************************************************
* @fn zcl_task
*
* @brief ZCL layer maintenance
*
* @param arg
*
* @return None
*/
_CODE_ZCL_ void zcl_task(void *arg)
{
if(zcl_vars.zb2zclQ.curNum == 0){
return;
}
u8 *pCmd = ev_queue_pop(&zcl_vars.zb2zclQ);
if(!pCmd){
return;
}
zcl_cmdHandler(pCmd);
}
/*********************************************************************
* @fn zcl_rx_handler
*
* @brief Receive handler for data from APS/AF layer
*
* @param pData Received data
*
* @return None
*/
_CODE_ZCL_ void zcl_rx_handler(void *pData)
{
ev_queue_push(&zcl_vars.zb2zclQ, pData);
TL_SCHEDULE_TASK(zcl_task, NULL);
}
从zcl_rx_handler到zcl_task,到最终的zcl_cmdHandler,函数里面调用的pCluster->cmdHandlerFunc(&inMsg),其实就回调了在zcl_registerCluster()中注册的zcl_onOff_cmdHandler()
_CODE_ZCL_ status_t zcl_registerCluster(u8 endpoint, u16 clusterId, u8 attrNum, const zclAttrInfo_t *pAttrTbl, cluster_cmdHdlr_t cmdHdlrFn, cluster_forAppCb_t cb)
{
..
zcl_vars.clusterList[zcl_vars.clusterNum].cmdHandlerFunc = cmdHdlrFn; //zcl_onOff_cmdHandler
...
return ZCL_STA_SUCCESS;
}
至此,处理消息的流程就分析到这里。