This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Z-stack HomeAutomation流程分析 (转)



HomeAutomation流程分析

基于Zstack-CC2530-2.3.0-1.4.0中HomeAutomation例程

在分析整个应用之前,我们首先需要了解每个任务大概的处理流程。按下light板(Enddevice)的右键,请求终端banding,此时按下switch板(协调器)的右键,终端banding建立。按下switch板的上键可以切换light板上LED的亮灭。通过这个,我们来了解整个过程的实现。

终端banding暂且不说,先看任务的初始化及处理。

Light板:

(1)任务初始化:

void zclSampleLight_Init( byte task_id )

{

  zclSampleLight_TaskID = task_id;//初始化任务ID

 

  // Set destination address to indirect

  //zclSampleLight_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;

  //zclSampleLight_DstAddr.endPoint = 0;

  //zclSampleLight_DstAddr.addr.shortAddr = 0;

 

  // This app is part of the Home Automation Profile

//AF层简单描述符的注册

  zclHA_Init( &zclSampleLight_SimpleDesc );

 

  // Register the ZCL General Cluster Library callback functions

  //对回调函数的注册,ZCL将处理相应的事件

  zclGeneral_RegisterCmdCallbacks( SAMPLELIGHT_ENDPOINT,

                                                        &zclSampleLight_CmdCallbacks );

 

  // Register the application's attribute list

  //属性列表的注册

  zcl_registerAttrList( SAMPLELIGHT_ENDPOINT,

                                   SAMPLELIGHT_MAX_ATTRIBUTES, zclSampleLight_Attrs );

 

  // Register the Application to receive the unprocessed Foundation command/response messages

//从后面可以看出除了上面那些特定事件外的其他事件,交由zclSampleLight来处理

  zcl_registerForMsg( zclSampleLight_TaskID );

  ……

}

 

void zcl_Init( uint8 task_id )

{

  zcl_TaskID = task_id;

 

  plugins = (zclLibPlugin_t  *)NULL;//相应CLUSTERID及回调函数列表

  attrList = (zclAttrRecsList *)NULL;//相应事件属性列表

  clusterOptionList = (zclClusterOptionList *)NULL;

}

(2)任务处理:

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 )

      {

        zclProcessMessageMSG( (afIncomingMSGPacket_t *)msgPtr );

      }

      else if ( zcl_RegisteredMsgTaskID != TASK_NO_TASK )

      {

           //这些事件由zclSampleLight_event_loop来处理

        // send it to another task to process.

        osal_msg_send( zcl_RegisteredMsgTaskID, msgPtr );

        dealloc = FALSE;

      }

……

    }

……

  }

  // Discard unknown events

  return 0;

}

下面我们来看一下,zclProcessMessageMSG中是如何来处理这些事件的。

void zclProcessMessageMSG( 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;

 

  if ( pkt->cmd.DataLength == 0 )

    return;   // Error, ignore the message

 

  // Initialize

  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);

 

  // Find the wanted endpoint

  //寻找我们想要的endpoint(前面初始化的时候,我们有注册)

  epDesc = afFindEndPointDesc( pkt->endPoint );

  if ( epDesc == NULL )

    return;   // Error, ignore the message

 

  if ( pkt->clusterId == ZCL_INVALID_CLUSTER_ID )

    return;   // 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))

  {

    return; // 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

    if ( zcl_ProfileCmd( inMsg.hdr.fc.type ) )

      return;

 

    interPanMsg = TRUE;

    options = AF_TX_OPTIONS_NONE;

  }

  else

#endif

  {

    interPanMsg = FALSE;

    options = zclGetClusterOption( pkt->endPoint, pkt->clusterId );

  }

 

  // Local and remote Security options must match except for Default Response command

//inMsg.hdr.fc.type= ZCL_FRAME_TYPE_SPECIFIC_CMD

//就是处理加密与否,但是还没有弄明白clusterOptionList在哪里处理的,所以不知道options = zclGetClusterOption( pkt->endPoint, pkt->clusterId );的结果,暂时跳过吧,~~~~(>_<)~~~~

  if ( !zcl_DefaultRspCmd( inMsg.hdr ) )//为真

  {

    securityEnable = ( options & AF_EN_SECURITY ) ? TRUE : FALSE;

    if ( pkt->SecurityUse != securityEnable )

    {

      if ( UNICAST_MSG( inMsg.msg ) )

      {

        // Send a Default Response command back with no Application Link Key security

        if ( securityEnable )

          zclSetSecurityOption( pkt->endPoint, pkt->clusterId, FALSE );

       

        defautlRspCmd.statusCode = status;

        defautlRspCmd.commandID = inMsg.hdr.commandID;

        zcl_SendDefaultRspCmd( inMsg.msg->endPoint, &(inMsg.msg->srcAddr),

                               inMsg.msg->clusterId, &defautlRspCmd,

                               ZCL_FRAME_SERVER_CLIENT_DIR, true,

                               inMsg.hdr.manuCode, inMsg.hdr.transSeqNum );

        if ( securityEnable )

          zclSetSecurityOption( pkt->endPoint, pkt->clusterId, TRUE );      

      }

     

      return;   // Error, ignore the message

    }

  }

 

  // Is this a foundation type message

  if ( zcl_ProfileCmd( inMsg.hdr.fc.type ) )//为假

  {

    if ( inMsg.hdr.fc.manuSpecific )

    {

      // We don't support any manufacturer specific command

      status = ZCL_STATUS_UNSUP_MANU_GENERAL_COMMAND;

    }

    else if ( ( inMsg.hdr.commandID <= ZCL_CMD_MAX ) &&

              ( zclCmdTable[inMsg.hdr.commandID].pfnParseInProfile != NULL ) )

    {

      zclParseCmd_t parseCmd;

     

      parseCmd.endpoint = pkt->endPoint;

      parseCmd.dataLen = inMsg.pDataLen;

      parseCmd.pData = inMsg.pData;

     

      // Parse the command, remember that the return value is a pointer to allocated memory

      inMsg.attrCmd = zclParseCmd( inMsg.hdr.commandID, &parseCmd );

      if ( (inMsg.attrCmd != NULL) && (zclCmdTable[inMsg.hdr.commandID].pfnProcessInProfile != NULL) )

      {

        // Process the command

        if ( zclProcessCmd( inMsg.hdr.commandID, &inMsg ) == FALSE )

        {

          // Couldn't find attribute in the table.

        }

      }

      

      // Free the buffer

      if ( inMsg.attrCmd )

        osal_mem_free( inMsg.attrCmd );

     

      if ( CMD_HAS_RSP( inMsg.hdr.commandID ) )

        return; // We're done

      

      status = ZSuccess;

    }

    else

    {

      // Unsupported message

      status = ZCL_STATUS_UNSUP_GENERAL_COMMAND;

    }

  }

  else  // Not a foundation type message, so it must be specific to the cluster ID.

       //inMsg.hdr.fc.type= ZCL_FRAME_TYPE_SPECIFIC_CMD

  {

    if (epDesc->simpleDesc == NULL)

    {

      pInPlugin = NULL;

    }

    else

    {

      // Find the appropriate plugin

      pInPlugin = zclFindPlugin( pkt->clusterId, epDesc->simpleDesc->AppProfId );

    }

    if ( pInPlugin && pInPlugin->pfnIncomingHdlr )

    {

      // 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 );

      if ( status == ZCL_STATUS_CMD_HAS_RSP || ( interPanMsg && status == ZSuccess ) )

        return; // We're done

    }

    ……

}

