实验三:Z-Stack 广播、组播通信实验(多节点)
一句话理解
本实验延续《3.4 ZigBee 3.0 通信实验》的工程,把节点扩展到 3 个以上,重点验证两种通信模式的行为差异:广播 = "群发短信给所有人",组播 = "只拉一个群聊"。谁的屏幕上出现数据,一眼就能看出用的是哪种模式。
前置知识
本实验基于 3.4 ZigBee 3.0 通信实验 的工程与代码,并建议先完成 实验二:通信实验(学号传输)。请先掌握:
- 设备初始化(
zclSampleSw_Init()):协调器建网 + 定时事件,节点入网 +aps_AddGroup()入组 + P2P 定时事件; - 三种通信的发送封装:
zclSampleSw_AF_P2P()/zclSampleSw_AF_Broadcast()/zclSampleSw_AF_Groupcast(); - 接收处理(
zclSampleSw_AF_RxProc()):按clusterId分类显示; - 3.4 章节中节点加入的组为 组 21(
GROUP_ID)。
本实验的增量是:扩展到 3 个以上节点,让不同节点加入不同的组,从而在同一个网络中直观对比"广播谁都收得到、组播只有组员收得到"。
实验目的
- 理解通讯模式:广播、组播(及单播)的行为差异与适用场景;
- 掌握相关网络参数配置:组 ID、端点、信道、PAN ID 对通信的影响;
- 修改 Z-Stack 协议栈程序,实现 3 个节点以上的广播通信。
实验器材
| 器材 | 数量 | 用途 |
|---|---|---|
| GEC2530 开发板 | 3 块以上 | 1 协调器 + 2 个以上路由器/终端 |
| ZigBee 仿真器 | 1 个 | 烧录程序 |
| USB 数据线 | 若干 | 供电 + 串口 |
| IAR EW for 8051 | 1 套 | 编译工程 |
实验原理
三种通信模式对比
📞
单播
点对点专线
目标:指定短地址
只有目标节点收到
目标:指定短地址
只有目标节点收到
📢
广播
大喇叭喊话
目标:0xFFFF 全网
网络内所有节点收到
目标:0xFFFF 全网
网络内所有节点收到
👥
组播
群聊
目标:组 ID
只有加入该组的节点收到
目标:组 ID
只有加入该组的节点收到
多节点分组设计(本实验核心)
🏛️
协调器(发送方)
发 ①广播 ②组播到组21 ③组播到组22
(三个定时事件,每秒一次)
(三个定时事件,每秒一次)
📡 广播 0xFFFF
🟢
节点 A(加入组 21)
收到:广播 ✅ 组播21 ✅ 组播22 ❌
📡 组播21
🔵
节点 B(加入组 22)
收到:广播 ✅ 组播21 ❌ 组播22 ✅
- 广播(0xFFFF):A、B 都能收到 → 验证"全网可达";
- 组播到组 21:只有 A 收到,B 收不到 → 验证"只有组员收到";
- 组播到组 22:只有 B 收到,A 收不到 → 反向验证。
实验步骤
步骤 1:按 3.4 章节搭好工程
- 打开 Z-Stack 3.0.1 的 SampleSwitch 工程;
- 按 3.4 章节完成初始化、事件定义、事件处理、接收处理(含实验二的学号修改,可继续用学号作为传输数据)。
步骤 2:为不同节点配置不同的组
3.4 章节中所有节点加入的都是组 21。本实验让节点 A 加入组 21、节点 B 加入组 22:
c
// 节点 A:加入组 21(保持 3.4 章节原样)
#define GROUP_ID 21
// 节点 B:改为加入组 22(编译节点 B 固件前修改)
#define GROUP_ID 22初始化代码不变,仍然是:
c
// 节点 A / B 初始化中创建并加入自己的组
aps_Group_t group = {
.ID = GROUP_ID, // 21 或 22,取决于编译哪个固件
.name = "",
};
aps_AddGroup(SAMPLESW_ENDPOINT, &group);步骤 3:协调器增加"第二个组"的组播发送
在 zclSampleSw_event_loop() 中,为组 22 增加一个组播定时事件(组 21 沿用 3.4 的 SAMPLEAPP_GROUPCAST_EVT):
c
// 头文件中新增:组 22 组播事件
#define SAMPLEAPP_GROUP22_EVT 0x0200
#define SAMPLEAPP_GROUP22_PERIOD 1000
#define GROUP22_ID 22
// 初始化中启动事件(协调器)
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_GROUP22_EVT,
SAMPLEAPP_GROUP22_PERIOD);事件处理中发送到组 22:
c
// Group 22 event
if ( events & SAMPLEAPP_GROUP22_EVT )
{
zclSampleSw_AF_Groupcast(GROUP22_ID, // 目标组:22
CLUSTER_GROUP22,
STUDENT_ID_LEN,
(uint8 *)STUDENT_ID); // 数据:学号
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_GROUP22_EVT,
SAMPLEAPP_GROUP22_PERIOD);
return ( events ^ SAMPLEAPP_GROUP22_EVT );
}在 zcl_samplesw.c 中定义新的簇 ID:
c
#define CLUSTER_GROUP22 3 // 用于区分"发往组22"的数据步骤 4:接收处分类显示
在 zclSampleSw_AF_RxProc() 中增加 CLUSTER_GROUP22 分支,并保留原有分类:
c
case CLUSTER_GROUP22:
gcCnt++;
HalLcdWriteString((char *)MSGpkt->cmd.Data, 3); // 显示学号
HalLcdWriteStringValue("G22:", gcCnt, 10, 4); // 显示组22计数
HalUARTWrite(0, MSGpkt->cmd.Data, MSGpkt->cmd.DataLength); // 串口打印
break;步骤 5:编译下载与验证
- 编译 CoordinatorEB → 协调器(Mini 板);
- 编译 RouterEB(组21) → 节点 A(标准板);
- 编译 EndDeviceEB(组22) → 节点 B(另一块板);
- 上电顺序:协调器 → 节点 A → 节点 B(全部入网);
- 观察三块板屏幕/串口:
| 板子 | 广播(0xFFFF) | 组播21 | 组播22 |
|---|---|---|---|
| 协调器 | 收不到 | 收不到 | 收不到 |
| 节点 A(组21) | ✅ 收到 | ✅ 收到 | ❌ 不收 |
| 节点 B(组22) | ✅ 收到 | ❌ 不收 | ✅ 收到 |
在线仿真环节:printf + Terminal I/O 查看辅助信息
在线仿真(连接仿真器、进入 C-SPY 调试会话)过程中,程序里的 printf 输出会实时显示在 IDE 界面的 Terminal I/O 窗口——无需外接串口线,是调试阶段查看辅助信息最直接的方式。
配置步骤(在线仿真)
- 接线:仿真器连接开发板与 PC(在线仿真必需);
- Project → Options → General Options → Library Configuration:Library 选择 Full(完整库,
printf才可用); - 点击 Debug(Ctrl+D) 进入在线仿真调试会话(C-SPY 连接仿真器);
- 菜单 View → Terminal I/O 打开 IDE 界面上的 Terminal I/O 窗口;
- 全速运行程序,
printf辅助信息实时显示在该窗口。
代码改造:接收处用 printf 分类打印
在 zclSampleSw_AF_RxProc() 中补充(或改用)printf,让三类数据带标签输出:
c
#include <stdio.h>
static void zclSampleSw_AF_RxProc(afIncomingMSGPacket_t *MSGpkt)
{
static uint8 bcCnt = 0, g21Cnt = 0, g22Cnt = 0;
switch( MSGpkt->clusterId )
{
case CLUSTER_BROADCAST: // 广播(全网)
bcCnt++;
printf("[BC] %s Cnt:%d\r\n", MSGpkt->cmd.Data, bcCnt);
break;
case CLUSTER_GROUPCAST: // 组播到组 21
g21Cnt++;
printf("[G21] %s Cnt:%d\r\n", MSGpkt->cmd.Data, g21Cnt);
break;
case CLUSTER_GROUP22: // 组播到组 22
g22Cnt++;
printf("[G22] %s Cnt:%d\r\n", MSGpkt->cmd.Data, g22Cnt);
break;
default:
break;
}
}现象
- 节点 A(组21)的 Terminal I/O:只有
[BC]和[G21]在增长,看不到[G22]; - 节点 B(组22)的 Terminal I/O:只有
[BC]和[G22]在增长,看不到[G21]。
三块板各开一个调试会话,谁收到什么一目了然。
注意
- Library 必须为 Full,否则
printf不可用; - Terminal I/O 只在**在线仿真调试会话(Debug)**中显示;
- 多板同时调试时,每个调试会话对应各自的 Terminal I/O 窗口。
实验现象与检查点
| 检查点 | 期望现象 |
|---|---|
| 广播后 | 节点 A、B 都显示学号 + 广播计数增长 |
| 组播到组 21 | 只有节点 A 显示(G21 计数增长) |
| 组播到组 22 | 只有节点 B 显示(G22 计数增长) |
| 三台设备 PAN ID | 完全一致(同一网络,呼应实验一) |
| 节点 A / B | 信道一致,均正常入网 |
常见问题
- 节点 B 收不到组播22:确认节点 B 编译时
GROUP_ID已改为 22,且协调器组播目标为GROUP22_ID; - 广播谁都没收到:检查协调器是否
NLME_PermitJoiningRequest(255)且节点已入网;广播地址为0xFFFF时休眠终端收不到,可用0xFFFD(发给所有非休眠设备)代替; - 计数混在一起:A 收到广播和组播21 时屏幕行会互相覆盖,建议用串口打印区分(在打印中标注 BC/G21/G22);
- PAN ID 不一致:附近有其他协调器时固定
ZDAPP_CONFIG_PAN_ID。
思考题
- 广播、组播、单播分别适合什么场景?为什么 ZigBee 中广播地址
0xFFFF不发给休眠终端? - 如果节点 B 同时加入组 21 和组 22(
aps_AddGroup调用两次),它能收到哪些数据? - 一个节点最多能加入多少个组?组的
endpoint字段有什么作用? - 结合实验一:组播能否替代广播实现"只让指定区域节点收到数据"?网络参数(PAN ID/信道)在其中起什么作用?
实验报告要求
- 说明本实验基于 3.4 章节的哪些代码,新增了什么;
- 粘贴分组配置、组22 事件、接收处理的代码及注释;
- 附三块板的屏幕/串口截图,用表格记录"广播/组播21/组播22"的接收情况;
- 记录三台设备的网络参数(PAN ID、信道)并分析;
- 回答思考题;
- 写出遇到的问题及解决办法。