3.4 ZigBee 3.0 通信实验
一句话理解
前几节学 API,这一节把代码跑起来:一台协调器 + 一台路由器(或终端),让三种通信方式(点对点 / 广播 / 组播)在真实板子上"飞"起来。
实验简介
实验设备:一个协调器 + 一个路由器(或终端)。
实验内容:
设备初始化:让每台设备"各就各位"
打开配套的 SampleSwitch 工程,在 zcl_samplesw.c 的 zclSampleSw_Init() 初始化函数末尾添加代码:
#ifdef ZDO_COORDINATOR
bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_FORMATION |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
NLME_PermitJoiningRequest(255);
// Broadcast
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_BROADCAST_EVT,
SAMPLEAPP_BROADCAST_PERIOD);
// groupcast
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_GROUPCAST_EVT,
SAMPLEAPP_GROUPCAST_PERIOD);
#else
bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_STEERING |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
// Add group
aps_Group_t group = {
.ID = GROUP_ID,
.name = "",
};
aps_AddGroup(SAMPLESW_ENDPOINT, &group);
// P2P
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_P2P_EVT,
SAMPLEAPP_P2P_PERIOD);
#endif协调器角色做了什么
事件和周期的定义在 zcl_samplesw.h 中:
// 广播事件
#define SAMPLEAPP_BROADCAST_EVT 0x0080
#define SAMPLEAPP_BROADCAST_PERIOD 1000
// 组播事件
#define SAMPLEAPP_GROUPCAST_EVT 0x0100
#define SAMPLEAPP_GROUPCAST_PERIOD 1000终端(或路由器)角色做了什么
创建组和组 ID 的代码:
// 创建组
aps_Group_t group = {
.ID = GROUP_ID,
.name = "",
};
// 组 ID 定义(在 zcl_samplesw.c 中)
#define GROUP_ID 21
// 加入组:端点号 + 待加入的组
aps_AddGroup( SAMPLESW_ENDPOINT, &group );P2P 事件定义(在 zcl_samplesw.h 中):
// P2P
#define SAMPLEAPP_P2P_EVT 0x0100
#define SAMPLEAPP_P2P_PERIOD 1000协调器的事件处理:广播 + 组播
在 zclSampleSw_event_loop() 事件处理函数中添加广播和组播事件处理:
// Broadcast event
if ( events & SAMPLEAPP_BROADCAST_EVT )
{
zclSampleSw_AF_Broadcast(CLUSTER_BROADCAST,
10, "Broadcast");
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_BROADCAST_EVT,
SAMPLEAPP_BROADCAST_PERIOD);
return ( events ^ SAMPLEAPP_BROADCAST_EVT );
}
// Groupcast event
if ( events & SAMPLEAPP_GROUPCAST_EVT )
{
zclSampleSw_AF_Groupcast(GROUP_ID,
CLUSTER_GROUPCAST,
10, "Groupcast");
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_GROUPCAST_EVT,
SAMPLEAPP_GROUPCAST_PERIOD);
return ( events ^ SAMPLEAPP_GROUPCAST_EVT );
}广播事件处理逻辑:调用 zclSampleSw_AF_Broadcast() 发出广播 → 再调用 osal_start_timerEx() 重新产生广播事件(形成周期发送)。组播事件同理。
其中用到的 Cluster ID(在 zcl_samplesw.c 中自定义):
#define CLUSTER_P2P 0
#define CLUSTER_BROADCAST 1
#define CLUSTER_GROUPCAST 2提示
这 3 个 Cluster ID 本质就是宏定义,值分别为 0、1、2。它们用来标识"这条数据是用哪种方式发来的",接收方据此分类处理。
终端(或路由器)的事件处理:点对点
同样在 zclSampleSw_event_loop() 中添加点对点事件处理:
// P2P Event
if ( events & SAMPLEAPP_P2P_EVT )
{
zclSampleSw_AF_P2P(0x0000,
CLUSTER_P2P,
4, "P2P");
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_P2P_EVT,
SAMPLEAPP_P2P_PERIOD);
return ( events ^ SAMPLEAPP_P2P_EVT );
}其中 0x0000 是协调器的网络地址(终端定时向协调器发数据)。发送后同样用 osal_start_timerEx() 重新产生事件,形成周期发送。
接收和处理数据:数据怎么"回来"
发送讲完了,接收同样重要。ZigBee 设备组网成功后,收到数据时系统会产生 AF_INCOMING_MSG_CMD 事件,通知应用层"来数据了"。
在 zclSampleSw_event_loop() 中,系统事件处理代码已经包含了对 AF_INCOMING_MSG_CMD 的识别:
uint16 zclSampleSw_event_loop( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
(void)task_id; // intentionally unreferenced parameter
if ( events & SYS_EVENT_MSG )
{
while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( zclSampleSw_TaskID )) )
{
switch ( MSGpkt->hdr.event )
{
case AF_INCOMING_MSG_CMD:
zclSampleSw_AF_RxProc( MSGpkt ); // ← 收到数据,交给数据处理函数
break;
default:
break;
}
}
// Release the memory
osal_msg_deallocate( (uint8 *)MSGpkt );
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
...
}开发者只需在收到事件后做自己的处理。例程已经定义好数据处理函数 zclSampleSw_AF_RxProc():
/*
* @param MSGpkt 接收到数据
*/
static void zclSampleSw_AF_RxProc(afIncomingMSGPacket_t *MSGpkt)
{
/* 计数器:记录接收到的点对点通信数据包个数 */
static uint8 p2pCnt = 0;
/* 计数器:记录接收到的广播通信数据包个数 */
static uint8 bcCnt = 0;
/* 计数器:记录接收到的组播通信数据包个数 */
static uint8 gcCnt = 0;
switch( MSGpkt->clusterId ) // 根据 Cluster ID 判断数据包类型
{
case CLUSTER_P2P:
p2pCnt++; // 接收到 P2P 数据包,计数
// 把接收到的数据和计数显示在屏幕上
HalLcdWriteStringValue((char *)MSGpkt->cmd.Data, p2pCnt, 10, 3);
break;
case CLUSTER_BROADCAST:
bcCnt++; // 接收到广播数据包,计数
HalLcdWriteStringValue((char *)MSGpkt->cmd.Data, bcCnt, 10, 3);
break;
case CLUSTER_GROUPCAST:
gcCnt++; // 接收到组播数据包,计数
HalLcdWriteStringValue((char *)MSGpkt->cmd.Data, gcCnt, 10, 4);
break;
default:
break;
}
}接收逻辑用一句话概括:看 clusterId 认"快递种类"(P2P / 广播 / 组播),分类计数并显示在屏幕上。
仿真调试
- 选择 RouterEB(或 EndDeviceEB) 角色编译工程,把固件下载到标准板;
- 选择 CoordinatorEB 角色编译工程,把固件下载到 Mini 板;
- 给 Mini 板上电 → Mini 板自动创建网络;
- 给标准板上电 → 自动加入 Mini 板创建的网络;
- 两块板都会收到数据包:
- Mini 板(协调器):屏幕显示收到的 P2P 数据包数量(终端定时发来的点对点数据);
- 标准板(路由器/终端):屏幕显示 广播数据包数量 和 组播数据包数量(协调器定时发来的)。
实验现象小结
| 板子 | 角色 | 屏幕能看到 |
|---|---|---|
| Mini 板 | 协调器 | P2P 数据包数量(来自终端) |
| 标准板 | 路由器/终端 | 广播数据包数量 + 组播数据包数量(来自协调器) |
数据每 1 秒发一次,屏幕上的数字会不停增长——这就是"数据在飞"的证据!
内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第四部分·3.4 ZigBee 3.0 通信实验,原站链接
本章自测
一、单项选择题(每题 4 分,共 40 分)
本实验使用的设备组合是(A. 一个协调器 + 一个路由器(或终端) B. 两个协调器 C. 三个终端 D. 一个路由器 + 一个终端) 答案:A
协调器在初始化时调用
NLME_PermitJoiningRequest(255)的作用是(A. 创建网络 B. 允许其他设备加入网络 C. 加入别人的网络 D. 关闭网络) 答案:B终端(或路由器)入网时,
bdb_StartCommissioning()使用的 commissioning 模式是(A. NWK_FORMATION B. NWK_STEERING(+ FINDING_BINDING) C. 不调用该函数 D. 仅 BDB_COMMISSIONING_MODE_TOUCHLINK) 答案:B本实验中终端加入的组
GROUP_ID被定义为(A. 8 B. 21 C. 0xFFFF D. 0x0000) 答案:B终端定时向协调器发送点对点数据时,目标网络地址填的是(A. 0x0000(协调器) B. 0x0EF1 C. 0xFFFF D. 组 ID 21) 答案:A
本实验自定义的三个 Cluster ID
CLUSTER_P2P / CLUSTER_BROADCAST / CLUSTER_GROUPCAST的值分别是(A. 0 / 1 / 2 B. 1 / 2 / 3 C. 0x0006 / 0x0008 / 0x0009 D. 8 / 21 / 255) 答案:AZigBee 设备组网成功后收到数据时,系统会通知应用层产生的事件是(A. SAMPLEAPP_P2P_EVT B. AF_INCOMING_MSG_CMD C. SYS_EVENT_MSG D. KEY_EVT) 答案:B
在数据处理函数
zclSampleSw_AF_RxProc()中,用来区分数据包是 P2P / 广播 / 组播的依据是(A. 源网络地址 B.MSGpkt->clusterIdC. 端点号 D. 数据长度) 答案:B关于实验中的周期发送,下列说法正确的是(A. 发送一次后就停止 B. 在事件处理末尾再次调用
osal_start_timerEx()重新产生事件,形成周期发送,周期约 1000ms C. 周期固定为 100ms 且不可改 D. 由硬件自动重发) 答案:B仿真下载与现象中,下列对应关系正确的是(A. Mini 板跑 CoordinatorEB,屏幕显示 P2P 数据包数量;标准板跑 RouterEB,屏幕显示广播 + 组播数量 B. Mini 板跑 RouterEB,标准板跑 CoordinatorEB C. 两块板都只显示 P2P 数量 D. 标准板显示 P2P 数量,Mini 板显示组播数量) 答案:A
二、问答题(每题 20 分,共 60 分)
- 题干:本实验中协调器角色与终端(路由器)角色在初始化阶段分别做了哪些主要工作? 参考答案要点:
- 协调器:
bdb_StartCommissioning()以 NWK_FORMATION 模式创建网络;调用NLME_PermitJoiningRequest(255)允许入网;启动广播事件和组播事件定时器。 - 终端 / 路由器:
bdb_StartCommissioning()以 NWK_STEERING 模式加入网络;用aps_AddGroup()创建并加入指定组(GROUP_ID=21);启动点对点事件定时器。 - 三种事件周期均为 1000ms。
- 题干:设备接收到 AF 数据后的处理流程是怎样的?开发者主要做哪部分工作? 参考答案要点:
- 收到数据后系统产生
AF_INCOMING_MSG_CMD事件。 event_loop()在 SYS_EVENT_MSG 中取出消息,识别该事件并调用zclSampleSw_AF_RxProc()。- 在 RxProc 中根据
MSGpkt->clusterId判断数据包类型(P2P / 广播 / 组播),分别计数并用HalLcdWriteStringValue()显示。 - 开发者只需在收到事件后编写自己的分类处理逻辑。
- 题干:实验最终两块板子上分别能看到什么现象?数据发送周期是多少? 参考答案要点:
- Mini 板(协调器):屏幕显示收到的 P2P 数据包数量(来自终端定时发来的点对点数据)。
- 标准板(路由器 / 终端):屏幕显示广播数据包数量和组播数据包数量(来自协调器定时发来的数据)。
- 数据每 1 秒(1000ms)发一次,屏幕上的计数数字会不断增长。