实验二:Z-Stack 通信实验(传输学号)
一句话理解
本实验直接利用《3.4 ZigBee 3.0 通信实验》的工程与流程,把三种通信方式(点对点 / 广播 / 组播)在真实板子上跑起来,并把传输的数据改成你自己的学号——接收方屏幕上看到的是学号在"飞"。
前置知识
本实验完全基于 3.4 ZigBee 3.0 通信实验 一节的知识,请先按该章节完成:
- 设备初始化(
zclSampleSw_Init()):- 协调器:建网 +
NLME_PermitJoiningRequest(255)+ 启动广播/组播定时事件; - 路由器/终端:入网 +
aps_AddGroup()加入组 + 启动点对点定时事件;
- 协调器:建网 +
- 事件定义(
zcl_samplesw.h):SAMPLEAPP_P2P_EVT/SAMPLEAPP_BROADCAST_EVT/SAMPLEAPP_GROUPCAST_EVT; - 事件处理(
zclSampleSw_event_loop()):周期触发zclSampleSw_AF_P2P()/zclSampleSw_AF_Broadcast()/zclSampleSw_AF_Groupcast()发送; - 接收处理(
zclSampleSw_AF_RxProc()):按clusterId分类显示。
本实验的增量是:把发送的数据内容从 "P2P"/"Broadcast"/"Groupcast" 改为自己的学号,并在接收处理中显示/打印学号。
实验目的
- 掌握 ZigBee 三种通信方式(点对点 / 广播 / 组播)的工程实现(复用 3.4 章节);
- 掌握修改传输数据内容的方法(把示例数据替换为自己的学号);
- 能够在接收方屏幕/串口看到自己学号的传输结果。
实验器材
| 器材 | 数量 | 用途 |
|---|---|---|
| GEC2530 开发板 | 2 块 | 1 协调器(Mini 板)+ 1 路由器/终端(标准板) |
| ZigBee 仿真器 | 1 个 | 烧录程序 |
| USB 数据线 | 2 根 | 供电 + 串口 |
| IAR EW for 8051 | 1 套 | 编译工程 |
| 串口调试助手 | 1 个 | 查看接收到的学号 |
实验原理
三种通信方式与学号数据流
🔗
点对点 P2P
路由器/终端 → 协调器(0x0000)
定时发送【学号】
定时发送【学号】
📡
📢
广播 Broadcast
协调器 → 全网
定时发送【学号】
定时发送【学号】
📡
👥
组播 Groupcast
协调器 → 组21
定时发送【学号】
定时发送【学号】
- P2P 链路:终端把学号单播给协调器(目标地址
0x0000); - 广播链路:协调器把学号发给网络中所有节点;
- 组播链路:协调器把学号只发给加入了组 21 的设备。
修改点就两处
- 发送处:把
zclSampleSw_AF_P2P()/AF_Broadcast()/AF_Groupcast()中作为数据传入的字符串,改成你的学号; - 接收处:
zclSampleSw_AF_RxProc()中把接收到的学号显示在屏幕,并可用串口打印(呼应实验一的 Terminal 打印习惯)。
实验步骤
步骤 1:按 3.4 章节搭好工程
- 打开 Z-Stack 3.0.1 的 SampleSwitch 工程;
- 按 3.4 章节完成初始化代码、事件定义、事件处理、接收处理;
- 先编译下载验证:协调器收到 "P2P"、标准板收到 "Broadcast"/"Groupcast",三种通信方式全部跑通。
步骤 2:定义自己的学号
在应用层(zcl_samplesw.c 或 zcl_samplesw.h)定义学号数据:
c
// 学号(请换成你自己的学号!)
#define STUDENT_ID "202311040123"
#define STUDENT_ID_LEN (sizeof(STUDENT_ID)) // 含结尾 '\0',共 13 字节步骤 3:发送处改为学号
终端(或路由器)— 点对点发送学号给协调器:
c
// P2P Event
if ( events & SAMPLEAPP_P2P_EVT )
{
zclSampleSw_AF_P2P(0x0000, // 目标:协调器
CLUSTER_P2P,
STUDENT_ID_LEN, // 数据长度(学号长度+1)
(uint8 *)STUDENT_ID); // 数据内容:学号
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_P2P_EVT,
SAMPLEAPP_P2P_PERIOD);
return ( events ^ SAMPLEAPP_P2P_EVT );
}协调器 — 广播/组播学号:
c
// Broadcast event
if ( events & SAMPLEAPP_BROADCAST_EVT )
{
zclSampleSw_AF_Broadcast(CLUSTER_BROADCAST,
STUDENT_ID_LEN,
(uint8 *)STUDENT_ID); // 广播学号
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,
STUDENT_ID_LEN,
(uint8 *)STUDENT_ID); // 组播学号
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_GROUPCAST_EVT,
SAMPLEAPP_GROUPCAST_PERIOD);
return ( events ^ SAMPLEAPP_GROUPCAST_EVT );
}步骤 4:接收处显示 + 串口打印学号
在 zclSampleSw_AF_RxProc() 中,把示例的显示逻辑改为显示学号,并加串口打印:
c
static void zclSampleSw_AF_RxProc(afIncomingMSGPacket_t *MSGpkt)
{
static uint8 p2pCnt = 0;
static uint8 bcCnt = 0;
static uint8 gcCnt = 0;
switch( MSGpkt->clusterId )
{
case CLUSTER_P2P:
p2pCnt++;
// 屏幕显示:收到的学号 + 计数
HalLcdWriteString((char *)MSGpkt->cmd.Data, 3);
HalLcdWriteStringValue("Cnt:", p2pCnt, 10, 4);
// 串口打印学号到 PC 终端(可选)
HalUARTWrite(0, MSGpkt->cmd.Data, MSGpkt->cmd.DataLength);
break;
case CLUSTER_BROADCAST:
bcCnt++;
HalLcdWriteString((char *)MSGpkt->cmd.Data, 3);
HalLcdWriteStringValue("BC:", bcCnt, 10, 4);
HalUARTWrite(0, MSGpkt->cmd.Data, MSGpkt->cmd.DataLength);
break;
case CLUSTER_GROUPCAST:
gcCnt++;
HalLcdWriteString((char *)MSGpkt->cmd.Data, 3);
HalLcdWriteStringValue("GC:", gcCnt, 10, 4);
HalUARTWrite(0, MSGpkt->cmd.Data, MSGpkt->cmd.DataLength);
break;
default:
break;
}
}步骤 5:编译下载与验证
- 选择 RouterEB(或 EndDeviceEB) 编译下载到标准板;
- 选择 CoordinatorEB 编译下载到 Mini 板;
- 先给 Mini 板上电(建网),再给标准板上电(自动入网);
- 观察两块板的屏幕与串口:
| 板子 | 角色 | 期望现象 |
|---|---|---|
| Mini 板 | 协调器 | 屏幕/串口显示你的学号(来自终端的 P2P 数据,每秒一次,计数递增) |
| 标准板 | 路由器/终端 | 屏幕/串口显示你的学号(来自协调器的广播 + 组播数据) |
在线仿真环节: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 p2pCnt = 0, bcCnt = 0, gcCnt = 0;
switch( MSGpkt->clusterId )
{
case CLUSTER_P2P:
p2pCnt++;
// 输出到 Terminal I/O 窗口
printf("[P2P] %s Cnt:%d\r\n", MSGpkt->cmd.Data, p2pCnt);
break;
case CLUSTER_BROADCAST:
bcCnt++;
printf("[BC] %s Cnt:%d\r\n", MSGpkt->cmd.Data, bcCnt);
break;
case CLUSTER_GROUPCAST:
gcCnt++;
printf("[GC] %s Cnt:%d\r\n", MSGpkt->cmd.Data, gcCnt);
break;
default:
break;
}
}现象
进入调试后,Terminal I/O 窗口实时显示 [P2P] 202311040123 Cnt:1、[BC] 202311040123 Cnt:5……学号与计数一目了然,且不会被 LCD 截断。
注意
- Library 必须为 Full,否则
printf不可用; - Terminal I/O 只在**在线仿真调试会话(Debug)**中显示;
HalUARTWrite串口打印与printf可并存。
实验现象与检查点
| 检查点 | 期望现象 |
|---|---|
| 协调器屏幕 | 显示学号 + P2P 计数(每秒递增) |
| 标准板屏幕 | 显示学号 + 广播计数、组播计数 |
| 串口输出 | 两块板串口均周期性打印学号 |
| 学号正确性 | 显示内容与你的学号逐位一致 |
| 三种通信均通 | P2P、广播、组播计数都在增长 |
常见问题
- 屏幕只显示 4 个字符:LCD 单行显示宽度有限,学号超过 4 位时可用
HalLcdWriteStringValue()分段显示,或直接用串口打印完整学号; - 看不到 P2P 数据:确认终端发送目标地址为
0x0000(协调器),且簇 ID 两端一致; - 组播收不到:确认标准板已
aps_AddGroup()加入组 21(GROUP_ID),且协调器组播地址用afAddrGroup; - 乱码:串口波特率与固件一致(默认 115200)。
思考题
- 为什么 LCD 显示学号会截断?串口打印相比屏幕显示有什么优势?
- 广播和组播都发给标准板,
clusterId为什么能区分两种数据?(提示:CLUSTER_BROADCAST/CLUSTER_GROUPCAST) - 如果想把学号按"数字位"逐字节发送(如发送 12 个数字而不是字符串),应该怎么改长度与发送内容?
- 三种通信方式分别适合什么业务场景?结合 ZigBee 应用举例。
实验报告要求
- 说明本实验复用了 3.4 章节的哪些知识;
- 粘贴修改后的核心代码(发送处 + 接收处)及注释;
- 附两块板的屏幕/串口截图,标注显示的学号与计数;
- 记录三种通信方式的传输现象;
- 回答思考题;
- 写出遇到的问题及解决办法。