【轉】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)注釋掉即可解決問題,重啟協調器,終端能夠正常地重連到協調器上。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM