CuiyyTime

芯科EmberZNet_ZigBee3.0_EFR32MG开发日记六:ZigBee3.0网络创建与加入2

0
阅读(5365)
 
6 ZigBee实验1操作

 

    使用一个节点按1-5节的介绍,完成Coord工程的烧写。

    使用另一个节点按1-5节的介绍,完成Router工程的烧写。

 

使用micro-USB数据线或2节AA电池为节点供电。

image

micro-USB数据线供电

image

电池供电

为COORD节点和Router节点分别打开电源开关。

COORD节点液晶显示如下表所示。

液晶屏显示内容

说明

COORD

节点类型:协调器

Node:90FD9FFFFEACD3C

节点物理地址

Channel:15

信道

PanId:0x256E

网络号

NodeId:0x0000

节点网络地址

Router节点液晶显示如下表所示。

液晶屏显示内容

说明

Router

节点类型:路由器

Node:90FD9FFFFEAA6294

节点物理地址

 

按下COORD节点的KEY1按键,液晶屏显示如下表所示,LED灯3开始闪烁。在COORD节点上按下KEY1按键,允许其它节点在300秒内加入Coord创建的网络,再次按下KEY1按键,可以立即禁止其它节点加入。

液晶屏显示内容

说明

COORD

节点类型:协调器

Node:90FD9FFFFEACD3C

节点物理地址

Channel:15

信道

PanId:0x256E

网络号

NodeId:0x0000

节点网络地址

PermitJoin… 298S

允许节点加入网络倒计时

 

按下Router节点的KEY1按键,液晶屏显示如下表所示。按下Router节点的KEY1按键,Router节点开始搜索网络,并曾试加入现有的网络。

液晶屏显示内容

说明

Router

节点类型:路由器

Node:90FD9FFFFEAA6294

节点物理地址

NetworkSteering...

正在曾试加入网络

 

等待一会儿,如果Router节点成功加入网络,液晶屏显示如下表所示。

液晶屏显示内容

说明

Router

节点类型:路由器

Node:90FD9FFFFEAA6294

Router节点物理地址

Channel:15

Router加入网络的信道

PanId:0x256E

Router加入网络的网络号

NodeId:0x2E9E

Router节点网络地址

 

7 ZigBee术语

Attribute属性

属性是一个反映物理数量或状态的数据值,比如开关值(On/Off)、灯的状态值(On/Off)、温度值、百分比等等。

 

Cluster群集或称为簇

群集是包含一个或多个属性(attribute)、命令(command)、报告(reporting)的群集。简单的说,群集就是属性、命令和报告的集合。每个群集都被分配一个唯一群集ID且每个群集最多有65536个属性、命令和报告。比如一个群集包含了不同情况下的开关、不同情况下的灯、不同情况下的温度值、不同情况下的百分比等等。

 

Device Description设备描述

设备描述是指一个大型目标应用的一部分,包括一个或多个群集。

 

EndPoint端点

端点是协议栈应用层的入口,即入口地址,也可以理解应用对象(Application Object)存在的地方,它是为实现一个设备描述而定义的一组群集。

每个ZigBee设备可以最多支持240这样的端点,端口0用于整个ZigBee设备的配置和管理,应用程序可以通过端点0与ZigBee堆栈的其他层通信,从而实现对这些层的初始化和配置。附属在端点0的对象被称为ZigBee设备对象(ZDO)。端点255用于向所有端点的广播,端点241~254是保留端点。其余的1~240端点留给用户层使用。

在一个终端节点上,可以有多个端点endpoint,这个概念是很重要的。

一个节点可以有多个端点,0号endpoint是ZigBee device object(ZDO)用的一个端点,255号是用作广播。我们自己可以定义的是1-240这些端点。每个端点对应一个任务taskid。因此,我们每增加一个端点,就要给它配置一个新任务taskid。

比如你在终端节点添加了温度检测模块以及人体检测模块,分别向协调器发送温度值以及人体检测的信息。

那么问题来了,如果我想在终端只读取温度值,不读取人体检测信息,该怎么办?

一个简单粗暴的做法是只建立一个用户任务taskid,在终端应用层删除掉人体监测模块部分,这样问题得以解决。但是如果我在终端加入的传感器模块够多,那么修整应用层会有很大的工作量。