还记得前面zclSampleLight_Init中,有调用zclGeneral_RegisterCmdCallbacks( SAMPLELIGHT_ENDPOINT,

                                                        &zclSampleLight_CmdCallbacks );函数,对端点SAMPLELIGHT_ENDPOINT进行注册,其数据结构如下:

typedef struct zclGenCBRec

{

  struct zclGenCBRec        *next;

  uint8                     endpoint; // Used to link it into the endpoint descriptor

  zclGeneral_AppCallbacks_t *CBs;     // Pointer to Callback function

} zclGenCBRec_t;

CB就是回调函数列表,其赋值列表如下。在上面的处理函数中,通过调用pInPlugin->pfnIncomingHdlr( &inMsg );调用,对事件进行相应处理。

static zclGeneral_AppCallbacks_t zclSampleLight_CmdCallbacks =

{

  zclSampleLight_BasicResetCB,              // Basic Cluster Reset command

  zclSampleLight_IdentifyCB,                // Identify command 

  zclSampleLight_IdentifyQueryRspCB,        // Identify Query Response command

  zclSampleLight_OnOffCB,                   // On/Off cluster command

  ……

};

对整个switchàlight控制,light板在APP层的流程基本如上。Switch板就略过了,基本上就是按下向上按键(HAL_KEY_SW_1)后,zclSampleSw_event_loop调用zclSampleSw_HandleKeys进行处理,调用zclGeneral_SendOnOff_CmdToggle,最终通过AF_DataRequest函数,将命令发送出去。

  • zstack串口应用总结

    在zstack中,串口应用主要有三种方式: 
    (1)与zstack交互进行通信:需要考虑到系统通信的数据格式,这种应用一般更加侧重于与zstack交互。 
    举个例子,在协调器中我们需要利用PC给zstack发出命令需要其执行相应的命令时,这个时候我们只要在协调器的编译项加上ZTOOL_P1 MT_TASK两个选项即可,如果这个时候如果需要MT层和应用层进行交互处理用户数据(具体用户数据的格式在zstack中的串口文档中有定义,大家可以自行查看),就必须在应用层中加入 
    case MT_SYS_APP_MSG: // Z-Architect Messages 
    (具体为什么加这个如果还不清楚把zstack OSAL运行机制搞清楚之后再开发吧。) 
    这个时候才能串口发送的数据经过MT层处理后送到我们的应用层,这是接收过程,发送过程直接调用HalUARTWrite()函数即可。 
    (2)自己的串口应用,与zstack命令格式无关,但不想自己配置串口,想偷懒:这种情况下也很简单,与上述编译项不同的是,此时需要加入的编译项为:MT_TASK ZAPP_P1,当中的串口号根据实际情况而定,这个时候只要加入编译项其实就行了,在应用过程中如果想在应用层处理串口的数据,和上述情况类似,这时候需要在应用层中加入的东西有: 
    #include "SPIMgr.h"头文件 .... 
    #if defined (ZAPP_P1) 
    SPIMgr_RegisterTaskID(SampleApp_TaskID);//在MT层注册 SPIMgr_ZAppBufferLengthRegister(7); // BUFFER大小 #endif ..... 
    case SPI_INCOMING_ZAPP_DATA: 
    Bindnode_SerialMSGCB(MSGpkt);//自己的串口数据处理函数 #if defined(ZAPP_P1) 
    SPIMgr_AppFlowControl ( SPI_MGR_ZAPP_RX_READY );//流控制 #endif break; ...... 
    这是最主要的三步,完成上述添加,大家就可以使用串口了。 
    (需要主要注意的是一个串口不能同时实现上面的两个功能,一定要实现上述ZAPP_Px编译和ZTOOL_Px同时存在的话,那么x一定不能相同,即要使用两个不同的串口,否则会出现冲突。) 
    (3)可能瞧不起zstack定义好的串口,自己单独写一个玩玩,或者想另外加一个串口实现双串口功能,这个很简单,大家看看serialapp例子就行,我不细说。