芯科ZigBee Minimal Application入网流程分析

it2025-01-18  13

前言

最近接触芯科的EFR32MG21A020F768型号的zigbee SOC,跑了一下simplicity studio 4.0上自带的ZigBee Minimal Application,顺便加点日志跟一下入网的流程。上电的流程已经有大神分析过,可以大致参考一下,这里只关注入网的流程: 上电流程

正文

我这次编译的是一个router类型设备,上电后自动搜索附近的网关。流程大致如下:

simple-main/simple-main.c

int MAIN(MAIN_FUNCTION_PARAMETERS) { halInit(); emberAfMainInit(); return emberAfMain(MAIN_FUNCTION_ARGUMENTS); }

在return的时候开始应用框架层的初始化

zcl-framework-core/af-main-soc.c

int emberAfMain(MAIN_FUNCTION_PARAMETERS) { ... emberAfRunEvents(); }

zcl-framework-core/af-event.c

EmberEventData emAfEvents[] = { EM_AF_SERVICE_DISCOVERY_EVENTS #ifdef EMBER_AF_GENERATED_EVENTS EMBER_AF_GENERATED_EVENTS #endif #ifdef EMBER_AF_PLUGIN_FRAGMENTATION EMBER_AF_FRAGMENTATION_EVENTS #endif EMBER_KEY_ESTABLISHMENT_TEST_HARNESS_EVENT { NULL, NULL } }; const char emAfStackEventString[] = "Stack"; // ***************************************************************************** // Functions // A function used to initialize events for idling void emAfInitEvents(void) { emberTaskEnableIdling(true); emAfTaskId = emberTaskInit(emAfEvents); } void emberAfRunEvents(void) { // Don't run events while crypto operation is in progress // (BUGZID: 12127) if (emAfIsCryptoOperationInProgress()) { // DEBUG Bugzid: 11944 emberAfCoreFlush(); return; } emberRunTask(emAfTaskId); }

emberAfRunEvents()函数顾名思义就是开始跑各种的事件,其中就包括我们选择的各种plugin的时候自动生成的回调函数,如下所示

af-gen-event.h

/ EmberEventData structs used to populate the EmberEventData table #define EMBER_AF_GENERATED_EVENTS \ { &emberAfPluginConnectionManagerPollEventControl, emberAfPluginConnectionManagerPollEventHandler }, \ { &emberAfPluginConnectionManagerRebootEventControl, emberAfPluginConnectionManagerRebootEventHandler }, \ { &emberAfPluginConnectionManagerRejoinEventControl, emberAfPluginConnectionManagerRejoinEventHandler }, \ { &emberAfPluginLedBlinkLed0EventFunctionEventControl, emberAfPluginLedBlinkLed0EventFunctionEventHandler }, \ { &emberAfPluginLedBlinkLed1EventFunctionEventControl, emberAfPluginLedBlinkLed1EventFunctionEventHandler }, \ { &emberAfPluginManufacturingLibraryCliCheckSendCompleteEventControl, emberAfPluginManufacturingLibraryCliCheckSendCompleteEventHandler }, \ { &emberAfPluginNetworkSteeringFinishSteeringEventControl, emberAfPluginNetworkSteeringFinishSteeringEventHandler }, \ { &emberAfPluginReportingTickEventControl, emberAfPluginReportingTickEventHandler }, \ { &emberAfPluginScanDispatchScanEventControl, emberAfPluginScanDispatchScanEventHandler }, \ { &emberAfPluginUpdateTcLinkKeyBeginTcLinkKeyUpdateEventControl, emberAfPluginUpdateTcLinkKeyBeginTcLinkKeyUpdateEventHandler }, \

1、这里我们先关注emberAfPluginConnectionManagerRebootEventHandler,这个是上电后自动入网的入口函数

connection-manager/connection-manager.c

//------------------------------------------------------------------------------ // Plugin event handlers //****************************************************************************** // Reboot event. To be called sometime after all system init functions have // executed. This function will check the network state, and initiate a search // for new networks to join if the device is not currently on a network. //****************************************************************************** void emberAfPluginConnectionManagerRebootEventHandler(void) { uint8_t shortPollForced; halCommonGetToken(&shortPollForced, TOKEN_FORCE_SHORT_POLL); if (shortPollForced) { emberAfAppPrint("Short poll forced to permanently enabled."); FORCE_SHORT_POLL(); } else { UNFORCE_SHORT_POLL(); } emberEventControlSetInactive(emberAfPluginConnectionManagerRebootEventControl); if (emberAfNetworkState() == EMBER_NO_NETWORK) { emberAfPluginConnectionManagerLeaveNetworkCallback(); emberAfPluginConnectionManagerStartSearchForJoinableNetwork(); } }

通过emberAfNetworkState()函数获取到当前设备没有连接任何网络的时候,就开始调用emberAfPluginConnectionManagerStartSearchForJoinableNetwork()去寻找可加入的网络

2、再看看emberAfPluginConnectionManagerRejoinEventHandler函数,其实做的事情也差不多,最后也调用到emberAfPluginConnectionManagerStartSearchForJoinableNetwork()去入网

connection-manager/connection-manager.c

//****************************************************************************** // Rejoin Event. This event is used to attempt a network rejoin and to verify // that the parent node has not died. //****************************************************************************** void emberAfPluginConnectionManagerRejoinEventHandler(void) { emberEventControlSetInactive(emberAfPluginConnectionManagerRejoinEventControl); printNetworkState(emberAfNetworkState()); switch (emberAfNetworkState()) { case EMBER_NO_NETWORK: emberAfPluginConnectionManagerStartSearchForJoinableNetwork(); break; case EMBER_JOINED_NETWORK_NO_PARENT: // since the sensor is a sleepy end device, perform the secure rejoing // every 30 minutes until we find a network. emberAfAppPrintln("Perform and schedule rejoin"); emberEventControlSetDelayMinutes( emberAfPluginConnectionManagerRejoinEventControl, REJOIN_TIME_MINUTES); emberAfStartMoveCallback(); break; case EMBER_JOINING_NETWORK: break; default: emberAfAppPrintln("No More Rejoin!"); break; } }

不过这里不是很明白为什么1、2点最后都调用到emberAfPluginConnectionManagerStartSearchForJoinableNetwork,不过最终的效果就是上电自动开始寻找可加入的网络

connection-manager/connection-manager.c

//------------------------------------------------------------------------------ // Plugin public API function implementations // ***************************************************************************** // If this is the first search, search only on preferred channels // Otherwise, search on all channels // Make sure another attempt occurs in 40 seconds until 20 failures are seen // If more than 20 network join attempts fail, inform user via callback // ***************************************************************************** void emberAfPluginConnectionManagerStartSearchForJoinableNetwork(void) { if (emberAfMfglibRunning() || emberAfMfglibEnabled()) { return; } if (networkJoinAttempts < REJOIN_ATTEMPTS) { networkJoinAttempts++; emberAfPluginNetworkSteeringStart(); emberAfPluginConnectionManagerStartNetworkSearchCallback(); // call the event in 40 seconds in case we don't get the stack status // callback (which will happen if there's no network to join) emberEventControlSetDelayQS(emberAfPluginConnectionManagerRejoinEventControl, QS_BETWEEN_JOIN_ATTEMPTS); } else { emberAfAppPrintln("Failed to find network to join within %d attempts", networkJoinAttempts); emberAfPluginConnectionManagerFinishedCallback(EMBER_NOT_JOINED); } }

可以看到,我们的router设备会尝试REJOIN_ATTEMPTS这么多次,去寻找可加入的网络。具体到怎么搜索的流程我们暂时不看了,一旦找到了适合的网络,就会自动入网,我们看看串口打印:

Examining beacon on channel 12 with panId 0x614A NWK Steering joining 0x614A on channel 12 EMBER_NETWORK_UP 0xBC6E sched report event for: 0x03E7BED1 NWK Steering stack status 0x90 NWK Steering network joined. Stack Status Handler: Processing message: len=12 profile=0000 cluster=0013 RX: ZDO, command 0x0013, status: 0x00 Device Announce: 0xBC6E Processing message: len=11 profile=0104 cluster=0006

可以看到找到的网络的频段为12,PANID为0x614A。当状态发生变化时,比如"EMBER_NETWORK_UP"表示联网,就会走如下流程,修改相关的状态

zcl-framework-core/af-main-soc.c

// ******************************************************************* // Handlers required to use the Ember Stack. // Called when the stack status changes, usually as a result of an // attempt to form, join, or leave a network. void emberStackStatusHandler(EmberStatus status) { emberAfPushCallbackNetworkIndex(); emAfStackStatusHandler(status); emberAfPopNetworkIndex(); }

重点关注emAfStackStatusHandler()

zcl-framework-core/af-main-common.c

void emAfStackStatusHandler(EmberStatus status) { ... #ifdef EMBER_AF_GENERATED_PLUGIN_STACK_STATUS_FUNCTION_CALLS EMBER_AF_GENERATED_PLUGIN_STACK_STATUS_FUNCTION_CALLS #endif ... }

又是通过宏定义来定义回调函数

YourProjectName_endpoint_config.h

#define EMBER_AF_GENERATED_PLUGIN_STACK_STATUS_FUNCTION_CALLS \ emberAfPluginReportingStackStatusCallback(status); \ emberAfPluginNetworkSteeringStackStatusCallback(status); \ emberAfPluginConnectionManagerStackStatusCallback(status); \

其中,

(1)emberAfPluginReportingStackStatusCallback()会上报设备默认的配置信息

// Generated reporting configuration defaults #define EMBER_AF_GENERATED_REPORTING_CONFIG_DEFAULTS {\ { EMBER_ZCL_REPORTING_DIRECTION_REPORTED, 1, 0x0006, 0x0000, CLUSTER_MASK_SERVER, 0x0000, 1, 65534, 0 }, \ }

report的方向、endpoint、cluster ID、attribute Id等等。

(2)emberAfPluginNetworkSteeringStackStatusCallback()函数里面更新一些秘钥信息

(3)emberAfPluginConnectionManagerStackStatusCallback()函数里面没有做太多事情,就是reset一下networkJoinAttempts全局变量

3、最后我们再关注一下emberAfPluginNetworkSteeringFinishSteeringEventHandler()函数

network-steering/network-steering-v2.c

// ============================================================================= // Finish Steering // At the end of the network steering process, we need to update the // trust center link key (if we are in a centralized network) and broadcast // a permit join to extend the network. This process needs to happen after // we send our device announce and possibly our network timeout request if we // are an end device. void emberAfPluginNetworkSteeringFinishSteeringEventHandler(void) { ... // Broadcast permit join to extend the network. // We are done! status = emberAfPermitJoin(EMBER_AF_PLUGIN_NETWORK_STEERING_COMMISSIONING_TIME_S, true); // Broadcast permit join? emberAfCorePrintln("%p: %p: 0x%X", PLUGIN_NAME, "Broadcasting permit join", status); cleanupAndStop(status); ... }

1、通过emberAfPermitJoin判断是否被允许入网

zcl-framework-core/af-main-common.c

// Public API EmberStatus emberAfPermitJoin(uint8_t duration, bool broadcastMgmtPermitJoin) { // Permit joining forever is bad behavior, so we want to limit // this. If 254 is not enough a re-broadcast should be done later. if (duration == EMBER_AF_PERMIT_JOIN_FOREVER) { emberAfAppPrintln("Limiting duration of permit join from forever (255) to 254"); duration = EMBER_AF_PERMIT_JOIN_MAX_TIMEOUT; } return emAfPermitJoin(duration, broadcastMgmtPermitJoin); } // Old API that doesn't restrict prevent permit joining forever (255) EmberStatus emAfPermitJoin(uint8_t duration, bool broadcastMgmtPermitJoin) { EmberStatus status = emberPermitJoining(duration); emberAfAppPrintln("pJoin for %d sec: 0x%x", duration, status); if (broadcastMgmtPermitJoin) { status = broadcastPermitJoin(duration); } return status;

从注释可以看到,在等待被允许入网的时间是有限的,一直等下去是一个不好的行为,EMBER_AF_PLUGIN_NETWORK_STEERING_COMMISSIONING_TIME_S这个宏就定义了等待的时间,我这里是180s。

2、最后调用cleanupAndStop()函数完成最后的入网工作

network-steering/network-steering-v2.c

static void cleanupAndStop(EmberStatus status) { emberAfCorePrintln("%p Stop. Cleaning up.", PLUGIN_NAME); emberAfPluginNetworkSteeringCompleteCallback(status, emAfPluginNetworkSteeringTotalBeacons, emAfPluginNetworkSteeringJoinAttempts, emAfPluginNetworkSteeringState); emAfPluginNetworkSteeringState = EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_NONE; emAfPluginNetworkSteeringJoinAttempts = 0; emAfPluginNetworkSteeringTotalBeacons = 0; emberEventControlSetInactive(finishSteeringEvent); }

connection-manager/connection-manager.c

/** @brief Complete * * This callback is fired when the Network Steering plugin is complete. * * @param status On success this will be set to EMBER_SUCCESS to indicate a * network was joined successfully. On failure this will be the status code of * the last join or scan attempt. Ver.: always * @param totalBeacons The total number of 802.15.4 beacons that were heard, * including beacons from different devices with the same PAN ID. Ver.: always * @param joinAttempts The number of join attempts that were made to get onto * an open Zigbee network. Ver.: always * @param finalState The finishing state of the network steering process. From * this, one is able to tell on which channel mask and with which key the * process was complete. Ver.: always */ void emberAfPluginNetworkSteeringCompleteCallback(EmberStatus status, uint8_t totalBeacons, uint8_t joinAttempts, uint8_t finalState) { emberAfAppPrintln("Network Steering Completed: %p (0x%X)", (status == EMBER_SUCCESS ? "Join Success" : "FAILED"), status); emberAfAppPrintln("Finishing state: 0x%X", finalState); emberAfAppPrintln("Beacons heard: %d\nJoin Attempts: %d", totalBeacons, joinAttempts); emberAfAppPrintln("Connection Manager: Network Find status %x", status); if (status == EMBER_SUCCESS) { emberEventControlSetInactive(emberAfPluginConnectionManagerRejoinEventControl); } else { // delay the rejoin for the retry time in seconds set by plugin option emberEventControlSetDelayQS(emberAfPluginConnectionManagerRejoinEventControl, (REJOIN_FAILED_RETRY_TIME_QS)); } }

入网成功的串口打印如下:

pJoin for 180 sec: 0x00 NWK Steering: Broadcasting permit join: 0x00 NWK Steering Stop. Cleaning up. Network Steering Completed: Join Success (0x00) Finishing state: 0x05 Beacons heard: 1 Join Attempts: 1 Connection Manager: Network Find status 00 Processing message: len=11 profile=0104 cluster=0006

结语

同样,断网"EMBER_NETWORK_DOWN"同样调用emberStackStatusHandler()函数,流程可以类似的分析了

最新回复(0)