Skip to content

3.4 ZigBee 3.0 通信实验 ​

一句话理解

前几节学 API,这一节把代码跑起来:一台协调器 + 一台路由器(或终端),让三种通信方式(点对点 / 广播 / 组播)在真实板子上"飞"起来。

实验简介 ​

实验设备:一个协调器 + 一个路由器(或终端)。

实验内容:

🔗
点对点
路由器(或终端)定时向协调器发送数据
·
👥
组播准备
路由器(或终端)加入一个组
·
📢
广播 + 组播
协调器定时发送广播数据和组播数据

设备初始化:让每台设备"各就各位" ​

打开配套的 SampleSwitch 工程,在 zcl_samplesw.c 的 zclSampleSw_Init() 初始化函数末尾添加代码:

c
#ifdef ZDO_COORDINATOR
  bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_FORMATION |
                          BDB_COMMISSIONING_MODE_FINDING_BINDING );

  NLME_PermitJoiningRequest(255);

  // Broadcast
  osal_start_timerEx(zclSampleSw_TaskID,
                     SAMPLEAPP_BROADCAST_EVT,
                     SAMPLEAPP_BROADCAST_PERIOD);
  // groupcast
  osal_start_timerEx(zclSampleSw_TaskID,
                     SAMPLEAPP_GROUPCAST_EVT,
                     SAMPLEAPP_GROUPCAST_PERIOD);
#else
  bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_STEERING |
                          BDB_COMMISSIONING_MODE_FINDING_BINDING );
  // Add group
  aps_Group_t group = {
    .ID = GROUP_ID,
    .name = "",
  };
  aps_AddGroup(SAMPLESW_ENDPOINT, &group);

  // P2P
  osal_start_timerEx(zclSampleSw_TaskID,
                     SAMPLEAPP_P2P_EVT,
                     SAMPLEAPP_P2P_PERIOD);
#endif

协调器角色做了什么 ​

🏛️
建网
bdb_StartCommissioning() 创建 ZigBee 网络
→
🚪
开门
NLME_PermitJoiningRequest(255) 允许其他设备加入
→
⏰
定时任务
启动广播事件 + 组播事件(周期 1000ms)

事件和周期的定义在 zcl_samplesw.h 中:

c
// 广播事件
#define SAMPLEAPP_BROADCAST_EVT   0x0080
#define SAMPLEAPP_BROADCAST_PERIOD 1000

// 组播事件
#define SAMPLEAPP_GROUPCAST_EVT   0x0100
#define SAMPLEAPP_GROUPCAST_PERIOD 1000

终端(或路由器)角色做了什么 ​

🚪
入网
bdb_StartCommissioning() 加入 ZigBee 网络
→
👥
建组 + 入组
创建组(ID=21)并调用 aps_AddGroup() 加入
→
⏰
定时任务
启动点对点通信事件(周期 1000ms)

创建组和组 ID 的代码:

c
// 创建组
aps_Group_t group = {
  .ID = GROUP_ID,
  .name = "",
};

// 组 ID 定义(在 zcl_samplesw.c 中)
#define GROUP_ID 21

// 加入组:端点号 + 待加入的组
aps_AddGroup( SAMPLESW_ENDPOINT, &group );

P2P 事件定义(在 zcl_samplesw.h 中):

c
// P2P
#define SAMPLEAPP_P2P_EVT   0x0100
#define SAMPLEAPP_P2P_PERIOD 1000

协调器的事件处理:广播 + 组播 ​

在 zclSampleSw_event_loop() 事件处理函数中添加广播和组播事件处理:

c
  // Broadcast event
  if ( events & SAMPLEAPP_BROADCAST_EVT )
  {
    zclSampleSw_AF_Broadcast(CLUSTER_BROADCAST,
                             10, "Broadcast");

    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,
                             10, "Groupcast");

    osal_start_timerEx(zclSampleSw_TaskID,
                       SAMPLEAPP_GROUPCAST_EVT,
                       SAMPLEAPP_GROUPCAST_PERIOD);

    return ( events ^ SAMPLEAPP_GROUPCAST_EVT );
  }

广播事件处理逻辑:调用 zclSampleSw_AF_Broadcast() 发出广播 → 再调用 osal_start_timerEx() 重新产生广播事件(形成周期发送)。组播事件同理。

其中用到的 Cluster ID(在 zcl_samplesw.c 中自定义):

c
#define CLUSTER_P2P        0
#define CLUSTER_BROADCAST  1
#define CLUSTER_GROUPCAST  2

提示

这 3 个 Cluster ID 本质就是宏定义,值分别为 0、1、2。它们用来标识"这条数据是用哪种方式发来的",接收方据此分类处理。

