zigbee binding



干货!!精讲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 对网络状态进行更新。

转自:http://bbs.elecfans.com/jishu_550869_1_7.html