干货!精讲Zigbee绑定指南
干货!精讲Zigbee绑定指南
一、绑定(Binding)
绑定是一种控制两个或者多个设备应用层之间信息流传递的机制。在ZigBee2006发布版本中,它被称为源绑定,所有的设备都可以执行绑定机制。
绑定允许应用程序发送一个数据包而不需要知道目标设备的短地址(此时将目标设备的短地址设置为无效地址0xFFFE)。应用支持子层(APS)从它的绑定表中确定目标设备的短地址,然后将数据发送给目标应用或者目标组。如果在绑定表中找到的短地址不止一个,协议栈会向所有找到的短地址发送数据。
说明:绑定是基于设备应用层端点的绑定,而且绑定只能在互为"补充的"设备间被创建。也就是说,当两个设备已经在他们的简单描述符结构中登记为一样的命令ID,并且一个作为输入另一个作为输出时,绑定才能成功。
图1:绑定图示
上图为两个设备建立的绑定关系,从上图我们理解绑定是基于端点(endpoint)的绑定。在设备1中端点号(endpoint)为3的开关1与设备2中端点号(endpoint)为5、7、8的灯建立了绑定。设备1中端点号(endpoint)为2的开关与设备2中端点号(endpoint)为17的灯建立了绑定。
二、建立绑定表(Building a Binding Table)
有三种方法可以建立一个绑定表:
l Zigbee Device Object Bind Request(ZDO绑定请求)——通过一个命令告诉设备创
建一个绑定表记录
l Zigbee Device Object End Device Bind Request(ZDO终端绑定请求)——两个设备
可以告诉协调器它们想要建立一个绑定表记录。协调器来协调并在两个设备中创建绑定表记录。
l Device Application(设备应用)—一个设备上的应用程序建立或者管理一个绑定表
三、绑定表的建立
TI的Zstack2006协议栈中提供两种可用的机制来配置设备绑定: (1)如果目的设备的扩展地址是已知的
(2)如果目的设备的扩展地址是未知的 3.1、 已知扩展地址的绑定
如果已经知道要绑定目标设备的扩展地址,那么在源设备端只需通过函数zb_BindDevice ()函数便可以进行绑定,zb_BindDevice ()中地址要设置为目标设备的扩展地址。
绑定函数说明:
zb_BindDevice ( uint8 create, //创建还是删除绑定,TRUE创建,FALSE删除
uint16 commandId, //命令ID,绑定是基于命令ID的绑定
uint8 *pDestination ) //扩展地址,可以为NULL,决定绑定类型
程序代码:
1、基于扩展地址的绑定:
void zb_BindDevice ( uint8 create, uint16 commandId, uint8 *pDestination )
{
if ( create )//这里为TRUE 创建绑定条目
{
if ( pDestination )//长地址不为空
{
//地址模式为64位地址,即扩展地址
destination.addrMode = Addr64Bit;
//将参数pDestination 的值复制到 destination.addr.extAddr中 osal_cpyExtAddr( destination.addr.extAddr, pDestination );
//调用函数 APSME_BindRequest进行基于长地址的绑定
ret = APSME_BindRequest( sapi_epDesc.endPoint, commandId, &destination, sapi_epDesc.endPoint );
if ( ret == ZSuccess )
{
//绑定成功后获取目的设备的短地址
ZDP_NwkAddrReq(pDestination, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 );
//定时触发事件 ZDO_NWK_UPDATE_NV
osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 );
}
}
函数说明:
1) 、void *osal_cpyExtAddr( void *dest, void *src )
说明:该函数的作用是复制扩展地址。将*src指向的扩展地址复制到指针*dest指向的内存
参数说明:*dest----------------------------目标地址指针
*src-----------------------------源地址指针
返回值:指针
2) 、ZStatus_t APSME_BindRequest( byte SrcEndpInt, uint16 ClusterId,
zAddrType_t *DstAddr, byte DstEndpInt)
说明:应用支持子层绑定函数,通过该函数可以在两个设备间的应用层创建绑定,以原语APSME_BIND.confirm返回绑定结果。注意这里传递的地址是64位的扩展地址。
参数说明:SrcEndpInt ---------------------------源设备的端点
ClusterId ------------------------------簇ID
*DstAddr -----------------------------目标设备的地址
DstEndpInt ---------------------------目标设备的端点
由上述参数我们可以看出绑定时基于端点和簇的绑定
返回值:ZStatus_t状态值
3)、afStatus_t ZDP_NwkAddrReq( byte *IEEEAddress, byte ReqType,
byte StartIndex, byte SecurityEnable )
说明:调用该函数可以由设备的扩展地址获取设备的网络地址,该消息以广播的形式发送给网络中的所有设备请求设备的短地址。如果一个设备的扩展地址和消息中所携带的扩展地址相同则会将自己的网络地址返回。
参数说明:*IEEEAddress ----------------------------目标设备扩展地址指针
ReqType -----------------------------------相应类型
StartIndex ---------------------------------开始索引
SecurityEnable ---------------------------安全选项
返回值:afStatus_t状态值
其他说明:ReqType响应类型说明,它的值可以是以下二者之一
ZDP_NWKADDR_REQTYPE_SINGLE-----------返回设备的短地址和扩展地址
ZDP_NWKADDR_REQTYPE_EXTENDED-----返回设备及相关设备的短地址和扩展地址
4) 、byte osal_start_timerEx( byte taskID, UINT16 event_id, UINT16 timeout_value )
说明:通过该函数将定时触发对应任务id为taskID的事件event_id。定时时长为timeout_value。在timeout_value溢出后将触发事件event_id。
参数说明:taskID --------------------------------------任务
id event_id ------------------------------------事件
timeout_value -----------------------------溢出时间
返回值:ZSUCCESS或者NO_TIMER_AVAIL
已知扩展地址绑定总结:在已知扩展地址绑定过程中,通过调用APSME_BindRequest()完成绑定,在绑定成功后又调用ZDP_NwkAddrReq()函数获取了绑定目标设备的16为网络地址。当上述步骤全部完成后,定时触发了事件ZDO_NWK_UPDATE_NV对网络状态进行更新。
2、未知扩展地址的绑定
这种绑定模式需要使要绑定的目标设备首先处于允许绑定的状态。源设备通过函数zb_BindDevice ()(扩展地址参数为NULL)进行绑定。这里用到了ZDO消息,首先我们回顾一下ZDO消息的流程,以请求IEEE地址为例,具体流程图如下:
图2:ZDO消息流程图
A) 、目标设备允许绑定。
使要绑定的目标设备处于允许绑定的模式,可以调用函数zb_AllowBind ()使目标设备进入允许绑定模式。 程序代码:
void zb_AllowBind ( uint8 timeout )
{
osal_stop_timerEx(sapi_TaskID, ZB_ALLOW_BIND_TIMER);
if ( timeout == 0 )
afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE);
}
Else
{
afSetMatch(sapi_epDesc.simpleDesc->EndPoint, TRUE);
if ( timeout != 0xFF )
{
if ( timeout > 64 )
{
timeout = 64; }
osal_start_timerEx(sapi_TaskID, ZB_ALLOW_BIND_TIMER, timeout*1000);
}
}
return;
}
说明:
1)、参数timeout
参数timeout是目标设备进入绑定模式持续的时间(s)。如果设置为OxFF,则该设备在任何时候都是允许绑定模式;如果设置为0x00,则取消目标设备进入允许绑定模式。如果设定的时间大于64s就默认为64s。
2)、uint8 afSetMatch( uint8 ep, uint8 action )
说明:允许或者禁止设备响应ZDO的描述符匹配请求。如果action参数为TRUE允许匹配,反之如果是FALSE则禁止匹配。
参数说明:ep --------------------------------------端点
endpoint action----------------------------------允许或者禁止匹配
返回值:TRUE或者 FALSE
3)、事件 ZB_ALLOW_BIND_TIMER
如果设定了允许ZDO描述符匹配,而设定的时间不是0xFFFF,即不是在任何时间都允许,那么就定时时长为timeout来触发事件ZB_ALLOW_BIND_TIMER关闭ZDO描述符匹配。
ZB_ALLOW_BIND_TIMER事件处理函数:
UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )
{ ……
if ( events & ZB_ALLOW_BIND_TIMER )
{
//这里action的参数为FALSE即关闭匹配描述符响应
afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE);
return (events ^ ZB_ALLOW_BIND_TIMER); } ……
}
B、源设备发起绑定请求
当目标设备已经进入允许绑定模式,则源设备可以使用函数zb_BindDevice()(地址参数设置为NULL)发送绑定请求。
程序代码:
void zb_BindDevice ( uint8 create, uint16 commandId, uint8 *pDestination )
{
if ( create )
{
if ( pDestination )//已知扩展地址的绑定
{ ……
}
else//未知扩展地址的绑定
{
destination.addrMode = Addr16Bit;
//16位短地址模式
//目的地址为广播地址,在全网进行匹配
destination.addr.shortAddr = NWK_BROADCAST_SHORTADDR;
//以下从两个方向进行Cluster匹配
if ( ZDO_AnyClusterMatches( 1, &commandId,
sapi_epDesc.simpleDesc->AppNumOutClusters,
sapi_epDesc.simpleDesc->pAppOutClusterList ) )
{
//匹配一个在允许绑定模式下的设备
7
ret = ZDP_MatchDescReq( &destination, NWK_BROADCAST_SHORTADDR,
sapi_epDesc.simpleDesc->AppProfId, 1, &commandId, 0, (cId_t *)NULL, 0 );
}
else if
( ZDO_AnyClusterMatches( 1, &commandId,
sapi_epDesc.simpleDesc->AppNumInClusters,
sapi_epDesc.simpleDesc->pAppInClusterList ) )
{
//匹配一个在允许绑定模式下的设备
ret = ZDP_MatchDescReq( &destination, NWK_BROADCAST_SHORTADDR,
sapi_epDesc.simpleDesc->AppProfId, 0, (cId_t *)NULL, 1, &commandId, 0 );
}
if ( ret == ZB_SUCCESS )
{
osal_start_timerEx(sapi_TaskID, ZB_BIND_TIMER, AIB_MaxBindingTime);
return;
// dont send cback event }
……
}
函数说明:
1)、byte ZDO_AnyClusterMatches( byte ACnt, uint16 *AList, byte BCnt, uint16 *BList ) 说明:在两个链表或者数组中寻找相同的簇(Cluster),即在*Alist和*Blist匹配相同的簇(Cluster),如果找到则返回TRUE否则返回FALSE。通过该函数在输入簇和输出簇中寻找对应的commandId是否存在。如果存在则会调用匹配描述符函数进行匹配。
参数说明:ACnt ------------------------------------A链表中条目的数量
*AList ----------------------------------链表A
BCnt ------------------------------------B链表中条目的数量
*BList ----------------------------------链表B
返回值:TRUE或者 FALSE
2
)、afStatus_t ZDP_MatchDescReq( zAddrType_t *dstAddr, uint16 nwkAddr,
uint16 ProfileID,
byte NumInClusters, cId_t *InClusterList,
byte NumOutClusters, cId_t *OutClusterList,
byte SecurityEnable )
说明:通过该函数将向网络中发送一条Match_Desc_req的消息,进行ZDO描述符匹配。匹配描述符是基于ProfileID和ClusterID匹配。
参数说明:dstAddr ------------------------------------目的地址
ProfileID ---------------------------------- ProfileID
NumInClusters ---------------------------输入簇数量
InClusterList ------------------------------输入簇列表
NumOutClusters -------------------------输出簇数量
OutClusterList ----------------------------输出簇列表
SecurityEnable ----------------------------安全选项
返回值:afStatus_t状态 3)、事件ZB_BIND_TIMER
说明:当发出描述符匹配请求后,定时触发事件ZB_BIND_TIMER告知上层绑定是否建立成功,定时时长为AIB_MaxBindingTime,该时长要保证绑定工作在触发事件ZB_BIND_TIMER前完成。
C、当源设备发出匹配描述符请求Match_Desc_req,全网进行匹配,并最终触发了匹配描述符应答Match_Desc_rsp通告匹配结果。具体代码如下:
程序代码:
case Match_Desc_rsp:
{
zAddrType_t dstAddr;
ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg );
if ( sapi_bindInProgress != 0xffff )
{
dstAddr.addrMode = Addr16Bit;
dstAddr.addr.shortAddr = pRsp->nwkAddr;
if ( APSME_BindRequest( sapi_epDesc.simpleDesc->EndPoint,
sapi_bindInProgress, &dstAddr, pRsp->epList[0] ) == ZSuccess )
{
osal_stop_timerEx(sapi_TaskID, ZB_BIND_TIMER);
osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 );
sapi_bindInProgress = 0xffff;
ZDP_IEEEAddrReq( pRsp->nwkAddr, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 );
zb_BindConfirm( sapi_bindInProgress, ZB_SUCCESS )
}
}
}
函数说明:
1)、ZDO_ActiveEndpointRsp_t *ZDO_ParseEPListRsp( zdoIncomingMsg_t *inMsg ) 说明:通过调用函数,对匹配结果进行处理。
参数说明:*inMsg --------------------------------接收到的信息
返回值:ZDO_ActiveEndpointRsp_t
2)、ZStatus_t APSME_BindRequest( byte SrcEndpInt, uint16 ClusterId,
zAddrType_t *DstAddr, byte DstEndpInt);
说明:应用支持子层绑定函数,通过该函数可以在两个设备间的应用层创建绑定,以原语APSME_BIND.confirm返回绑定结果。注意这里传递的地址是64位的扩展地址。
参数说明:SrcEndpInt ---------------------------源设备的端点
ClusterId ------------------------------簇ID
*DstAddr -----------------------------目标设备的地址
DstEndpInt ---------------------------目标设备的端点
由上述参数我们可以看出绑定时基于端点和簇的绑定
返回值:ZStatus_t状态值
3)、 afStatus_t ZDP_IEEEAddrReq( uint16 shortAddr, byte ReqType,
byte StartIndex, byte SecurityEnable )
说明:调用该函数可以由设备的网络地址获取设备的扩展地址,该消息以单播的形式发送给目的设备。目的设备接收到请求后将自己的扩展地址返回。
参数说明:shortAddr ----------------------------目标设备短地址
ReqType -----------------------------------相应类型
StartIndex ---------------------------------开始索引
SecurityEnable ---------------------------安全选项
返回值:afStatus_t状态值
绑定总结:
以上两种绑定机制,最终都是用函数APSME_BindRequest()创建绑定。不同的是,前者采用的目的地址是64位扩展地址,而后者采用的目的地址是16位网络地址。前者已知扩展地址,调用了ZDP_NwkAddrReq()函数获得目的设备短地址;后者利用描述匹配得到了短地址,然后调用了ZDP_IEEEAddrReq()函数,获取目的设备的扩展地址。
四、删除绑定
void zb_BindDevice ( uint8 create, uint16 commandId, uint8 *pDestination )
{
if ( create )//创建绑定条目
{
……
}
else//删除绑定条目 creat为FALSE
{
// 删除本地绑定条目中对应于commandId的绑定条目 BindingEntry_t *pBind;
// 在绑定表中查找对应于commandId的绑定条目
while ( pBind = bindFind( sapi_epDesc.simpleDesc->EndPoint, commandId, 0 ) )
{
bindRemoveEntry(pBind);
//删除找到的对应于commandId的绑定条目
}
osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 );
}
return;
}
ZDO_NWK_UPDATE_NV 函数说明:
1)、BindingEntry_t *bindFind( uint8 ep, uint16 clusterID, uint8 skipping )
说明:在绑定表中寻找对应于clusterID的绑定条目。
参数说明:ep ------------------------------------------端点
clusterID ---------------------------------簇ID
返回值:绑定条目指针
2)、byte bindRemoveEntry( BindingEntry_t *pBind )
说明:在绑定表中删除对应的绑定条目。
参数说明:*pBind -------------------------------------指向绑定条目的指针
返回值:TURE或者FALSE
3) 、byte osal_start_timerEx( byte taskID, UINT16 event_id, UINT16 timeout_value )
说明:通过该函数将定时触发对应任务id为taskID的事件event_id。定时时长为timeout_value。在timeout_value溢出后将触发事件event_id。
参数说明:taskID --------------------------------------任务
id event_id ------------------------------------事件
timeout_value -----------------------------溢出时间
返回值:ZSUCCESS或者NO_TIMER_AVAIL
删除绑定总结:在删除绑定过程中,首先调用函数bindFind()在绑定表中寻找对应于clusterID的特定的绑定条目,当找到要删除的条目后调用函数bindRemoveEntry()将寻找到的对应于clusterID的绑定条目删除。当上述步骤全部完成后,定时触发了事件ZDO_NWK_UPDATE_NV对网络状态进行更新。