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