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《=, 这样导致终端无法得知之前连接的设备是否还在线,那么终端只能认为该网络是全新的网络,需要重新连接,不能重连旧设备。
最后发现f8wConfig.cfg文件里“-DREJOIN_POLL_RATE=0”,那么假如协调器重启了,终端就不能重新连上去了,只需把重连的轮询频率改成440即可“-DREJOIN_POLL_RATE=440”。
5.总结
在zigbee程序开发过程中,如果遇到无法解决的问题,应该从多个角度下手去分析:代码+调试器+抓包工具,其中抓包工具的作用非常大,数据对不对,只需抓一下包就能知道什么回事。
|