2.1 BDB 简介
ZigBee设备在相互发送数据之前,需要先组建网络。BDB(Base Device Behavior,设备基本行为)是ZigBee 3.0 的一个新特性,为各个ZigBee设备提供了一套统一的机制,让它们正确地组建ZigBee网络。BDB主要包含以下3方面的内容:
- Commissioning Modes:Commissioning模式,定义了ZigBee设备之间组网的基本规范
- BDB Security:定义了一些网络安全规范
- Reset Methods:开发者可以使用多个复位方法
在协议栈的BDB文件夹里面可以找到BDB相关的代码文件,如图所示。
由于BDB Security和Reset Methods这两部分,开发者一般接触的比较少,所以暂时不展开讲解。Commissioning Modes是ZigBee组网的核心内容,下节课将重点讲解。
2.2 BDB Commissioning Modes
本节课讲解的多个理论知识,理解起来并不容易,如果大家暂时不理解也没关系,随着后续的学习,相信大家会逐步加深理解!
Commissioning 可以直译为“调试”或“尝试运作”,这有点奇怪,所以本文暂不做翻译。读者可以笼统地把其理解为“组建网络”的意思,因此“Commissioning Modes”可笼统地理解为“组网模式”,但为了严谨性,本文仍使用Commissioning。
BDB提供了4种主要的Commissioning模式给开发者使用,分别是Network Steering、Network Formation、Finding and Binding(F & B)和Touchlink。
Network Steering
定义了设备如何加入到ZigBee网络中,具体方式如下:
- 如果设备还没有在ZigBee网络中,那么它们会寻找一个合适的ZigBee网络并加入到其中。
- 特别地,对于路由器类型的设备,在入网成功后,允许其它设备通过本设备来加入到这个ZigBee网络中。
所有需要加入到ZigBee网络中的设备都必须要支持Network Steering。
Network Formation
Network Formation 规定协调器类型的设备需要去建立一个中心信任的安全网络。这种网络的特点是所有需要加入到网络中的设备都需要经过信任中心的同意才能加入,而协调器本身就是这个信任中心。
类似地,对于路由器类型的设备,如果条件允许的话会创建一个分布式安全网络。这里暂时不展开讲解这种网络了。
所有的协调器类型设备都必须要支持 Network Formation ,而对于路由器类型的设备来说,这是可选的模式。
Finding and Binding(F & B)
顾名思义,Finding and Binding的意思就是发现与绑定,那么发现与绑定什么呢?
ZigBee 3.0是使用Cluster(集群)来描述设备的功能的。每种设备都有各自的功能,都有各自的一系列Cluster。这里的发现与绑定是指ZigBee设备的Cluster之间的相互发现、相互绑定。随着后续章节对Cluster深入地讲解,读者将会更深入地了解到其中的原理。
所有的ZigBee设备都必须要支持Finding and Binding(F & B)。
Touchlink
一般用于两个ZigBee设备之间直接进行通信,举个例子说明其原理。
假设现在有两个支持Toucklink的ZigBee设备:
- 一个是无线按钮,支持通过Touchlink的方式发送一个恢复出厂设置的指令;
- 另一个是灯,支持接收通过Touchlink方式发来的恢复出厂设置指令并执行相应的处理。
用户可以拿着这个按钮靠近这个灯,让两者相距约2cm,然后按下按钮发送指令,这个灯会收到指令并执行相应处理。
如果用户把按钮拿远一些测试,会发现灯收不到这个指令。如果用户把这个按钮拿去靠近一个新买的相同的灯测试,会发现这个灯也会收到这个指令。
从这个例子中可以总结出Touchlink具有下面这些特点:
- 通信距离短,约2cm。按钮向灯发送指令时,灯会通过检测按钮的网络信号强度来判断按钮的距离,从而决定是否处理该指令。
- 设备之间可以直接通信,可以把这个按钮拿去直接跟一个新买的相同的灯通信。
Touchlink用得相对比较少,ZigBee设备并不一定都需要支持Touchlink,开发者可以让自己开发的设备支持或不支持这个功能。
BDB Commissioning Modes API简介
虽然上述的Commissioning模式比较复杂,但是得益于Z-Stack 3.0 ,ZigBee设备的组网代码非常简单,只需要调用1个API就可以了。
打开配套的工程代码,可以在bdb_interface.h文件中找到BDB Commissioning Modes的API,如图所示。
这个函数需要传入一个模式参数,可以在bdb.h中找到由协议栈定义好的模式,如图所示。
于是,终于可以组建ZigBee网络了。首先,让协调器创建网络,代码如下:
bdb_StartCommissioning(//组建网络
BDB_COMMISSIONING_MODE_NWK_FORMATION | //支持Network Formation
BDB_COMMISSIONING_MODE_FINDING_BINDING //支持Finding and Binding(F & B)
);
然后,让路由器或终端设备加入网络中,代码如下:
bdb_StartCommissioning(//设备入网
BDB_COMMISSIONING_MODE_NWK_STEERING | //支持Network Steering
BDB_COMMISSIONING_MODE_FINDING_BINDING //支持Finding and Binding(F & B)
);
一般地,在实际的开发过程中,开发者只需要使用这个API就基本可以满足网络创建和设备入网的功能需求了。这是BDB的一大优点,使用简单!
2.3 ZigBee 3.0 组网实验
上节课学习了BDB Commissioning Modes基本原理之后,本节课讲解怎么样实现设备之间的网络组建。
裁剪应用层UI
协议栈裁剪是开发者需要掌握的一项技术,因为 Z-Stack 3.0 包含了丰富的功能,但是在实际的开发过程中,部分功能可能是冗余的,甚至会影响到开发过程。
打开本章节配套的工程代码,展开App层代码,如图所示。
红框中的两个文件是针对TI的评估板而设计的UI,在产品开发过程并不需要用到,而且还会影响其他的开发工作,因此需要把它们裁剪掉。右击文件,选择Remove,如图所示。
裁剪后如图所示。
zcl_samplesw.c文件中会调用到这些UI,也需要把其全部删除。点击Edit,然后选择Find and Replace→Find in Files…,如图所示。
在弹出的窗口中输入“UI_”,然后点击Find按钮,如图所示。
最后,在文件zclsamplesw.c中根据搜索结果,一个一个地把带“UI”的地方删除,如图所示。
编写设备组网代码
如同前面章节所述,设备组网代码非常简单:
- 第一步,调用bdb_StartCommissioning()让协调器创建网络。
- 第二步,还是调用bdb_StartCommissioning(),让其他设备加入到网络。
角色判断
在编译工程的时候,可以选择让编译出来的程序工作在协调器中、路由器中或者终端中,即选择设备在ZigBee网络中的角色。在代码中,可以利用宏ZDO_COORDINATOR来判断当前的工作模式,判断方法是如果宏ZDO_COORDINATOR被定义了,那么说明程序工作在协调器环境中,否则就是工作在路由器或者终端环境中。
编写代码
在zcl_samplesw.c的初始化函数zclSampleSw_Init()中添加如下代码:
#ifdef ZDO_COORDINATOR
bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_FORMATION |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
NLME_PermitJoiningRequest(255);
#else
bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_STEERING |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
#endif
代码比较简单,如果是协调器那么就让协调器创建网络,如果是路由器或者终端那么就加入到网络。
其中的NLME_PermitJoiningRequest(255)是允许其他设备加入到由本协调器创建的网络中,这一点在介绍Network Formation时也有讲述过,也就是设备要经过信任中心的同意才能加入到网络中。参数255表示一直允许,如果改为0则表示一直不允许;如果改为1254表示在1254秒内允许。
处理设备入网失败
然而,设备入网的过程可能会受到多种因素的影响,从而导致入网失败,例如在入网过程信号受到干扰等。开发者需要学会处理设备入网失败。一种简单而有效的办法就是在设备入网失败后,让设备自动重新尝试入网。
在zcl_samplesw.c文件中可以找到一个zclSampleSw_ProcessCommissioningStatus()函数,这个函数的作用是处理Commissioning结果,例如协调器创建网络是否成功、设备是否成功加入到网络等。
可以在这个函数中根据Commissioning的结果采取相应的处理,例如在设备入网失败后重新调用bdb_StartCommissioning()来再次入网。
可以用事件的机制来实现这个过程,当设备入网失败后就启动一个事件,让程序在1s后重新尝试入网。Z-Stack 3.0的事件使用方法在前面的章节已经讲解过了,这里就不再讲了,而是直接使用。
1.在头文件zcl_samplesw.h定义一个用户事件,并且定义重新入网的时间间隔,代码如下:
/* 协调器 */
#ifdef ZDO_COORDINATOR
/* 路由器或者终端 */
#else
// 重新加入事件
#define SAMPLEAPP_REJOIN_EVT 0x0100
// 时间间隔:1000ms(1秒)
#define SAMPLEAPP_REJOIN_PERIOD 1000
#endif
由于设备入网是针对路由器或者终端类型的ZigBee设备的,因此同样地使用了宏来判断程序角色。
2.在函数zclSampleSw_ProcessCommissioningStatus中找到找到入网失败处理位置,如图所示。
3.在入网失败的位置添加如下代码:
/* 协调器 */
#ifdef ZDO_COORDINATOR
/* 路由器或者终端 */
#else
/* 启动重新加入事件 */
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_REJOIN_EVT,
SAMPLEAPP_REJOIN_PERIOD);
#endif
代码中,首先判断设备角色,然后启动刚才定义的事件。
开发者也可以在入网成功的位置添加一些代码,例如在显示器上显示入网成功的信息等。
3.在应用层事件处理函数zclSampleSw_event_loop()中处理该事件,也就是重新启动入网,代码如下。
#ifdef ZDO_COORDINATOR
#else
if ( events & SAMPLEAPP_REJOIN_EVT )//如果事件类型为重新加入网络事件
{
/* 重新加入网络 */
bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_STEERING |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
return ( events ^ SAMPLEAPP_REJOIN_EVT );
}
#endif
编译下载
角色选择
前文中多次提及到设备的网络角色,其实在编译代码的时候可以选择不同的网络角色,从而编写不同角色的程序。
在工程中单击选项卡,可以看到如图所示的选择框,可以选择待开发设备的对应的工作模式。
其中CoordinatorEB、RouterEB、EndDeviceEB的含义分别是:
(1)CoordinatorEB:协调器角色设备。
(2)RouterEB:路由器角色设备。
(3)EndDeviceEB:终端角色设备。
除了此之外,还有EndDeviceEB-OTAClient和RouterEB-OTAClient,其实这两个指的是带OTA功能的终端设备和带OTA功能的路由设备。
使用角色
首先选择CoordinatorEB,如图所示,编译完成后下载到其中一块开发板中,建议使用ZigBee Mini板。此开发板在ZigBee网络中会充当协调器角色设备。
接着选择RouterEB,如图所示。同样编译完成后下载到另一个板子中,建议使用ZigBee标准板。此开发板ZigBee网络中即会充当路由器角色设备。
注意:切换工程后,同样需要在工程的预编译那里开启LED、串口、显示屏等功能,并且把屏幕的路径加入到工程中。
仿真调试
给协调器上电后,协调器会自动创建网络。创建网络成功后,在屏幕上可以看到网络ID(PanID),如图所示。
接着给路由器或终端上电后,它会自动加入到由协调器创建的网络中,加入后可以在屏幕上看到被分配到的网络地址和父节点的网络地址,路由器或终端设备加入后如图所示。
- 路由器
- 终端