Skip to content

7.2 终端设备开发 ​

一句话理解

终端设备开发三步:定义并启动上报事件 → 写事件处理函数(调用上报 API)→ 开启对应宏。

终端设备开发内容 ​

  1. 定义和启动属性上报事件;
  2. 定义事件处理函数,进行属性上报;
  3. 启用对应的宏定义。

定义和启动属性上报事件 ​

在 zcl_samplesw.h 中添加事件定义:

c
#ifdef ZDO_COORDINATOR
#else
  /*
   * 此处省略了部分代码
   */
  #define SAMPLEAPP_REPORT_EVT    0x0040    // 上报事件
  #define SAMPLEAPP_REPORT_PERIOD 3000      // 上报周期:3000ms(3 秒)
#endif

在 zcl_samplesw.c 的初始化函数 zclSampleSw_Init() 中启动上报事件:

c
osal_start_timerEx( zclSampleSw_TaskID,
                    SAMPLEAPP_REPORT_EVT,      // 事件
                    SAMPLEAPP_REPORT_PERIOD ); // 延迟时间(3 秒后触发)

定义事件处理函数 ​

打开 zclSampleSw_event_loop(),添加上报事件处理:

c
#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 中可以找到:

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():

c
/*
 * 数据上报事件的处理函数,用于上报数据
 */
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 );
}
⏰
定时事件
每 3 秒触发 SAMPLEAPP_REPORT_EVT
→
📦
组装数据
属性 ID + 类型 + 值(seqNum 递增)
→
📡
上报
zcl_SendReportCmd() 发给协调器(0x0000)

启用对应的宏定义 ​

最后,开启宏 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 分) ​

  1. 终端设备开发的三步流程是(A. 定义并启动上报事件 → 写事件处理函数调用上报 API → 开启对应宏 B. 先开宏 → 再编译 → 再上电 C. 先写协调器代码 → 再写终端代码 D. 先绑定 → 再组网 → 再上报) 答案:A

  2. 上报事件 SAMPLEAPP_REPORT_EVT 定义在哪个头文件中(A. zcl_samplesw.c B. zcl_samplesw.h C. zcl.c D. OSAL.h) 答案:B

  3. 本实验中 SAMPLEAPP_REPORT_PERIOD 的取值和含义是(A. 3000 ms,即每 3 秒上报一次 B. 3000 s,即每 50 分钟上报一次 C. 300 ms,即每 0.3 秒上报一次 D. 30 s,即每半分钟上报一次) 答案:A

  4. 在 zclSampleSw_Init() 中用于启动上报事件的 OSAL API 是(A. osal_set_event() B. osal_start_timerEx() C. osal_msg_allocate() D. HalLcdWriteString()) 答案:B

  5. 在事件处理 zclSampleSw_event_loop() 中,处理完一次 SAMPLEAPP_REPORT_EVT 后,为了实现周期上报,应当(A. 不再启动下一次 B. 再次调用 osal_start_timerEx() 启动下一个上报事件 C. 调用 osal_stop_timerEx() D. 直接关机) 答案:B

  6. 用于完成属性上报的核心 API 是(A. zcl_SendReadCmd() B. zcl_SendWriteCmd() C. zcl_SendReportCmd() D. zcl_SendDiscoverCmd()) 答案:C

  7. 在 zclSampleSw_ReportTest() 中,协调器的网络地址被填写为(A. 0xFFFF B. 0x0000 C. 0x1234 D. 0x0001) 答案:B

  8. 本实验中上报的属性 ATTRID_ON_OFF_SWITCH_TYPE 的数据类型被设置为(A. ZCL_DATATYPE_UINT8 B. ZCL_DATATYPE_CHAR_STR C. ZCL_DATATYPE_ENUM8 D. ZCL_DATATYPE_BOOL) 答案:C

  9. 调用 zcl_SendReportCmd() 时,通信方向参数被设置为 ZCL_FRAME_CLIENT_SERVER_DIR,其含义是(A. 服务端 → 客户端 B. 客户端 → 服务端 C. 广播 D. 组播) 答案:B

  10. 终端侧必须开启的、与上报功能直接相关的宏是(A. ZCL_REPORT_DESTINATION_DEVICE B. ZCL_REPORT C. ZCL_REPORTING_DEVICE D. ZCL_READ) 答案:C

二、问答题(每题 20 分,共 60 分) ​

  1. 简述终端设备开发中"定义和启动属性上报事件"的两个关键步骤。参考答案要点:
  • 在 zcl_samplesw.h 中添加事件定义 SAMPLEAPP_REPORT_EVT(如 0x0040)和上报周期 SAMPLEAPP_REPORT_PERIOD(如 3000 ms);
  • 在 zclSampleSw_Init() 中调用 osal_start_timerEx(),延迟 3 秒后首次触发上报事件;
  • 事件和周期都放在 #ifdef ZDO_COORDINATOR #else ... #endif 中,仅终端侧生效。
  1. 在 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 释放申请的两块内存。
  1. 为什么终端侧需要再次调用 osal_start_timerEx()?如果不调用会怎样?参考答案要点:
  • OSAL 定时器是一次性的,触发一次后即结束;
  • 在事件处理分支末尾再次启动同周期定时器,才能形成"每 3 秒上报一次"的周期上报;
  • 若不再次启动,则只上报一次就不再发送;
  • 同时在事件分支 return (events ^ SAMPLEAPP_REPORT_EVT) 清除该事件标志。

aioteer · 物联网专业课程群

💬AI 伴学配置