zigbee 笔记5

zigbee 笔记5

zigbee ZCL流程分析

现在一般淘宝上面买的zigbee开发板给的资料都是ZStack-CC2530-2.3.0-1.4.0这个版本的资料,但是这个是已经比较旧的版本了,和现在ZHA,ZLL还是有点不一样的,其中主要不一样的地方就是ZCL,自己理解来说,我觉得ZCL是TI在zigbee收发的基础函数上面封装的一套规范,通过ZCL可以直接使用ZHA和ZLL的规范,不需要管具体发送的是什么数据;

但是,好了,现在不管发送什么数据,但是我现在想要多做一个透传接口,发现就很困难了;我现在遇到的问题就是这样的,公司zigbee网关外包出去,里面有封装好透传接口,主要用于门锁的透传加密数据,但是现在终端和路由是我们自己开发,就需要把这个透传接口加上去;这时候就需要清除了解ZCL具体的收发的封装了;下面我们来分析一下:

  • 主要操作的文件是zcl.c, zcl_general.c, app.c这3个文件

  • 先来看应用程序吧,zcl_samplesw.c:

    static zclGeneral_AppCallbacks_t zclSampleSw_CmdCallbacks =
    {
      zclSampleSw_BasicResetCB,               // Basic Cluster Reset command
      zclSampleSw_IdentifyCB,                 // Identify command
    #ifdef ZCL_EZMODE
      NULL,                                   // Identify EZ-Mode Invoke command
      NULL,                                   // Identify Update Commission State command
    #endif
      NULL,                                   // Identify Trigger Effect command
      zclSampleSw_IdentifyQueryRspCB,         // Identify Query Response command
      zclgetonoff,                                   // On/Off cluster commands
      NULL,                                   // On/Off cluster enhanced command Off with Effect
      NULL,                                   // On/Off cluster enhanced command On with Recall Global Scene
      NULL,                                   // On/Off cluster enhanced command On with Timed Off
    #ifdef ZCL_LEVEL_CTRL
      NULL,                                   // Level Control Move to Level command
      NULL,                                   // Level Control Move command
      NULL,                                   // Level Control Step command
      NULL,                                   // Level Control Stop command
    #endif
    #ifdef ZCL_GROUPS
      NULL,                                   // Group Response commands
    #endif
    #ifdef ZCL_SCENES
      NULL,                                   // Scene Store Request command
      NULL,                                   // Scene Recall Request command
      NULL,                                   // Scene Response command
    #endif
    #ifdef ZCL_ALARMS
      NULL,                                   // Alarm (Response) commands
    #endif
    #ifdef SE_UK_EXT
      NULL,                                   // Get Event Log command
      NULL,                                   // Publish Event Log command
    #endif
      NULL,                                   // RSSI Location command
      NULL,                                    // RSSI Location Response command
      zclDoorLockDataIn                       //doorlock function,在这里添加doorlock的回调函数
    };
    ...
    static void zclDoorLockDataIn(uint8 *data,uint16 len);//注册门锁数据回调函数
    //具体操作函数
    static void zclDoorLockDataIn(uint8 *data,uint16 len){
    
        HalLedSet(HAL_LED_2, HAL_LED_MODE_OFF);
         
    }
    
  • 好了,应用程序端就这么简单,在注册回调函数里面添加透传接口的回调,但是想想都知道,直接在回调列表里面添加肯定不行,那么我们看看这个结构体zclGeneral_AppCallbacks_t 的定义,来到了zcl_general.h文件,那么我们就继续照葫芦画瓢:

    typedef struct
    {
      zclGCB_BasicReset_t               pfnBasicReset;                // Basic Cluster Reset command
      zclGCB_Identify_t                 pfnIdentify;                  // Identify command
    #ifdef ZCL_EZMODE
      zclGCB_IdentifyEZModeInvoke_t     pfnIdentifyEZModeInvoke;      // Identify EZ-Mode Invoke command
      zclGCB_IdentifyUpdateCommState_t  pfnIdentifyUpdateCommState;   // Identify Update Commission State command
    #endif
      zclGCB_IdentifyTriggerEffect_t    pfnIdentifyTriggerEffect;     // Identify Trigger Effect command
      zclGCB_IdentifyQueryRsp_t         pfnIdentifyQueryRsp;          // Identify Query Response command
      zclGCB_OnOff_t                    pfnOnOff;                     // On/Off cluster commands
      zclGCB_OnOff_OffWithEffect_t      pfnOnOff_OffWithEffect;       // On/Off cluster enhanced command Off with Effect
      zclGCB_OnOff_OnWithRecallGlobalScene_t  pfnOnOff_OnWithRecallGlobalScene;  // On/Off cluster enhanced command On with Recall Global Scene
      zclGCB_OnOff_OnWithTimedOff_t     pfnOnOff_OnWithTimedOff;      // On/Off cluster enhanced command On with Timed Off
    #ifdef ZCL_LEVEL_CTRL
      zclGCB_LevelControlMoveToLevel_t  pfnLevelControlMoveToLevel;   // Level Control Move to Level command
      zclGCB_LevelControlMove_t         pfnLevelControlMove;          // Level Control Move command
      zclGCB_LevelControlStep_t         pfnLevelControlStep;          // Level Control Step command
      zclGCB_LevelControlStop_t         pfnLevelControlStop;          // Level Control Stop command
    #endif
    #ifdef ZCL_GROUPS
      zclGCB_GroupRsp_t                 pfnGroupRsp;                  // Group Response commands
    #endif
    #ifdef ZCL_SCENES
      zclGCB_SceneStoreReq_t            pfnSceneStoreReq;             // Scene Store Request command
      zclGCB_SceneRecallReq_t           pfnSceneRecallReq;            // Scene Recall Request command
      zclGCB_SceneRsp_t                 pfnSceneRsp;                  // Scene Response command
    #endif
    #ifdef ZCL_ALARMS
      zclGCB_Alarm_t                    pfnAlarm;                     // Alarm (Response) commands
    #endif
    #ifdef SE_UK_EXT
      zclGCB_GetEventLog_t              pfnGetEventLog;               // Get Event Log command
      zclGCB_PublishEventLog_t          pfnPublishEventLog;           // Publish Event Log command
    #endif
      zclGCB_Location_t                 pfnLocation;                  // RSSI Location command
      zclGCB_LocationRsp_t              pfnLocationRsp;               // RSSI Location Response command
      
      zclGCB_DoorLock_t                 pfnDoorLock;                    //添加doorlock门锁回调函数
        
    } zclGeneral_AppCallbacks_t;
    

    修改这个结构体,然后定义一下zclGCB_DoorLock_t这个回调函数:

    typedef void (*zclGCB_DoorLock_t)( uint8 *data,uint16 len);//我们需要透传数据和数据长度
    

    好了,zcl_general.h文件我们也改好了;

  • 下面我们要正式分析zcl流程了:

    ZCL的所有数据都是在zcl.c里面的uint16 zcl_event_loop( uint8 task_id, uint16 events );这里跑的;

    uint16 zcl_event_loop( uint8 task_id, uint16 events )
    {
      uint8 *msgPtr;
    
      (void)task_id;  // Intentionally unreferenced parameter
    
      if ( events & SYS_EVENT_MSG )
      {
        msgPtr = osal_msg_receive( zcl_TaskID );
        while ( msgPtr != NULL )
        {
          uint8 dealloc = TRUE;
    
          if ( *msgPtr == AF_INCOMING_MSG_CMD )//就是这里,对各种命令数据进行处理
          {
            zcl_ProcessMessageMSG( (afIncomingMSGPacket_t *)msgPtr );//我们debug进去看看
          }
          else
          {
            uint8 taskID;
            taskID = zcl_getExternalFoundationHandler( (afIncomingMSGPacket_t *)msgPtr );
    
            if ( taskID != TASK_NO_TASK )
            {
              // send it to another task to process.
              osal_msg_send( taskID, msgPtr );
              dealloc = FALSE;
            }
          }
    
        ....
    }
    

    我们看到具体处理数据的是zcl_ProcessMessageMSG( (afIncomingMSGPacket_t *)msgPtr ),进去看看,比较长:

    zclProcMsgStatus_t zcl_ProcessMessageMSG( afIncomingMSGPacket_t *pkt )
    {
      endPointDesc_t *epDesc;
      zclIncoming_t inMsg;//这个就是接收到的数据,
      zclLibPlugin_t *pInPlugin;
      zclDefaultRspCmd_t defautlRspCmd;
      uint8 options;
      uint8 securityEnable;
      uint8 interPanMsg;
      ZStatus_t status = ZFailure;
      uint8 defaultResponseSent = FALSE;
      
        
      // Initialize
      rawAFMsg = (afIncomingMSGPacket_t *)pkt;
      inMsg.msg = pkt;
      inMsg.attrCmd = NULL;
      inMsg.pData = NULL;
      inMsg.pDataLen = 0;
    
      inMsg.pData = zclParseHdr( &(inMsg.hdr), pkt->cmd.Data );//官方就是这么取数据的
      inMsg.pDataLen = pkt->cmd.DataLength;
      inMsg.pDataLen -= (uint16)(inMsg.pData - pkt->cmd.Data);
    
     // memcpy(gStr1,inMsg.pData,30); 可以这样取数据到数组看看透传过来的数据对不对?
      // Temporary workaround to allow callback functions access to the
      // transaction sequence number.  Callback functions will call
      // zcl_getParsedTransSeqNum() to retrieve this number.
      savedZCLTransSeqNum = inMsg.hdr.transSeqNum;
    
      // Find the wanted endpoint,获取端点信息
      epDesc = afFindEndPointDesc( pkt->endPoint );
      if ( epDesc == NULL )
      {
        rawAFMsg = NULL;
        return ( ZCL_PROC_EP_NOT_FOUND );   // Error, ignore the message
      }
    
      if ( ( epDesc->simpleDesc == NULL ) ||
           ( zcl_DeviceOperational( pkt->endPoint, pkt->clusterId, inMsg.hdr.fc.type,
                                    inMsg.hdr.commandID, epDesc->simpleDesc->AppProfId ) == FALSE ) )
      {
        rawAFMsg = NULL;
        return ( ZCL_PROC_NOT_OPERATIONAL ); // Error, ignore the message
      }
    
    #if defined ( INTER_PAN )
      if ( StubAPS_InterPan( pkt->srcAddr.panId, pkt->srcAddr.endPoint ) )
      {
        // No foundation command is supported thru Inter-PAN communication.
        // But the Light Link cluster uses a different Frame Control format
        // for it's Inter-PAN messages, where the messages could be confused
        // with the foundation commands.
        if ( zcl_ProfileCmd( inMsg.hdr.fc.type ) )
        {
          rawAFMsg = NULL;
          return ( ZCL_PROC_INTERPAN_FOUNDATION_CMD );
        }
    
        interPanMsg = TRUE;
        options = AF_TX_OPTIONS_NONE;
      }
      else
    #endif
      {
        interPanMsg = FALSE;
        options = zclGetClusterOption( pkt->endPoint, pkt->clusterId );
      }
      
      if ( pkt->cmd.DataLength < ZCL_VALID_MIN_HEADER_LEN  )
      {
        return ( ZCL_PROC_INVALID );   // Error, ignore the message
      }
      
      
      // Find the appropriate plugin,获取回调
      pInPlugin = zclFindPlugin( pkt->clusterId, epDesc->simpleDesc->AppProfId );
    
      // Local and remote Security options must match except for Default Response command
      if ( ( pInPlugin != NULL ) && !zcl_DefaultRspCmd( inMsg.hdr ) )//这里和安防有关,有些工程师需要用到密钥的,就和这里有关,我们的架构没有密钥,开发的网络,所以可以不管这里
      {
        securityEnable = ( options & AF_EN_SECURITY ) ? TRUE : FALSE;
        .......
      }
    
      // Is this a foundation type message,
      if ( !interPanMsg && zcl_ProfileCmd( inMsg.hdr.fc.type ) )//我们debug正常的onoff指令是跑到下面的else那里的,所以这里如果你的透传接口是跑到下面的代码,就要看看你的inMsg.hdr.fc.type这里的type是否赋值错误,我自己添加的时候就是出现这样的情况,所以就自己添加了特殊处理的代码,下面说。。。
      {
        ........
      }
      else  // Not a foundation type message, so it must be specific to the cluster ID.
      {
        if ( pInPlugin && pInPlugin->pfnIncomingHdlr )//和onoff指令一样进到这里了,但是往下不了,有可能和你的透传接口的custerID有关,是否pInPlugin的custerID和透传ID一样
        {
          // The return value of the plugin function will be
          //  ZSuccess - Supported and need default response
          //  ZFailure - Unsupported
          //  ZCL_STATUS_CMD_HAS_RSP - Supported and do not need default rsp
          //  ZCL_STATUS_INVALID_FIELD - Supported, but the incoming msg is wrong formatted
          //  ZCL_STATUS_INVALID_VALUE - Supported, but the request not achievable by the h/w
          //  ZCL_STATUS_SOFTWARE_FAILURE - Supported but ZStack memory allocation fails
          status = pInPlugin->pfnIncomingHdlr( &inMsg );//就是这里了,执行到这里基本上已经成功,这里执行了之后会调用zcl_general.c里面的回调处理方法找到相关的处理函数再回调上去到应用层,具体处理回调函数是:static ZStatus_t zclGeneral_HdlIncoming( zclIncoming_t *pInMsg )
            
          if ( status == ZCL_STATUS_CMD_HAS_RSP || ( interPanMsg && status == ZSuccess ) )
          {
            rawAFMsg = NULL;
            return ( ZCL_PROC_SUCCESS ); // We're done
          }
        }
    .......
    }
    

    下面我们继续,上面说到if ( !interPanMsg && zcl_ProfileCmd( inMsg.hdr.fc.type ) ) 这里跑的和onoff指令不一样,进不到else那里,发现是这个type问题,我们往上找,看哪里有给type赋值,发现是这个函数:

    inMsg.pData = zclParseHdr( &(inMsg.hdr), pkt->cmd.Data );,就是这个一开始取数据的时候执行的函数,我们进去看看:

    uint8 *zclParseHdr( zclFrameHdr_t *hdr, uint8 *pData )
    {
      // Clear the header
      zcl_memset( (uint8 *)hdr, 0, sizeof ( zclFrameHdr_t ) );
    
      // Parse the Frame Control
      //这里处理一下门锁的cmd-type
      if(*pData==0x1c){//我是直接debug,然后发现透传的时候传到这里的是0x1c,所以直接进行特殊处理,后来发现manuSpecific也要进行处理,所以就写成这样了,建议如果懂真正流程的话不要和我这么改,我是不懂的
         hdr->fc.type =1;
         hdr->fc.manuSpecific =0;
      }else{
         hdr->fc.type = zcl_FCType( *pData );
          hdr->fc.manuSpecific = zcl_FCManuSpecific( *pData ) ? 1 : 0;
      }
    
      if ( zcl_FCDirection( *pData ) )
      {
        hdr->fc.direction = ZCL_FRAME_SERVER_CLIENT_DIR;
      }
      else
      {
        hdr->fc.direction = ZCL_FRAME_CLIENT_SERVER_DIR;
      }
    
    ...
    }
    

    修改完了就可以进去下一步了,好了到了 if ( pInPlugin && pInPlugin->pfnIncomingHdlr )这里又卡住进不去了,debug看看,发现是没有找到pInPlugin ,往上找什么时候赋值,发现是:pInPlugin = zclFindPlugin( pkt->clusterId, epDesc->simpleDesc->AppProfId );这个给它赋值了,进入看看:

    
    static zclLibPlugin_t *plugins = (zclLibPlugin_t *)NULL;
    
    ZStatus_t zclGeneral_RegisterCmdCallbacks( uint8 endpoint, zclGeneral_AppCallbacks_t *callbacks )
    {
      zclGenCBRec_t *pNewItem;
      zclGenCBRec_t *pLoop;
    
      // Register as a ZCL Plugin
      if ( zclGenPluginRegisted == FALSE )
      {
        zcl_registerPlugin( ZCL_CLUSTER_ID_GEN_BASIC,
                            ZCL_CLUSTER_ID_GEN_MULTISTATE_VALUE_BASIC,
                            zclGeneral_HdlIncoming );
          //.......
      }
    }
    
    ZStatus_t zcl_registerPlugin( uint16 startClusterID,
              uint16 endClusterID, zclInHdlr_t pfnIncomingHdlr )
    {
     ...
    
      // Fill in the plugin record.
      pNewItem->next = (zclLibPlugin_t *)NULL;
      pNewItem->startClusterID = startClusterID;
      pNewItem->endClusterID = endClusterID;
      pNewItem->pfnIncomingHdlr = pfnIncomingHdlr;
    
      // Find spot in list
      if (  plugins == NULL )
      {
        plugins = pNewItem;//在这里,然后再往上找发现在zclGeneral_RegisterCmdCallbacks里面调用了,然后再往上找发现在应用层初始化的时候调用了zclGeneral_RegisterCmdCallbacks,开始串起来了吧,就是要注册回调才能有这样的处理,发现startClusterID=0,endClusterID= 0x0014,难怪找不到回调
      }
     ...
    }
    
    static zclLibPlugin_t *zclFindPlugin( uint16 clusterID, uint16 profileID )
    {
      zclLibPlugin_t *pLoop = plugins;//找一下这个全局变量
    
      (void)profileID;  // Intentionally unreferenced parameter
    
      while ( pLoop != NULL )
      {
        if ( ( clusterID >= pLoop->startClusterID ) && ( clusterID <= pLoop->endClusterID ) )
            //就是这里了,我们的透传ID是3073->0x0c01,看上面的分析
        {
          return ( pLoop );
        }else if(clusterID==0x0c01)//特殊处理一下雍敏网关的透传接口,因为它的透传接口不在该范围,doorlock,然后就能找到该回调了
        {
          return ( pLoop );
        }
    
        pLoop = pLoop->next;
      }
    
      return ( (zclLibPlugin_t *)NULL );
    }
    
    

    终于能进到: status = pInPlugin->pfnIncomingHdlr( &inMsg );这里了,然后百度发现这里就是回调到回调函数处理那里的:static ZStatus_t zclGeneral_HdlIncoming( zclIncoming_t *pInMsg );我们去到:zcl_general.c文件里面查看这个函数:

    static ZStatus_t zclGeneral_HdlIncoming( zclIncoming_t *pInMsg )
    {
      ZStatus_t stat = ZSuccess;
    
    #if defined ( INTER_PAN )
      if ( StubAPS_InterPan( pInMsg->msg->srcAddr.panId, pInMsg->msg->srcAddr.endPoint ) )
        return ( stat ); // Cluster not supported thru Inter-PAN
    #endif
      if ( zcl_ClusterCmd( pInMsg->hdr.fc.type ) )//这里我们已经在上面修改过了所以会走下一步
      {
        // Is this a manufacturer specific command?
        if ( pInMsg->hdr.fc.manuSpecific == 0 )//就是这里,我们也修改过了,所以可以走下一步
        {
          stat = zclGeneral_HdlInSpecificCommands( pInMsg );//我们进去看看
        }
        else
        {
          // We don't support any manufacturer specific command.
          stat = ZFailure;
        }
      }
      else
      {
        // Handle all the normal (Read, Write...) commands -- should never get here
        stat = ZFailure;
      }
      return ( stat );
    }
    

    继续进到 stat = zclGeneral_HdlInSpecificCommands( pInMsg );函数

    static ZStatus_t zclGeneral_ProcessDookLockInfo(zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs );//定义门锁处理函数,透传接口处理
    
    //很明显了,就是这里回调函数到应用层的,我们看看
    static ZStatus_t zclGeneral_HdlInSpecificCommands( zclIncoming_t *pInMsg )
    {
      ZStatus_t stat;
      zclGeneral_AppCallbacks_t *pCBs;
    
      // make sure endpoint exists
      pCBs = zclGeneral_FindCallbacks( pInMsg->msg->endPoint );//获取回调函数指针
      if ( pCBs == NULL )
        return ( ZFailure );
    
      switch ( pInMsg->msg->clusterId )//根据clusterID来分辨回调函数
      {
    #ifdef ZCL_BASIC
        case ZCL_CLUSTER_ID_GEN_BASIC:
          stat = zclGeneral_ProcessInBasic( pInMsg, pCBs );
          break;
    #endif // ZCL_BASIC
    
    #ifdef ZCL_IDENTIFY
        case ZCL_CLUSTER_ID_GEN_IDENTIFY:
          stat = zclGeneral_ProcessInIdentity( pInMsg, pCBs );
          break;
    #endif // ZCL_IDENTIFY
    
    #ifdef ZCL_GROUPS
        case ZCL_CLUSTER_ID_GEN_GROUPS:
          if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) )
            stat = zclGeneral_ProcessInGroupsServer( pInMsg );
          else
            stat = zclGeneral_ProcessInGroupsClient( pInMsg, pCBs );
          break;
    #endif // ZCL_GROUPS
    
    #ifdef ZCL_SCENES
        case ZCL_CLUSTER_ID_GEN_SCENES:
          if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) )
            stat = zclGeneral_ProcessInScenesServer( pInMsg, pCBs );
          else
            stat = zclGeneral_ProcessInScenesClient( pInMsg, pCBs );
          break;
    #endif // ZCL_SCENES
    
    #ifdef ZCL_ON_OFF
        case ZCL_CLUSTER_ID_GEN_ON_OFF:
          stat = zclGeneral_ProcessInOnOff( pInMsg, pCBs );
          break;
    #endif // ZCL_ON_OFF
    
    #ifdef ZCL_LEVEL_CTRL
        case ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL:
          stat = zclGeneral_ProcessInLevelControl( pInMsg, pCBs );
          break;
    #endif // ZCL_LEVEL_CTRL
    
    #ifdef ZCL_ALARMS
        case ZCL_CLUSTER_ID_GEN_ALARMS:
          if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) )
            stat = zclGeneral_ProcessInAlarmsServer( pInMsg, pCBs );
          else
            stat = zclGeneral_ProcessInAlarmsClient( pInMsg, pCBs );
          break;
    #endif // ZCL_ALARMS
    
    #ifdef ZCL_LOCATION
        case ZCL_CLUSTER_ID_GEN_LOCATION:
          if ( zcl_ServerCmd( pInMsg->hdr.fc.direction ) )
            stat = zclGeneral_ProcessInLocationServer( pInMsg, pCBs );
          else
            stat = zclGeneral_ProcessInLocationClient( pInMsg, pCBs );
          break;
    #endif // ZCL_LOCATION
    
        case ZCL_CLUSTER_ID_DOORLOCK_INFO://参考onoff的回调函数,我们写好这个回调,这里要添加一些clusterID,在zcl.h文件里面:
              /*
              .....
              // Light Link cluster
    #define ZCL_CLUSTER_ID_LIGHT_LINK                           0x1000
    
    //door lock self control ID门锁自定义透传ID  
    #define ZCL_CLUSTER_ID_DOORLOCK_INFO                         0x0C01
    .....
              */
              
            stat = zclGeneral_ProcessDookLockInfo( pInMsg, pCBs );
        break;
        case ZCL_CLUSTER_ID_GEN_POWER_CFG:
        case ZCL_CLUSTER_ID_GEN_DEVICE_TEMP_CONFIG:
        case ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG:
        case ZCL_CLUSTER_ID_GEN_TIME:
        default:
          stat = ZFailure;
          break;
      }
    
      return ( stat );
    }
    
    //参考onoff的处理函数进行修改
    //door lock process门锁处理函数
    static ZStatus_t zclGeneral_ProcessDookLockInfo(zclIncoming_t *pInMsg, zclGeneral_AppCallbacks_t *pCBs )
    {
      ZStatus_t stat = ZSuccess;
      if ( pCBs->pfnDoorLock )
            {
              pCBs->pfnDoorLock( pInMsg->pData,pInMsg->pDataLen );
            }
       return ( stat );
    }
    

    至此,我们已经实现了这个回调功能了更加多的功能以后再说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值