在EmberZNet协议栈中,采用emberAfSendBroadcast这个函数进行无线数据包发送前需要使用emAfApsFrameEndpointSetup配置好源端点和目的端点,在emberAfSendBroadcast函数的参数中,indexOrDestination目的地址,目的地址中便包含了16位的短地址(协调器默认为0x0000)。终端将数据包发出,短地址匹配的协调器会收到这个消息(短地址匹配不对则丢弃该包),然后协议栈底层进行解析,将数据包发给协调器的对应endpoint(不匹配则丢弃该包)。

简单来说,endpoint是用来管理同一个节点上不同任务的工具,相当于一个分类箱,将不同功能分别存放在不同任务上,这样做的好处是规范数据包,你不用去规定第几个字节是属于哪个模块信息。

 

Profile配置文件或称应用模式

配置文件可以理解为共同促进交互式应用的多个设备描述项的集合。定义了属性ID与群集(簇)ID,使之看起来就像设备的某种特性,以家庭智能控制系统为例,灯配置文件设定了远程控制设备的群集OnOffDRC含有一种属性OnOff,且该属性为无符号8位值,值0xFF意味着"开",0x00为"关",0xF0则为无效。

通常,配置文件也为设备定义了,哪些群集是强制托管,哪些群集是可选择的。另外,配置文件还定义了一些可选择的ZigBee协议托管服务。每一个应用都对应一个配置文件(Profile),配置文件内容包括:设备ID(Device ID)、群集ID(Cluster ID)、属性ID(Attribute ID),及AF(应用框架)使用何种服务类型等信息。

在ZigBee协议中,一个配置文件中允许最多2^16个设备,2^8个群集,每个群集支持最多2^16个属性。

 

Node节点

节点也可以理解为一个容器,包含一组ZigBee设备,分享一个无线信道。每个节点有且只有一个无线信道。一个节点除了64位的IEEE地址,16位的网络地址,每个节点还提供了8位应用层入口地址(端点:EndPoint),对应于用户应用对象。

如节点一Z1:开关控制装置和节点二Z2:灯装置。

事件(Event):开关2控制灯4的亮灭,开关1控制灯组1~3的亮灭。

群集(Cluster):不同情况下开关的状态和灯的状态。

端点(EndPoint):开关和灯。

属性(Attribute):开关和灯都有两种属性:开和关。

 

Bindling绑定

ZigBee定义了一个称为端点绑定的特殊过程。绑定即在源节点的某个端点(EndPoint)和目标节点的某个端点之间创建一条逻辑链路。绑定可以发生在两个或多个设备之间。协调器节点维护一个基本上包括两个或多个端点之间的逻辑链路的绑定表。

在绑定表中,包含如下信息,源节点和目标节点的IEEE地址、端点号、群集号。对于可以建立绑定关系的两个节点,它们的群集的属性必须一个选择"输入",另一个选择"输出",而且群集号必须相等,只有这样,它们彼此才能建立绑定。如果目标设备的扩展地址是已知的,则调用zb_BindDeviceRequest()函数可以创建一个绑定条目。

 

Addressing寻址

在ZigBee网络中,使用两种地址:一种是64位的IEEE地址,也叫物理地址,64位地址在所有ZigBee设备之中是唯一的,其中包含一个由IEEE分配、也是全球唯一的24位制造商特定组织标识符OUI(Organizationally Uqique Identifier)。

另一种是16位的网络地址(NWK Address),当设备加入ZigBee网络时,从允许其加入的父设备上获取16位网络地址。该地址在ZigBee网络中唯一,用于数据传输和数据包路由。

在ZigBee中,数据包可以单点传送(unicast),多点传送(multicast)或者广播传输(broadcast),所以必须有地址模式参数。一个单点传送数据包只发送给一个设备,多点传送数据包则要传送个一组设备,而广播数据则要发送给整个网络的所有节点。当应用程序需要将数据包发送给网络上的一组设备时,还可以使用组寻址方式(Group Addressing)。

 

8例程入口及初始化

 

天诚ZigBee开发套件的例程是基于芯科的ZigBee协议栈EmberZnet提供的例程基础上演变而来。例程的入口在文件simple-main.c内。