终端(或路由器)的事件处理:点对点 ​

同样在 zclSampleSw_event_loop() 中添加点对点事件处理:

c
  // P2P Event
  if ( events & SAMPLEAPP_P2P_EVT )
  {
    zclSampleSw_AF_P2P(0x0000,
                       CLUSTER_P2P,
                       4, "P2P");

    osal_start_timerEx(zclSampleSw_TaskID,
                       SAMPLEAPP_P2P_EVT,
                       SAMPLEAPP_P2P_PERIOD);

    return ( events ^ SAMPLEAPP_P2P_EVT );
  }

其中 0x0000 是协调器的网络地址(终端定时向协调器发数据)。发送后同样用 osal_start_timerEx() 重新产生事件,形成周期发送。

接收和处理数据:数据怎么"回来" ​

发送讲完了,接收同样重要。ZigBee 设备组网成功后,收到数据时系统会产生 AF_INCOMING_MSG_CMD 事件,通知应用层"来数据了"。

在 zclSampleSw_event_loop() 中,系统事件处理代码已经包含了对 AF_INCOMING_MSG_CMD 的识别:

c
uint16 zclSampleSw_event_loop( uint8 task_id, uint16 events )
{
  afIncomingMSGPacket_t *MSGpkt;
  (void)task_id; // intentionally unreferenced parameter

  if ( events & SYS_EVENT_MSG )
  {
    while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( zclSampleSw_TaskID )) )
    {
      switch ( MSGpkt->hdr.event )
      {
        case AF_INCOMING_MSG_CMD:
          zclSampleSw_AF_RxProc( MSGpkt );   // ← 收到数据,交给数据处理函数
          break;
        default:
          break;
      }
    }
    // Release the memory
    osal_msg_deallocate( (uint8 *)MSGpkt );
    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }
  ...
}

开发者只需在收到事件后做自己的处理。例程已经定义好数据处理函数 zclSampleSw_AF_RxProc():

c
/*
 * @param MSGpkt 接收到数据
 */
static void zclSampleSw_AF_RxProc(afIncomingMSGPacket_t *MSGpkt)
{
  /* 计数器:记录接收到的点对点通信数据包个数 */
  static uint8 p2pCnt = 0;
  /* 计数器:记录接收到的广播通信数据包个数 */
  static uint8 bcCnt = 0;
  /* 计数器:记录接收到的组播通信数据包个数 */
  static uint8 gcCnt = 0;

  switch( MSGpkt->clusterId )  // 根据 Cluster ID 判断数据包类型
  {
    case CLUSTER_P2P:
      p2pCnt++;  // 接收到 P2P 数据包,计数
      // 把接收到的数据和计数显示在屏幕上
      HalLcdWriteStringValue((char *)MSGpkt->cmd.Data, p2pCnt, 10, 3);
      break;

    case CLUSTER_BROADCAST:
      bcCnt++;  // 接收到广播数据包,计数
      HalLcdWriteStringValue((char *)MSGpkt->cmd.Data, bcCnt, 10, 3);
      break;

    case CLUSTER_GROUPCAST:
      gcCnt++;  // 接收到组播数据包,计数
      HalLcdWriteStringValue((char *)MSGpkt->cmd.Data, gcCnt, 10, 4);
      break;

    default:
      break;
  }
}

接收逻辑用一句话概括:看 clusterId 认"快递种类"(P2P / 广播 / 组播),分类计数并显示在屏幕上。

📥
收到数据
系统产生 AF_INCOMING_MSG_CMD 事件
→
🔀
事件分发
event_loop 识别事件,调用 zclSampleSw_AF_RxProc()
→
🏷️
分类处理
按 clusterId 判断 P2P/广播/组播,计数并显示

仿真调试 ​

  1. 选择 RouterEB(或 EndDeviceEB) 角色编译工程,把固件下载到标准板;
  2. 选择 CoordinatorEB 角色编译工程,把固件下载到 Mini 板;
  3. 给 Mini 板上电 → Mini 板自动创建网络;
  4. 给标准板上电 → 自动加入 Mini 板创建的网络;
  5. 两块板都会收到数据包:
  • Mini 板(协调器):屏幕显示收到的 P2P 数据包数量(终端定时发来的点对点数据);
  • 标准板(路由器/终端):屏幕显示 广播数据包数量 和 组播数据包数量(协调器定时发来的)。

实验现象小结

板子角色屏幕能看到
Mini 板协调器P2P 数据包数量(来自终端)
标准板路由器/终端广播数据包数量 + 组播数据包数量(来自协调器)

