07_Z-Stack多节点组网及终端消息上报

07_Z-Stack多节点组网及终端消息上报

多节点组网

​ 实现两个终端与协调器组网。

  • zcl_samplesw.c文件中,定义全局变量用于保存连接的设备地址
c 复制代码
#ifdef ZCL_REPORT
#define ZCLAPP_ENDDEVICE_NUM 2    // 连接设备数量
static uint16 zclSampleSw_EndDeviceAddrs[ZCLAPP_ENDDEVICE_NUM];  // 全局变量保存连接到的设备地址
static uint8 zclSampleSw_ProcessInReportCmd( zclIncomingMsg_t *pInMsg );
#endif // ZCL_REPORT
  • zclSampleSw_Init初始化函数中,协调器创建网络,终端加入网络
c 复制代码
#ifdef ZDO_COORDINATOR
  // Init Uart
  zclSampleSw_InitUart();
  
  ZDO_RegisterForZDOMsg ( zclSampleSw_TaskID, Device_annce );  // 注册 Device_annce 簇ID
  
  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_ProcessCommissioningStatus中
  // 当终端设备入网成功后, 开始上报
  osal_start_timerEx(zclSampleSw_TaskID, 
                     SAMPLEAPP_REPORT_EVT, 
                     SAMPLEAPP_REPORT_PERIOD); // 初始触发上报数据事件
  zclSampleSw_DeviceAnnce();  // 成功加入网络后 广播自己的地址
  */
#endif
  • 在回调函数zclSampleSw_ProcessCommissioningStatus中,若终端成功加入网络则广播自己的地址,否则触发重连事件
c 复制代码
    case BDB_COMMISSIONING_NWK_STEERING:
      if(bdbCommissioningModeMsg->bdbCommissioningStatus == BDB_COMMISSIONING_SUCCESS)
      {
        // 成功加入网络
        //We are on the nwk, what now?
        #ifdef ZDO_COORDINATOR
        #else
        osal_start_timerEx(zclSampleSw_TaskID, 
                     SAMPLEAPP_REPORT_EVT, 
                     SAMPLEAPP_REPORT_PERIOD); // 初始触发上报数据事件
        zclSampleSw_DeviceAnnce();  // 加入网络后 广播自己的地址
        #endif
      }
      else
      {
        #ifdef ZDO_COORDINATOR
        #else
        // 触发重新连接事件
        osal_start_timerEx(zclSampleSw_TaskID, 
                           SAMPLEAPP_REJOIN_EVT, 
                           SAMPLEAPP_REJOIN_PERIOD);
        #endif
      }
    break;
c 复制代码
/**
  * @brief 终端广播自己的网络地址
  */
  static void zclSampleSw_DeviceAnnce( void )
  {    
      ZDP_DeviceAnnce(
          NLME_GetShortAddr(),//获取本设备的网络地址(短地址)
          NLME_GetExtAddr(),//获取本设备的物理地址(通常就是MAC地址)
          ZDO_Config_Node_Descriptor.CapabilityFlags,
          0
       );    
  }
  • zclSampleSw_event_loop事件处理函数中,终端处理重连事件
c 复制代码
#ifdef ZDO_COORDINATOR
#else
  // Rejoin 重新加入网络
  if ( events & SAMPLEAPP_REJOIN_EVT )
  {
    bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_STEERING |
                      BDB_COMMISSIONING_MODE_FINDING_BINDING );
    return ( events ^ SAMPLEAPP_REJOIN_EVT );
  }
#endif
  • zclSampleSw_event_loop事件处理函数中,协调器处理ZDO_CB_MSG事件,将收到的终端广播的地址保存
c 复制代码
#ifdef ZDO_COORDINATOR
       case ZDO_CB_MSG:
          zclSampleSw_processZDOMgs( (zdoIncomingMsg_t *)MSGpkt ); // ZDO消息处理函数
          break; 
#endif
c 复制代码
/**
  * @brief ZDO消息处理函数
  */
  static void zclSampleSw_processZDOMgs(zdoIncomingMsg_t *pMsg)
  {     
    switch ( pMsg->clusterID )//判断消息中的Cluster ID
    {
      case Device_annce: // 收到终端的宣告广播
      {
        static uint8 idx = 0;
        // 把终端的网络地址保存到全局变量中
        if(idx < ZCLAPP_ENDDEVICE_NUM)
        {
          zclSampleSw_EndDeviceAddrs[idx] = pMsg->srcAddr.addr.shortAddr;
          // 打印信息
          HalLcdWriteStringValue("EndAddr:", zclSampleSw_EndDeviceAddrs[idx], 16, idx%2+3);
          idx++;
        }
        else if(idx > ZCLAPP_ENDDEVICE_NUM)
        {
          
        }
      }
        break;
      default:
        break;
    }
  }

