5.3 ZCL 开关命令收发实验
一句话理解
本实验把 5.1 / 5.2 的"接收套路"和"发送套路"串起来:智能插座入网 → 广播地址 → 网关收到地址后周期发 On/Off → 插座收到命令执行开关。
实验简介
- 设备:一个网关(协调器) + 一个智能插座(终端或路由器)
- 内容:智能插座加入网络后,网关自动周期性地向它发送 On / Off 指令,控制插座开关。
智能插座开发
打开 zcl_samplesw.c,在初始化函数 zclSampleSw_Init() 中找到如下代码:
#ifdef ZDO_COORDINATOR
// 协调器角色
zclSampleSw_InitUart();
ZDO_RegisterForZDOMsg ( zclSampleSw_TaskID, Device_annce );
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 );
zclSampleSw_DeviceAnnce(); // ← 向整个网络广播本设备地址
#endifzclSampleSw_DeviceAnnce() 是自定义函数,作用是向整个网络广播一个包含本设备地址的数据包,让协调器知道"我来了、我在哪"。定义如下:
static void zclSampleSw_DeviceAnnce( void )
{
ZDP_DeviceAnnce(
NLME_GetShortAddr(), // 获取本设备网络地址(短地址)
NLME_GetExtAddr(), // 获取本设备物理地址(MAC 地址)
ZDO_Config_Node_Descriptor.CapabilityFlags, // 能力标志(暂忽略)
0 // 暂忽略
);
}它调用了协议栈 API ZDP_DeviceAnnce()——直接套用即可实现广播本设备地址。
处理指令:注册 On/Off 回调
在 zcl_samplesw.c 的命令回调表 zclSampleSw_CmdCallbacks 中注册回调函数:
static zclGeneral_AppCallbacks_t zclSampleSw_CmdCallbacks =
{
zclSampleSw_BasicResetCB, // Basic Cluster Reset
NULL, // Identify Trigger Effect
#ifdef ZDO_COORDINATOR
NULL, // 协调器不处理 On/Off 命令
#else
zclSampleSw_OnOffCB, // 终端:On/Off 命令处理函数
#endif
...
};zclSampleSw_OnOffCB 函数定义
static void zclSampleSw_OnOffCB( uint8 cmd )
{
if ( cmd == COMMAND_ON ) // 收到"打开"命令
{
HalLcdWriteString( "Set ON", 4 ); // 屏幕打印提示
HalLedSet( HAL_LED_ALL, HAL_LED_MODE_ON ); // 点亮所有 LED
}
else if ( cmd == COMMAND_OFF ) // 收到"关闭"命令
{
HalLcdWriteString( "Set OFF", 4 ); // 屏幕打印提示
HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );// 熄灭所有 LED
}
}收到命令 → 判断是 ON 还是 OFF → 打印屏幕 + 开关 LED。
网关开发
网关做两件事:接收智能插座广播的地址 + 周期发送 On/Off 指令。
① 接收地址信息
协调器收到插座广播(Annce)的地址消息时,产生系统事件 ZDO_CB_MSG,在事件处理函数中处理:
// 在 zclSampleSw_event_loop() 的 switch 中:
#ifdef ZDO_COORDINATOR
case ZDO_CB_MSG:
zclSampleSw_processZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
break;
#endif事件处理函数 zclSampleSw_processZDOMsgs 定义:
static void zclSampleSw_processZDOMsgs( zdoIncomingMsg_t *pMsg )
{
switch ( pMsg->clusterID ) // 判断消息中的 Cluster ID
{
case Device_annce: // 设备入网广播
{
// 保存目标设备(插座)的网络地址到全局变量
zclSampleSw_OnOffTestAddr = pMsg->srcAddr.addr.shortAddr;
// 屏幕显示节点地址
HalLcdWriteStringValue( "Node:", pMsg->srcAddr.addr.shortAddr, 16, 3 );
HalLcdWriteString( "On/Off Test...", 4 );
// 周期产生发送 On/Off 指令的事件
osal_start_timerEx( zclSampleSw_TaskID,
SAMPLEAPP_ONOFF_TEST_EVT, // 事件
SAMPLEAPP_ONOFF_TEST_PERIOD ); // 周期
}
break;
default:
break;
}
}② 处理 SAMPLEAPP_ONOFF_TEST_EVT 事件
在 zclSampleSw_event_loop() 中添加:
#ifdef ZDO_COORDINATOR
if ( events & SAMPLEAPP_ONOFF_TEST_EVT )
{
zclSampleSw_OnOffTest(); // 发送 On/Off 命令给目标设备
// 重新启动定时器,形成周期发送
osal_start_timerEx( zclSampleSw_TaskID,
SAMPLEAPP_ONOFF_TEST_EVT,
SAMPLEAPP_ONOFF_TEST_PERIOD );
return ( events ^ SAMPLEAPP_ONOFF_TEST_EVT );
}
#endif③ 发送 On/Off 指令
zclSampleSw_OnOffTest() 函数定义:
static void zclSampleSw_OnOffTest( void )
{
afAddrType_t destAddr; // 目标设备地址
static uint8 txID = 0; // 数据包序号
static bool on = true; // 当前期望的开关状态
destAddr.endPoint = SAMPLESW_ENDPOINT; // 端点号
destAddr.addrMode = Addr16Bit; // 16 位网络地址(P2P)
destAddr.addr.shortAddr = zclSampleSw_OnOffTestAddr; // 插座网络地址
if ( on ) // 期望插座"开"
{
HalLcdWriteString( "Command: ON", 4 );
zclGeneral_SendOnOff_CmdOn( // 发送"打开"命令
SAMPLESW_ENDPOINT, // 源端点
&destAddr, // 目标地址
TRUE, // 簇限定命令
txID++ ); // 序号
}
else // 期望插座"关"
{
HalLcdWriteString( "Command: OFF", 4 );
zclGeneral_SendOnOff_CmdOff( // 发送"关闭"命令
SAMPLESW_ENDPOINT,
&destAddr,
TRUE,
txID++ );
}
on = !on; // 反转期望状态(开→关→开…)
}仿真调试
- 编译协调器工程,烧录到一块开发板(充当网关);
- 编译终端(或路由器)工程,烧录到另一块开发板(充当智能插座);
- 先后给网关和智能插座供电;
- 智能插座自动加入网关创建的网络,随后插座屏幕会显示网关发来的命令:
- 网关屏幕:
Command: ON→Command: OFF交替显示(每周期反转一次); - 插座屏幕:
Set ON(LED 全亮)→Set OFF(LED 全灭)交替显示。
实验现象小结
| 设备 | 屏幕显示 | 现象 |
|---|---|---|
| 网关 | Command: ON / OFF 交替 | 周期发送开关命令 |
| 插座 | Set ON / Set OFF 交替 | LED 同步点亮 / 熄灭 |
看到 LED 跟着命令"呼吸"地亮灭,就是 ZCL 命令收发成功了!
内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第四部分·5.3 ZCL 开关命令收发实验,原站链接
本章自测
一、单项选择题(每题 4 分,共 40 分)
本实验采用的硬件设备组合是(A. 两个终端设备 B. 一个网关(协调器)+ 一个智能插座(终端或路由器) C. 两个协调器 D. 两个路由器) 答案:B
智能插座加入网络后,用于向整个网络广播自身地址的自定义函数是(A.
zclSampleSw_OnOffTest()B.zclSampleSw_DeviceAnnce()C.zclSampleSw_OnOffCB()D.bdb_StartCommissioning()) 答案:BzclSampleSw_DeviceAnnce()内部直接套用的协议栈 API 是(A.ZDP_DeviceAnnce()B.NLME_GetShortAddr()C.HalLedSet()D.osal_start_timerEx()) 答案:A在本实验的回调表
zclSampleSw_CmdCallbacks中,协调器角色在 On/Off 命令位置填的是(A.zclSampleSw_OnOffCBB.NULL(协调器不处理 On/Off 命令) C.zclSampleSw_BasicResetCBD. 任意处理函数均可) 答案:BzclSampleSw_OnOffCB(uint8 cmd)收到COMMAND_ON时执行的动作是(A. 熄灭所有 LED B. 屏幕打印 "Set ON" 并点亮所有 LED C. 发送反转命令 D. 复位本设备) 答案:B协调器收到插座广播的地址消息时,会产生哪个系统事件(A.
ZCL_INCOMING_MSGB.ZDO_CB_MSGC.AF_INCOMING_MSG_CMDD.SYS_EVENT_MSG) 答案:B在
zclSampleSw_processZDOMsgs()中,协调器是根据什么判断收到的是设备入网广播的(A.pMsg->clusterID == Device_annceB.pInMsg->zclHdr.commandIDC. 源端点号 D.addrMode) 答案:A网关周期性发送 On/Off 命令依靠的机制是(A.
while死循环 B.osal_start_timerEx()定时事件SAMPLEAPP_ONOFF_TEST_EVT,事件处理末尾再次启动定时器形成周期发送 C. 串口接收中断 D. 无线射频中断) 答案:BzclSampleSw_OnOffTest()中语句on = !on;的作用是(A. 递增数据包序号 B. 反转期望状态,使CmdOn与CmdOff交替发送 C. 清空屏幕 D. 复位目标地址) 答案:B本实验中目标地址
destAddr.addrMode设置为(A.Addr64Bit(64 位扩展地址) B.Addr16Bit(16 位网络地址,P2P) C. 广播地址 D. 组地址) 答案:B
二、问答题(每题 20 分,共 60 分)
- 题干:简述本实验中智能插座(终端/路由器端)需要完成的主要工作。 参考答案要点:
- 以终端/路由器角色加入网络:
bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_STEERING | BDB_COMMISSIONING_MODE_FINDING_BINDING); - 调用
zclSampleSw_DeviceAnnce(),内部用ZDP_DeviceAnnce()向全网广播本设备短地址(NLME_GetShortAddr())和扩展地址(NLME_GetExtAddr()),让协调器知道"我来了、我在哪"; - 在回调表
zclSampleSw_CmdCallbacks的 On/Off 位置注册zclSampleSw_OnOffCB(); zclSampleSw_OnOffCB()中判断cmd:收到COMMAND_ON屏幕打印 "Set ON" 并点亮所有 LED,收到COMMAND_OFF打印 "Set OFF" 并熄灭所有 LED。
- 题干:简述网关(协调器)端从收到插座地址广播到周期性发送 On/Off 命令的完整流程。 参考答案要点:
- 插座入网广播后,协调器产生
ZDO_CB_MSG事件; - 在
zclSampleSw_processZDOMsgs()中按pMsg->clusterID == Device_annce分支,把插座短地址pMsg->srcAddr.addr.shortAddr保存到全局变量zclSampleSw_OnOffTestAddr,并在屏幕显示节点地址; - 用
osal_start_timerEx()启动SAMPLEAPP_ONOFF_TEST_EVT定时事件; - 事件触发后调用
zclSampleSw_OnOffTest():填充目标地址(端点、Addr16Bit、插座短地址),根据静态变量on选择调zclGeneral_SendOnOff_CmdOn或CmdOff,然后on = !on反转状态; - 事件处理末尾再次
osal_start_timerEx()重启该事件,形成"开→关→开→关"的周期发送。
- 题干:本实验编译下载两块板子后,预期的仿真调试现象是什么? 参考答案要点:
- 先后给网关和插座供电,插座自动加入网关创建的网络;
- 网关屏幕交替显示
Command: ON与Command: OFF(每周期反转一次); - 插座屏幕交替显示
Set ON(LED 全亮)与Set OFF(LED 全灭); - LED 随网关命令周期性地"呼吸"式亮灭,即说明 ZCL 命令收发成功。