zigbee zcl规范及其协议栈实现2

对通用命令的处理:
对zcl的通用命令的响应,zcl自己已经实现,比如读取某个clusterid的某个属性值,应用只需将这个属性设置好即可,
通用命令包括,zcl.h
/*** Foundation Command IDs ***/
#define ZCL_CMD_READ                                    0x00
#define ZCL_CMD_READ_RSP                                0x01
#define ZCL_CMD_WRITE                                   0x02
#define ZCL_CMD_WRITE_UNDIVIDED                         0x03
#define ZCL_CMD_WRITE_RSP                               0x04
#define ZCL_CMD_WRITE_NO_RSP                            0x05
#define ZCL_CMD_CONFIG_REPORT                           0x06
#define ZCL_CMD_CONFIG_REPORT_RSP                       0x07
#define ZCL_CMD_READ_REPORT_CFG                         0x08
#define ZCL_CMD_READ_REPORT_CFG_RSP                     0x09
#define ZCL_CMD_REPORT                                  0x0a
#define ZCL_CMD_DEFAULT_RSP                             0x0b
#define ZCL_CMD_DISCOVER                                0x0c
#define ZCL_CMD_DISCOVER_RSP                            0x0d
而zcl.c中默认的处理函数数组是
static CONST zclCmdItems_t zclCmdTable[] =
{
#ifdef ZCL_READ
  /* ZCL_CMD_READ */                { zclParseInReadCmd,             zclProcessInReadCmd             },
  /* ZCL_CMD_READ_RSP */            { zclParseInReadRspCmd,          zclSendMsg                      },
#else
  /* ZCL_CMD_READ */                { NULL,                          NULL                            },
  /* ZCL_CMD_READ_RSP */            { NULL,                          NULL                            },
#endif // ZCL_READ

#ifdef ZCL_WRITE
  /* ZCL_CMD_WRITE */               { zclParseInWriteCmd,            zclProcessInWriteCmd            },
  /* ZCL_CMD_WRITE_UNDIVIDED */     { zclParseInWriteCmd,            zclProcessInWriteUndividedCmd   },
  /* ZCL_CMD_WRITE_RSP */           { zclParseInWriteRspCmd,         zclSendMsg                      },
  /* ZCL_CMD_WRITE_NO_RSP */        { zclParseInWriteCmd,            zclProcessInWriteCmd            },
#else
  /* ZCL_CMD_WRITE */               { NULL,                          NULL                            },
  /* ZCL_CMD_WRITE_UNDIVIDED */     { NULL,                          NULL                            },
  /* ZCL_CMD_WRITE_RSP */           { NULL,                          NULL                            },
  /* ZCL_CMD_WRITE_NO_RSP */        { NULL,                          NULL                            },
#endif // ZCL_WRITE

#ifdef ZCL_REPORT
  /* ZCL_CMD_CONFIG_REPORT */       { zclParseInConfigReportCmd,     zclSendMsg                      },
  /* ZCL_CMD_CONFIG_REPORT_RSP */   { zclParseInConfigReportRspCmd,  zclSendMsg                      },
  /* ZCL_CMD_READ_REPORT_CFG */     { zclParseInReadReportCfgCmd,    zclSendMsg                      },
  /* ZCL_CMD_READ_REPORT_CFG_RSP */ { zclParseInReadReportCfgRspCmd, zclSendMsg                      },
  /* ZCL_CMD_REPORT */              { zclParseInReportCmd,           zclSendMsg                      },
#else
  /* ZCL_CMD_CONFIG_REPORT */       { NULL,                          NULL                            },
  /* ZCL_CMD_CONFIG_REPORT_RSP */   { NULL,                          NULL                            },
  /* ZCL_CMD_READ_REPORT_CFG */     { NULL,                          NULL                            },
  /* ZCL_CMD_READ_REPORT_CFG_RSP */ { NULL,                          NULL                            },
  /* ZCL_CMD_REPORT */              { NULL,                          NULL                            },
#endif // ZCL_REPORT

  /* ZCL_CMD_DEFAULT_RSP */         { zclParseInDefaultRspCmd,       zclSendMsg                      },
  
#ifdef ZCL_DISCOVER  
  /* ZCL_CMD_DISCOVER */            { zclParseInDiscCmd,             zclProcessInDiscCmd             },
  /* ZCL_CMD_DISCOVER_RSP */        { zclParseInDiscRspCmd,          zclSendMsg                      }
#else
  /* ZCL_CMD_DISCOVER */            { NULL,                          NULL                            },
  /* ZCL_CMD_DISCOVER_RSP */        { NULL,                          NULL                            }
#endif // ZCL_DISCOVER
};