数据每 1 秒发一次,屏幕上的数字会不停增长——这就是"数据在飞"的证据!

内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第四部分·3.4 ZigBee 3.0 通信实验,原站链接


本章自测 ​

一、单项选择题(每题 4 分,共 40 分) ​

  1. 本实验使用的设备组合是(A. 一个协调器 + 一个路由器(或终端) B. 两个协调器 C. 三个终端 D. 一个路由器 + 一个终端) 答案:A

  2. 协调器在初始化时调用 NLME_PermitJoiningRequest(255) 的作用是(A. 创建网络 B. 允许其他设备加入网络 C. 加入别人的网络 D. 关闭网络) 答案:B

  3. 终端(或路由器)入网时,bdb_StartCommissioning() 使用的 commissioning 模式是(A. NWK_FORMATION B. NWK_STEERING(+ FINDING_BINDING) C. 不调用该函数 D. 仅 BDB_COMMISSIONING_MODE_TOUCHLINK) 答案:B

  4. 本实验中终端加入的组 GROUP_ID 被定义为(A. 8 B. 21 C. 0xFFFF D. 0x0000) 答案:B

  5. 终端定时向协调器发送点对点数据时,目标网络地址填的是(A. 0x0000(协调器) B. 0x0EF1 C. 0xFFFF D. 组 ID 21) 答案:A

  6. 本实验自定义的三个 Cluster ID CLUSTER_P2P / CLUSTER_BROADCAST / CLUSTER_GROUPCAST 的值分别是(A. 0 / 1 / 2 B. 1 / 2 / 3 C. 0x0006 / 0x0008 / 0x0009 D. 8 / 21 / 255) 答案:A

  7. ZigBee 设备组网成功后收到数据时,系统会通知应用层产生的事件是(A. SAMPLEAPP_P2P_EVT B. AF_INCOMING_MSG_CMD C. SYS_EVENT_MSG D. KEY_EVT) 答案:B

  8. 在数据处理函数 zclSampleSw_AF_RxProc() 中,用来区分数据包是 P2P / 广播 / 组播的依据是(A. 源网络地址 B. MSGpkt->clusterId C. 端点号 D. 数据长度) 答案:B

  9. 关于实验中的周期发送,下列说法正确的是(A. 发送一次后就停止 B. 在事件处理末尾再次调用 osal_start_timerEx() 重新产生事件,形成周期发送,周期约 1000ms C. 周期固定为 100ms 且不可改 D. 由硬件自动重发) 答案:B

  10. 仿真下载与现象中,下列对应关系正确的是(A. Mini 板跑 CoordinatorEB,屏幕显示 P2P 数据包数量;标准板跑 RouterEB,屏幕显示广播 + 组播数量 B. Mini 板跑 RouterEB,标准板跑 CoordinatorEB C. 两块板都只显示 P2P 数量 D. 标准板显示 P2P 数量,Mini 板显示组播数量) 答案:A

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

  1. 题干:本实验中协调器角色与终端(路由器)角色在初始化阶段分别做了哪些主要工作? 参考答案要点:
  • 协调器:bdb_StartCommissioning() 以 NWK_FORMATION 模式创建网络;调用 NLME_PermitJoiningRequest(255) 允许入网;启动广播事件和组播事件定时器。
  • 终端 / 路由器:bdb_StartCommissioning() 以 NWK_STEERING 模式加入网络;用 aps_AddGroup() 创建并加入指定组(GROUP_ID=21);启动点对点事件定时器。
  • 三种事件周期均为 1000ms。
  1. 题干:设备接收到 AF 数据后的处理流程是怎样的?开发者主要做哪部分工作? 参考答案要点:
  • 收到数据后系统产生 AF_INCOMING_MSG_CMD 事件。
  • event_loop() 在 SYS_EVENT_MSG 中取出消息,识别该事件并调用 zclSampleSw_AF_RxProc()。
  • 在 RxProc 中根据 MSGpkt->clusterId 判断数据包类型(P2P / 广播 / 组播),分别计数并用 HalLcdWriteStringValue() 显示。
  • 开发者只需在收到事件后编写自己的分类处理逻辑。
  1. 题干:实验最终两块板子上分别能看到什么现象?数据发送周期是多少? 参考答案要点:
  • Mini 板(协调器):屏幕显示收到的 P2P 数据包数量(来自终端定时发来的点对点数据)。
  • 标准板(路由器 / 终端):屏幕显示广播数据包数量和组播数据包数量(来自协调器定时发来的数据)。
  • 数据每 1 秒(1000ms)发一次,屏幕上的计数数字会不断增长。

aioteer · 物联网专业课程群

💬AI 伴学配置