ZCL 规范

来源:互联网 发布:武汉茶港大院 知乎 编辑:程序博客网 时间:2024/06/03 23:11

转自:http://www.xuebuyuan.com/1035142.html

对通用命令的处理:
对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 recordtypedef 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进一步操作

原创粉丝点击