1.比如在发送端(客户端),发出一个个命令是ZCL_CMD_READ,hdr.fc.type=ZCL_FRAME_TYPE_PROFILE_CMD的请求,目的端点是SAMPLELIGHT_ENDPOINT(13)
2.服务端将端点SAMPLELIGHT_ENDPOINT使用zclHA_Init( &zclSampleLight_SimpleDesc )注册在zcl任务,af层从空中接到消息后会转发给SAMPLELIGHT_ENDPOINT所在的任务zcl,然后
zcl_event_loop->zclProcessMessageMSG,
发现hdr.fc.type=ZCL_FRAME_TYPE_PROFILE_CMD,就直接调用zclCmdTable[ZCL_CMD_READ]的那个函数,就是zclProcessInReadCmd,
此函数就在zcl的全局属性链表头部attrList开始寻找对应端点的节点元素的属性指针,返回。。。
    if ( zclFindAttrRec( pInMsg->msg->endPoint, pInMsg->msg->clusterId, readCmd->attrID[i], &attrRec ) )
    {
      statusRec->data = attrRec.attr.dataPtr;
      statusRec->status = ZCL_STATUS_SUCCESS;
      statusRec->dataType = attrRec.attr.dataType;
    }
3.
所以,服务器端需要先将支持的clusterid的各个属性注册一下,比如zcl_samplelight.c
  zcl_registerAttrList( SAMPLELIGHT_ENDPOINT, SAMPLELIGHT_MAX_ATTRIBUTES, zclSampleLight_Attrs );
