zoukankan      html  css  js  c++  java
  • 【转】zigbee终端无法重连的问题解决

    zigbee终端无法重连的问题解决

    1.zigbee重连的原因

    (1)zigbee由于各种原因的干扰导致信号太差而掉线。 
    (2)协调器重启。

    2.zigbee终端重连的处理

    (1)zigbee掉线后会进入回调函数:void ZDO_SyncIndicationCB( uint8 type, uint16 shortAddr );

    产生ZDO_NWK_JOIN_REQ,之后会重新初始化网络:

    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事件
      }
    

    (2)接着会重新启动设备,按重连的方式初始化网络。

      if ( events & ZDO_NETWORK_INIT )
      {
        // Initialize apps and start the network
        devState = DEV_INIT;
        osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
    
        ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
    
        // Return unprocessed events
        return (events ^ ZDO_NETWORK_INIT);
      }      
    

    (3)启动设备的时候,终端节点是已孤儿节点(Orphan)来入网的。

    void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
    {
     ...........
        else if ( startMode == MODE_RESUME )
        {
          if ( logicalType == NODETYPE_ROUTER )
          {
            ZMacScanCnf_t scanCnf;
            devState = DEV_NWK_ORPHAN;
    
            /* if router and nvram is available, fake successful orphan scan */
            scanCnf.hdr.Status = ZSUCCESS;
            scanCnf.ScanType = ZMAC_ORPHAN_SCAN;
            scanCnf.UnscannedChannels = 0;
            scanCnf.ResultListSize = 0;
            nwk_ScanJoiningOrphan(&scanCnf);
    
            ret = ZSuccess;
          }
          else
          {
            devState = DEV_NWK_ORPHAN;
            ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
                                          zgDefaultStartingScanDuration );
          }
        }
        else
        {
        }
      }
    .....
    }
    

    (4)加入以孤儿节点的方式还是入网失败,则协议栈会让zigbee的入网模式改为MODE_JOIN或者MODE_REJOIN。

    void ZDApp_ProcessNetworkJoin( void )
    {
    ........
      else if ( devState == DEV_NWK_ORPHAN || devState == DEV_NWK_REJOIN )
      {
        // results of an orphaning attempt by this device
        if (nwkStatus == ZSuccess)
        {
          // Verify NWK key is available before sending Device_annce
          if ( ZG_SECURE_ENABLED && ( ZDApp_RestoreNwkKey() == false ) )
          {
            osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
    
        // wait for auth from trust center
        devState = DEV_END_DEVICE_UNAUTH;
    
        // Start the reset timer for MAX UNAUTH time
        ZDApp_ResetTimerStart( MAX_DEVICE_UNAUTH_TIMEOUT );
      }
      else
      {
    
        devState = DEV_END_DEVICE;
        osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
        // setup Power Manager Device
        // The receiver is on, turn network layer polling off.
        if ( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE )
        {
          {
            NLME_SetPollRate( 0 );
            NLME_SetQueuedPollRate( 0 );
            NLME_SetResponseRate( 0 );
          }
        }
    
        if ( ZSTACK_ROUTER_BUILD )
        {
          // NOTE: first two parameters are not used, see NLMEDE.h for details
          if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
          {
            NLME_StartRouterRequest( 0, 0, false );
          }
        }
    
        ZDApp_AnnounceNewAddress();
      }
    }
    else
    {
      if ( devStartMode == MODE_RESUME )
      {
        if ( ++retryCnt <= MAX_RESUME_RETRY )
        {
          //如果nwkPanId没有设置,则让设备入网模式设为第一次入网;如果nwkPanId有设置过则入网模式设为重新入网
          if ( _NIB.nwkPanId == 0xFFFF || _NIB.nwkPanId == INVALID_PAN_ID )
            devStartMode = MODE_JOIN;//第一次入网
          else
          {
            devStartMode = MODE_REJOIN;//重新入网
            _tmpRejoinState = true;
          }
        }
        // Do a normal join to the network after certain times of rejoin retries
        else if( AIB_apsUseInsecureJoin == true )
        {
          devStartMode = MODE_JOIN;
        }
      }
      // Clear the neighbor Table and network discovery tables.
      nwkNeighborInitTable();
      NLME_NwkDiscTerm();
    
      // setup a retry for later...
      ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
           + (osal_rand()& EXTENDED_JOINING_RANDOM_MASK)) );
        }
      }
    .......
      }
    }
    

    (4)接着重新再次初始化设备,这次是以rejoin的方式来初始化设备的。其实在这里只是启动一个网络扫描而已: NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );

    void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
    {
     ..........
      if ( ZG_BUILD_JOINING_TYPE && (logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE) )
      {
        if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )       //根据当前启动模式是连接或者重连模式则启动一个网络扫描
        {
          devState = DEV_NWK_DISC;
    
      #if defined( MANAGED_SCAN )
          ZDOManagedScan_Next();
          ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );
      #else
          ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );//启动网络扫描请求
        #if defined ( ZIGBEE_FREQ_AGILITY )
          if ( !( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE ) &&
                ( ret == ZSuccess ) && ( ++discRetries == 4 ) )
          {
            // For devices with RxOnWhenIdle equals to FALSE, any network channel
            // change will not be recieved. On these devices or routers that have
            // lost the network, an active scan shall be conducted on the Default
            // Channel list using the extended PANID to find the network. If the
            // extended PANID isn't found using the Default Channel list, an scan
            // should be completed using all channels.
            zgDefaultChannelList = MAX_CHANNELS_24GHZ;
          }
        #endif // ZIGBEE_FREQ_AGILITY
        #if defined ( ZIGBEE_COMMISSIONING )
          if (startMode == MODE_REJOIN && scanCnt++ >= 5 )
          {
            // When ApsUseExtendedPanID is commissioned to a non zero value via
            // application specific means, the device shall conduct an active scan
            // on the Default Channel list and join the PAN with the same
            // ExtendedPanID. If the PAN is not found, an scan should be completed
            // on all channels.
            // When devices rejoin the network and the PAN is not found from
            zgDefaultChannelList = MAX_CHANNELS_24GHZ;
          }
        #endif // ZIGBEE_COMMISSIONING
      #endif
        }
    .......
      if ( ret != ZSuccess )
      {
        osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
      }
    }
    

    (5)有设备接入则会调用回调函数:ZDO_NetworkDiscoveryConfirmCB(uint8 status),在这个回调函数会产生ZDO_NWK_DISC_CNF事件。然后在void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )里面处理该事件,重连过程中会从NV中读取panid等数据, 最后发出重新连接的申请NLME_ NLME_ReJoinRequest( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel);

    void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr ) 
    { 
    ……. 
    switch ( msgPtr->event ) 
    { 
    ……….. 
    case ZDO_NWK_DISC_CNF: 
    if (devState != DEV_NWK_DISC) 
    break;

      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        // Process the network discovery scan results and choose a parent
        // device to join/rejoin itself
        networkDesc_t *pChosenNwk;
        if ( ( (pChosenNwk = ZDApp_NwkDescListProcessing()) != NULL ) && (zdoDiscCounter > NUM_DISC_ATTEMPTS) )
        {
          if ( devStartMode == MODE_JOIN )
          {
            devState = DEV_NWK_JOINING;
            ZDApp_NodeProfileSync( pChosenNwk->stackProfile);
            if ( NLME_JoinRequest( pChosenNwk->extendedPANID, pChosenNwk->panId,
                                  pChosenNwk->logicalChannel,
                                  ZDO_Config_Node_Descriptor.CapabilityFlags,
                                  pChosenNwk->chosenRouter, pChosenNwk->chosenRouterDepth ) != ZSuccess )
            {
              ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
                                          + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
            }
          } // if ( devStartMode == MODE_JOIN )
          else if ( devStartMode == MODE_REJOIN )//重连的处理
          {
            ZStatus_t rejoinStatus;
    
            devState = DEV_NWK_REJOIN;
    
            // Before trying to do rejoin, check if the device has a valid short address
            // If not, generate a random short address for itself
            if ( _NIB.nwkDevAddress == INVALID_NODE_ADDR )
            {
              uint16 commNwkAddr;
               //从NV里面读取网络信息
              // Verify if the Network address has been commissioned by external tool
              if ( ( osal_nv_read( ZCD_NV_COMMISSIONED_NWK_ADDR, 0,
                                 sizeof(commNwkAddr),
                                 (void*)&commNwkAddr ) == ZSUCCESS )   &&
                   ( commNwkAddr != INVALID_NODE_ADDR ) )
              {
                _NIB.nwkDevAddress = commNwkAddr;
    
                // clear Allocate address bit because device has a commissioned address
                _NIB.CapabilityFlags &= ~CAPINFO_ALLOC_ADDR;
              }
              else
              {
                _NIB.nwkDevAddress = osal_rand();
              }
              ZMacSetReq( ZMacShortAddress, (byte*)&_NIB.nwkDevAddress );
            }
    
            // Check if the device has a valid PanID, if not, set it to the discovered Pan
            if ( _NIB.nwkPanId == INVALID_PAN_ID )
            {
              _NIB.nwkPanId = pChosenNwk->panId;
              ZMacSetReq( ZMacPanId, (byte*)&(_NIB.nwkPanId) );//设置新的panID到NV里面
            }
    
            tmp = true;
            ZMacSetReq( ZMacRxOnIdle, &tmp ); // Set receiver always on during rejoin
    
            // Perform Secure or Unsecure Rejoin depending on available configuration
            if ( ZG_SECURE_ENABLED && ( ZDApp_RestoreNwkKey() == TRUE ) )
            {
              rejoinStatus = NLME_ReJoinRequest( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel);   //发出重新连接的申请
            }
            else
            {
              rejoinStatus = NLME_ReJoinRequestUnsecure( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel);   //发出重新连接的申请
            }
            if ( rejoinStatus != ZSuccess )
            {
              ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
                                          + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
            }
          } // else if ( devStartMode == MODE_REJOIN )
          // The receiver is on, turn network layer polling off.
          ...........
          }
        }
        else
        {
          if ( continueJoining )
          {
            zdoDiscCounter++;
            ZDApp_NetworkInit( (uint16)(BEACON_REQUEST_DELAY
                  + ((uint16)(osal_rand()& BEACON_REQ_DELAY_MASK))) );
    #endif
          }
        }
      }
      break;
    ..........
    

    } 
    }

    (6)设备入网成功会调用入网成功的回调函数ZDO_JoinConfirmCB(uint16 PanId, ZStatus_t Status),接着声明本设备的短地址(这样协调器才知道设备入网成功),并产生ZDO_STATE_CHANGE_EVT网络改变的事件。devState 的状态是在void ZDO_JoinConfirmCB( )里面改变的,ZDApp_ProcessNetworkJoin( void )里面才会处理入网成功。

    void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
    {
          (void)PanId;  // remove if this parameter is used.
          nwkStatus = (byte)Status;
    
      if ( Status == ZSUCCESS )
      {
        if ( ZSTACK_END_DEVICE_BUILD
          || (ZSTACK_ROUTER_BUILD && ((_NIB.CapabilityFlags & ZMAC_ASSOC_CAPINFO_FFD_TYPE) == 0)))
        {
          neighborEntry_t *pItem;
    
          // We don't need the neighbor table entries.  
          // Clear the neighbor Table to remove beacon information
          nwkNeighborInitTable();
    
          // Force a neighbor table entry for the parent
          pItem = nwkNeighborFindEmptySlot();
          if ( pItem != NULL )
          {
            osal_memset( pItem, 0x00, sizeof ( neighborEntry_t  )  );
            pItem->neighborAddress = _NIB.nwkCoordAddress;
            osal_cpyExtAddr( pItem ->neighborExtAddr, _NIB. nwkCoordExtAddress );
            pItem->panId = _NIB. nwkPanId;
            pItem->linkInfo.rxLqi = DEF_LQI;
            pItem->linkInfo.txCounter = DEF_LINK_COUNTER;
            pItem->linkInfo.txCost = DEF_LINK_COST;
          }
        }
        if ( (devState == DEV_HOLD) )
        {
          // Began with HOLD_AUTO_START
          devState = DEV_NWK_JOINING;//改变devState状态,代表入网已经成功
        }
    
        if ( !ZG_SECURE_ENABLED )
        {
          // Notify to save info into NV
          ZDApp_NVUpdate();
        }
      }
      else
      {
      }
    
      // Pass the join confirm to higher layer if callback registered
      if (zdoCBFunc[ZDO_JOIN_CNF_CBID] != NULL )
      {
        zdoJoinCnf_t joinCnf;
    
        joinCnf.status = Status;
        joinCnf.deviceAddr = _NIB.nwkDevAddress;
        joinCnf.parentAddr = _NIB.nwkCoordAddress;
        zdoCBFunc[ZDO_JOIN_CNF_CBID]( (void*)&joinCnf );
      }
    
      // Notify ZDApp
      ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_IND, sizeof(osal_event_hdr_t), (byte*)NULL );
    }
    
    //入网成功的处理
    void ZDApp_ProcessNetworkJoin( void )
    {
        if ( (devState == DEV_NWK_JOINING) ||
          ((devState == DEV_NWK_ORPHAN)  &&
           (ZDO_Config_Node_Descriptor.LogicalType == NODETYPE_ROUTER)) )
        {
        }else if( devState == DEV_NWK_ORPHAN || devState == DEV_NWK_REJOIN ){
        .........
        else        //该处暗含:if(devState == DEV_NWK_JOINING)
        {
             // Assume from address conflict
             if ( _NIB.nwkAddrAlloc == NWK_ADDRESSING_STOCHASTIC )
             {
               // Notify the network
               ZDApp_AnnounceNewAddress();//声明本设备的短地址
    
              // Notify apps
              osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );//告知网络状态改变
            }
          }
    }
    

    4.zigbee重连失败分析。

    (1)重连成功与重连失败首先要用抓包工具Packet Sniffer来分析 
    下面是重连成功的图:能看的的数据包是===》Beancon Request==>协调器的回应包==》终端的NWK Rejoin Request 数据包 ===》终端的Data Request ==》协调器回应 NWK Rejoin Response包 
    重连成功的图

    下面是重连失败的图:能看到的数据包是 ===》Beancon Request==>协调器的回应包==》终端的NWK Rejoin Request 数据包 ===》 Beancon Request 
    重连失败的图

    *从上面的数据包对比可知,重连失败的数据包没有=》终端的Data Request《=, 这样导致终端无法得知之前连接的设备是否还在线,那么终端只能认为该网络是全新的网络,需要重新连接,不能重连旧设备。 
    接下来要查明为什么不发Data Request的包,查看代码发现某个地方设置不查询数据NLME_SetPollRate(0);当时写这行代码是为了zigbee设备能够更好的休眠,尽量减少数据的发送,故关闭数据轮询。把NLME_SetPollRate(0)注释掉即可解决问题,重启协调器,终端能够正常地重连到协调器上。

  • 相关阅读:
    Windows彻底卸载VMWare虚拟机
    DFS遍历拷贝所有子文件夹及目录列表 (Java版)
    IDEA -01 -忽略指定文件夹 -防止加载Vue-cli执行"npm install"命令后的项目时卡死
    SourceTree&Git -01 -代码拉取推送流程 -提交时的相关注意事项
    [牛客网 -leetcode在线编程 -02] minimum-depth-of-binary-tree -树的最短深度
    [牛客网 -leetcode在线编程 -01] max-points-on-a-line -穷举
    SpringBoot -生成Entity和Dto互转的双向枚举类 -使用注解@Mapper(componentModel = "spring")
    Druid Spring Boot Starter 从配置到简单运行 -解决zone不匹配 -解决dataSource加载失败
    Vuex基础 -01 -实现简易计数器 -支持 加数/ 减数/ 奇数再加/ 异步加法(setTimeout 1000ms) -单组件演示语法
    SpringBoot -基础学习笔记
  • 原文地址:https://www.cnblogs.com/yelin/p/6054858.html
Copyright © 2011-2022 走看看