芯科ZigBee Minimal Application入网流程分析

前言

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

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值