这个端点注册了n个属性值,放在全局属性链表头部attrList所指向的链表的最后(attrList总是指向链表的头部,zclAttrRecsList attrList
typedef struct zclAttrRecsList
{
  struct zclAttrRecsList *next;
  uint8                  endpoint;      // Used to link it into the endpoint descriptor
  uint8                  numAttributes; // Number of the following records
  CONST zclAttrRec_t     *attrs;        // attribute records
} zclAttrRecsList;

// Attribute record
typedef struct
{
  uint16  attrId;         // Attribute ID
  uint8   dataType;       // Data Type - defined in AF.h
  uint8   accessControl;  // Read/write - bit field
  void    *dataPtr;       // Pointer to data field
} zclAttribute_t;

typedef struct
{
  uint16          clusterID;    // Real cluster ID
  zclAttribute_t  attr;
} zclAttrRec_t;

CONST zclAttrRec_t zclSampleLight_Attrs[SAMPLELIGHT_MAX_ATTRIBUTES] =
{
  // *** General Basic Cluster Attributes ***
  {
    ZCL_CLUSTER_ID_GEN_BASIC,             // Cluster IDs - defined in the foundation (ie. zcl.h)
    {  // Attribute record
      ATTRID_BASIC_HW_VERSION,            // Attribute ID - Found in Cluster Library header (ie. zcl_general.h)
      ZCL_DATATYPE_UINT8,                 // Data Type - found in zcl.h
      ACCESS_CONTROL_READ,                // Variable access control - found in zcl.h
      (void *)&zclSampleLight_HWRevision  // Pointer to attribute variable
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_ZCL_VERSION,
      ZCL_DATATYPE_UINT8,
      ACCESS_CONTROL_READ,
      (void *)&zclSampleLight_ZCLVersion
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_MANUFACTURER_NAME,
      ZCL_DATATYPE_CHAR_STR,
      ACCESS_CONTROL_READ,
      (void *)zclSampleLight_ManufacturerName
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_MODEL_ID,
      ZCL_DATATYPE_CHAR_STR,
      ACCESS_CONTROL_READ,
      (void *)zclSampleLight_ModelId
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_DATE_CODE,
      ZCL_DATATYPE_CHAR_STR,
      ACCESS_CONTROL_READ,
      (void *)zclSampleLight_DateCode
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_POWER_SOURCE,
      ZCL_DATATYPE_UINT8,
      ACCESS_CONTROL_READ,
      (void *)&zclSampleLight_PowerSource
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_LOCATION_DESC,
      ZCL_DATATYPE_CHAR_STR,
      (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE),
      (void *)zclSampleLight_LocationDescription
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_PHYSICAL_ENV,
      ZCL_DATATYPE_UINT8,
      (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE),
      (void *)&zclSampleLight_PhysicalEnvironment
    }
  },
  {
    ZCL_CLUSTER_ID_GEN_BASIC,
    { // Attribute record
      ATTRID_BASIC_DEVICE_ENABLED,
      ZCL_DATATYPE_BOOLEAN,
      (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE),
      (void *)&zclSampleLight_DeviceEnable
    }
  },

  // *** Identify Cluster Attribute ***
  {
    ZCL_CLUSTER_ID_GEN_IDENTIFY,
    { // Attribute record
      ATTRID_IDENTIFY_TIME,
      ZCL_DATATYPE_UINT16,
      (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE),
      (void *)&zclSampleLight_IdentifyTime
    }
  },

  // *** On/Off Cluster Attributes ***
  {
    ZCL_CLUSTER_ID_GEN_ON_OFF,
    { // Attribute record
      ATTRID_ON_OFF,
      ZCL_DATATYPE_UINT8,
      ACCESS_CONTROL_READ,
      (void *)&zclSampleLight_OnOff
    }
  },
};
4.如果zclCmdTable[ZCL_CMD_READ]对应的处理函数是zclSendMsg,则会将这个消息以ZCL_INCOMING_MSG事件发给上层任务去处理,前提是上层任务使用zcl_registerForMsg注册过。

对zcl不同领域的不同clusterid的不同command的处理:
例如服务器端接收到如下的clusterid(通用领域的).和命令号是COMMAND_TOGGLE怎么处理的呢?
// General Clusters
#define ZCL_CLUSTER_ID_GEN_BASIC                             0x0000
#define ZCL_CLUSTER_ID_GEN_POWER_CFG                         0x0001
#define ZCL_CLUSTER_ID_GEN_DEVICE_TEMP_CONFIG                0x0002
#define ZCL_CLUSTER_ID_GEN_IDENTIFY                          0x0003
#define ZCL_CLUSTER_ID_GEN_GROUPS                            0x0004
#define ZCL_CLUSTER_ID_GEN_SCENES                            0x0005
#define ZCL_CLUSTER_ID_GEN_ON_OFF                            0x0006//this
#define ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG              0x0007
#define ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL                     0x0008
#define ZCL_CLUSTER_ID_GEN_ALARMS                            0x0009
#define ZCL_CLUSTER_ID_GEN_TIME                              0x000A
#define ZCL_CLUSTER_ID_GEN_LOCATION                          0x000B
#define ZCL_CLUSTER_ID_GEN_KEY_ESTABLISHMENT                 0x0800

首先要了解general领域的clusterid  onoff 的属性和命令各有哪些
/*********************************/
/*** On/Off Cluster Attributes ***/
/*********************************/
#define ATTRID_ON_OFF                                     0x0000

/*******************************/
/*** On/Off Cluster Commands ***/
/*******************************/
#define COMMAND_OFF                                       0x00
#define COMMAND_ON                                        0x01
#define COMMAND_TOGGLE                                    0x02
1.比如在发送端(客户端),发出一个个clusterid是ZCL_CLUSTER_ID_GEN_ON_OFF ,命令是COMMAND_TOGGLE,hdr.fc.type=ZCL_FRAME_TYPE_SPECIFIC_CMD的请求,目的端点是SAMPLELIGHT_ENDPOINT(13)
2.服务器端将端点号SAMPLELIGHT_ENDPOINT使用zclHA_Init( &zclSampleLight_SimpleDesc )注册在zcl任务,af层从空中接到消息后会转发给SAMPLELIGHT_ENDPOINT所在的任务zcl,然后
zcl_event_loop->zclProcessMessageMSG,
发现hdr.fc.type=ZCL_FRAME_TYPE_SPECIFIC_CMD,就调用zclGeneral_HdlIncoming->zclGeneral_HdlInSpecificCommands,如下
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 )
  {
#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_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 );
}
此函数会在zcl_general的全局处理函数数组链表头部zclGenCBs开始寻找对应端点的节点元素的数组指针
static zclGeneral_AppCallbacks_t *zclGeneral_FindCallbacks( uint8 endpoint )

{
  zclGenCBRec_t *pCBs;
  pCBs = zclGenCBs;
  while ( pCBs )
  {
    if ( pCBs->endpoint == endpoint )
      return ( pCBs->CBs );
    pCBs = pCBs->next;
  }
  return ( (zclGeneral_AppCallbacks_t *)NULL );
}
case下的各个函数,看似有点多余,直接调用回调函数数组中的对应函数不就行了-----那些函数里面有判断条件。

3.所以,服务器端需要先将支持的clusterid的处理函数注册一下,比如zcl_samplelight.c
  zclGeneral_RegisterCmdCallbacks( SAMPLELIGHT_ENDPOINT, &zclSampleLight_CmdCallbacks );
这个端点注册了回调函数数组zclSampleLight_CmdCallbacks,放在全局回调函数数组链表头部zclGenCBs所指向的链表的最后(zclGenCBs总是指向链表的头部,zclGenCBRec_t zclGenCBs
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;

typedef struct
{
  zclGCB_BasicReset_t               pfnBasicReset;                // Basic Cluster Reset command
  zclGCB_Identify_t                 pfnIdentify;                  // Identify command
  zclGCB_IdentifyQueryRsp_t         pfnIdentifyQueryRsp;          // Identify Query Response command
  zclGCB_OnOff_t                    pfnOnOff;                     // On/Off cluster commands
  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
  zclGCB_GroupRsp_t                 pfnGroupRsp;                  // Group Response commands
  zclGCB_SceneStoreReq_t            pfnSceneStoreReq;             // Scene Store Request command
  zclGCB_SceneRecallReq_t           pfnSceneRecallReq;            // Scene Recall Request command
  zclGCB_SceneRsp_t                 pfnSceneRsp;                  // Scene Response command
  zclGCB_Alarm_t                    pfnAlarm;                     // Alarm (Response) commands
  zclGCB_Location_t                 pfnLocation;                  // RSSI Location command
  zclGCB_LocationRsp_t              pfnLocationRsp;               // RSSI Location Response command
} zclGeneral_AppCallbacks_t;
//
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
  NULL,                                     // Level Control Move to Level command
  NULL,                                     // Level Control Move command
  NULL,                                     // Level Control Step command
  NULL,                                     // Group Response commands
  NULL,                                     // Scene Store Request command
  NULL,                                     // Scene Recall Request command
  NULL,                                     // Scene Response command
  NULL,                                     // Alarm (Response) command
  NULL,                                     // RSSI Location commands
  NULL,                                     // RSSI Location Response commands
};

4.在具体的clusterid处理函数中在根据command进行不同处理,比如zclSampleLight_OnOffCB
static void zclSampleLight_OnOffCB( uint8 cmd )
{
  // Turn on the light
  if ( cmd == COMMAND_ON )
    zclSampleLight_OnOff = LIGHT_ON;


  // Turn off the light
  else if ( cmd == COMMAND_OFF )
    zclSampleLight_OnOff = LIGHT_OFF;


  // Toggle the light
  else
  {
    if ( zclSampleLight_OnOff == LIGHT_OFF )
      zclSampleLight_OnOff = LIGHT_ON;
    else
      zclSampleLight_OnOff = LIGHT_OFF;
  }


  // In this sample app, we use LED4 to simulate the Light
  if ( zclSampleLight_OnOff == LIGHT_ON )
    HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
  else
    HalLedSet( HAL_LED_4, HAL_LED_MODE_OFF );
}


综上,

inMsg.hdr.fc.type=ZCL_FRAME_TYPE_PROFILE_CMD的时候(表示是zcl通用命令)会根据inMsg.hdr.commandID直接调用zcl.c中预定义的函数数组指针zclCmdTable处理之,根据不同的commandid,调用不同的处理函数。如果处理函数是zclSendMsg则会将这个消息以ZCL_INCOMING_MSG事件发给上层任务去处理
从发送函数可以看出
ZStatus_t zcl_SendCommand( uint8 srcEP, afAddrType_t *destAddr,
                           uint16 clusterID, uint8 cmd, uint8specific, uint8 direction,
                           uint8 disableDefaultRsp, uint16 manuCode, uint8 seqNum,
                           uint16 cmdFormatLen, uint8 *cmdFormat )


inMsg.hdr.fc.type=ZCL_FRAME_TYPE_SPECIFIC_CMD的时候(zcl的功能领域专用命令)会使用 用zclGeneral_RegisterCmdCallbacks( SAMPLELIGHT_ENDPOINT, &zclSampleLight_CmdCallbacks )注册了的函数数组指针zclSampleLight_CmdCallbacks 处理之,根据不同的 inMsg.msg->clusterId会调用不同的处理函数,然后在处理函数中会根据commandid进一步操作
posted on 2013-03-17 01:52  _song  阅读(2654)  评论(1编辑  收藏  举报