ZCL层消息上报

​ 终端定时上报数据给协调器,协调器收到数据后在屏幕上显示

  • 在回调函数zclSampleSw_ProcessCommissioningStatus中,终端在成功入网后触发SAMPLEAPP_REPORT_EVT上报事件
c 复制代码
case BDB_COMMISSIONING_NWK_STEERING:
  if(bdbCommissioningModeMsg->bdbCommissioningStatus == BDB_COMMISSIONING_SUCCESS)
  {
    // 成功加入网络
    //We are on the nwk, what now?
    #ifdef ZDO_COORDINATOR
    #else
    osal_start_timerEx(zclSampleSw_TaskID, 
                 SAMPLEAPP_REPORT_EVT, 
                 SAMPLEAPP_REPORT_PERIOD); // 初始触发上报数据事件
    zclSampleSw_DeviceAnnce();  // 加入网络后 广播自己的地址
    #endif
  }
  else
  {
	...
  }
break;
  • zclSampleSw_event_loop事件处理函数中,处理SAMPLEAPP_REPORT_EVT事件
c 复制代码
#ifdef ZDO_COORDINATOR
#else
  // Report  上报数据
  if ( events & SAMPLEAPP_REPORT_EVT )
  {
      zclSampleSw_ReportTest(); // 上报数据处理函数
    
      osal_start_timerEx(zclSampleSw_TaskID, 
                     SAMPLEAPP_REPORT_EVT, 
                     SAMPLEAPP_REPORT_PERIOD);  // 重复触发上报事件
      
      return ( events ^ SAMPLEAPP_REPORT_EVT );
  }
#endif
c 复制代码
  /**
  * @brief 终端上报数据给协调器 
  */
  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;  // 上报属性个数
      reportCmd->attrList[0].attrID = ATTRID_ON_OFF_SWITCH_TYPE;
      reportCmd->attrList[0].dataType = ZCL_DATATYPE_ENUM8;
      *((uint8 *)(reportCmd->attrList[0].attrData)) = seqNum;  // 上报的数据=seqNum
      // 调用上报函数
      zcl_SendReportCmd( SAMPLESW_ENDPOINT, // 源端点号
                         &destAddr,  // 目标设备地址信息
                         ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG, // 属性所属Cluster ID 
                         reportCmd, // 上报的属性值
                         ZCL_FRAME_CLIENT_SERVER_DIR, // 通信方向 
                         TRUE, // 关闭默认相应
                         seqNum++ ); // 每上报一次 seqNum+1
      
      HalLcdWriteStringValue("Report: ", (seqNum-1), 10, 4);
      
      osal_mem_free(reportCmd->attrList[0].attrData); // 释放内存
      osal_mem_free(reportCmd);
  }
c 复制代码
extern ZStatus_t zcl_SendReportCmd(
    uint8 srcEP, //源端点号
    afAddrType_t *dstAddr, //目标设备地址信息
    uint16 realClusterID, //属性所属Cluster ID
    zclReportCmd_t *reportCmd,//描述待上报的属性值
    uint8 direction, //通信方向
    uint8 disableDefaultRsp, //是否关闭默认响应(目标设备的响应)
    uint8 seqNum); //数据包标号,由开发者自定义
  • 协调器收到上报信息后会触发ZCL_INCOMING_MSG事件,在zclSampleSw_event_loop事件处理函数中调用zclSampleSw_ProcessIncomingMsg处理该事件,在zclSampleSw_ProcessIncomingMsg中判断命令ID是否为ZCL_CMD_REPORT,然后调用zclSampleSw_ProcessInReportCmd处理终端上报的信息
c 复制代码
// zclSampleSw_event_loop 调用 zclSampleSw_ProcessIncomingMsg
case ZCL_INCOMING_MSG:
          {
            zclSampleSw_ProcessIncomingMsg( (zclIncomingMsg_t *)MSGpkt );
          }
          break;
c 复制代码
// zclSampleSw_ProcessIncomingMsg 调用 zclSampleSw_ProcessInReportCmd
case ZCL_CMD_REPORT:
      zclSampleSw_ProcessInReportCmd( pInMsg );  
      break;