#include PLATFORM_HEADER

#include "app/framework/include/af.h"

// Our entry point is typically main(), except during testing.

#ifdef EMBER_TEST

#define MAIN nodeMain

#else

#define MAIN main

#endif

//入口函数

int MAIN(MAIN_FUNCTION_PARAMETERS)

{

halInit(); //硬件初始化

emberAfMainInit(); //主要是串口初始化

return emberAfMain(MAIN_FUNCTION_ARGUMENTS); //主程序

}

主要分三段:

1、硬件初始化,关闭中断。

2、主要是串口初始化和打开中断。

3、开始执行Znet。

 

先看第一步硬件初始化:

// halInit is called on first initial boot, not on wakeup from sleep.

//这个函数只有在初始化的时候调用, 而不是唤醒之后调用

void halInit(void)

{

//When the Cortex-M3 exits reset, interrupts are enable. Explicitly

//disable them for the rest of Init.

//关闭中断

__disable_irq();

// Configure BASEPRI to be at the interrupts disabled level so that when we

// turn interrupts back on nothing fires immediately.

// 也是关闭中断,会判断一个是否使用寄存器来关闭, __disable_irq这个是m3内核关闭全局中断

INTERRUPTS_OFF();

// Bootloader might be at the base of flash, or even in the NULL_BTL case,

// the BAT/AAT will be at the beginning of the image.

// Setting the vectorTable is required.

// 设置矢量表,用STM32 OTA的应用程序段都要做矢量偏移

SCB->VTOR = (uint32_t)halAppAddressTable.baseTable.vectorTable;

// Always Configure Interrupt Priorities. This is necessary for key behavior

// such as fault Handlers to be serviced at the correct priority levels.

// 设置NV中断

#define EXCEPTION(vectorNumber, functionName, deviceIrqn, deviceIrqHandler, priorityLevel, subpriority) \

NVIC_SetPriority(deviceIrqn, NVIC_EncodePriority(PRIGROUP_POSITION, priorityLevel, subpriority));

#include NVIC_CONFIG

#undef EXCEPTION

//Now that all the individual priority bits are set, we have to set the

//distinction between preemptive priority and non-preemptive subpriority

//This sets the priority grouping binary position.

//PRIGROUP_POSITION is defined inside of nvic-config.h.

//设置中断组

NVIC_SetPriorityGrouping(PRIGROUP_POSITION);

// Always Configure System Handlers Control and Configuration

SCB->CCR |= SCB_CCR_STKALIGN_Msk; //

#if defined(SCB_CCR_DIV_0_TRP_Msk)

SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;

#endif

#if defined(SCB_SHCSR_USGFAULTENA_Msk)

SCB->SHCSR = (SCB_SHCSR_USGFAULTENA_Msk

| SCB_SHCSR_BUSFAULTENA_Msk

| SCB_SHCSR_MEMFAULTENA_Msk);

#endif

//使能内存保护单元

halInternalEnableMPU();

// Determine and record the reason for the reset. Because this code uses

// static variables in RAM, it must be performed after RAM segements are

// initialized, but the RESETINFO segment is left uninitialized.

halInternalClassifyReset();

// Zero out the EMHEAP segment.

{

// IAR warns about "integer conversion resulted in truncation" if

// _EMHEAP_SEGMENT_SIZE is used directly in MEMSET(). This segment

// should always be smaller than a 16bit size.

MEMSET(_EMHEAP_SEGMENT_BEGIN, 0, (_EMHEAP_SEGMENT_SIZE & 0xFFFFu));

}

// Zero out the APP_RAM segment.

{

// IAR warns about "integer conversion resulted in truncation" if

// _APP_RAM_SEGMENT_SIZE is used directly in MEMSET(). This segment

// should always be smaller than a 16bit size.

MEMSET(_APP_RAM_SEGMENT_BEGIN, 0, (_APP_RAM_SEGMENT_SIZE & 0xFFFFu));

}

__enable_irq();

/* Configure board. Select either EBI or SPI mode. */

CHIP_Init();

#ifdef _EFR_DEVICE

EMU_UnlatchPinRetention();

#endif

halConfigInit();

RTCDRV_Init();

#ifdef _EFR_DEVICE

#ifndef HAL_CONFIG

halInternalInitBoard();

#endif

TEMPDRV_Init();

EMU_EM4Init_TypeDef em4Init = EMU_EM4INIT_DEFAULT;

em4Init.em4State = emuEM4Hibernate;

EMU_EM4Init(&em4Init);

halInternalEm4Wakeup();

#elif defined(_EZR_DEVICE)

halInternalStartSymbolTimer(); // TODO: move to macInit or emRadioInit

#endif

//初始化系统时钟

halInternalStartSystemTimer();

#if (PHY_RAIL || PHY_DUALRAIL)

RAIL_TxPowerCurvesConfig_t txPowerCurvesConfig = { RAIL_Curves24Hp, RAIL_CurvesSg, RAIL_Curves24Lp, RAIL_PiecewiseSegments };

RAIL_InitTxPowerCurves(&txPowerCurvesConfig);

#endif//(PHY_RAIL || PHY_DUALRAIL)

}

