Solution to the problem that the zigbee terminal cannot reconnect
[Copy link]
1. Reasons for Zigbee reconnection
(1) Zigbee disconnects due to poor signal quality caused by interference from various reasons.
(2) The coordinator restarts.
2. Processing of Zigbee terminal reconnection
(1) After Zigbee is disconnected, it will enter the callback function: void ZDO_SyncIndicationCB( uint8 type, uint16 shortAddr );
generate ZDO_NWK_JOIN_REQ, and then reinitialize the network:
case ZDO_NWK_JOIN_REQ: //Reconnection eventif
( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
{
retryCnt = 0;
devStartMode = MODE_RESUME; //Put the device in network recovery mode (I think it is also the reconnection mode)
_tmpRejoinState = true; //Initialize the temporary state to reconnect
osal_cpyExtAddr( ZDO_UseExtendedPANID, _NIB.extendedPANID ); //Initialize ZDO to the previous PANID
zgDefaultStartingScanDuration = BEACON_ORDER_60_MSEC; //Send a beacon every 60 milliseconds
ZDApp_NetworkInit( 0 ); //Reinitialize the network and generate a ZDO_NETWORK_INIT event
}
(2) The device will then be restarted and the network will be initialized in the reconnection manner.
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) When starting the device, the terminal node is an orphan node (Orphan) to join the network.
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) If joining the network as an orphan node still fails, the protocol stack will change the ZigBee network access mode to MODE_JOIN or 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_SetQueuedPoll Rate( 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 )
{
//If nwkPanId is not set, set the device network access mode to first network access; if nwkPanId has been set, set the network access mode to re-entryif
( _NIB.nwkPanId == 0xFFFF || _NIB.nwkPanId == INVALID_PAN_ID )
devStartMode = MODE_JOIN;//First network accesselse
{
devStartMode
= MODE_REJOIN;//Re-entry_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) Then initialize the device again, this time by rejoining. In fact, here we just start a network scan: 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) ) //Start a network scan according to whether the current startup mode is connection or reconnection mode
{
devState = DEV_NWK_DISC;
#if defined( MANAGED_SCAN )
ZDOManagedScan_Next();
ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );
#else
ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );//Start network scan request
#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) If a device is connected, the callback function will be called: ZDO_NetworkDiscoveryConfirmCB(uint8 status), and the ZDO_NWK_DISC_CNF event will be generated in this callback function. Then process the event in void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr ), read panid and other data from NV during the reconnection process, and finally issue a reconnection request 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_JO IN )
else if (devStartMode == MODE_REJOIN)//Reconnection processing
{
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;
//Read the network from NV Information
// 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) );//Set the new panID into 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); //Issue reconnection request
}
else
{
rejoinStatus = NLME_ReJoinRequestUnsecure( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel); //Issue reconnection request
}
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) When the device successfully joins the network, the callback function ZDO_JoinConfirmCB(uint16 PanId, ZStatus_t Status) will be called, and then the short address of the device will be declared (so that the coordinator knows that the device has successfully joined the network), and the ZDO_STATE_CHANGE_EVT network change event will be generated. The state of devState is changed in void ZDO_JoinConfirmCB(), and the successful network joining will be processed in 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; //Change the devState status, indicating that the network has been successfully connected
}
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 );
}
//Processing successful network access
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 //This implies: if(devState == DEV_NWK_JOINING)
{
// Assume from address conflict
if ( _NIB.nwkAddrAlloc == NWK_ADDRESSING_STOCHASTIC )
{
// Notify the network
ZDApp_AnnounceNewAddress();//Declare the short address of this device
// Notify apps
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );//Notify network status change
}
}
}
4. Analysis of zigbee reconnection failure.
(1) Reconnection success and reconnection failure First, use the packet capture tool Packet Sniffer to analyze
the following is a successful reconnection diagram: The visible data packets are ===》Beancon Request==>Coordinator's response packet==》Terminal's NWK Rejoin Request data packet===》Terminal's Data Request ==》Coordinator's response NWK Rejoin Response packet
The following is a picture of a failed reconnection: The data packets you can see are ===》Beancon Request==>Coordinator's response packet==》Terminal's NWK Rejoin Request data packet===》Beancon Request
From the comparison of the above data packets, we can see that the data packet of the failed reconnection does not have the "Data Request" of the terminal. This makes it impossible for the terminal to know whether the previously connected device is still online. Then the terminal can only think that the network is a new network and needs to be reconnected. It cannot reconnect the old device.
Finally, it was found that "-DREJOIN_POLL_RATE=0" in the f8wConfig.cfg file, so if the coordinator is restarted, the terminal cannot reconnect. Just change the reconnection polling frequency to 440 "-DREJOIN_POLL_RATE=440".
5. Summary
During the development of the ZigBee program, if you encounter a problem that you cannot solve, you should analyze it from multiple angles: code + debugger + packet capture tool. The packet capture tool is very useful. You only need to capture the packet to know whether the data is correct.
|