6.2 ZCL 属性读写实验
一句话理解
协调器向终端先写后读:写入一个值 → 读回来 → 屏幕对比,验证"写进去、读出来"是否一致。
实验简介
- 设备:一个协调器 + 一个终端
- 内容:协调器向终端发写命令(写入属性值),再发读命令(读回该属性),最后比较写入信息和读到的信息是否一致。
定义读写事件
在 zcl_samplesw.h 中定义读事件和写事件:
#ifdef ZDO_COORDINATOR
// Read test event
#define SAMPLEAPP_READ_EVT 0x0001
#define SAMPLEAPP_READ_PERIOD 1000
// Write test event
#define SAMPLEAPP_WRITE_EVT 0x0002
#define SAMPLEAPP_WRITE_PERIOD 1000
#endif协调器收到终端广播(Annce)后,启动读命令事件(zclSampleSw_processZDOMsgs 中):
case Device_annce:
zclSampleSw_TestAddr = pMsg->srcAddr.addr.shortAddr; // 记录终端地址
HalLcdWriteStringValue( "Node:", pMsg->srcAddr.addr.shortAddr, 16, 3 );
HalLcdWriteString( "Test...", 4 );
osal_start_timerEx( zclSampleSw_TaskID,
SAMPLEAPP_READ_EVT,
SAMPLEAPP_READ_PERIOD ); // 启动读事件
break;读命令事件处理
在 zclSampleSw_event_loop() 中:
if ( events & SAMPLEAPP_READ_EVT ) // 读命令事件
{
zclSampleSw_ReadTest(); // 读命令处理函数
// 读完之后启动写命令事件(交替执行)
osal_start_timerEx( zclSampleSw_TaskID,
SAMPLEAPP_WRITE_EVT,
SAMPLEAPP_WRITE_PERIOD );
return ( events ^ SAMPLEAPP_READ_EVT );
}zclSampleSw_ReadTest() 定义:
static void zclSampleSw_ReadTest( void )
{
afAddrType_t destAddr;
zclReadCmd_t *readCmd;
static uint8 txID = 0;
destAddr.endPoint = SAMPLESW_ENDPOINT;
destAddr.addrMode = afAddr16Bit;
destAddr.addr.shortAddr = zclSampleSw_TestAddr; // 终端地址
// 申请动态内存(1 个属性 + 属性 ID 空间)
readCmd = (zclReadCmd_t *)osal_mem_alloc( sizeof(zclReadCmd_t) +
sizeof(uint16) );
if ( readCmd == NULL ) return;
readCmd->numAttr = 1; // 待读属性数量 = 1
readCmd->attrID[0] = ATTRID_ON_OFF_SWITCH_ACTIONS; // 待读属性 ID
// 发送读命令
zcl_SendRead( SAMPLESW_ENDPOINT,
&destAddr,
ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG, // Cluster ID
readCmd,
ZCL_FRAME_CLIENT_SERVER_DIR, // 方向:客户端 → 服务端
TRUE,
txID++ );
osal_mem_free( readCmd ); // 释放内存
}写命令事件处理
读事件处理下方是写事件处理:
if ( events & SAMPLEAPP_WRITE_EVT ) // 写命令事件
{
zclSampleSw_WriteTest(); // 写命令处理函数
// 写完之后启动读命令事件(交替执行)
osal_start_timerEx( zclSampleSw_TaskID,
SAMPLEAPP_READ_EVT,
SAMPLEAPP_READ_PERIOD );
return ( events ^ SAMPLEAPP_WRITE_EVT );
}zclSampleSw_WriteTest() 定义:
static void zclSampleSw_WriteTest( void )
{
afAddrType_t destAddr;
zclWriteCmd_t *writeCmd;
static uint8 txID = 0;
destAddr.endPoint = SAMPLESW_ENDPOINT;
destAddr.addrMode = afAddr16Bit;
destAddr.addr.shortAddr = zclSampleSw_TestAddr;
// 申请动态内存(写命令 + 写记录)
writeCmd = (zclWriteCmd_t *)osal_mem_alloc( sizeof(zclWriteCmd_t) +
sizeof(zclWriteRec_t) );
if ( writeCmd == NULL ) return;
writeCmd->attrList[0].attrData = (uint8 *)osal_mem_alloc( sizeof(uint8) );
if ( writeCmd->attrList[0].attrData == NULL ) return;
writeCmd->numAttr = 1; // 写 1 个属性
writeCmd->attrList[0].attrID = ATTRID_ON_OFF_SWITCH_ACTIONS; // 属性 ID
writeCmd->attrList[0].dataType = ZCL_DATATYPE_ENUM8; // 数据类型
*(writeCmd->attrList[0].attrData) = txID; // 写入值 = txID
HalLcdWriteStringValue( "Write:", txID, 10, 4 ); // 屏幕显示写入值
zcl_SendWrite( SAMPLESW_ENDPOINT,
&destAddr,
ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG, // Cluster ID
writeCmd,
ZCL_FRAME_CLIENT_SERVER_DIR,
TRUE,
txID++ );
osal_mem_free( writeCmd->attrList[0].attrData ); // 释放内存
osal_mem_free( writeCmd );
}交替节奏
协调器读 → 写 → 读 → 写循环交替:每次写完启动读事件,每次读完启动写事件。屏幕会看到 Write: N 与 Read: N 交替刷新。
读响应处理
终端返回读响应后,协调器在 zclSampleSw_ProcessInReadRspCmd() 中取出属性值:
#ifdef ZCL_READ
static uint8 zclSampleSw_ProcessInReadRspCmd( zclIncomingMsg_t *pInMsg )
{
zclReadRspCmd_t *readRspCmd;
uint8 i;
readRspCmd = (zclReadRspCmd_t *)pInMsg->attrCmd;
for ( i = 0; i < readRspCmd->numAttr; i++ ) // 遍历返回的属性
{
if ( pInMsg->clusterId == ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG &&
readRspCmd->attrList[i].attrID == ATTRID_ON_OFF_SWITCH_ACTIONS )
{
uint8 val;
val = *(readRspCmd->attrList[i].data); // 取出属性值
HalLcdWriteStringValue( "Read:", val, 10, 4 ); // 屏幕显示读取值
}
}
return TRUE;
}
#endif // ZCL_READ仿真调试
- 分别编译协调器和终端(路由器)工程,下载到两块开发板;
- 终端设备加入 ZigBee 网络后,观察协调器屏幕:
- 屏幕交替显示 Write: 6(写入值)和 Read: 6(读回值);
- 数值会随 txID 递增(7、8、9…),但 Write 和 Read 始终一致。
实验现象小结
| 屏幕提示 | 含义 |
|---|---|
| Write: N | 刚写入属性值 N |
| Read: N | 刚读回属性值 N |
写进去和读出来完全一致,说明属性读写链路打通了!
内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第四部分·6.2 ZCL 属性读写实验,原站链接
本章自测
一、单项选择题(每题 4 分,共 40 分)
本实验的核心思路是(A. 协调器只发读命令 B. 协调器向终端先写后读:写入一个值 → 读回来 → 屏幕对比是否一致 C. 终端主动上报属性 D. 只做广播通信) 答案:B
本实验中被读写的属性是(A.
ATTRID_ON_OFFB.ATTRID_ON_OFF_SWITCH_ACTIONSC.ATTRID_BASIC_SW_BUILD_IDD. 自定义厂商属性) 答案:B在
zcl_samplesw.h中,SAMPLEAPP_READ_EVT与SAMPLEAPP_WRITE_EVT的事件值分别定义为(A.0x0001和0x0002B.0x0003和0x0004C.0x0000和0xFFFFD. 由协议栈自动分配) 答案:A协调器是在什么时机启动读命令事件的(A. 上电立即启动 B. 收到终端
Device_annce后记录终端地址,并用osal_start_timerEx()启动SAMPLEAPP_READ_EVTC. 串口收到字符后 D. 收到写响应后) 答案:B读事件与写事件的交替关系是(A. 读完之后启动写事件,写完之后再启动读事件,循环交替 B. 整个流程只执行一次 C. 写事件先启动 D. 两个事件同时并行触发) 答案:A
zclSampleSw_ReadTest()中用osal_mem_alloc()申请的动态内存大小是(A.sizeof(zclReadCmd_t)B.sizeof(zclReadCmd_t) + sizeof(uint16)(1 个属性 + 属性 ID 空间) C. 固定 10 字节 D. 不需要申请动态内存) 答案:BReadTest()中readCmd->numAttr与readCmd->attrID[0]分别设置为(A. 0,任意属性 ID B. 1,ATTRID_ON_OFF_SWITCH_ACTIONSC. 2,两个属性 D. 由目标设备决定) 答案:BWriteTest()中实际写入终端属性的值来自(A. 固定常量 0 B. 静态变量txID(*(writeCmd->attrList[0].attrData) = txID;,随后txID++) C. 终端属性当前值 D. 终端短地址) 答案:B协调器在哪个函数中遍历读响应、取出属性值并在屏幕显示 "Read:"(A.
zclSampleSw_ProcessIncomingMsg()B.zclSampleSw_ProcessInReadRspCmd()C.zclSampleSw_processZDOMsgs()D.zclSampleSw_OnOffCB()) 答案:B本实验预期的屏幕现象是(A. 只显示
Write: NB.Write: N与Read: N交替刷新,数值随txID递增(7、8、9…),但 Write 与 Read 始终一致 C. 屏幕固定显示 0 D. 只显示Read: N) 答案:B
二、问答题(每题 20 分,共 60 分)
- 题干:简述本实验中协调器"读—写"交替的事件节奏。 参考答案要点:
- 终端入网广播后,协调器在
zclSampleSw_processZDOMsgs()的Device_annce分支中记录终端短地址,并启动SAMPLEAPP_READ_EVT; - 读事件触发
zclSampleSw_ReadTest(),读完后用osal_start_timerEx()启动SAMPLEAPP_WRITE_EVT; - 写事件触发
zclSampleSw_WriteTest(),写完后再启动SAMPLEAPP_READ_EVT; - 如此"读 → 写 → 读 → 写"循环交替,屏幕上
Write: N与Read: N交替刷新。
- 题干:
zclSampleSw_WriteTest()中是如何构造并发送一条写命令的? 参考答案要点:
- 填充目标地址:端点
SAMPLESW_ENDPOINT、addrMode = afAddr16Bit、短地址为终端地址zclSampleSw_TestAddr; - 用
osal_mem_alloc()申请sizeof(zclWriteCmd_t) + sizeof(zclWriteRec_t)空间,再为attrList[0].attrData申请 1 字节内存; - 设置
numAttr = 1,attrList[0].attrID = ATTRID_ON_OFF_SWITCH_ACTIONS,dataType = ZCL_DATATYPE_ENUM8,*attrData = txID(写入值); - 屏幕打印
Write: N,调用zcl_SendWrite()发送(方向 Client→Server,序号txID++); - 发送后依次
osal_mem_free()释放attrData和writeCmd动态内存。
- 题干:本实验是如何验证"属性读写链路打通"的? 参考答案要点:
- 写命令把
txID值写入终端的ATTRID_ON_OFF_SWITCH_ACTIONS属性; - 读命令再把同一属性读回;
- 终端返回读响应后,协调器在
zclSampleSw_ProcessInReadRspCmd()中遍历readRspCmd->attrList,匹配簇 ID 与属性 ID 后用*(readRspCmd->attrList[i].data)取出属性值,屏幕打印Read: val; - 观察到
Write: N与Read: N数值始终一致(数值随txID递增但两边同步),即说明"写进去、读出来"一致,属性读写链路打通。