总的来说, 第一步主要就是初始化芯片内部的一些配置,比如中断,MPU,时钟等等。

第二步:开中断和初始化串口。

void emberAfMainInit(void)

{

SETUP_FAKE_EEPROM_FOR_SIMULATION();

INTERRUPTS_ON(); // Safe to enable interrupts at this point

//初始化串口,设置串口号和波特率=115200等。

emberSerialInit(APP_SERIAL, BAUD_RATE, PARITY_NONE, 1);

emberAfCorePrintln("Reset info: 0x%x (%p)",

halGetResetInfo(),

halGetResetString());

#if defined(EXTENDED_RESET_INFO)

emberAfCorePrintln("Extended Reset info: 0x%2X (%p)",

halGetExtendedResetInfo(),

halGetExtendedResetString());

if (halResetWasCrash()) {

halPrintCrashSummary(serialPort);

halPrintCrashDetails(serialPort);

halPrintCrashData(serialPort);

}

#endif

}

第三部分初始化,主要是初始化各种回调函数, 协议栈,中断等。

int emberAfMain(MAIN_FUNCTION_PARAMETERS)

{

EmberStatus status;

//第1步,调用了一个回调函数emberAfMainStartCallback,让用户自己实现一些初始化之前的动作。

{

int returnCode;

if (emberAfMainStartCallback(&returnCode, APP_FRAMEWORK_MAIN_ ARGUMENTS)) {

return returnCode;

}

}

//第2步, 初始化Ember Stack ,这是库函数

// Initialize the Ember Stack.

status = emberInit();

if (status != EMBER_SUCCESS) {

emberAfCorePrintln("%pemberInit 0x%x", "ERROR: ", status);

// The app can choose what to do here. If the app is running

// another device then it could stay running and report the

// error visually for example. This app asserts.

assert(false);

} else {

emberAfDebugPrintln("init pass");

}

#if defined(EMBER_ENABLE_EM4)

#if defined(EMBER_TEST)

uint8_t reset;

reset = halGetResetInfo();

if (reset == RESET_2xx_SOFTWARE_EM4) {

// This can only be called if idle-sleep plugin is enabled, and em4 is OK.

emberAfCameBackFromEM4Callback();

}

#else

uint16_t extReset;

extReset = halGetExtendedResetInfo();

emberAfCorePrintln("extReset :%d , RESET_SOFTWARE_EM4 : %d, RESET_ EXTERNAL_EM4PIN = %d",

extReset,

RESET_SOFTWARE_EM4,

RESET_EXTERNAL_EM4PIN);

if (extReset == RESET_SOFTWARE_EM4 || extReset == RESET_EXTERNAL_EM4PIN) {

emberAfCameBackFromEM4Callback();

}

#endif // EMBER_TEST

#endif

//第3步,初始化网络架构, 基本都是库函数

// This will initialize the stack of networks maintained by the framework,

// including setting the default network.

emAfInitializeNetworkIndexStack();

//第4步,初始化消息回调数组,全部清空。最大的是EMBER_APS_UNICAST_ MESSAGE _COUNT 10

// Initialize messageSentCallbacks table

emAfInitializeMessageSentCallbackArray();

//第5步,端点初始化配置信息

emberAfEndpointConfigure();

//第6步,用户应用初始化(这部分是使用回调实现的),Clusters 初始化。

emAfInit();

//第7步,初始化地址缓存数据

// The address cache needs to be initialized and used with the source routing

// code for the trust center to operate properly.

securityAddressCacheInit(EMBER_AF_PLUGIN_ADDRESS_TABLE_SIZE, // offset

EMBER_AF_PLUGIN_ADDRESS_TABLE_TRUST_CENTER_CACHE_SIZE); // size

//第8步, 网络初始化

EM_AF_NETWORK_INIT();

//第9步,命令初始化

COMMAND_READER_INIT();

//第10步,设置厂家编号,芯科的编号是0x1002

// Set the manufacturing code. This is defined by ZigBee document 053874r10

// Ember's ID is 0x1002 and is the default, but this can be overridden in App Builder.

emberSetManufacturerCode(EMBER_AF_MANUFACTURER_CODE);

emberSetMaximumIncomingTransferSize(EMBER_AF_INCOMING_BUFFER_LENGTH);

emberSetMaximumOutgoingTransferSize(EMBER_AF_MAXIMUM_SEND_PAYLOAD_LENGTH);

//第11步,设置电源模式

emberSetTxPowerMode(EMBER_AF_TX_POWER_MODE);

while (true) {

//看门狗喂狗

halResetWatchdog(); // Periodically reset the watchdog.

emberTick(); // Allow the stack to run.

// Allow the ZCL clusters and plugin ticks to run. This should go

// immediately after emberTick

// Skip these ticks if a crypto operation is ongoing

if (0 == emAfIsCryptoOperationInProgress()) {

emAfTick();

}

emberSerialBufferTick();

//事件处理函数 , 用户自定义事件,及系统事件运行

emberAfRunEvents();

#if defined(ZA_CLI_FULL)

if (emberProcessCommandInput(APP_SERIAL)) {

emberAfGuaranteedPrint("%p>", ZA_PROMPT);

}

#endif

#if defined(EMBER_TEST)

if (true) {

// Simulation only

uint32_t timeToNextEventMax = emberMsToNextStackEvent();

timeToNextEventMax = emberAfMsToNextEvent(timeToNextEventMax);

simulatedTimePassesMs(timeToNextEventMax);

}

#endif

// After each interation through the main loop, our network index stack

// should be empty and we should be on the default network index again.

emAfAssertNetworkIndexStackIsEmpty();

if (false) {

break;

}

}

return 0;

}

