7.2 终端设备开发
一句话理解
终端设备开发三步:定义并启动上报事件 → 写事件处理函数(调用上报 API)→ 开启对应宏。
终端设备开发内容
- 定义和启动属性上报事件;
- 定义事件处理函数,进行属性上报;
- 启用对应的宏定义。
定义和启动属性上报事件
在 zcl_samplesw.h 中添加事件定义:
#ifdef ZDO_COORDINATOR
#else
/*
* 此处省略了部分代码
*/
#define SAMPLEAPP_REPORT_EVT 0x0040 // 上报事件
#define SAMPLEAPP_REPORT_PERIOD 3000 // 上报周期:3000ms(3 秒)
#endif在 zcl_samplesw.c 的初始化函数 zclSampleSw_Init() 中启动上报事件:
osal_start_timerEx( zclSampleSw_TaskID,
SAMPLEAPP_REPORT_EVT, // 事件
SAMPLEAPP_REPORT_PERIOD ); // 延迟时间(3 秒后触发)定义事件处理函数
打开 zclSampleSw_event_loop(),添加上报事件处理:
#ifdef ZDO_COORDINATOR
#else
/*
* 此处省略了部分代码
*/
if ( events & SAMPLEAPP_REPORT_EVT )
{
zclSampleSw_ReportTest(); // 属性上报事件的处理函数
// 启动下一个上报事件(周期上报)
osal_start_timerEx( zclSampleSw_TaskID,
SAMPLEAPP_REPORT_EVT,
SAMPLEAPP_REPORT_PERIOD );
return ( events ^ SAMPLEAPP_REPORT_EVT );
}
#endif属性上报 API 简介
只需调用属性上报 API 即可完成上报。在 zcl.c 中可以找到:
/*
* 上报一个或多个属性值
*/
extern ZStatus_t zcl_SendReportCmd(
uint8 srcEP, // 源端点号
afAddrType_t *dstAddr, // 目标设备地址信息
uint16 realClusterID, // 属性所属 Cluster ID
zclReportCmd_t *reportCmd, // 描述待上报的属性值
uint8 direction, // 通信方向
uint8 disableDefaultRsp, // 是否关闭默认响应
uint8 seqNum ); // 数据包序号(自增)属性上报处理函数
编写事件处理函数 zclSampleSw_ReportTest():
/*
* 数据上报事件的处理函数,用于上报数据
*/
static void zclSampleSw_ReportTest( void )
{
static uint8 seqNum = 0;
zclReportCmd_t *reportCmd;
afAddrType_t destAddr; // 目标设备地址
destAddr.addrMode = afAddr16Bit;
destAddr.endPoint = SAMPLESW_ENDPOINT;
destAddr.addr.shortAddr = 0x0000; // 0x0000 = 协调器的网络地址
// 申请内存空间(上报命令 + 上报记录)
reportCmd = (zclReportCmd_t *)osal_mem_alloc( sizeof(zclReportCmd_t) +
sizeof(zclReport_t) );
if ( reportCmd == NULL ) return;
reportCmd->attrList[0].attrData = (uint8 *)osal_mem_alloc( sizeof(uint8) );
if ( reportCmd->attrList[0].attrData == NULL ) return;
reportCmd->numAttr = 1; // 属性数量 = 1
reportCmd->attrList[0].attrID = ATTRID_ON_OFF_SWITCH_TYPE; // 属性 ID
reportCmd->attrList[0].dataType = ZCL_DATATYPE_ENUM8; // 数据类型
*((uint8 *)(reportCmd->attrList[0].attrData)) = seqNum; // 属性值
// 上报数据
zcl_SendReportCmd( SAMPLESW_ENDPOINT, // 源端点号
&destAddr, // 地址信息
ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG, // Cluster ID
reportCmd,
ZCL_FRAME_CLIENT_SERVER_DIR, // 客户端 → 服务端
TRUE, // 关闭默认响应
seqNum++ ); // 序号递增
HalLcdWriteStringValue( "Report: ", (seqNum - 1), 10, 4 ); // 屏幕显示
// 释放内存空间
osal_mem_free( reportCmd->attrList[0].attrData );
osal_mem_free( reportCmd );
}启用对应的宏定义
最后,开启宏 ZCL_REPORTING_DEVICE:
工程 Options → C/C++ Compiler → Preprocessor → Defined symbols,添加:
ZCL_ON_OFF
ZCL_GROUPS
ZCL_REPORTING_DEVICE
HAL_LED=TRUE小结
终端侧 = 事件(谁触发)+ 组装(报什么)+ API(怎么发)+ 宏(开关打开)。四件事做完,终端就能每 3 秒向协调器上报一次数据。
内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第四部分·7.2 终端设备开发,原站链接
本章自测
一、单项选择题(每题 4 分,共 40 分)
终端设备开发的三步流程是(A. 定义并启动上报事件 → 写事件处理函数调用上报 API → 开启对应宏 B. 先开宏 → 再编译 → 再上电 C. 先写协调器代码 → 再写终端代码 D. 先绑定 → 再组网 → 再上报) 答案:A
上报事件
SAMPLEAPP_REPORT_EVT定义在哪个头文件中(A.zcl_samplesw.cB.zcl_samplesw.hC.zcl.cD.OSAL.h) 答案:B本实验中
SAMPLEAPP_REPORT_PERIOD的取值和含义是(A. 3000 ms,即每 3 秒上报一次 B. 3000 s,即每 50 分钟上报一次 C. 300 ms,即每 0.3 秒上报一次 D. 30 s,即每半分钟上报一次) 答案:A在
zclSampleSw_Init()中用于启动上报事件的 OSAL API 是(A.osal_set_event()B.osal_start_timerEx()C.osal_msg_allocate()D.HalLcdWriteString()) 答案:B在事件处理
zclSampleSw_event_loop()中,处理完一次SAMPLEAPP_REPORT_EVT后,为了实现周期上报,应当(A. 不再启动下一次 B. 再次调用osal_start_timerEx()启动下一个上报事件 C. 调用osal_stop_timerEx()D. 直接关机) 答案:B用于完成属性上报的核心 API 是(A.
zcl_SendReadCmd()B.zcl_SendWriteCmd()C.zcl_SendReportCmd()D.zcl_SendDiscoverCmd()) 答案:C在
zclSampleSw_ReportTest()中,协调器的网络地址被填写为(A. 0xFFFF B. 0x0000 C. 0x1234 D. 0x0001) 答案:B本实验中上报的属性
ATTRID_ON_OFF_SWITCH_TYPE的数据类型被设置为(A.ZCL_DATATYPE_UINT8B.ZCL_DATATYPE_CHAR_STRC.ZCL_DATATYPE_ENUM8D.ZCL_DATATYPE_BOOL) 答案:C调用
zcl_SendReportCmd()时,通信方向参数被设置为ZCL_FRAME_CLIENT_SERVER_DIR,其含义是(A. 服务端 → 客户端 B. 客户端 → 服务端 C. 广播 D. 组播) 答案:B终端侧必须开启的、与上报功能直接相关的宏是(A.
ZCL_REPORT_DESTINATION_DEVICEB.ZCL_REPORTC.ZCL_REPORTING_DEVICED.ZCL_READ) 答案:C
二、问答题(每题 20 分,共 60 分)
- 简述终端设备开发中"定义和启动属性上报事件"的两个关键步骤。参考答案要点:
- 在
zcl_samplesw.h中添加事件定义SAMPLEAPP_REPORT_EVT(如 0x0040)和上报周期SAMPLEAPP_REPORT_PERIOD(如 3000 ms); - 在
zclSampleSw_Init()中调用osal_start_timerEx(),延迟 3 秒后首次触发上报事件; - 事件和周期都放在
#ifdef ZDO_COORDINATOR #else ... #endif中,仅终端侧生效。
- 在
zclSampleSw_ReportTest()中是如何组装并发送一条属性上报命令的?参考答案要点:
- 设定目标地址:
addrMode = afAddr16Bit,短地址填协调器 0x0000,端点为 SAMPLESW_ENDPOINT; - 用
osal_mem_alloc申请zclReportCmd_t及一条zclReport_t记录内存,并为 attrData 单独申请一个 uint8; - 填写
numAttr = 1、attrID = ATTRID_ON_OFF_SWITCH_TYPE、dataType = ZCL_DATATYPE_ENUM8,属性值为自增的seqNum; - 调用
zcl_SendReportCmd(),方向为客户端→服务端,disableDefaultRsp = TRUE,序号seqNum++; - 屏幕打印
Report: N,最后用osal_mem_free释放申请的两块内存。
- 为什么终端侧需要再次调用
osal_start_timerEx()?如果不调用会怎样?参考答案要点:
- OSAL 定时器是一次性的,触发一次后即结束;
- 在事件处理分支末尾再次启动同周期定时器,才能形成"每 3 秒上报一次"的周期上报;
- 若不再次启动,则只上报一次就不再发送;
- 同时在事件分支
return (events ^ SAMPLEAPP_REPORT_EVT)清除该事件标志。