最近接触芯科的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()函数,流程可以类似的分析了