9ZigBee3.0设备建网解析

天诚ZigBee开发套件的EFR32-Zigbee3.0设备建网流程如下:

1、在coord.isc文件工程的“Plugins”添加红框内的插件【必须】与蓝框内的插件【可选】。

image

2、调用Coord工程内的network-creator.c文件内的emberAfPluginNetworkCreatorStart (bool centralizedNetwork)函数启动NetworkCreator创建一个随机网络。

image

创建网络类型,集中型或分布式网络。

doFormCentralizedNetwork = centralizedNetwork;

开始调度扫描。

return scheduleScans();

此函数——调用最终执行扫描信道调度函数scheduleScans()

3、进入scheduleScans()函数

image

EmberAfPluginScanDispatchScanData data = {}; //可控扫描因素。

data.scanType = EMBER_ACTIVE_SCAN; //主扫描类型

data.scanType = EMBER_ENERGY_SCAN; //次级扫描类型

 

自动扫描网络和信道时,仅通道扫描时间,协议栈默认是:

EMBER_AF_PLUGIN_NETWORK_CREATOR_SCAN_DURATION 4

 

扫描类型协议栈有以下几种类型:

enum

{

  /** An energy scan scans each channel for its RSSI value. */

  EMBER_ENERGY_SCAN,

  /** An active scan scans each channel for available networks. */

  EMBER_ACTIVE_SCAN,

  /** A fake scan that is used to turn off the radio. */

  EMBER_START_RADIO_OFF_SCAN,

  /** A green power channel delivery scan. */

  EMBER_STACK_GP_CHANNEL_DELIVERY_SCAN,

EMBER_LAST_SCAN_TYPE = EMBER_STACK_GP_CHANNEL_DELIVERY_SCAN

};

4、扫描完成调到scanHandler(EmberAfPluginScanDispatchScanResults *results)函数。