c 复制代码
static uint8 zclSampleSw_ProcessInReportCmd( zclIncomingMsg_t *pInMsg )
{
  zclReportCmd_t *reportCmd;
  uint8 i;
  reportCmd = (zclReportCmd_t *)pInMsg->attrCmd;
  for ( i = 0; i < reportCmd->numAttr; i++ ) // 解包
  {
    if( pInMsg->clusterId == ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG && 
        reportCmd->attrList[i].attrID == ATTRID_ON_OFF_SWITCH_TYPE)
    {
        int8 attrDat = *(reportCmd->attrList[i].attrData);  // 接收消息
        for(uint8 j=0; j < ZCLAPP_ENDDEVICE_NUM; j++) 
        {
            // 根据 短地址分行打印上报的消息
          if(pInMsg->srcAddr.addr.shortAddr == zclSampleSw_EndDeviceAddrs[j])
            HalLcdWriteStringValue("Rx Value:", attrDat, 10, 3+j%2);
        }
    }
  }
  return ( TRUE );
}
  • 预编译选项中开启ZCL_REPORT_DESTINATION_DEVICEZCL_REPORT

终端断电无法重连问题解决

  • Tools文件夹下f8wConfig.cfg文件中配置PAN ID为固定数值
D 复制代码
// -DZDAPP_CONFIG_PAN_ID=0xFFFF
-DZDAPP_CONFIG_PAN_ID=0x0001
  • 预编译选项中开启NV_INIT=1NV_RESTORE=1, 开启后Zigbee 会将一些网络配置参数和网络状态存储到非易失活存储器中, 以便重连时能迅速恢复网络状态

  • ZDO文件夹下ZDApp.c文件中的ZDApp_ProcessOSALMsg函数里加入以下内容(加到最后即可)

c 复制代码
    case ZDO_NWK_JOIN_REQ:      //重连事件
      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        retryCnt = 0;
        devStartMode = MODE_RESUME;                                     //让设备处于网络恢复模式
        _tmpRejoinState = true;                                         //初始化临时状态为重连
        osal_cpyExtAddr( ZDO_UseExtendedPANID, _NIB.extendedPANID );    //初始化ZDO为之前的PANID
        zgDefaultStartingScanDuration = BEACON_ORDER_60_MSEC;           //每60毫秒发送一个信标
        ZDApp_NetworkInit( 0 );                                         //重新初始化网络产生ZDO_NETWORK_INIT事件
      }
      break;
  • 完成以上步骤后,终端断电后也能重新连上协调器了,具体重连策略可看参考资料中官方论坛的帖子

参考资料

第7章:基于ZCL的属性上报实验 - ZigBee 3.0 开发指南

zigbee端末掉线和重连的问题 cc2530协议栈2.5.1a - Zigbee 和 Thread 论坛 - Zigbee 和 Thread - E2E™ 设计支持

zigbee终端无法重连的问题解决_zigbee 设置掉电后不重连-CSDN博客

相关推荐
顾念`16 分钟前
履带小车+六轴机械臂(2)
单片机·嵌入式硬件
努力创造奇迹20 分钟前
STM32 HAL库 ADC+TIM+DMA 3路 1S采样一次电压
stm32·单片机·嵌入式硬件
leoFY1231 小时前
STM32 BOOT设置,bootloader,死锁使用方法
stm32·单片机·嵌入式硬件
czhaii2 小时前
单片机任意普通IO引脚使用定时器扩展外部中断的巧妙方法
单片机·嵌入式硬件
Better Rose3 小时前
【2025年认证杯数学中国数学建模网络挑战赛】A题 解题建模过程与模型代码(基于matlab)
c语言·数学建模·matlab
大模型铲屎官4 小时前
# Unity动画控制核心:Animator状态机与C#脚本实战指南 (Day 29)
c语言·unity·c#·游戏引擎·游戏开发·动画控制·animator状态机
二块烧肉4 小时前
STM32 认识STM32
stm32·单片机·嵌入式硬件
XW-ABAP4 小时前
c语言练习4
java·c语言·前端
程序猿阿伟5 小时前
《鸿蒙软总线:基于UDP的数据传输奥秘与优势》
单片机·udp·harmonyos
瓢儿菜20185 小时前
proteus8.17 环境配置
单片机·proteus·环境配置