image

判断扫描结果。

if (emberAfPluginScanDispatchScanResultsAreFailure(results)) {

} else { // success

if (emberAfPluginScanDispatchScanResultsAreComplete(results)) {

扫描成功则进入。

handleScanComplete(results);

 

5、进入handleScanComplete(EmberAfPluginScanDispatchScanResults *results)函数。

image

判断扫描结果与否。

if (results->status != EMBER_SUCCESS) {

扫描成功则尝试创建一个网络。

results->status = tryToFormNetwork();

 

6、进入tryToFormNetwork(void)函数。

image

选择一个随机信道。

uint8_t channel = (halCommonGetRandom() & 0x0F)

+ EMBER_MIN_802_15_4_CHANNEL_NUMBER;

创建一个随机PanId。

EmberPanId panId = emberAfPluginNetworkCreatorGetPanIdCallback();

networkParameters.panId = panId;

设置发射功率。

networkParameters.radioTxPower = EMBER_AF_PLUGIN_NETWORK_CREATOR_RADIO_POWER;

创建一个随机扩展PanId。

fillExtendedPanId(networkParameters.extendedPanId);

判断是否是次级信道。

// If we are on our secondary channel mask and we didn't find any channels that

// were below our channel composite thresholds, then we pick from the "least

// worst channels" so that network formation is still possible.

if (maskIsSecondary() && currentChannelMask == 0) {

currentChannelMask = makeLeastWorstChannelMask();

}

uint8_t channel处的值均是使用halCommonGetRandom()获取的随机数赋值。

currentChannelMask处判断是否是次级信道,如果是并且currentChannelMask为0,则选择一个最糟糕的信道来创建。

设置网络安全模式且随机创建一个网络KEY。

status = emberAfPluginNetworkCreatorSecurityStart(doFormCentralizedNetwork);

一直轮询,直到通道为上面设置的通道位置,则往下执行。

if (!READBIT(currentChannelMask, channel)) {

continue;

}

在设置的通道建立网络

status = emberFormNetwork(&networkParameters);

如果建网成功,则执行用户层的网络创建完成callback。

if (status == EMBER_SUCCESS) {

emberAfPluginNetworkCreatorCompleteCallback(&networkParameters,

(maskIsSecondary()

? true

: false));

跳出while循环。

CLEARBIT(currentChannelMask, channel);

8、如果想要以自己指定的PANID、发射功率、通道、集中or分布式建立网络则可使用emberAfPluginNetworkCreatorNetworkForm()函数。

image

10 ZigBee3.0设备入网解析

 

1、在Router.isc文件工程的Plugins添加红色框内的插件【必须】与蓝色框内的插件【可选】。

image

2、调用Router工程内的network-steering.c文件内EmberStatus emberAfPluginNetworkSteeringStart(void)函数启动Network Steering。

image

此函数-----调用最终执行扫描信道的状态机函数status = stateMachineRun()。

 

3、进入status = stateMachineRun()函数。

由于在步骤2中” emAfPluginNetworkSteeringState =…”得到emAfPluginNetworkSteeringState=EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_PRIMARY_INSTALL_CODE,在状态机运行的时emAfPluginNetworkSteeringState在tryNextMethod()函数中是++的形式递增的,所以由上面的变量可知设备首次是以二维码扫描入网的方式开始入网,但我们工程配置是以集中式入网方式。

enum {

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_NONE = 0x00,

// These next two states are only run if explicitly configured to do so

// See emAfPluginNetworkSteeringSetConfiguredKey()

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_PRIMARY_CONFIGURED=0x01, EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_SECONDARY_CONFIGURED = 0x02,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_PRIMARY_INSTALL_CODE = 0x03,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_SECONDARY_INSTALL_CODE = 0x04,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_PRIMARY_CENTRALIZED = 0x05,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_SECONDARY_CENTRALIZED = 0x06,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_PRIMARY_DISTRIBUTED = 0x07,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_SECONDARY_DISTRIBUTED = 0x08,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_FINISHED = 0x09,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_UPDATE_TCLK = 0x10,

EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_VERIFY_TCLK = 0x20,

};

typedef uint8_t EmberAfPluginNetworkSteeringJoiningState;

image

状态机前两次会执行” if ((status = setupSecurity()) != EMBER_SUCCESS) {”处,然后到return tryNextMethod()会直接返回,直到emAfPluginNetworkSteeringState=EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_PRIMARY_CENTRALIZED,采用集中式策略入网。

然后执行” currentChannelMask = emAfPluginNetworkSteeringPrimaryChannelMask;”处确定信道掩码方式,然后执行gotoNextChannel();进入信道扫描配置函数;

 

注:注意这里信道掩码分为两种:

a)    Primary:优先信道掩码,一般包含11、14、15、19、20、24、25,都是和蓝牙、WiFi信道重叠部分比较少的信道,减少干扰。

b)    Secondary:11-26,所有信道。

 

4、进入void gotoNextChannel(void)函数进入通道扫描函数后,配置好EmberAfPluginScanDispatchScanData结构体参数,则执行status = emberAfPluginScanDispatchScheduleScan(&scanData);该函数其实底层就是一个扫描事件。

image

扫描策略。

scanData.scanType = EMBER_ACTIVE_SCAN;

EmberAfPluginScanDispatchScanData结构体说明:

扫描策略:

enum

{

  /** An energy scan scans each channel for its RSSI value. */

  EMBER_ENERGY_SCAN,

  /** An active scan scans each channel for available networks. */

  EMBER_ACTIVE_SCAN,

  /** A fake scan that is used to turn off the radio. */

  EMBER_START_RADIO_OFF_SCAN,

  /** A green power channel delivery scan. */

  EMBER_STACK_GP_CHANNEL_DELIVERY_SCAN,

  EMBER_LAST_SCAN_TYPE = EMBER_STACK_GP_CHANNEL_DELIVERY_SCAN

};

扫描持续时间设置。

scanData.duration = EMBER_AF_PLUGIN_NETWORK_STEERING_SCAN_DURATION;

持续时间说明:

* @param channelMask Bits set as 1 indicate that this particular channel

* should be scanned. Bits set to 0 indicate that this particular channel

* should not be scanned. For example, a channelMask value of 0x00000001

* indicates that only channel 0 should be scanned. Valid channels range

* from 11 to 26 inclusive. This translates to a channel mask value of 0x07

* FF F8 00. As a convenience, a channelMask of 0 is reinterpreted as the

* mask for the current channel.

* @param duration Sets the exponent of the number of scan periods,

* where a scan period is 960 symbols, and a symbol is 16 microseconds.

* The scan will occur for ((2^duration) + 1) scan periods. The value

* of duration must be less than 15. The time corresponding to the first

* few values is as follows: 0 = 31 msec, 1 = 46 msec, 2 = 77 msec,

* 3 = 138 msec, 4 = 261 msec, 5 = 507 msec, 6 = 998 msec.

在下图处Plugins处配置:

image

扫描结果回调。

scanData.handler = scanResultsHandler;

扫描调度器。

status = emberAfPluginScanDispatchScheduleScan(&scanData);

 

回调函数scanResultsHandler:

image

 

通道扫描完成,然后执行scanCompleteCallback(results->channel, results->status)。

image

 

在执行“tryToJoinNetwork();”处,尝试去加入网络:

image

判断扫到的网络PANID是否有效,无效则执行下个通道扫描gotoNextChannel();

有效则开始加入这个网络。

 

5、假设网络PANID无效

如果无效则会回到步骤4:

image

注意: 假设如果在emAfPluginNetworkSteeringState = EMBER_ AF_ PLUGIN_NETWORK_STEERING_STATE_SCAN_PRIMARY_CENTRALIZED状态的优先信道掩码7个通道都没有扫描到可用的网络【判断可不可用是判断网络的PANID是否有效】,则会触发上图” tryNextMethod();”处执行,执行emAfPluginNetworkSteeringState=EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_SCAN_SECONDARY_CENTRALIZED状态,依次类推,状态++直到状态机的值大于所设定的上限【emAfPluginNetworkSteeringState > LAST_JOINING_STATE】还是没尝试加入成功,则停止入网。

image

6、假设网络PANID有效

如果网络PANID有效,则配置网络结构体参数,然后开始在此信道,加入网络,执行status = emberJoinNetwork(nodeType, &networkParams),加入失败则停止加入网络。

image