ESP IDF Mesh Stack Pratical Guide Practical

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 88

ESP
-
IDF Mesh Stack
Practical Guide
ESP
-
IDF V3.1
The purpose of this guide is to provide a step-by-step tutorial for developing a mesh application using the mesh
stack of ESP32-based boards, and it represents a supplement to the Mesh API Reference and the Mesh API
Guidelines that can be found in the ESP-IDF Programming Guide.
License
Copyright © 2018 Riccardo Bertini <m_bertini@hotmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
To fully benefit from the contents of this guide, prior knowledge of the following topics is recommended:
ESP-IDF Wi-Fi Basics (API reference and guidelines)
ESP-IDF projects authoring (guide)
ESP32 application startup flow (guide)
ESP32 logging mechanism (guide)
FreeRTOS tasks basics (guide)
FreeRTOS event groups (guide)
ESP
-
IDF Mesh Stack Practical Guide
Structure of a Mesh Application
An application which uses the mesh stack of ESP32 boards can generally be divided into the following components:
ESP-IDF Mesh Stack
Introduction
Page 1
Driver Module
The inter
-
task synchronization between the two modules is performed by using an
Event Group
, which represents
an abstract type offered by the FreeRTOS kernel consisting of an array of bits which can be used as semaphores via
the relative API.
A
Mesh Initializer Function
, which represents the entry point of a mesh application and whose tasks are to
initialize and configure the Wi
-
Fi and Mesh stacks and to enable mesh networking on the device.
The
Mesh Events General Handler
, which is a function called asynchronously by the mesh stack after its
internal handlers each time a mesh event occurs and whose task is to pass each event with its provided
additional information to its relative specific handler.
A set of
Mesh Events Specific Handlers
, which are functions whose tasks are to perform the application
-
level
handling of mesh events raised by the mesh stack and to start driver components when the appropriate
requirements for their execution are met.
The
Setup Module
, whose purpose is to carry out the setup process of mesh networking on the device, consists of
the following components:
The collection of functions and data structures provided by the ESP
-
IDF to offer mesh networking functionalities
represents the Mesh Stack, which is implemented on top and uses the functionalities offered by the Wi-Fi Stack,
which directly manages the device's Wi
-
Fi interface.
From here at the application level a mesh application can be divided into the following two modules:
The Driver Module represents the part of the application that utilizes the features offered by the mesh stack to
implement a certain
service
, and while its actual logic and structure depend on its purpose, its components can be
generally divided into the following categories according to the mesh networking features they use, and so the
requirements that must be met before their execution can begin:
The
Internal Driver Components
are components that require the device to be connected to a mesh network
but don't require to communicate with the external Distribution System (DS), and so they can be started as
soon as the device connects to a parent in the mesh network.
The External Driver Components are components that require the device to communicate with the external
DS, and so they can be started as soon as the device is informed of the accessibility of the external DS.
The Root Driver Components represent components that are to be executed exclusively on the root node of
the mesh network, and they can be started as soon as the root node is able to communicate with the external
router at the IP level.
ESP-IDF Mesh Stack
Introduction
Page 2
The minimal subset of libraries required to develop a mesh application, with their paths relative to the $ESP-IDF
environment variable, is as follows:
/*-- C standard libraries --*/
#include <string.h> //C standard string library
/*-- Environment-specific libraries --*/
#include "esp_system.h" //ESP32 base system library
#include "esp_log.h" //ESP32 logging library
#include "nvs_flash.h"
//ESP32 flash memory library
#include "esp_wifi.h" //ESP32 main Wi-Fi library
#include "esp_event_loop.h" //ESP32 Wi-Fi events library
#include "esp_mesh.h" //ESP32 main Mesh library
#include "freertos/FreeRTOS.h" //FreeRTOS base library
#include "freertos/task.h" //FreeRTOS tasks library
#include "freertos/event_groups.h" //FreeRTOS event groups library
#include "lwip/sockets.h" //LwIP base library
Type Definitions
The following additional custom data types are used in the context of this guide for developing a mesh application,
whose members and uses are described thoroughly in the following sections:
typedef enum //Mesh Organization Mode
{
SELF_ORGANIZED, //Self-Organized Networking
FIXED_ROOT, //Fixed-Root Networking
MANUAL_NETWORKING //Manual Networking
}mesh_org_t;
typedef struct //Describes the status of a
{ node in the mesh network
uint8_t mid[6]; //Mesh Network Identifier (MID)
uint8_t channel; //Mesh Wi-Fi Channel
mesh_org_t org; //Mesh Organization Mode
uint8_t root_addr[6]; //Root node's SoftAP MAC address
uint8_t parent_addr[6]; //Node's parent SoftAP MAC address
mesh_type_t my_type; //Node's type
int8_t my_layer; //Node's layer in the mesh network
}
mesh_status_t
;
Global Variables
The following global variables are used in this guide for developing a mesh application:
EventGroupHandle_t mesh_event_group; //Mesh Event Group Handler
/*-- Mesh Setup Flags --*/
#define MESH_ON BIT0 //Whether mesh networking is enabled on the node or not
#define MESH_PARENT BIT1 //Whether the node is connected to a parent or not
#define MESH_CHILD BIT2 //Whether the node has children connected or not
#define MESH_TODS BIT3 //Whether the external DS is reachable or not
#define MESH_VOTE BIT4 //Whether a new root election is currently in progress
in the mesh network or not (this flag is used active
low: 0 = election in progress, 1 = no election)
Required Headers
Code Premises
ESP-IDF Mesh Stack
Introduction
Page 3
/*-- Driver Components Executions Flags --*/ //Indicate whether each driver component
is running or not (where here a single
component for each of the three driver
categories is used)
#define MESH_DRIVER_INTERNAL BIT5 //Whether the internal drivers are running or not
#define MESH_DRIVER_EXTERNAL BIT6 //Whether the external drivers are running or not
#define MESH_DRIVER_ROOT BIT7 //Whether the root drivers are running or not
mesh_status_t
mesh_state
;
//Describes the node's current status in the mesh network
Also note that within this guide the parameters used in the code examples are shown in capital letters and
represent predefined constants (e.g. MESH_ROUTER_SSID, MESH_ROOT_IPSTATIC, etc.), whose values in an actual
project can be set for example by providing an appropriate
Kconfig.projbuild
configuration file and using the
menuconfig utility.
ESP-IDF Mesh Stack
Introduction
Page 4
Mesh Initializer Function
Wi
-
Fi Stack Configuration
It's not necessary to provide a Wi
-
Fi event group to synchronize tasks on the state of the Wi
-
Fi Stack.
The address of the function that the Wi-Fi stack will call for the handling of Wi-Fi events must be set to NULL
to later devolve such handling to the mesh stack.
It's not necessary to directly configure or enable the Wi-Fi interface modes (Station or SoftAP).
Since mesh networking operates at the data
-
link level, it's not necessary to set IP configurations for the
device Station and SoftAP interfaces, and furthermore to ensure that they don't obtain or offer dynamic IP
configurations, the station DHCP client and the SoftAP DHCP server must be preemptively disabled.
Note that the only exception to this rule is given by the node that will become the root of the mesh network,
whose station interface requires an IP configuration to allow it to connect to the external router
(we'll see later).
The tasks that must be performed to configure the device's Wi
-
Fi stack for the purposes of mesh networking are
similiar to its standard configuration process, with the following differences due to the fact that its management is
not carried out directly at the application level but is devolved almost entirely to the mesh stack.
From here the steps required to configure the Wi
-
Fi stack for the purposes of mesh networking are:
The setup process of a mesh application is divided into a first phase relative to the inizializations and configurations
required to enable mesh networking on a device, which are carried out by the Mesh Initializer Function, and a
second event
-
driven phase represented by the application
-
level handling of the mesh events raised by the mesh
stack, which are carried out by the Mesh Events General Handler and the set of Mesh Events Specific Handlers.
The Mesh Initializer Function represents the entry point of a mesh application, and its purposes are to initialize
and configure the Wi-Fi and Mesh Stacks on the device and then enable mesh networking,
Setup Module
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 5
Initialize the system flash storage
1)
By default the Wi-Fi stack stores its configuration in the system's flash memory, which consequently needs
to be initialized beforehand by using the following function:
//File nvs.flash.h
esp_err_t nvs_flash_init(void)
The ESP_ERR_NVS_NO_FREE_PAGES represents a recoverable error, whose recovery can be attempted by
completely erasing the flash memory through the following function and then trying to initialize it again:
//File nvs.flash.h
esp_err_t nvs_flash_erase(void)
No errors in this function can be recovered, so the initialization procedure of the system flash storage appears
as below:
esp_err_t mesh_init()
{
/*-- Wi-Fi Stack Configuration --*/
esp_err_t ret = nvs_flash_init(); //Flash storage initialization
if(ret == ESP_ERR_NVS_NO_FREE_PAGES) //Error recovery attempt
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret); //At this point if errors persist, it is not
possible to proceed with the program's execution
}
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 6
Initialize the TCP/LwIP Stack
2)
Next we need to initialize the data structures relative to the TCP/LwIP stack and create the core LwIP task,
which can be obtained by calling the following function:
//File tcpip_adapter.h (automatically included by the previous headers)
void tcpip_adapter_init(void)
esp_err_t mesh_init()
{
tcpip_adapter_init(); //Initialize the TCP/LwIP stack
}
Note that this step is always required even if the TCP/LwIP stack in a mesh network is used only on the root
node on its station interface to connect with the external router.
Devolve the Handling of Wi
-
Fi Events
3)
To devolve the handling of Wi-Fi events, which will be later performed by the mesh stack, the following
function must be called with two NULL arguments:
//File esp_event_loop.h
esp_err_t esp_event_loop_init(system_event_cb_t cb, void* ctx)
esp_err_t mesh_init()
{
ESP_ERROR_CHECK(esp_event_loop_init(NULL,NULL)); //Devolve the handling
of Wi-Fi events to
} the Mesh Stack
Initialize the Wi
-
Fi Stack
4)
Next we need to initialize the Wi-Fi stack, which is obtained by calling the following function:
//File esp_wifi.h
typedef struct //Wi-Fi stack Initialization Parameters
{
system_event_handler_t event_handler;//Wi-Fi event handler
wifi_osi_funcs_t* osi_funcs; //Wi-Fi OS functions
wpa_crypto_funcs_t wpa_crypto_funcs;//Wi-Fi station crypto functions
int static_rx_buf_num;//Wi-Fi static RX buffer number
int dynamic_rx_buf_num; //Wi-Fi dynamic RX buffer number
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 7
int tx_buf_type; //Wi-Fi TX buffer type
int static_tx_buf_num;//Wi-Fi static TX buffer number
int dynamic_tx_buf_num; //Wi-Fi dynamic TX buffer number
int csi_enable; //Wi-Fi CSI enable flag
int ampdu_rx_enable;//Wi-Fi AMPDU RX enable flag
int ampdu_tx_enable;//Wi-Fi AMPDU TX enable flag
int nvs_enable; //Wi-Fi NVS flash enable flag
int
nano_enable
;
//printf/scan family enable flag
int tx_ba_win;//Wi-Fi Block Ack TX window size
int rx_ba_win; //Wi-Fi Block Ack RX window size
int wifi_task_core_id;//Wi-Fi Task Core ID
int
magic
;
//Wi
-
Fi init magic number
}wifi_init_config_t;
esp_err_t esp_wifi_init(const wifi_init_config_t* config)
For the purposes of mesh networking we can initialize the Wi-Fi Stack with its default parameters by using the
following macro:
//File esp_wifi.h
#define WIFI_INIT_CONFIG_DEFAULT() \
{
\
.event_handler = &esp_event_send, \
.osi_funcs = &g_wifi_osi_funcs, \
.wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs, \
.static_rx_buf_num = CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM,
\
.dynamic_rx_buf_num = CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM, \
.tx_buf_type = CONFIG_ESP32_WIFI_TX_BUFFER_TYPE, \
.static_tx_buf_num = WIFI_STATIC_TX_BUFFER_NUM, \
.dynamic_tx_buf_num = WIFI_DYNAMIC_TX_BUFFER_NUM, \
.csi_enable = WIFI_CSI_ENABLED, \
.ampdu_rx_enable = WIFI_AMPDU_RX_ENABLED, \
.ampdu_tx_enable = WIFI_AMPDU_TX_ENABLED, \
.nvs_enable = WIFI_NVS_ENABLED, \
.nano_enable = WIFI_NANO_FORMAT_ENABLED,
\
.tx_ba_win = WIFI_DEFAULT_TX_BA_WIN, \
.rx_ba_win = WIFI_DEFAULT_RX_BA_WIN, \
.wifi_task_core_id = WIFI_TASK_CORE_ID, \
.magic = WIFI_INIT_CONFIG_MAGIC
\
};
esp_err_t mesh_init()
{
wifi_init_config_t initcfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&initcfg)); //Initialize the Wi-Fi Stack
}
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 8
Disable Station DHCP Client and SoftAP DHCP Server
5)
At this point we must disable the Station interface DHCP client and SoftAP interface DHCP server, which is
obtained by calling the following functions:
//File tcpip_adapter.h
typedef enum //TCP/IP interface enumerates
{
TCPIP_ADAPTER_IF_STA = 0,//Station interface
TCPIP_ADAPTER_IF_AP,//SoftAP interface
TCPIP_ADAPTER_IF_ETH,//Ethernet interface
TCPIP_ADAPTER_IF_MAX,
}tcpip_adapter_if_t;
esp_err_t tcpip_adapter_dhcpc_stop(tcpip_adapter_if_t tcpip_if)
esp_err_t tcpip_adapter_dhcps_stop(tcpip_adapter_if_t tcpip_if)
esp_err_t mesh_init()
{
ESP_ERROR_CHECK(tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA));
ESP_ERROR_CHECK(tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP));
}
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 9
Enable the Wi
-
Fi Interface
6)
The last step of the Wi-Fi configuration is to enable the Wi-Fi interface itself, which is obtained by calling the
following function:
//File esp_wifi.h
esp_err_t esp_wifi_start(void)
esp_err_t mesh_init()
{
ESP_ERROR_CHECK(esp_wifi_start()); //Enable the Wi-Fi interface
}
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 10
Mesh Stack Configuration
Once the Wi-Fi interface on the device has been enabled the Mesh Stack configuration can commence, which is
performed through the following steps:
Initialize the Mesh Stack
1)
The mesh stack on a device can be initialized by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_init(void)
esp_err_t mesh_init()
{
/*-- Mesh Stack Configuration --*/
ESP_ERROR_CHECK(esp_mesh_init()); //Initialize the mesh stack
}
2)
Create the Mesh Event Group
Next we need to create the mesh event group, which can be allocated dynamically on the heap by calling the
following function:
//File event_groups.h
typedef
void* EventGroupHandle_t
EventGroupHandle_t
xEventGroupCreate(void)
Where the return of the function represents the allocated event group's handler, which must be assigned to
the
mesh_event_group
global variable that was previously defined.
As for its bits, to describe the node's status in the mesh network we'll use the following flags:
A flag representing whether mesh networking is enabled on the node or not (MESH_ON)
A flag representing whether the node is connected to a parent or not
(MESH_PARENT)
A flag representing whether the node has children connected or not
(MESH_CHILD)
A flag representing whether the external DS is accessible or not (MESH_TODS)
A flag representing whether a new root election is currently in progress
(MESH_VOTE)
in the mesh network or not (that we'll use as an active low flag, i.e.
0 = election in progress, 1 = no election)
Note that once the mesh stack has been initialized on a device we should refrain from directly modifying
settings related to the Wi-Fi stack, since such changes may interfere with mesh networking functionalities.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 11
Set the Base Mesh Configuration
3)
At this point we must set the device's base mesh configuration, which should be shared by all the devices that
we want to partecipate in the same mesh network, a base configuration that is described by the following
data structure:
//File esp_mesh.h
typedef struct //Mesh Base Configuration
{
mesh_addr_t mesh_id; //Mesh Network Identifier (MID)
uint8_t channel;//Mesh Wi-Fi Channel (1-14)
mesh_event_cb_t event_cb;//Mesh Events General Handler's address
mesh_ap_cfg_t mesh_ap; //Mesh SoftAP Settings
mesh_router_t router;//Mesh Router Settings
const mesh_crypto_funcs_t* crypto_funcs; //Mesh packets cryptographic algorithm
}mesh_cfg_t;
Where:
The mesh_id member represents the Mesh Network Identifier (MID) of the mesh network relative to
the node, which represents the mesh network that the device will either join or create as a root note
depending on its further configuration and other factors such as if other nodes broadcasting beacon
frames with the same MID are found once mesh networking is enabled on the node.
An MID has the format of a MAC address (6 bytes), and is described by the following union that as we'll
see is used for a variety of purposes in the mesh networking API:
typedef struct //Destination Address (IPv4:PORT) of a mesh
{
ip4_addr_t
ip4;
uint16_t
port;
}
__attribute__((packed))
mip_t;
packet destined outside the mesh network
//Destination IP
//Destination port
//("__attribute__((packed))" is for the
byte alignment of the in-memory
representation of the struct)
In addition to these flags, to allow the setup module to synchronize with the current state of the driver module
an additional flag should be provided for each of its components, representing whether the component is
currently being executed or not (this is to avoid creating undesidered duplicate tasks of the same component,
we'll see in more detail later), and assuming a single component for each of the three driver categories
previously discussed the following three additional flags are required:
A flag representing whether the internal driver component is currently (MESH_DRIVER_INTERNAL)
being executed or not
A flag representing whether the external driver component is currently
(MESH_DRIVER_EXTERNAL)
being executed or not
A flag representing whether the root driver component is currently
(MESH_DRIVER_ROOT)
being executed or not
From here we can associate a bit to each required flag as shown earlier in the code premises.
esp_err_t mesh_init()
{
mesh_event_group = xEventGroupCreate(); //Create the Mesh Event Group
xEventGroupSetBits(mesh_event_group,MESH_VOTE); //Set the MESH_VOTE flag in
the mesh event group
} (for it's active low)
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 12
typedef union //Used to store an address in mesh networking
{
uint8_t addr[6]; //Depending on the context may represent a Mesh
Network Identifier (MID), a node's MAC address
or a Mesh Group Identifier (GID)
mip_t mip;//Destination address of a mesh packet destined for
} mesh_addr_t; the external DS
The channel member represents the Wi-Fi channel (1-14) to be used for mesh networking.
Note that due to the devices' limitation that the channel of their Station and SoftAP interfaces must
coincide, and since the root node will need to connect to the external router on its AP channel, the Mesh
Wi-Fi channel must coincide with the router's AP channel, which must then be known at this point in the
configuration (note that it could also be retrieved by performing a Wi-Fi scan beforehand, a possibility not
discussed here).
The event_cb member represents the address of the Mesh Events General Handler function, which so is
registered in the mesh stack by setting the node's base mesh configuration.
The mesh_ap member defines the device's base mesh SoftAP settings, and consists the following struct:
//File esp_mesh.h
typedef struct //Base Mesh SoftAP Settings
{
uint8_t password[64]; //Required from nodes to connect to a parent node
uint8_t max_connection;//Maximum number of connected children for each
}mesh_ap_cfg_t; node (must be
1, default and max = 10)
Beside these settings note that the SSID a node uses for its SoftAP interface its derived from its base MAC
address using a proprietary (or in any case undisclosed) algorithm, SSID that is also hidden from the
node's beacon frames but instead is carried in the Vendor IE field, which will be referred to as Mesh IE
from now on and, and as we'll discuss in more detail later, is typically encrypted in a node's Wi-Fi beacon
frames.
As a consequence of this, to attempt to connect to a parent node in the mesh network a node is always
required to perform a Wi-Fi scan beforehand to retrieve its candidate parent SSID from its Mesh IE, which
is decrypted and used by the mesh stack in a way that is transparent to the application (this will be
discussed in greater detail later in the Manual Networking organization mode).
Also note that in the current ESP-IDF version if the Mesh SoftAP password differs between nodes, after a
certain number of failed attempts to connect to a parent, if possible nodes will attempt to connect to the
external router as the root node of a new mesh network, even if such mesh network and the existing one
share the same MID.
Finally note that there exist other Mesh SoftAP settings that don't belong to a node's base mesh
configuration (See "Additional Mesh SoftAP Settings" later).
The router member defines the device's mesh router settings, which will be used by the root node to
connect with the external router's AP, and consists in the following struct:
//File esp_mesh.h
typedef struct //Mesh Router Settings
{
uint8_t ssid[32]; //Router's AP SSID
uint8_t ssid_len;//Router's AP SSID length
uint8_t password[64]; //Router's AP password
uint8_t bssid[6]; //Router's AP BSSID (required only if the root node
}mesh_router_t; must connect to a router with a specific BSSID)
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 13
The crypto_funcs member defines the cryptographic algorithm to be applied to the Mesh IE in the Wi-Fi
beacon frames, where currently this parameter can be set to NULL to use no encryption or to the address
of the defined "g_wifi_default_mesh_crypto_funcs" constant to use the AES encryption (default).
From here, once the mesh_cfg_t struct has been initialized with the desidered values, the device's base
mesh configuration can be set by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_config(const mesh_cfg_t* base_config)
void mesh_events_handler(mesh_event_t event)
{
/* We'll see later */
}
esp_err_t mesh_init()
{
/*-- Mesh Base Configuration --*/
mesh_cfg_t mesh_config = {0}; //Holds the base mesh
configuration to apply
//Mesh Network Identifier (MID)
memcpy(&mesh_config.mesh_id.addr,MESH_NETWORK_ID,6);
memcpy(&mesh_state.mid,MESH_NETWORK_ID,6); //Also update the MID in
the mesh_state struct
//Mesh Wi-Fi Channel
mesh_config.channel =MESH_WIFI_CHANNEL;
mesh_state.channel = MESH_WIFI_CHANNEL; //Also update the mesh channel
in the mesh_state struct
//Mesh Events General Handler address
mesh_config.event_cb = &mesh_events_handler;
//Base Mesh SoftAP settings
memcpy((uint8_t*)&mesh_config.mesh_ap.password, //SoftAP Password
,strlen(MESH_SOFTAP_PASSWORD));
mesh_config.mesh_ap.max_connection =MESH_SOFTAP_MAXCONNECTIONS; //SoftAP Max
Connection
//Mesh Router Settings
memcpy((uint8_t*)&mesh_config.router.ssid, //Router SSID
MESH_ROUTER_SSID,strlen(MESH_ROUTER_SSID));
mesh_config.router.ssid_len =strlen(MESH_ROUTER_SSID); //Router SSID length
memcpy((uint8_t*)&mesh_config.router.password, //Router Password
MESH_ROUTER_PASSWORD,strlen(MESH_ROUTER_PASSWORD));
if(MESH_ROUTER_SPECIFIC_BSSID) //If the Root node must connect to
memcpy((uint8_t*)&mesh_config.router.bssid,a router with a specific BSSID
MESH_ROUTER_BSSID,strlen(MESH_ROUTER_BSSID));//Router BSSID
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 14
//Mesh IE Crypto Function
if(MESH_IE_CRYPTO) //If the Mesh IE should be encrypted
mesh_config.crypto_funcs = &g_wifi_default_mesh_crypto_funcs; //AES encrypt.
else
mesh_config.crypto_funcs
=
0
;
//No encrypt.
ESP_ERROR_CHECK(esp_mesh_set_config(&mesh_config)); //Apply the Base
Mesh Configuration
}
Once the base mesh configuration on a device has been set it is possible to configure a set of additional settings
relating to mesh networking, some that if defined should be shared among all nodes partecipating in the same
mesh network and others that can differ from one node to another in the same network, settings that if not
explicitely configured will be set to their default values once mesh networking is enabled on a node.
Mesh Shared Settings
(
optional
)
4)
The following mesh settings, if defined, should be shared among all nodes participating in the same mesh
network, and can be divided into the following categories:
Mesh Organization Mode
Mesh Topology Settings
Mesh Self-Organized Root Settings
Other Root Settings
Mesh Additional SoftAP Settings
Mesh Organization Mode
In general an ESP32 mesh network can be organized in three different ways:
Self
-
Organized Network
(default)
1
-
In a self-organized mesh network once mesh networking is enabled on a node, it will start
broadcasting in the Mesh IE of its beacon frame its MID and scan for the beacon frames of other
nodes and the router's AP, where:
If one or more nodes are found that have already joined a mesh network and match all of the
following conditions:
They advertise in their Mesh IE the same MID set on the node
They are not on the maximum layer of their mesh network
Their mesh network capacity has not been reached
They are able to accept further children nodes
the node will select and attempt to connect as a child to its
preferred parent
, which is
represented by the node whose RSSI is above a predefined threshold, is located on the
shallowest layer of the mesh network and has the fewest children connected.
If one or more nodes are found that have not yet joined a mesh network and are advertising in
their Mesh IE the same MID set on the node (i.e. nodes where mesh networking has just been
enabled) and the router's AP is also found, a Root Node Election will take place, which is a
process carried out in a predefined number of rounds (default = 10) where in the first one each
node broadcasts its own MAC address and RSSI with the router, and in the following ones
the MAC address and RSSI of the node with the highest advertised RSSI, thus "voting" for such
node to elected as root.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 15
From here, at the end of the election rounds, each participant will determine its own voting
percentage (number of nodes voting for it to be elected / total number of nodes in the election),
and if such percentage is above a predefined threshold (default = 0.9) the node will proclaim itself
root and attempt to connect to the external router to form a new mesh network.
It must be noted that due to the variations of the nodes' RSSI with the router during the election
process it is possible that at its end no node reaches the voting percentage threshold required to
proclaim itself root, thus resulting in a failed election which immediately triggers a new one until a
root node is found for the network (also note that to limit the chances of this happening it's
possible to set a different voting percentage threshold for a node, we'll see later).
If no node matching the above conditions is found, the node will keep searching for a number of
scans equal to the number of rounds in a root election (default = 10) plus a fixed number of extra
scans (default = 3).
Thereupon, if no suitable node is found but the router's AP is, the node will attempt to connect to
it as the root node to form a new mesh network, while if the router's is not found, the node will
not be able to either join an existing or create a new mesh network, and it will continue to
perform periodic scans indefinitely until a device meeting the required conditions is found.
In a self
-
organized mesh network, once connected to a parent, non
-
root nodes will keep periodically
scanning in the background for a "better" preferred parent – i.e. a node matching the conditions
described previously but located on a shallower level of the mesh network than their current parent
and if such node is found the nodes will disconnect from their current parent and attempt to connect to
it as their child.
A self-organized mesh network also presents the following self-healing features:
Should the connection between a node and its parent become unstable or fail, the node will
disconnect from it and automatically search for another preferred parent to connect to.
Should the nodes in the second layer of the mesh network (or in the first layers in case of multiple
simultaneous failures) detect a root node failure, which occurs if the nodes don't receive its
beacon frame in a predefined amount of time, the nodes will disconnect from it and start a new
root election process as described above.
A self
-
organized mesh network also presents a
root conflicts resolution
algorithm, by virtue of which
should the network present more than one root node at any given moment, after an internal
communication between them the one with the highest RSSI with the router will ask the other root
nodes to yield, causing them to disconnect from the router and search for a preferred parent to connect
to (see also the MESH_EVENT_ROOT_ASKED_YIELD event later)
Since a mesh network is self
-
organized by default, no action is required to set this mesh organization
mode on a node, even if, should a different organization mode be used, self
-
organized networking can
be reenabled at any time by clearing its fixed-root setting via the esp_mesh_fix_root() function and
by reenabling its self
-
organized networking features by calling the
esp_mesh_set_self_organized()
function, both of which will be covered later.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 16
Fixed
-
Root Network
2
-
A Fixed
-
Root mesh network represents a variant of a self
-
organized network, where a node's behaviour
depends on whether, by applying an user's custom criteria, the node recognizes itself as being the fixed
root of the network or not.
From here, once mesh networking has been enabled on a node:
If by applying the user's custom criteria the node recognizes itself to be the
fixed
-
root
of the mesh
network, it must manually attempt to connect with the external router as the root node to form a
new mesh network.
Note that if the external router is not in range or its AP settings don't match the ones set in the
fixed root's router configuration, the node will remain stuck in a loop repeatedly attempting and
failing to connect with it.
If by applying the user's custom criteria the node recognizes itself not to be the fixed-root of the
network, it will scan and attempt to connect to a
preferred parent
as with self
-
organized
networking, where note that in any case the node won't attempt to connect with the router or start
a new root election, which are disabled in this mesh organization mode, and so if the node fails to
find a preferred parent it will continue to search for it indefinetely.
As with self
-
organized networking once connected to a parent non
-
root (i.e. non
-
fixed
-
root) nodes will
keep periodically scanning in the background for a "better" preferred parent, disconnecting from their
current one and attempting to connect to it if found.
Again, as a
self
-
healing
feature, should the connection between a non
-
fixed
-
root node and its parent
become unstable or fail, the node will disconnect from it and automatically search for another preferred
parent to connect to, even if in this case, unlike what happens in self
-
organized network, should the
nodes in the first layers of the network detect a root node failure they won't start a new root election or
attempt in any case to connect with the router, thus keeping on scanning indefinetely for a fixed
-
root
node to connect to.
Fixed-Root networks also present the same root conflicts resolution algorithm described for self-
organized networks, where in this case, upon receiving the request to yield from the fixed root with the
highest RSSI with the router, all the other fixed roots must fall back to operate as non
-
fixed
-
root nodes,
thus disconnecting from the router and searching for a preferred parent to connect to.
To set up a fixed
-
root mesh network, the following fixed
-
root setting must be enabled on all of its nodes:
//File esp_mesh.h
esp_err_t esp_mesh_fix_root(bool enable)
Also note that when a node connects to a parent node, should their fixed root settings differ, the child
node will automatically change it to match the one of its parent (see also the MESH_EVENT_ROOT_FIXED
event later).
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 17
Manual Networking
3
-
In a manual mesh network nodes apply none of the implicit actions or behaviours described in the
previous mesh organization modes, and so the setup and management of the mesh network is left
entirely to the programmer.
From here, once mesh networking has been enabled on a node, a preliminary manual Wi-Fi scan must
be performed to search for the mesh nodes and/or the external router to attempt to connect to
according to a user's custom criteria, where the status in the mesh network of the nodes that were
found, and so their capability to accept child connections, can be retrieved from their Mesh IE (as we'll
see in more detail later).
Note that performing a preliminary manual Wi-Fi scan in Manual Networking is also mandatory to allow
a connection to a parent node to be performed, since as discussed before the SSID of nodes in a mesh
network, which is derived from their base MAC address using a proprietary (or in any case undisclosed)
algorithm, is hidden from their Wi
-
Fi beacon frames but is put (typically encrypted) in their Mesh IE, SSID
that as we'll see is decrypted by the Mesh Stack in a manner transparent to the application.
From here, once a node successfully connects to a parent, it will remain its child indefinitely until the
connection between the two becomes unstable or fails, and while the mesh stack will still attempt to
reconnect the node to its parent, in this case if desidered it's also possible to perform another Wi
-
Fi scan
to search for another parent to fall back to.
Also note that a manual mesh network presents no automatic procedures such as the root node's self
-
healing or root node conflicts resolution, and indeed in this organization mode the root node is treated
as the fixed
-
root of a fixed
-
root network.
To set up a manual mesh network, other than enabling the fixed
-
root setting as previously seen with
Fixed-Root networking, the self-organized networking features of each of its nodes must be disabled,
which is obtained by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_self_organized(bool enable,
bool
select_parent
)
Going back to the code premises of our mesh application, the "
mesh_org_t
" enumerated type represents the
possible organization modes of a mesh network, while in the "
mesh_status_t
" struct the "
org
" member
represents the current organization mode used by a node in the network, which as said before should be
shared among all other nodes partecipating in the same mesh.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 18
esp_err_t mesh_init()
{
/*-- Mesh Organization Mode --*/
switch(MESH_ORGANIZATION_MODE)
{
/* No action is required to set the mesh
organization mode to self-organized */
case SELF_ORGANIZED:
mesh_state.org = SELF_ORGANIZED;
/* Setting the mesh organization mode to Fixed-Root
requires the fixed root setting on a node to be enabled */
case FIXED_ROOT:
ESP_ERROR_CHECK(esp_mesh_fix_root(true));
mesh_state.org = FIXED_ROOT;
break;
/* Setting the mesh organization mode to Manual Networking
requires the fixed root setting to be enabled and the
node's self-organized networking features to be disabled */
case MANUAL_NETWORKING:
ESP_ERROR_CHECK(esp_mesh_fix_root(true));
ESP_ERROR_CHECK(esp_mesh_set_self_organized(false,false));
mesh_state.org = MANUAL_NETWORKING;
break;
}
}
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 19
Mesh Topology Settings
Set the Mesh Network Maximum Layer
The maximum layer of a mesh network represents the layer where joining nodes are configured as leaf
nodes
, i.e. nodes that are not allowed to have children nodes, and it can be set via the following
function:
//File esp_mesh.h
esp_err_t esp_mesh_set_max_layer(int max_layer)
esp_err_t mesh_init()
{
ESP_ERROR_CHECK(esp_mesh_set_max_layer(MESH_MAX_LAYER)); //Set the mesh
maximum layer
}
Set the Mesh Network Maximum Capacity
In general the maximum capacity of a mesh network, i.e. the maximum number of nodes that can be
part of the same network, is constrained by its maximum layer "L" and the maximum number of children
allowed for each node "C", and is equal to:
max nodes = C(i-1)
i=1
L
with a default theoretical maximum capacity of 111'111'111'111'111 nodes (L = 15, C = 10).
Theoretical scenarios aside, it is also possible to set an upper limit for the capacity of a mesh network,
and this is obtained by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_capacity_num(int max_nodes)
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 20
esp_err_t mesh_init()
{
//Set the mesh network maximum capacity
ESP_ERROR_CHECK(esp_mesh_set_capacity_num(MESH_MAX_CAPACITY));
}
Mesh Self-Organized Root Settings
The following settings are relevant to a self-organized mesh network only, even if they can be configured
regardless of the intended mesh organization mode since in general it may vary during the application's
execution.
Set the Root Election Voting Percentage Threshold
The voting percentage threshold for a node to proclaim itself as root once the rounds of a root election
are over can be changed via the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_vote_percentage(float threshold)
Note that lowering the voting percentage threshold from its default value proves useful in dynamic
environments where the nodes' RSSI with the router vary very quickly, which may lead to none of them
reaching the default voting percentage threshold thus resulting in a failed root election.
Note however that setting this parameter to a low value may cause multiple nodes to proclaim
themselves as root in the network, conflicts that as discussed before can be resolved by the self-
organized root conflicts resolution algorithm.
esp_err_t mesh_init()
{
/* Possibly check the validity of the custom root
election threshold, i.e. threshold
(0.0,1.0] */
//Set the root election election threshold
ESP_ERROR_CHECK(esp_mesh_set_vote_percentage(ROOT_ELECTION_THRESHOLD));
}
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 21
esp_err_t mesh_init()
{
mesh_attempts_t self_attempts;
self_attempts.scan =ROOT_ELECTION_MIN_ROUNDS; //Set the minimum rounds
self_attempts.vote = 15; in a root election
self_attempts.fail = 120; //In this case the other members
self_attempts.monitor_ie = 10; of the mesh_attempts_t struct
are set to their default values
ESP_ERROR_CHECK(esp_mesh_set_attempts(&self_attempts)); //Apply the set
number of
} attempts
Set the minimum number of Rounds in a Root Election
The minimum number of rounds in a root node election and consequently its duration (and also the
minimum number of scans in a self
-
organized network before newly activated nodes attempt to connect
to the router as root if no other node is found) can be changed via the following function:
//File esp_mesh_internal.h (automatically included by the previous headers)
typedef struct //Self-Organized implicit actions' number of attempts
{
int scan; //Minimum root election rounds (default=10) <----
int vote;//Maximum self-healing root election rounds (default=15)
int fail; //Maximum parent reconnection tries before
switching to another parent (default=120)
int monitor_ie; //Maximum number of beacon frames with an updated Mesh IE
received from the node's parent before the node must
update its own Mesh (default=10)
}mesh_attempts_t;
esp_err_t esp_mesh_set_attempts(mesh_attempts_t* attempts)
The other members of the mesh_attempts_t struct define the number of attempts of other implicit
actions in a self
-
organized network as described above, and they must be set to valid values (possibly
their defaults) before calling the
esp_mesh_set_attempts()
function.
In any case the minimum number of rounds in a root election should be tailored to the expected number
of nodes and possibly the physical topology of the mesh network, where the larger the network the more
rounds are advised to increase the possibility for the optimal root node to be elected while reducing the
chances of root node conflicts to arise.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 22
esp_err_t mesh_init()
{
//Set the root node healing delay
ESP_ERROR_CHECK(esp_mesh_set_root_healing_delay(MESH_ROOT_HEALING_DELAY));
}
Set the Root Node Healing Delay
The root node healing delay, i.e. the time from the moment the nodes on the second layer of the mesh
network (or in general in the first layers in case of multiple simultaneous failures) receive the last beacon
frame from the root node and the moment they disconnect from it and start a new root election can be
set via the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_root_healing_delay(int delay_ms)
It should be noted that while lowering the root healing delay allows for quicker self-healing of the mesh
network and the restoring of connectivity with the external DS, setting it to too low a value may cause
unnecessary root elections and so delays if due to its load or other internal causes the beacon frame sent
by the root note is delayed to the point of triggering the root node self
-
healing process in the rest of the
network.
Other Root Node Settings
esp_err_t mesh_init()
{
//Set the root node's TODS receiving queue size
ESP_ERROR_CHECK
(
esp_mesh_set_xon_qsize
(
MESH_ROOT_TODS_QUEUE_SIZE
));
}
Set the size of the root node's receiving queue for packets destined for the external DS
The size of the root node's receiving queue for packets destined for the external DS (ot TODS queue) can
be set via the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_xon_qsize(int qsize)
In general the size of the root node's TODS queue should be tailored to the expected network throughput
destined for the external DS, where an increased queue size corresponds to a lower probability of packet
loss at the root node at the cost of greater memory occupation.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 23
Mesh Additional SoftAP Settings
The following settings complement the Mesh SoftAP settings previously discussed in a node's base mesh
configuration.
Set the Mesh SoftAP Authmode
The authentication protocol used by nodes to communicate over the mesh network can be set via the
following function:
//File esp_wifi_types.h (automatically included by the previous headers)
typedef
enum //SoftAP authmode enumerates
{
WIFI_AUTH_OPEN = 0,//Open (no authentication)
WIFI_AUTH_WEP,//WEP (buggy, avoid)
WIFI_AUTH_WPA_PSK,//WPA_PSK
WIFI_AUTH_WPA2_PSK,//WPA2_PSK
WIFI_AUTH_WPA_WPA2_PSK,//WPA_WPA2_PSK
WIFI_AUTH_WPA2_ENTERPRISE,//WPA2_ENTERPRISE
WIFI_AUTH_MAX
}wifi_auth_mode_t;
//File esp_mesh.h
esp_err_t esp_mesh_set_ap_authmode(wifi_authmode_t authmode)
esp_err_t mesh_init()
{
//Set Mesh SoftAP Authmode
ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(MESH_SOFTAP_AUTHMODE));
}
Also note that the data received via mesh networking is processed along with the chosen authentication
protocol entirely by the mesh stack, while the nodes' SoftAP interfaces are left with no authentication or
password set, and so result open (but with their SSID hidden) at the Wi-Fi interface level.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 24
esp_err_t mesh_init()
{
//Set the mesh children disassociation delay
ESP_ERROR_CHECK
(
esp_mesh_set_ap_assoc_expire
(
CHILD_DISASSOCIATION_DELAY
));
}
Set the Mesh Children Disassociaton Delay
The mesh children disassociation delay is the time from the moment the last data is received from a
node's child to the moment the node marks it as inactive (i.e. failed), disassociating it.
The mesh children disassociation delay can be set by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_ap_assoc_expire(int delay_s)
Note that it's not necessary for a child node to explicitly send a mesh packet to its parent for its
disassociation timer to reset, since implicit mesh "state" packets are periodically sent from child to
parent by the mesh internal management.
Mesh Node
-
specific Settings
(
optional
)
5)
Unlike the mesh shared settings, the following settings if defined may vary from one node to another in the
mesh network.
Add the node to one or more Mesh Groups
A mesh group represents a subset of a mesh network comprised of zero or more nodes used for
multicasting purposes to allow the sending of mesh packets to all nodes belonging to a certain group.
A mesh group is identified by its Group Identifier (GID), which as with the Mesh Network Identifier (MID)
has the format of a MAC address (6 bytes) and is described by the mesh_addr_t union previously
detailed.
From here a node can be added to one or more mesh groups by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_set_group_id(const mesh_addr_t* gids,int num)
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 25
esp_err_t mesh_init()
{
mesh_addr_t group_ids[NODE_GROUPS_NUMBER];
/* Set the GIDs of the groups we want to add the node to */
//Add the node to the aforementioned mesh groups
ESP_ERROR_CHECK(esp_mesh_set_group_id(&group_ids,NODE_GROUPS_NUMBER));
}
Set custom MAC Addresses for the node's Wi-Fi Interfaces (Station and/or SoftAP)
As an additional configuration it is also possible to set custom MAC addresses for the node's Station
and/or SoftAP interfaces, which may prove useful in the design and configuration of a manual mesh
network, and is obtained by calling the following function:
//File esp_interface.h (automatically included by the previous headers)
typedef enum //Networking interface enumerates
{
ESP_IF_WIFI_STA = 0, //Wi-Fi station interface (or mode)
ESP_IF_WIFI_AP,//Wi-Fi softAP interface (or mode)
ESP_IF_ETH, //Ethernet interface
ESP_IF_MAX
} esp_interface_t;
//File esp_wifi.h
typedef esp_interface_t wifi_interface_t;
esp_err_t esp_wifi_set_mac(wifi_interface_t ifx_mode,
const uint8_t* mac[])
Note that the MAC addresses of a node's Station and SoftAP interfaces cannot coincide, and trying to do
so will cause the function to return the ESP_ERR_WIFI_MAC error, and as an additional constraint the bit
0 of the most significant byte of the MAC address cannot be set (for example xA:xx:xx:xx:xx:xx is a valid
address, while x5:xx:xx:xx:xx:xx is not).
Also note that in mesh network the MAC address of a node's SoftAP interface (corresponding to its
BSSID) is used exclusively by nodes to connect to it as children, while the MAC address of a node's Station
interface is also propagated upwards in the routing tables of all the node's ancestors, thus representing
the label by which a node is addressable in the mesh network.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 26
esp_err_t mesh_init()
{
uint8_t mac[6]; //Used to set a custom MAC address for an interface
if
(MESH_STATION_USE_CUSTOM_MAC) //If a custom MAC address for
{
the station interface is used
memcpy(mac,MESH_STATION_CUSTOM_MAC,6);
ESP_ERROR_CHECK(esp_wifi_set_mac(ESP_IF_WIFI_STA,mac));
}
if
(MESH_SOFTAP_USE_CUSTOM_MAC) //If a custom MAC address for
{ the softAP interface is used
memcpy(mac,MESH_SOFTAP_CUSTOM_MAC,6);
ESP_ERROR_CHECK
(
esp_wifi_set_mac
(
ESP_IF_WIFI_AP
,
mac
));
}
}
Enable Mesh Networking6)
Once all the desidered settings have been configured, mesh networking can be enabled on the device, which
is obtained by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_start(void)
esp_err_t mesh_init()
{
ESP_ERROR_CHECK
(
esp_mesh_start
());
//Enable Mesh Networking
return
ESP_OK; //End of the Mesh Initializer Function
}
Once the esp_mesh_start() function returns successfully mesh networking will be enabled on the device,
which will cause the MESH_EVENT_STARTED event to raise in the mesh stack, thereby causing the mesh setup
process to pass into its event-driven phase.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 27
Summarizing, the tasks that must be performed by the Mesh Initializer Function are:
Also note that the majority of the mesh settings we have discussed so far can also be modified after mesh
networking has been enabled on a device, which may cause it to disconnect or change its status in its current
mesh network depending on the changes applied.
ESP-IDF Mesh Stack
Mesh Initializer Function
Page 28
Event
-
driven Setup Phase
Once mesh networking has been enabled on a node, its setup process enters its event-driven phase, which
consists in the application-level handling of the Mesh Events raised by the Mesh Stack.
A Mesh Event can be represented as a set of conditions on the state of the mesh stack on a node which, other
than evolving internally through the application's execution, is affected by the Wi-Fi Events and the data received
from the Wi-Fi stack, and once all the conditions representing an event have been met such event will be raised
by the mesh stack.
Once raised, a mesh event is first handled internally in the mesh stack by its Specific Implicit Handler, which
performs a set of Implicit Actions that depend on other conditions relative to the mesh stack's state, in particular
on the Mesh Organization Mode set on the node.
Once its internal handling is complete the event with its ID and a set of additional information are passed by the
mesh stack at the application level to the Mesh Events General Handler function previously registered by setting
the node's base mesh configuration, which in turn will call, passing the additional information provided, the
Specific Handler relative to such event, where the actual application-level handling of the event is performed,
whose Typical Actions depend again on the mesh status of the node (once more in particular on its Mesh
Organization Mode), the additional information provided by the mesh stack, and possibly the intended logic of
the setup process.
The mesh events that can be raised by the mesh stack can also be organized into the following categories:
The Mesh Main Setup Events, which are relative to the main setup process of a node, which ends when it
connects to a parent in the mesh network.
The Mesh Network Update Events, which are raised once a node's main setup process is finished upon
receiving specific updates from the mesh network.
The Mesh Root-specific Events, which may rise on the current, former or candidate root nodes in the mesh
network.
The full list of mesh events along with their description, the implicit actions performed by their implicit handlers,
the additional information passed at the application-level by the mesh stack, and the typical actions that should be
performed by their specific handlers are summarized in the the following tables:
ESP-IDF Mesh Stack
Mesh Events Handling
Page 29
Mesh Main Setup Events
ESP-IDF Mesh Stack
Mesh Events Handling
Page 30
Mesh Network Update Events
ESP-IDF Mesh Stack
Mesh Events Handling
Page 31
Mesh Root
-
specific Events
ESP-IDF Mesh Stack
Mesh Events Handling
Page 32
Mesh Events General Handler
The ID and possible additional information on the mesh events that occur are passed by the mesh stack to the
Mesh Events General Handler using the following data structures:
Mesh Events ID Definitions
//File esp_mesh.h
typedef
enum //Mesh Events IDs
{
/*-- Mesh Main Setup Events --*/
MESH_EVENT_STARTED,
MESH_EVENT_STOPPED,
MESH_EVENT_SCAN_DONE,
MESH_EVENT_PARENT_CONNECTED,
MESH_EVENT_PARENT_DISCONNECTED,
MESH_EVENT_NO_PARENT_FOUND,
/*-- Mesh Network Update Events --*/
MESH_EVENT_CHILD_CONNECTED,
MESH_EVENT_CHILD_DISCONNECTED,
MESH_EVENT_ROUTING_TABLE_ADD,
MESH_EVENT_ROUTING_TABLE_REMOVE,
MESH_EVENT_ROOT_ADDRESS,
MESH_EVENT_ROOT_FIXED,
MESH_EVENT_TODS_STATE,
MESH_EVENT_VOTE_STARTED,
MESH_EVENT_VOTE_STOPPED,
MESH_EVENT_LAYER_CHANGE,
MESH_EVENT_CHANNEL_SWITCH,
/*-- Mesh Root-specific Events --*/
MESH_EVENT_ROOT_GOT_IP,
MESH_EVENT_ROOT_LOST_IP,
MESH_EVENT_ROOT_SWITCH_REQ,
MESH_EVENT_ROOT_SWITCH_ACK,
MESH_EVENT_ROOT_ASKED_YIELD,
}mesh_event_id_t;
ESP-IDF Mesh Stack
Mesh Events Handling
Page 33
Mesh Events Additional Information Types
Mesh Main Setup Events
/*============================== MESH_EVENT_SCAN_DONE ==============================*/
//File esp_mesh.h
typedef
struct
{
uint8_t number; //The number of APs that were found in the Wi-Fi scan
}
mesh_event_scan_done_t
;
/*=========================== MESH_EVENT_PARENT_CONNECTED ===========================*/
//File esp_event.h (automatically included by the previous headers)
typedef
struct //SYSTEM_EVENT_STA_CONNECTED event-specific info
{
uint8_t ssid[32]; //SSID of the AP the station connected to
uint8_t ssid_len;//SSID length of the AP the station connected to
uint8_t
bssid
[
6
];
//BSSID of the AP the station connected to
uint8_t channel;//Wi-Fi channel of the AP the station connected to
wifi_auth_mode_t authmode;//The authmode used by AP the station connected to
} system_event_sta_connected_t;
//File esp_mesh.h
typedef
struct
{
system_event_sta_connected_t connected; //Information on the parent's AP (as with the
the SYSTEM_EVENT_STA_CONNECTED Wi-
Fi event)
uint8_t self_layer;//The node's layer in the mesh network
}
mesh_event_connected_t
;
/*========================= MESH_EVENT_PARENT_DISCONNECTED =========================*/
//File esp_event.h
typedef
struct //SYSTEM_EVENT_STA_DISCONNECTED event-specific info
{
uint8_t ssid[32]; //SSID of the AP the station disconnected from
uint8_t ssid_len;//SSID length of the AP the station disconnected from
uint8_t bssid[6]; //BSSID of the AP the station disconnected from
uint8_t reason;//The reason for the disconnection
}
system_event_sta_disconnected_t
;
//File esp_mesh.h
typedef
system_event_sta_disconnected_t mesh_event_disconnected_t;
//Information on the disconnected parent's AP
(same as with the SYSTEM_EVENT_STA_DISCONNECTED Wi-Fi event)
/*=========================== MESH_EVENT_NO_PARENT_FOUND ===========================*/
//File esp_mesh.h
typedef
struct
{
int scan_times; //The total number of scans performed searching
} mesh_event_no_parent_found_t; for a viable parent for the node
ESP-IDF Mesh Stack
Mesh Events Handling
Page 34
Mesh Network Update Events
/*=========================== MESH_EVENT_CHILD_CONNECTED ===========================*/
//File esp_event.h
typedef
struct
//SYSTEM_EVENT_AP_STACONNECTED event
-
specific info
{
uint8_t mac[6]; //MAC address of the client that has connected to the SoftAP
uint_t aid;//The aid given by the SoftAP to the connected client
}system_event_ap_staconnected_t;
//File esp_mesh.h
typedef
system_event_ap_staconnected_t mesh_event_child_connected_t;
//Connected station interface information
(same as with the SYSTEM_EVENT_AP_STACONNECTED Wi-Fi event)
/*========================== MESH_EVENT_CHILD_DISCONNECTED ==========================*/
//File esp_event.h
typedef
struct //SYSTEM_EVENT_AP_STADISCONNECTED event-specific info
{
uint8_t
mac
[
6
];
//MAC address of the client that has disconnected from the SoftAP
uint_t aid; //The aid given by the SoftAP to the disconnected client
}system_event_ap_stadisconnected_t;
//File esp_mesh.h
typedef
system_event_ap_stadisconnected_t
mesh_event_child_disconnected_t
;
//Disconnected station interface information
(same as with the SYSTEM_EVENT_AP_STADISCONNECTED Wi-Fi event)
/*========================== MESH_EVENT_ROUTING_TABLE_ADD ==========================*/
/*========================= MESH_EVENT_ROUTING_TABLE_REMOVE =========================*/
//File esp_mesh.h
typedef
struct
{
uint16_t
rt_size_change
;
//The number of entries that were added or removed
from the node's routing table
uint16_t rt_size_new; //The current number of entries in the node's
routing table
} mesh_event_routing_table_change_t;
/*============================= MESH_EVENT_ROOT_ADDRESS =============================*/
//File esp_mesh.h
typedef
mesh_addr_t mesh_event_root_address_t; //The root node's SoftAP MAC address
/*============================== MESH_EVENT_ROOT_FIXED ==============================*/
//File esp_mesh.h
typedef
struct
{
bool
is_fixed
;
//The parent's and thus the node's
} mesh_event_root_fixed_t; new value of the fixed root setting
ESP-IDF Mesh Stack
Mesh Events Handling
Page 35
/*============================= MESH_EVENT_TODS_STATE =============================*/
//File esp_mesh.h
typedef
enum //Whether the external DS is currently accessible or not
{
MESH_TODS_REACHABLE, //External DS accessible
MESH_TODS_UNREACHABLE, //External DS not accessible
} mesh_event_toDS_state_t;
/*============================= MESH_EVENT_VOTE_STARTED =============================*/
//File esp_mesh.h
typedef
struct
{
int reason; //The reason for the new root election (currently only due to
root node self-healing or the root waiving its root status)
int attempts; //The maximum number of rounds in the new root election
mesh_addr_t rc_addr; //The SoftAP MAC address of the node that was designated by the
current root to become the new root (currently unimplemented)
}
mesh_event_vote_started_t
;
/*============================= MESH_EVENT_LAYER_CHANGE =============================*/
//File esp_mesh.h
typedef
struct
{
uint8_t
new_layer
;
//The new node's layer in the mesh network
} mesh_event_layer_change_t;
/*============================ MESH_EVENT_CHANNEL_SWITCH ============================*/
//File esp_mesh.h
typedef
struct
{
uint8_t channel; //The new Mesh Wi-Fi Channel
} mesh_event_channel_switch_t;
Mesh Root
-
specific Events
/*============================= MESH_EVENT_ROOT_GOT_IP =============================*/
//File esp_event.h
typedef
struct //SYSTEM_EVENT_STA_GOT_IP event-specific info
{
tcpip_adapter_ip_info_t ip_info; //IP configuration obtained by the station interface
bool
ip_changed
;
//Whether this IP configuration
}system_event_sta_got_ip_t; overrode a previous one
//File esp_mesh.h
typedef
system_event_sta_got_ip_t mesh_event_root_got_ip_t;
//The IP configuration obtained by the root node
(same as with the SYSTEM_EVENT_STA_GOT_IP Wi-Fi event)
ESP-IDF Mesh Stack
Mesh Events Handling
Page 36
/*=========================== MESH_EVENT_ROOT_SWITCH_REQ ===========================*/
//File esp_mesh.h
typedef
struct
{
int reason; //Reason for the switch request
(currently only because the root node waived its root status)
mesh_addr_t rc_addr; //The MAC address of the candidate root's Station interface
} mesh_event_root_switch_req_t;
/*=========================== MESH_EVENT_ROOT_ASKED_YIELD ===========================*/
//File esp_mesh.h
typedef
struct
{
int8_t
rssi
;
//The requesting root's RSSI with the router
uint16_t capacity; //The number of nodes in the requesting root's network
uint8_t addr[6]; //The MAC address of the requesting root's station interface
} mesh_event_root_conflict_t;
Mesh Events Additional Information union
typedef
union //Mesh Events additional information union
{
/*-- Mesh Main Setup Events additional information --*/
mesh_event_connected_t connected; //MESH_EVENT_PARENT_CONNECTED
mesh_event_disconnected_t
disconnected
;
//MESH_EVENT_PARENT_DISCONNECTED
mesh_event_scan_done_t scan_done; //MESH_EVENT_SCAN_DONE
mesh_event_no_parent_found_t
no_parent
;
//MESH_EVENT_NO_PARENT_FOUND
/*-- Mesh Network Update Events additional information --*/
mesh_event_child_connected_t
child_connected
;
//MESH_EVENT_CHILD_CONNECTED
mesh_event_child_disconnected_t child_disconnected; //MESH_EVENT_CHILD_DISCONNECTED
mesh_event_routing_table_change_t routing_table; //MESH_EVENT_ROUTING_TABLE_ADD +
MESH_EVENT_ROUTING_TABLE_REMOVE
mesh_event_root_address_t root_addr; //MESH_EVENT_ROOT_ADDRESS
mesh_event_root_fixed_t
root_fixed
;
//MESH_EVENT_ROOT_FIXED
mesh_event_toDS_state_t toDS_state; //MESH_EVENT_TODS_STATE
mesh_event_vote_started_t vote_started; //MESH_EVENT_VOTE_STARTED
mesh_event_layer_change_t layer_change; //MESH_EVENT_LAYER_CHANGE
mesh_event_channel_switch_t channel_switch; //MESH_EVENT_CHANNEL_SWITCH
/*-- Mesh Root-specific Events additional information--*/
mesh_event_root_got_ip_t got_ip; //MESH_EVENT_ROOT_GOT_IP
mesh_event_root_switch_req_t switch_req; //MESH_EVENT_ROOT_SWITCH_REQ
mesh_event_root_conflict_t root_conflict; //MESH_EVENT_ROOT_ASKED_YIELD
}
mesh_event_info_t
;
Mesh Events Summary Struct
This represents the summary struct that is passed by the mesh stack to the Mesh Events General Handler for the
application
-
level handling of mesh events:
typedef
struct
{
mesh_event_id_t id;
mesh_event_info_t info;
//Summary struct passed by the mesh stack
to the Mesh Events General Handler
//Event ID
//Event additional information (if applicable)
} mesh_event_t;
ESP-IDF Mesh Stack
Mesh Events Handling
Page 37
From here at the application level the Mesh Events General Handler function should be declared as follows:
void mesh_events_handler(mesh_event_t event)
where the event parameter represents the summary struct passed by the mesh stack containing the information
on the mesh event that has occured.
Regarding its definition, as discussed before the task of the Mesh Events General Handler consists in calling the
specific handler relative to each event that occurs, passing it the additional information provided by the mesh
stack where applicable, and therefore its general structure appears as follows:
void mesh_events_handler(mesh_event_t event)
{
switch(event.id)
{
case
EVENT1:
mesh_EVENT1_handler(
&
event.info.EVENT1_t); //call EVENT1 specific handler
break
;
case
EVENT2:
mesh_EVENT2_handler(
&
event.info.EVENT2_t); //call EVENT2 specific handler
break;
case
EVENTN:
mesh_EVENTN_handler(
&
event.info.EVENTN_t); //call EVENTN specific handler
break
;
default
:
ESP_LOGE(TAG,"Unknown Mesh Event with ID: %u",event.id);
break
;
}
return
;
}
void mesh_events_handler(mesh_event_t event)
{
switch
(event.id)
{
/*-- Mesh Main Setup Events --*/
case MESH_EVENT_STARTED:
mesh_STARTED_handler();
break
;
case
MESH_EVENT_STOPPED:
mesh_STOPPED_handler();
break
;
case
MESH_EVENT_SCAN_DONE:
mesh_SCAN_DONE_handler(&event.info.scan_done);
break
;
case
MESH_EVENT_PARENT_CONNECTED:
mesh_PARENT_CONNECTED_handler(&event.info.connected);
break
;
case
MESH_EVENT_PARENT_DISCONNECTED:
mesh_PARENT_DISCONNECTED_handler(&event.info.disconnected);
break;
case
MESH_EVENT_NO_PARENT_FOUND:
mesh_NO_PARENT_FOUND_handler(&event.info.no_parent);
break
;
So, considering the mesh events that can currently be raised by the mesh stack, the actual definition of the Mesh
Events General Handler appears as follows:
ESP-IDF Mesh Stack
Mesh Events Handling
Page 38
/*-- Mesh Network Update Events --*/
case
MESH_EVENT_CHILD_CONNECTED:
mesh_CHILD_CONNECTED_handler(&event.info.child_connected);
break
;
case
MESH_EVENT_CHILD_DISCONNECTED:
mesh_CHILD_DISCONNECTED_handler(&event.info.child_disconnected);
break
;
case
MESH_EVENT_ROUTING_TABLE_ADD:
mesh_ROUTING_TABLE_ADD_handler(&event.info.routing_table);
break
;
case
MESH_EVENT_ROUTING_TABLE_REMOVE:
mesh_ROUTING_TABLE_REMOVE_handler(&event.info.routing_table);
break
;
case
MESH_EVENT_ROOT_ADDRESS:
mesh_ROOT_ADDRESS_handler(&event.info.root_addr);
break
;
case
MESH_EVENT_ROOT_FIXED:
mesh_ROOT_FIXED_handler(&event.info.root_fixed);
break
;
case
MESH_EVENT_TODS_STATE:
mesh_TODS_STATE_handler(&event.info.toDS_state);
break
;
case
MESH_EVENT_VOTE_STARTED:
mesh_VOTE_STARTED_handler(&event.info.vote_started);
break
;
case
MESH_EVENT_VOTE_STOPPED:
mesh_VOTE_STOPPED_handler();
break
;
case
MESH_EVENT_LAYER_CHANGE:
mesh_LAYER_CHANGE_handler(&event.info.layer_change);
break
;
case
MESH_EVENT_CHANNEL_SWITCH:
mesh_CHANNEL_SWITCH_handler(&event.info.channel_switch);
break
;
/*-- Mesh Root-specific Events --*/
case
MESH_EVENT_ROOT_GOT_IP:
mesh_ROOT_GOT_IP_handler(&event.info.got_ip);
break
;
case
MESH_EVENT_ROOT_LOST_IP:
mesh_ROOT_LOST_IP_handler();
break
;
case
MESH_EVENT_ROOT_SWITCH_REQ:
mesh_ROOT_SWITCH_REQ_handler(&event.info.switch_req);
break
;
case
MESH_EVENT_ROOT_SWITCH_ACK:
mesh_ROOT_SWITCH_ACK_handler();
break
;
case
MESH_EVENT_ROOT_ASKED_YIELD:
mesh_ROOT_ASKED_YIELD_handler(&event.info.root_conflict);
break
;
default
:
ESP_LOGE(TAG,"Unknown Mesh Event with ID: %u",event.id);
break
;
}
return
;
}
It should also be noted that depending on the mesh stack's configuration previously set via the mesh initializer
function and the logic of the driver module, some mesh events may never be raised by the mesh stack, thus
making their application-level handling unnecessary in specific contexts.
ESP-IDF Mesh Stack
Mesh Events Handling
Page 39
void mesh_EVENTi_handler(EVENTi_t* info)
{
/* Specific handler logic */
return
;
}
Mesh Events Specific Handlers
Following the previous definition of the Mesh Events General Handler, the mesh events specific handlers should
be defined according to the following general structure:
Utility Functions
The following utility functions will be used in the follow
-
up analysis of the mesh events specific handlers:
/* Resets the node's mesh status and the flags in the mesh event group to a
set of default values representing a node that is not connected to a parent */
void mesh_reset_status()
{
EventBits_t eventbits;//Used to check the flags in the mesh event group
memset(mesh_state->root_addr,0,6); //Reset the root address
memset(mesh_state->parent_addr,0,6); //Reset the node's parent address
mesh_state->my_layer = -1; //Reset the node's mesh layer
if
(((eventbits =xEventGroupGetBits(mesh_event_group))>>2)&1) //Reset the node's
mesh_state->my_type =MESH_NODE;
else
mesh_state->my_type =MESH_IDLE;
type depending on
it having children
connected or not
xEventGroupClearBits(mesh_event_group,MESH_PARENT|MESH_TODS);
//Clear the MESH_PARENT
and MESH_TODS flags
xEventGroupSetBits(mesh_event_group,MESH_VOTE); //Set the MESH_VOTE flag
return
; (no root election)
}
/* Resets the root node's station IP configuration (and implicitly its DNS Servers) */
void mesh_root_reset_stationIP()
{
tcpip_adapter_ip_info_t ipinfo; //Used to set an interface's IP configuration
if(MESH_ROOT_IPDYNAMIC)//Prevents an error from happening later should the node
ESP_ERROR_CHECK(tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA)); become root again
//Reset the root node's station IP configuration
inet_pton(AF_INET,"0.0.0.0",
&
ipinfo.ip);
inet_pton(AF_INET,"0.0.0.0",
&
ipinfo.netmask);
inet_pton(AF_INET,"0.0.0.0",
&
ipinfo.gw);
ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA,&ipinfo));
return
;
}
Where the tcpip_adapter_set_ip_info() function allows to set an interface's IP configuration, and is defined as
follows:
where the
info
argument must be present only if additional information is provided by the mesh stack for the
event, which as discussed before is passed by the Mesh Events General Handler to the specific handler in question.
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 40
Mesh Events Specific Handlers Typical Actions
Described below are the typical actions that should be performed by each mesh specific handler:
Mesh Main Setup Events
MESH_EVENT_STARTED
This event is raised when mesh networking has been enabled on a device by calling the esp_mesh_start()
function and, in addition to setting the MESH_ON flag in the mesh event group and resetting the node's status via
the
mesh_reset_status()
utility function previously described, further action depends on the node's
mesh
organization mode as follows:
Self-Organized Networking
If self
-
organized networking is used on the node, the implicit handler of this event would have already started
the automatic procedure to join the node in an existing mesh network or create a new one as previously
described, and so no further action is required in the application
-
level handling of this event.
Fixed-Root Networking
If fixed
-
root networking is used, a node must determine according to a user's custom criteria if it represents
the fixed root of the network or not, and in case it does it should promptly attempt to manually connect to
the external router using the following function:
//File esp_wifi_types.h (automatically included by the previous headers)
typedef struct //Station Mode Configuration
{
uint8_t ssid[32]; //SSID of the AP to connect to
uint8_t password[64]; //Password of the AP to connect to
bool bssid_set; //If set, connect only to the AP with
the following specific BSSID
uint8_t bssid[6]; //Specific BSSID of the AP to connect to
uint8_t channel;//AP's channel
/* Other station mode-related members */
} wifi_sta_config_t;
typedef
union //Holds a Wi-Fi interface mode configuration
{ (Station OR SoftAP)
wifi_sta_config_t sta; //Station Mode Configuration <---
wifi_ap_config_t ap; //SoftAP Mode Configuration
} wifi_config_t;
//File tcpip_adapter.h
esp_err_t tcpip_adapter_set_ip_info(tcpip_adapter_if_t tcpip_if,
tcpip_adapter_ip_info_t* ip_info)
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 41
It should be noted, as already pointed out, that if the external router is not in range or its AP settings don't
match the ones set in the fixed root's router configuration, the node will remain stuck in a loop attempting
and failing to connect with it.
Otherwise, if the node doesn't recognize itself to be the fixed
-
root of the mesh network, no further action is
required in the application-handling of this event, since its implicit handler would have already started to
search for the node's
preferred parent
to connect to (where note that if none is found the node will keep
searching indefinetely for a viable parent node to connect to).
Manual Networking
If manual networking is used, a manual Wi
-
Fi scan must be performed to search for the mesh nodes and/or
the external router, which can be carried out by calling the following function:
//File esp_wifi_types.h
typedef enum //Wi-Fi Scan types
{
WIFI_SCAN_TYPE_ACTIVE = 0, //Active Wi-Fi scan (scan by sending a probe request)
WIFI_SCAN_TYPE_PASSIVE, //Passive Wi-Fi scan (scan by waiting for a beacon
}wifi_scan_type_t; frame without explicitly sending a probe request)
typedef
struct //Active scan time per Wi-Fi channel
{
uint32_t min;//The minimum active scan time per Wi-Fi channel
(default = 120ms)
uint32_t max;//The maximum active scan time per Wi-Fi channel
} wifi_active_scan_time_t; (default = 120ms, must be <=1500ms)
typedef
struct //Scan time per Wi-Fi channel
{
wifi_active_scan_time_t active;//Active scan time per Wi-Fi channel
uint32_t passive;//Passive scan time per Wi-Fi channel
} wifi_scan_time_t; (must be <=1500ms)
//File esp_mesh.h
typedef
enum //Mesh node types
{
MESH_IDLE, //Idle Node
MESH_ROOT, //Root Node
MESH_NODE, //Intermediate Parent Node
MESH_LEAF, //Leaf Node
}mesh_type_t;
esp_err_t esp_mesh_set_parent(const wifi_config_t* parent,
const mesh_addr_t*
my_mid
,
mesh_type_t my_type,
int my_layer)
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 42
typedef
struct //Wi-Fi Scan configuration
{
uint8_t* ssid;//Whether to scan for an AP with a specific SSID only
uint8_t* bssid;//Whether to scan for an AP with a specific BSSID only
uint8_t channel;//Whether to scan on a specific Wi-Fi channel only
(1-13) or perform an all-channel scan (0, default)
bool show_hidden;
//Whether to include the APs with a hidden SSID
in their Wi-Fi beacon frames in the scan results
wifi_scan_type_t scan_type; //The type of the Wi-Fi scan to perform
(active or passive)
wifi_scan_time_t scan_time; //The scan time for each Wi-Fi channel
} wifi_scan_config_t;
esp_err_t esp_wifi_scan_start(const wifi_scan_config_t* scan_conf,
bool
block
)
Where note that the Wi
-
Fi scan should be limited to the Mesh Wi
-
Fi channel, the APs with hidden SSID must
be included in its results, and performing a passive scan is preferred.
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 43
void mesh_STARTED_handler()
{
wifi_config_t parent_ap = {0}; //Used to connect the fixed root to the external router
wifi_scan_config_t
scan_config
= {
0
};
//Used to store the configuration of the Wi
-
Fi
scan to perform in Manual Networking mode
xEventGroupSetBits(mesh_event_group,MESH_ON); //Set the MESH_ON flag
mesh_reset_status(); //Reset the node's status
switch
(mesh_state.org)//Further actions depend on the Mesh
{ Organization Mode set on the node
case
SELF_ORGANIZED
:
//For Self
-
Organized networking,
break
;no further action is required
case
FIXED_ROOT:
/* Determine according to a user's custom criteria if the
node represents the fixed
-
root of the mesh network or not */
if
(IS_NODE_FIXED_ROOT)//If the node is the fixed-root, it must attempt
{to directly connect to the external router
strcpy((char*)parent_ap.sta.ssid,MESH_ROUTER_SSID);
if
(MESH_ROUTER_SPECIFIC_BSSID)
{
parent_ap.sta.bssid_set = true;
strcpy((char*)parent_ap.sta.password,MESH_ROUTER_PASSWORD);
}
strToMAC(MESH_ROUTER_BSSID,parent_ap.sta.bssid);
parent_ap.sta.channel =MESH_WIFI_CHANNEL;
ESP_ERROR_CHECK
(
esp_mesh_set_parent
(
&
parent_ap
,
MESH_NETWORK_ID
,
MESH_ROOT
,
1
));
}
break
;//If the node is NOT the fixed root, no further action is required
case
MANUAL_NETWORKING:
//Set the configuration of the Wi-Fi scan to perform
scan_config.show_hidden
=
1
;
//Include APs with hidden SSID in the scan
results (to allow mesh nodes to be found)
scan_config.channel =MESH_WIFI_CHANNEL;//Scan on the Mesh Wi-Fi Channel only
scan_config.scan_type =WIFI_SCAN_TYPE_PASSIVE; //Passive Wi-Fi scans should be
used in manual mesh networking
ESP_ERROR_CHECK
(
esp_wifi_scan_start
(
&
scan_config
,
false
));
//Start the Wi
-
Fi scan
break
;
}
return
;
}
MESH_EVENT_STOPPED
This event may be raised both because mesh networking was disabled intentionally by calling the
esp_mesh_stop() function (we'll see later), or due to a fatal error in the mesh stack, and other than resetting the
node's mesh status and clearing the MESH_ON flag in the mesh event group, should the disabling have occured
unintentionally, as an error recovery attempt it is possible to try to re-enable mesh networking by calling the
esp_mesh_start() function.
Also note that the handling of errors that might occur in the application's logic due to the disabling of mesh
networking is left entirely to the Driver Module.
void mesh_STOPPED_handler()
{
//Reset the node's status
//Clear the MESH_ON flag
//If the disabling was unintentional,
try to reenable mesh networking
mesh_reset_status();
xEventGroupClearBits(mesh_event_group,MESH_ON);
if(/* unintentional */)
ESP_ERROR_CHECK(esp_mesh_start());
return;
}
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 44
MESH_EVENT_SCAN_DONE
(Manual Networking only)
This event is raised when the mesh stack completes a manual Wi
-
Fi scan requested by calling the
esp_wifi_scan_start() function, and in its application-level handler to search for a suitable parent to attempt to
connect the node to, for each of the APs that were found in the scan the length of its vendor IE field must be
retrieved, which is obtained by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_scan_get_ap_ie_len(int* ie_len)
From here by checking the length of the AP's vendor IE against the length of a mesh IE, which is defined as follows:
typedef
struct
//Node's Mesh IE (extract)
{
uint8_t
mesh_id
[
6
];
//The node's MID
uint8_t
mesh_type
;
//The node's type
uint8_t
layer_cap
;
//The mesh maximum layer of the node minus its current layer
uint8_t
layer
;
//The current layer of the node in the mesh network
uint8_t
assoc_cap
;
//The node's maximum children connections
uint8_t
assoc
;
//The number of children currently connected to the node
}
__attribute__((packed))
mesh_assoc_t
;
If the AP's vendor IE is the same length as a mesh IE, that AP is relative to a mesh node, and its information
and Mesh IE can be retrieved by calling the following function:
//File esp_wifi.h
typedef
struct
//Information (record) on an AP that was found in the Wi
-
Fi scan
{
uint8_t
ssid
[
33
];
//AP SSID
uint8_t
bssid
[
6
];
//AP BSSID
wifi_auth_mode_t
authmode
;
//AP Authmode
uint8_t
primary
;
//AP (primary) Wi
-
Fi channel
int8_t
rssi
;
//RSSI with the AP
}
wifi_ap_record_t
;
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 45
//File esp_mesh.h
esp_err_t esp_mesh_scan_get_ap_record(wifi_ap_record_t* ap_rec,
void*
ap_ie
)
From here, by checking its AP record and Mesh IE, it can be determined whether a connection attempt to such
node can be performed according to the following conditions:
The node belongs to the same mesh network
(ap_ie.mesh_id == MESH_NETWORK_ID)-
The node is not an idle node
(ap_ie.mesh_type != MESH_IDLE)
(where note that in this instance in
self
-
organized networking the initial root election is triggered between the idle nodes)
-
The node is not a leaf node
(ap_ie.layer_cap > 0)-
The node's maximum children connections hasn't been reached
(
ap_ie.assoc_cap
>
ap_ie.assoc
)
-
Preferably
, the RSSI with the node is above a defined minimum threshold
(
ap_rec.rssi
>
MESH_PARENT_MIN_RSSI
)
-
Otherwise, if its vendor IE is not the same length as a Mesh IE, the AP is relative to an external network,
possibly the external router, which can be determined by retrieving the AP record using the same
esp_mesh_scan_get_ap_record()
function described before, this time with a second NULL argument since
the vendor IE is of no interest, and then checking whether the AP's SSID and possibly its BSSID correspond to
the SSID and the possible specific BSSID that were set in the router settings in the node's base mesh
configuration.
From here, if an external AP has been determined as being relative to the external router, no particular
conditions must be met if attempting to connect to it as the root node is desidered, apart from possibly
having an RSSI above a defined minimum threshold
(ap_rec.rssi >MESH_PARENT_MIN_RSSI)
.
From here, once the records and the Mesh IEs of all APs that were found in the scan have been parsed, by applying
a user's custom criteria it must be determined whether and which one of the APs the node should attempt to
connect to as a child, an attempt that can be performed by using the
esp_mesh_set_parent()
previously with the following considerations:
In the "
parent
" argument representing the node's station base configuration the "ssid" member must be set
to the SSID that was passed by the mesh stack in the parent's AP record (which was previously transparently
retrieved and decrypted from its Mesh IE), while the "password" member must be set to an empty string
since, as discussed before, mesh nodes use no authentication protocol on their SoftAP interfaces.
The "
my_layer
" argument must be set to the mesh layer of the parent node + 1
(ap_ie.layer + 1)
.
The "
my_type
" argument must be set to either MESH_NODE or MESH_LEAF depending on the node's
expected mesh layer ("
my_layer
") and the maximum layer allowed in the mesh network (MESH_MAX_LAYER)
Connecting to a Node:
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 46
Connecting to the External Router:
The "parent" argument representing the node's station base configuration must be initialized with the
information found in the router's AP record.
The "my_layer" argument must be set to 1 (for the node will represent the root of the mesh network should
it successfully connect to the external router)
The "my_type" argument must be set to MESH_ROOT for the same reason as above.
Finally, if no suitable parent to attempt to connect the node to was found, a new Wi-Fi scan should be performed
after a defined time interval, repeating this process until a viable parent for the node is found.
void mesh_SCAN_DONE_handler(mesh_event_scan_done_t* info)
{
wifi_scan_config_t scan_config = {0}; //Configuration of the new Wi-Fi scan to
perform if no parent is found for the node
//Parent connection attempt variables
bool parent_found = false;//Whether a viable parent for the node was found
wifi_config_t parent = {0}; //Information on the parent's AP to connect
to (this node's station base configuration)
int my_layer = -1;//The supposed mesh layer of the node should
it successfully connect to its parent
mesh_type_t my_type =MESH_IDLE;//The supposed type of the node should it
successfully connect to its parent
//Information on the APs that were found in the scan
wifi_ap_record_t ap_rec;//Used to store the records of the APs that were found
mesh_assoc_t ap_ie;//Used to store the Mesh IEs of the nodes that were found
int ap_ie_len =0;//Length of the vendor IEs of the APs that were found
for
(int i=0;i<info.number;i++) //For each of the APs that were found in the scan
{
esp_mesh_scan_get_ap_ie_len(
&
ap_ie_len
);
//Retrieve the length of the AP vendor IE
//If the AP is relative to a mesh node
if
(ap_ie_len ==
sizeof
(mesh_assoc_t))
{
esp_mesh_scan_get_ap_record(
&
ap_rec,
&
ap_ie); //Retrieve the AP record and Mesh IE
if
((checkArrayEqual(ap_ie.mesh_id,MESH_NETWORK_ID,6) &&
(ap_ie.mesh_type != MESH_IDLE) &&
(
ap_ie.layer_cap
) &&
(ap_ie.assoc >= ap_ie.assoc_cap) &&
(ap_rec.rssi >= MESH_PARENT_MIN_RSSI))
/* A connection attempt to this node is possible */
}
//Otherwise if it's relative to an external network
else
{
esp_mesh_scan_get_ap_record(
&
ap_rec,
NULL
); //Retrieve the AP record only
if
((!strcmp((char*)ap_rec.ssid,MESH_ROUTER_SSID)) &&
((!
MESH_ROUTER_SPECIFIC_BSSID
) ||
(checkArrayEqual(ap_rec.bssid,MESH_ROUTER_BSSID,6))) &&
(ap_rec.rssi >= MESH_PARENT_MIN_RSSI))
/* This external network corresponds to the external
router and a connection attempt is possible */
}
} //end for
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 47
/* Determine the parent to attempt to connect the node to
(if any) and initialize the parent connection attempt variables */
if(parent_found) //If a suitable parent was found for the node, attempt a connection
ESP_ERROR_CHECK(esp_mesh_set_parent(
&
parent,MESH_NETWORK_ID,my_type,my_layer));
else
//Otherwise perform a new Wi-Fi scan after a defined time interval
{
scan_config.show_hidden = 1;//Include APs with hidden SSID in the scan
results (to allow mesh nodes to be found)
scan_config.channel =MESH_WIFI_CHANNEL;//Scan on the Mesh Wi-Fi Channel only
scan_config.scan_type =WIFI_SCAN_TYPE_PASSIVE; //Passive Wi-Fi scans should be
used in manual mesh networking
vTaskDelay(WIFI_SCAN_RETRY_INTERVAL/portTICK_PERIOD_MS); //Wait for a time interval
ESP_ERROR_CHECK(esp_wifi_scan_start(
&
scan_config,false)); //Start the Wi-Fi scan
}
}
MESH_EVENT_PARENT_CONNECTED
Once the node successfully connects to a parent in the mesh network, in addition to setting the MESH_PARENT flag
in the mesh event group and updating its parent address and mesh layer, the latter also defines the node's type
according to the following conditions:
If(my_layer == 1), the node represents the root node of the mesh network (MESH_ROOT)
If(1 <my_layer <MESH_MAX_LAYER)
, the node represents an
intermediate parent node
(MESH_NODE)
If(my_layer == MESH_MAX_LAYER)
, the node represents a
leaf node
(MESH_LEAF)
From here, in addition to updating the node's type accordingly, if the node is the root an IP configuration must be
applied to its station interface to allow it to communicate with the external router, which can be retrieved
dynamically by re
-
enabling its station DHCP client or by applying a predefined static IP configuration, the latter of
which can be set by using the
tcpip_adapter_set_ip_info()
function described previously and, if setting the
DNS server addresses for the station interface is also desidered, this can be obtained via the following function:
//File tcpip_adapter.h
esp_err_t tcpip_adapter_set_dns_info(tcpip_adapter_if_t tcpip_if,
tcpip_adapter_dns_type_t type,
tcpip_adapter_dns_info_t* addr)
Lastly, since upon receving this event the node is effectively connected to a mesh network and so its main setup
phase is over, its
internal driver components
, i.e. the driver components that don't the require the node to access
the external DS, can be started.
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 48
void mesh_PARENT_CONNECTED_handler(mesh_event_connected_t* info)
{
tcpip_adapter_ip_info_t ipinfo; //Possibly used to set a custom static IP
configuration for the root node's station interface
tcpip_adapter_dns_info_t dnsaddr; //Possibly used to set the DNS servers addresses
of the root node's station interface
xEventGroupSetBits(mesh_event_group,MESH_PARENT); //Set the MESH_PARENT flag
memcpy
(
mesh_state.parent_addr,info
-
>connected.bssid,
6
);
//Update the node's parent MAC
mesh_state.my_layer =info->self_layer;//Update the node's mesh layer
if
(mesh_state.my_layer == 1)//If the node is the root, an IP configuration
{must be applied to its station interface to
mesh_state.my_type =MESH_ROOT; allow it to communicate with the external router
if
(
MESH_ROOT_IPDYNAMIC
)
//If a dynamic IP configuration is used, enable the station
ESP_ERROR_CHECK(tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA)); DHCP client
else
//Otherwise if a static IP configuration is used, apply it
{
//Set the Root station static IP configuration
inet_pton
(
AF_INET
,
MESH_ROOT_STATIC_IP
,
&
ipinfo.ip
);
inet_pton(AF_INET,MESH_ROOT_STATIC_NETMASK,
&
ipinfo.netmask);
inet_pton(AF_INET,MESH_ROOT_STATIC_GATEWAY,
&
ipinfo.gw);
ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA,
&
ipinfo)); //Apply
//Set the Root station DNS servers (if desidered)
inet_pton(AF_INET,MESH_ROOT_STATIC_DNS_PRIMARY,
&
dnsaddr.ip);
ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, //Primary DNS
TCPIP_ADAPTER_DNS_MAIN,
&
dnsaddr));
inet_pton(AF_INET,MESH_ROOT_STATIC_DNS_SECONDARY,
&
dnsaddr.ip);
ESP_ERROR_CHECK
(
tcpip_adapter_set_dns_info
(
TCPIP_ADAPTER_IF_STA
,
//Secondary DNS
TCPIP_ADAPTER_DNS_BACKUP,
&
dnsaddr));
}
}
else
//If the node is not root, just update its type
if
(
mesh_state.my_layer
<
MESH_MAX_LAYER
)
//INTERMEDIATE PARENT node
mesh_state.my_type =MESH_NODE;
else
//LEAF node (my_layer == MESH_MAX_LAYER)
mesh_state.my_type =MESH_LEAF;
startMeshInternalDrivers(); //Start the node's internal driver components
return
;
}
Also note that once a node connects to a parent node in the mesh network, the parent passes its child the following
information in this order:
The MAC address of the root node's SoftAP interface (triggering the MESH_EVENT_ROOT_ADDRESS event)
Its fixed-root setting (which overrides the one set on the child should they differ, triggering the
MESH_EVENT_ROOT_FIXED event)
If the parent had previously been informed of it, that the external DS is accessible (triggering the
MESH_EVENT_TODS_STATE event)
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 49
MESH_EVENT_PARENT_DISCONNECTED
This event is raised when a node disconnects from its parent or when it fails to connect or automatically reconnect
to it (we'll see in more detail later when this happens), where the two circumstances can be discriminated by
checking and appropriately managing the MESH_PARENT flag in the mesh event group.
From here, while this event's implicit handler will attempt to reconnect the node to its parent, at the application-
level, should the node have disconnected from it:
If the node is root it must reset its station IP configuration by calling mesh_root_reset_stationIP() utility
function described previously, and furthermore it must inform the mesh network that the external DS is no
longer accessible, which can be obtained by calling the following function:
//File esp_mesh.h
esp_err_t esp_mesh_post_toDS_state(bool reachable)
The node's mesh status must be reset, which can be obtained by calling the mesh_status_reset() utility
function described earlier, which will also cause its MESH_PARENT flag to be cleared in order to discriminate
the semantics of further instances of this event as discussed above.
If, on the other hand, the node failed to connect or automatically reconnect to its parent no actions are required,
and note that in Manual Networking mode, while this event's implicit handler will still attempt to reconnect the
node to its parent, if desidered it's possible to start a Wi-Fi scan to search for another parent to fall back to.
Also note that, again, the handling of errors that might occur in the application's logic due to the node having
disconnected from its parent (and thus from the mesh network) is left entirely to the Driver Module.
void mesh_PARENT_DISCONNECTED_handler(mesh_event_disconnected_t* info)
{
EventBits_t eventbits;//Used to check the flags in the mesh event group
wifi_scan_config_t scan_config = {0}; //Configuration of the Wi-Fi scan to perform
be performed to fall back on another parent
(Manual Networking only)
//If the MESH_PARENT flag is set, the node disconnected from its parent
if
(((eventbits =xEventGroupGetBits(mesh_event_group))>>1)&1)
{
if
(mesh_state.my_type == MESH_ROOT)//If the node is root, inform
{ the mesh network that the
ESP_ERROR_CHECK(esp_mesh_post_toDS_state(false)); external DS is no longer
mesh_root_reset_station_IP(); accessible and reset its
} station IP configuration
mesh_reset_status(); //Reset the node's status
}
//Otherwise if the node failed to connect or
reconnect to its parent, no action is required
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 50
if
((mesh_state.org == MANUAL_NETWORKING)&&(/* a fallback is desidered */))
{
ESP_ERROR_CHECK
(
esp_wifi_scan_stop
());
//Stop a Wi
-
Fi scan already in progress
//Set the configuration of the Wi-Fi scan to perform to search for a fallback parent
scan_config.show_hidden = 1;//Include APs with hidden SSID in the scan
results (to allow mesh nodes to be found)
scan_config.channel
=
MESH_WIFI_CHANNEL
;
//Scan on the Mesh Wi
-
Fi Channel only
scan_config.scan_type =WIFI_SCAN_TYPE_PASSIVE; //Passive Wi-Fi scans should be
used in manual mesh networking
ESP_ERROR_CHECK(esp_wifi_scan_start(
&
scan_config,false)); //Start the Wi-Fi scan
}
return
;
}
Note that in general, should the disconnection from a node's parent have occured unintentionally (i.e. not due to
automatic routines such as the finding of a better preferred parent for the node or a node being elected root that
disconnects from its parent to connect with the router), the mesh stack will perform a fixed number of attempts to
reconnect the node to its previous parent before searching for another preferred parent to connect to (self-
organized networking and for the non
-
fixed
-
root nodes of a fixed
-
root network) or will attempt indefinetely to
reconnect the node to its previous parent (manual networking and fixed-root nodes in a fixed-root network).
MESH_EVENT_NO_PARENT_FOUND
This event is raised in a self
-
organized network and for the non
-
fixed
-
root nodes in a fixed
-
root network should a
node fail to find a viable parent to attempt to connect to in a fixed number or scans.
From here, while the mesh stack will keep performing periodic scans to search for a viable parent for the node, in
the application
-
level handling of this event it's possible to implement advanced error
-
recovery procedures such as
performing an all-channel Wi-Fi scan searching for an available parent on a different Mesh Wi-Fi Channel and/or
changing the node's mesh configuration.
Note that in any case the node's main setup phase and consequently the program's execution cannot proceed until
a viable parent is found.
void mesh_NO_PARENT_FOUND_handler(mesh_event_no_parent_found_t* info)
{
/* Possibly perform error recovery procedures to find
a mesh network or an external router to connect to */
return
;
}
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 51
MESH_EVENT_CHILD_CONNECTED
This event is raised when a child successfully connects to a node, and apart from setting the MESH_CHILD flag in
the mesh event group if it was not previously set (i.e. if it's the first child to connect to the node), no other action is
required in the application-level handler of this event.
void mesh_CHILD_CONNECTED_handler(mesh_event_child_connected_t* info)
{
EventBits_t eventbits;//Used to check the flags in the mesh event group
//If the MESH_CHILD flag is not set in the mesh event group
(i.e. this is the first child node to connect), set it
if
(!(((eventbits =xEventGroupGetBits(wifi_event_group))>>2)&1))
xEventGroupSetBits(mesh_event_group,MESH_CHILD);
return
;
}
Also note that after a child connects to a parent, its own and its descendants' station interfaces MAC addresses will
be added to the parent's routing table, triggering the MESH_EVENT_ROUTING_TABLE_ADD event.
MESH_EVENT_CHILD_DISCONNECTED
This event is raised when a child disconnects from a node, and other than clearing the MESH_CHILD flag in the
mesh event group if it was the last child to disconnect from the node, no other action is required in the application-
level handler of this event.
void mesh_CHILD_DISCONNECTED_handler(mesh_event_child_disconnected_t* info)
{
wifi_sta_list_t children;//Used to store information on the clients
connected to the node's SoftAP interface
ESP_ERROR_CHECK(esp_wifi_ap_get_sta_list(
&
children)); //(see below)
if
(children.num == 0)//If it was the last child
xEventGroupClearBits(mesh_event_group,MESH_CHILD); to disconnect, clear the
return
; MESH_CHILD flag
}
Where the esp_wifi_ap_get_sta_list() function, which allows to retrieve information on the clients (children
nodes) connected to the node's SoftAP interface, is defined as follows:
//File esp_wifi_types.h
typedef
struct //Information on a specific client (child)
{ connected to the node's SoftAP interface
uint8_t mac[6]; //Client's (child's) MAC address
}wifi_sta_info_t;
typedef
struct //Information on the clients (children)
{ connected to the node's SoftAP interface
wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM];//Information on each client (child)
int num;
//Number of clients (children) connected
to the node's SoftAP interface
}wifi_sta_list_t;
Mesh Network Update Events
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 52
Also note that after a child disconnects from its parent node, its own and its descendants' station interfaces MAC
addresses will be removed from the parent's routing table, triggering the MESH_EVENT_ROUTING_TABLE_REMOVE
event.
MESH_EVENT_ROUTING_TABLE_ADD
This event is raised after a node's descendant (with its possible further descendants) joins the mesh network,
whose station MAC addresses are added to the node's routing table by this event's implicit handler, while at the
application
-
level handling, apart from some possible logging, no other action is required.
void mesh_ROUTING_TABLE_ADD_handler(mesh_event_routing_table_change_t* info)
{
/* Possible logging */
return;
}
MESH_EVENT_ROUTING_TABLE_REMOVE
This event is raised after a node's descendant (with its possible further descendants) disconnects from the mesh
network, whose station MAC addresses are removed from the node's routing table by this event's implicit handler,
while at the application
-
level handling, apart from some possible logging, no other action is required.
void mesh_ROUTING_TABLE_REMOVE_handler(mesh_event_routing_table_change_t* info)
{
/* Possible logging */
return;
}
MESH_EVENT_ROOT_ADDRESS
This event is raised after a node connects to its parent, which passes it the root's SoftAP MAC address, and
whenever a root switch occurs in the mesh network, where the new root's SoftAP MAC address is propagated from
parent to children through the entire network.
From here, apart from updating the root node's MAC address in the node's mesh status, no further actions are
required in the application-level handling of this event.
void mesh_ROOT_ADDRESS_handler(mesh_event_root_address_t* info)
{
memcpy(mesh_state.root_addr,info->addr,6); //Update the root node's MAC address
return; in the node's mesh status
}
//File esp_wifi.h
esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t* stalist)
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 53
MESH_EVENT_ROOT_FIXED
This event is raised after a node connects to its parent if their fixed
-
root settings differ, where this event's implicit
handler will change its value to match the one of its parent.
From here, at the application
-
level handling, the mesh organization mode on the node should be updated to reflect
the new value of its fixed root setting, where note that if fixed-root networking is set in this way the node can only
be a non
-
fixed
-
root node.
void mesh_ROOT_FIXED_handler(mesh_event_root_fixed_t* info)
{
if
(info->is_fixed)//Change the node's mesh organization mode accordingly
mesh_state.org =FIXED_ROOT;
else
mesh_state.org =SELF_ORGANIZED;
return;
}
MESH_EVENT_TODS_STATE
This event is raised after a node connects to its parent if the latter was previously informed that the external DS is
reachable, or whenever the root node posts an update on its accessibility via the esp_mesh_post_toDS_state()
function, information that is propagated from parent to children through the entire network.
From here, in the application
-
level handler, if the node is informed that the external DS is reachable, in addition to
setting the MESH_TODS flag in the mesh event group its external driver components can be started, otherwise, if
the external DS is no longer reachable, the MESH_TODS flag must be cleared, again leaving the handling of the
possible errors that might occur in the application's logic due to the external DS being not longer accessible entirely
to the Driver Module.
void mesh_TODS_STATE_handler(mesh_event_toDS_state_t* info)
{
if
(*info == MESH_TODS_REACHABLE)//If the external DS is reachable
{
xEventGroupSetBits(mesh_event_group,MESH_TODS); //Set the MESH_TODS flag
startMeshExternalDrivers(); //Start external driver components
}
else
//If the external DS is NOT reachable
xEventGroupClearBits(mesh_event_group,MESH_TODS); //Clear the MESH_TODS flag
return
;
}
MESH_EVENT_VOTE_STARTED
(Self
-
Organized Network Only)
This event is raised when a new root election is started in a self
-
organized network, which may be caused by the
root node self
-
healing process or by the root node waiving its root status (we'll see later).
From here, the implicit handler of this event will carry out the election process along with the other nodes in the
network, while at the application level, other than clearing the MESH_VOTE flag (since it's active
-
low) no further
action is required.
void mesh_VOTE_STARTED_handler(mesh_event_vote_started_t* info)
{
xEventGroupClearBits(mesh_event_group,MESH_VOTE); //Clear the MESH_VOTE flag
return
; (for it's active low)
}
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 54
MESH_EVENT_VOTE_STOPPED (Self-Organized Network Only)
This event is raised once a new root election in a self-organized network ends, where the event's implicit handler
will check whether the voting percentage threshold on the node has been reached, and if this is the case:
If the root election was triggered due to the root node self-healing process, the node will disconnect from its
current parent and attempt to connect to the router as the root node.
If the root election was triggered as a result of the current root node waiving its root status, the node will
send it a root switch request (triggering the MESH_EVENT_ROOT_SWITCH_REQ event on the current root).
From here at the application level, apart from setting the MESH_VOTE flag (since it's active-low) no further action is
required.
void mesh_VOTE_STOPPED_handler()
{
xEventGroupSetBits(mesh_event_group,MESH_VOTE); //Set the MESH_VOTE flag
return
; (for it's active low)
}
MESH_EVENT_LAYER_CHANGE
This event is raised whenever a network topology change affects the ancestors of a node, which other than
changing its mesh layer may also cause its type to change from intermediate parent to leaf or vice versa, where if
this is the case the event's implicit handler will modify the node's possibility to accept children nodes appropriately.
From here in the application-level handler, apart from updating the node's mesh layer and possibly its type in its
mesh status, no further action is required.
void mesh_LAYER_CHANGE_handler(mesh_event_layer_change_t* info)
{
mesh_state.my_layer = info->new_layer; //Update the node's mesh layer
//Check whether the layer change causes the node's type
to change from INTERMEDIATE PARENT to LEAF or vice versa
if
((mesh_state.my_layer < MESH_MAX_LAYER)&&(mesh_state.my_type != MESH_NODE))
mesh_state.my_type = MESH_NODE;
else
if
(mesh_state.my_layer == MESH_MAX_LAYER)
mesh_state.my_type = MESH_LEAF;
return
;
}
Also note that the node's current children won't be disconnected if its type changes to leaf node, and that changes
to the root's type are not taken into account since when a node becomes root it always switches parents (to the
router), and so its type will be updated in the MESH_EVENT_PARENT_CONNECTED event specific handler.
MESH_EVENT_CHANNEL_SWITCH
This event is raised should the external router switch its AP onto a different Wi-Fi channel, which will cause the root
node's station and thus its SoftAP interfaces to switch onto such channel, which in turn will cause all the nodes in
the mesh network to switch their Wi-Fi interfaces in cascade onto the new channel, which so will represent the
new Mesh Wi-Fi Channel.
The actual Mesh Wi-Fi Channel switch is performed by the implicit handler of this event, while at the application
level, apart from updating such information in the node's mesh status, no further action is required.
void mesh_CHANNEL_SWITCH_handler(mesh_event_channel_switch_t* info)
{
mesh_state.channel =info->channel;//Update the node's Mesh Wi-Fi Channel
return
;
}
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 55
Mesh Root-specific Events
MESH_EVENT_ROOT_GOT_IP
This event is raised on the root node once it obtains an IP configuration for its station interface, which as discussed
previously in the MESH_EVENT_PARENT_CONNECTED handler can be retrieved both dynamically from its station
DHCP client or by applying a predefined static IP configuration.
From here, in the application-level handler of this event, in addition to starting the root driver components the
mesh network must be informed that the router and consequently the external DS is now accessible.
void mesh_ROOT_GOT_IP_handler(mesh_event_root_got_ip_t* info)
{
startMeshRootDrivers(); //Start root driver components
ESP_ERROR_CHECK(esp_mesh_post_toDS_state(true)); //Inform the mesh network that the
return
;
external DS is accessible
}
MESH_EVENT_ROOT_LOST_IP
This event is raised on the current or a former root node once the lease time of its station dynamic IP configuration
expires (which for the current root node also implies that its DHCP client failed to renew or otherwise retrieve a
new IP configuration for the interface).
From here if the node is still the root it won't be able to communicate with the external router until its station
DHCP client retrieves a new IP configuration for the interface, and thus the mesh network must be informed that
the external DS is currently no longer accessible.
void mesh_ROOT_LOST_IP_handler()
{
if
(mesh_state.my_type == MESH_ROOT) //If the node is the current root
ESP_ERROR_CHECK(esp_mesh_post_toDS_state(false)); //Inform the mesh network that
return
; the external DS is no longer
} accessible
MESH_EVENT_ROOT_SWITCH_REQ
(Self
-
Organized Network Only)
This event is raised on the root node when it receives a root switch request from a candidate root node following
the election caused by the root waiving its root status, and while this event's implicit handler will send back a root
switch acknowledgment to the candidate root, disconnect the node from the router and start searching for a
preferred parent to connect to, in the application-level handler the root station IP configuration must be reset and
the node's type must be changed to intermediate parent (a temporary change since shortly afterwards the node
will disconnect from its parent).
void mesh_ROOT_SWITCH_REQ_handler(mesh_event_root_switch_req_t* info)
{
mesh_root_reset_stationIP(); //Reset the root node's station IP configuration
mesh_state.my_type =MESH_NODE; //Change the node's type from
return
; root to intermediate parent
}
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 56
MESH_EVENT_ROOT_SWITCH_ACK (Self-Organized Network Only)
This event is raised on the candidate root node when it receives the root switch acknowledgment from the former
root following the election caused by it waiving its root status, and while this event's implicit handler will
disconnect the node from its current parent and attempt to connect it to the router as the new root, in the
application-level handler apart from possible logging no action is required.
void mesh_ROOT_SWITCH_ACK_handler()
{
/* Possible logging */
return
;
}
MESH_EVENT_ROOT_ASKED_YIELD
Whenever a root conflict is detected in a mesh network, after an internal communication between all the root
nodes, the one with the highest RSSI with the router will ask the others to yield, causing this event to be raised.
From here while the event's implicit handler will disconnect the node from the router and search for a preferred
parent to connect to, in the application-level handler as with the MESH_EVENT_ROOT_SWITCH_REQ specific
handler the root station IP configuration must be reset and the node's type must be changed to intermediate
parent (a temporary change since shortly afterwards the node will disconnect from its parent).
Also note that if this event raises on a fixed-root of a fixed-root network, the node will permanently change to a
non-fixed-root node.
void mesh_ROOT_ASKED_YIELD_handler(mesh_event_root_conflict_t* info)
{
mesh_root_reset_stationIP(); //Reset the root node's station IP configuration
mesh_state.my_type =MESH_NODE; //Change the node's type from
return
; root to intermediate parent
}
ESP-IDF Mesh Stack
Mesh Events Specific Handlers
Page 57
Following our previous analysis of the Mesh Events Specific Handlers, depending on their category the components
constituting the Driver Module should be started:
The Internal Driver Components, i.e. the driver components that don't require a node to communicate with
the external DS, as soon as the main setup phase on a node is finished, that is as soon as the node connects to
a parent in the mesh network (MESH_EVENT_PARENT_CONNECTED).
The External Driver Components, i.e. the driver components that require a node to communicate with the
the external DS, as soon as the node is informed that it is reachable (MESH_EVENT_TODS_REACHABLE with
*info == MESH_TODS_REACHABLE).
The Root Driver Components, i.e. the driver components that are to be executed exclusively on the root node
of the network, as soon as the root obtains an IP configuration for its station interface
(MESH_EVENT_ROOT_GOT_IP).
Note that, since the events that start the driver components can generally be raised multiple times during the
application's execution, it's necessary for the setup module to keep track of which driver components are currently
being executed to avoid creating undesidered duplicate tasks of the same components, and this can be obtained by
providing in the mesh event group a flag for each driver component representing whether it is currently running or
not, as was shown previously in the code premises section.
From here the actual semantics of the synchronization of the execution of the driver module components is left to
the programmer, where a simple yet blunt solution is given, in the specific handlers of events that start driver
components, by previously checking and killing any existing tasks relative to the components they would start
before actually creating them again, which can be obtained as follows:
void InternalDriverComponent_A()
{
/* internal driver component A logic */
//Clear this driver component's execution flag in the mesh event group
xEventGroupClearBits(mesh_event_group,MESH_DRIVER_INTERNAL_A);
vTaskDelete(NULL); //Delete this driver component's task
}
void InternalDriverComponent_B()
{
/* internal driver component B logic */
//Clear this driver component's execution flag in the mesh event group
xEventGroupClearBits(mesh_event_group,MESH_DRIVER_INTERNAL_B);
vTaskDelete(NULL); //Delete this driver component's task
}
Driver Module
ESP-IDF Mesh Stack
Driver Module
Page 58
/* Called at the end of the MESH_EVENT_PARENT_CONNECTED event specific handler */
void startMeshInternalDrivers()
{
EventBits_t eventbits; //Used to check the flags in the mesh event group
static TaskHandle_t componentA_handler; //ComponentA driver task handler
static TaskHandle_t componentB_handler; //ComponentB driver task handler
/* For each driver component, if its relative task is running, kill it
and set its flag in the mesh event group before starting its task */
if
(((eventbits =xEventGroupGetBits(mesh_event_group))>>MESH_DRIVER_INTERNAL_A)&1)
vTaskDelete(componentA_handler);
xEventGroupSetBits(mesh_event_group,MESH_DRIVER_INTERNAL_A);
xTaskCreate(internalDriver_A,"internaldriverA",4096,NULL,10,
&
componentA_handler);
if
(((eventbits =xEventGroupGetBits(mesh_event_group))>>MESH_DRIVER_INTERNAL_B)&1)
vTaskDelete(componentB_handler);
xEventGroupSetBits(mesh_event_group,MESH_DRIVER_INTERNAL_B);
xTaskCreate(internalDriver_B,"internaldriverB",4096,NULL,10,
&
componentB_handler);
}
Also note that, as with all networking applications, the driver components should include routines for handling
errors that may occur during their execution, which can be triggered by checking the return values of the Mesh
API functions used and/or the appropriate flags in the mesh event group, whose values as we have seen are
asynchronously updated by the appropriate event specific handlers during the application's execution.
ESP-IDF Mesh Stack
Driver Module
Page 59
Mesh API for the Driver Module
Listed below are the functions offered by the Mesh API organized into categories that can be used for developing
the driver module of a mesh application:
Mesh Network Communication
As described in the
Mesh API Guide
the communication between nodes in a mesh network is implemented through
the exchange of
Mesh Packets
, which represent data structures that can be embedded entirely into the body of a
Wi-Fi frame as follows:
In addition to its payload, which represents the relevant information exchanged between nodes, a mesh packet is
comprised of the following information:
The Source Address, representing the station MAC address of the node inside the mesh network the packet
originated from, and is implemented in the code via a
mesh_addr_t
union (or more precisely its "
addr
"
member).
Note that packets destined for a node whose data originated from a host outside the mesh network carry the
source address of the root node, while the actual address of the external host is carried within an additional
option in the packet (we'll see later).
The
Destination Address
, representing the address of the host the packet is destined for, which for nodes
inside the mesh network consists in their Station MAC address, while for external hosts in their IPv4:PORT
combination, destination address that again is implemented in the code via a
mesh_addr_t
union.
Note that packets destined for an external host are delivered by the mesh network to the root node, where
the data will be extracted from the packet and sent to the external router by using IP sockets.
A
Flag
, consisting of an integer used as a bitmap to associate a set of properties to a packet as follows:
//File esp_mesh.h
/*-- Mesh Flag Properties --*/
#define MESH_DATA_P2P (0x02) //Denotes that the packet is destined for
a non-root node inside the mesh network
#define MESH_DATA_FROMDS (0x04) //Denotes that the packet contains data
which originated from an external host
#define MESH_DATA_TODS (0x08) //Denotes that the packet is
destined for an external host
#define MESH_DATA_NONBLOCK (0x10) //Denotes that the non-blocking variant of the
esp_mesh_send() or esp_mesh_recv() function
must be used to send/receive the packet
#define MESH_DATA_DROP (0x20) //Denotes that the packet's data is not of
critical importance and thus can be
discarded in case of congestion on
a newly elected root node
#define MESH_DATA_GROUP (0x40) //Denotes that the packet is destined for all
nodes belonging to a certain mesh group
(multicast)
ESP-IDF Mesh Stack
Driver Module
Page 60
A Packet Data Descriptor, holding a set of information related to the packet's data and represented by the
following data structure:
//File esp_mesh.h
typedef
enum
{
MESH_PROTO_BIN,
MESH_PROTO_HTTP
,
MESH_PROTO_JSON,
MESH_PROTO_MQTT,
//Identifies the protocol the payload
of the mesh packet belongs to
//Binary Data (default)
//HTTP protocol
//JSON protocol
//MQTT protocol
}
mesh_proto_t
;
typedef
enum //Defines the type of service that must
{ or was used to deliver the mesh packet
MESH_TOS_P2P, //Reliable point-to-point transmission (default)
MESH_TOS_E2E, //Reliable end-to-end transmission (currently unimplemented)
MESH_TOS_DEF, //Unreliable transmission
} mesh_tos_t;
typedef
struct //Mesh Packet Data Descriptor
{
uint8_t*
data
;
//Pointer to the send/receive buffer
uint16_t size; //Size of the data to send/receive or that has been received
mesh_proto_t proto; //The protocol the payload of the mesh packet belongs to
mesh_tos_t tos; //The type of service that must or
} mesh_data_t; was used to deliver the mesh packet
A list of Additional Options associated with the packet, each is represented by the following data structure:
//File esp_mesh.h
/*
--
Mesh Packet Additional Option Types
--
*/
#define MESH_OPT_SEND_GROUP (7) //Defines that the additional option's value
represents the list of MAC addresses which
the packet is destined for (this is a
multicast method alternative to sending
packets to a specific mesh group)
#define MESH_OPT_RECV_DS_ADDR (8) //Defines that the additional option's
value represents the IPv4:PORT address
of the external host the data in
the mesh packet originated from
typedef
struct //Mesh Packet Additional Option Descriptor
{
uint8_t type; //The additional option's type (either of the above)
uint8_t* val; //The address where the additional
option's value is (or must be) stored
uint16_t len; //The length in bytes of the additional option's value
} __attribute__((packed)) mesh_opt_t; //__attribute__((packed)) is for byte
alignment purposes of the in
-
memory
representation of the struct
Also note that in the current ESP-IDF version only up to 1 additional option is supported per mesh packet.
ESP-IDF Mesh Stack
Driver Module
Page 61
Mesh Packets Routing Algorithm
The routing algorithm used by nodes to forward mesh packets they receive on their Station or SoftAP interfaces
consists in the following:
If the packet's destination address matches the node's Station or SoftAP MAC addresses, the packet is copied
into the related receiving queue (we'll see later) in the mesh stack.
1)
If the packet's destination address is found in the node's routing table, the packet is sent to the node's child
whose sub-routing table contains such MAC address (we'll see in more detail later).
2)
If the packet's destination address is not found in the node's routing table, the packet is sent to the node's
parent, except for the root node where it is discarded since no route was found to deliver it.
3)
Since it is the station MAC address of nodes that is propagated upwards in the routing table of their
ancestors, when sending to another node inside the network its station MAC address must be used as
destination address (except for the root node, see later) to ensure that a route is found to deliver the packet,
otherwise, as with the routing algorithm above, the packet may reach the root node where it is discarded
since no route was found to deliver it (note however that a node still recognizes itself as the target of a
packet destined for its SoftAP MAC address, if such packet happens to pass through the node itself).
Since the ESP-IDF mesh stack currently offers no advertisement system on the nodes connected to a mesh
network, each node is aware only of the presence of its descendants whose addresses are stored in its
routing table, with the root being the only node that is aware of the presence of every node connected to
the mesh network.
Furthermore, since a non-root node has no means of acknowledging the presence or the MAC address of a
node which is not its descendant, to send it a packet its MAC address must be known prior to the program's
execution (moreover there is also no guarantee that the node is actually connected to the network, with the
risk of wasting network resources in transmitting packets that travel up to the root node before being
dropped since no route was found to deliver them).
As described also in the Mesh API Guide, nodes in a mesh network implement an Upstream Flow Control
mechanism, where a parent node allocates to each of its children a receiving window, which they must apply
for before they are allowed to tramit data upstream to their parent.
From here, in case of congestion in the network or if packets destined for a parent are not parsed in time by
its application level, its receving window relative to one or more children may be depleted, which will
prevent the nodes from transmitting data upstream in the network until the congestion is mitigated or the
application level in the parent parses the received data.
Notes on Mesh Communication
ESP-IDF Mesh Stack
Driver Module
Page 62
Where note that if sending a packet to the root node the "to" argument must be NULL.
The usage of the esp_mesh_send() function is best described through the following example:
void SenderDriver()
{
esp_err_t send_ret; //Used to store the result of the send()
uint8_t send_buffer[MESH_MPS]; //Send Buffer (MESH_MPS = 1472 bytes)
mesh_addr_t dest; //Packet's destination address
mesh_data_t data_des; //Packet's data descriptor
int send_flag = 0; //Packet's flag
mesh_opt_t send_opt; //Possible additional send option
uint8_t opt_val[50]; //Possible additional option's value
//1) Copy the data to send (the payload) into the sending buffer
memcpy(send_buffer,PACKET_CONTENTS,sizeof(PACKET_CONTENTS));
//2) Packet Data Description Initialization
data_des.data =send_buffer; //Address of the sending buffer
data_des.size =sizeof(PACKET_CONTENTS); //Size of the packet's payload
data_des.proto =PACKET_DATA_PROTOCOL; //Payload's protocol
data_des.tos =PACKET_TYPE_OF_SERVICE; //Packet's type of service
Send a packet over the mesh network
A node can send a mesh packet destined for another node inside the network or to an external host by calling
the following function:
//File esp_mesh.h
esp_err_t esp_mesh_send(const mesh_addr_t* to,
const mesh_data_t* data,
int flag,
const mesh_opt_t opt[],
int
opt_count
)
ESP-IDF Mesh Stack
Driver Module
Page 63
//3) Send Flag Initialization
if
(SEND_NONBLOCK) //If we want to use the non-blocking
send_flag += MESH_DATA_NONBLOCK; version of the esp_mesh_send() function
if
(SEND_NONCRITICAL) //If the packet to send is not of critical
send_flag += MESH_DATA_DROP; importance and so can be dropped in case
of congestion on a newly elected root node
//4) Destination-specific initializations and send() calls
/*================== Send to a specific (non-root) node ==================*/
send_flag += MESH_DATA_P2P; //Defines that the packet is destined
for a non-root node inside the network
memcpy(dest.addr,DEST_NODE_MAC,6); //Set the destination address to the
node's station MAC Address
send_ret =esp_mesh_send(&dest,&data_des,send_flag,NULL,0); //Send the packet
/*========================= Send to the Root Node =========================*/
send_ret =esp_mesh_send(NULL,&data_des,send_flag,NULL,0); //Send the packet
(Note that the "dest" argument must be NULL when sending to the root node)
/*========================= Send to a Mesh Group =========================*/
send_flag += MESH_DATA_P2P; //Defines that the packet is destined
for a non-root node inside the network
send_flag += MESH_DATA_GROUP; //Define that the packet is destined
for a mesh group
memcpy(dest.addr,DEST_GROUP_GID,6); //Set the destination address to the
destination mesh group's ID
send_ret =esp_mesh_send(
&
dest,
&
data_des,send_flag,NULL,0); //Send the packet
/*======================= Send to an External Host =======================*/
send_flag += MESH_DATA_TODS; //Define that the packet's destination
is outside the mesh network
inet_pton(AF_INET,DEST_HOST_IP,
&
dest.mip.ip4); //Set the destination IP
dest.mip.port =DEST_HOST_PORT; //Set the destination port
send_ret =esp_mesh_send(
&
dest,
&
data_des,send_flag,NULL,0); //Send the packet
Forward data coming from an external
/*================== host to a specific node (root only) ==================*/
send_flag += MESH_DATA_P2P; //Defines that the packet is destined
for a non-root node inside the network
send_flag += MESH_DATA_FROMDS; //Define that the data in the packet
originated from an external host
memcpy(opt_val,SOURCE_IPV4_PORT,6); //Copy the IPv4:PORT address of the
external source to the option's buffer
send_opt.type =MESH_OPT_RECV_DS_ADDR; //Defines that the additional option's
value represents the IPv4:PORT
address of the external host the
data in the packet originated from
send_opt.val =send_opt; //Set the address of the option's value
send_opt.len = 6; //Set the length of the option's value
memcpy(dest.addr,DEST_NODE_MAC,6); //Set the packet's destination address
//Send the packet (with the additional option)
send_ret =esp_mesh_send(
&
dest,
&
data_des,send_flag,
&
send_opt,1);
//5) Check the result of the send operation
if
(send_ret != ESP_OK)
{
/* Possible error recovery operations */
}
}
ESP-IDF Mesh Stack
Driver Module
Page 64
Receive a packet at the application-level
While packets destined for a node are autonomously received by its mesh stack and stored in the appropriate
receiving queue, to pass such packets at the application level the following function must be called:
//file esp_mesh.h
esp_err_t esp_mesh_recv(mesh_addr_t* from,
mesh_data_t* data,
int timeout_ms,
int* flag,
mesh_opt_t opt[],
int
opt_count
)
Where the "
size
" member of the
mesh_data_t
struct pointed by the
data
argument must be initialized
beforehand to the maximum data length in bytes that can be received at the application level, i.e. the size of
the receiving buffer, while once the function has been called such attribute will hold the actual length of the
received data.
Note that packets whose data originated from an external host can be distinguished from packets whose data
originated from inside the mesh network by checking the MESH_DATA_FROMDS bit in the received packet's
flag and by the presence of the additional option of type "MESH_OPT_RECV_DS_ADDR", which as discussed
before is used to attach the source's IPv4:PORT to the packet.
The following example describes the usage of the esp_mesh_recv() function:
void ReceiverDriver()
{
esp_err_t
recv_ret
;
//Used to store the result of the recv()
uint8_t recv_buffer[MESH_MTU]; //Receive Buffer (MESH_MTU = 1500 bytes)
mesh_addr_t src = {0}; //Received packet's source address
mesh_data_t data_des = {0}; //Received packet's data descriptor
int recv_flag = 0; //Received packet's flag
mesh_opt_t
recv_opt
;
//Received packet's additional option
uint8_t
opt_val
[
50
];
//Received packet's additional option's value
//1) Set the address of the receive buffer
data_des.data =recv_buffer;
//2) Set the address of the buffer where to store the value of an additional
option in the received packet (if any)
recv_opt.val =opt_val;
ESP-IDF Mesh Stack
Driver Module
Page 65
//Receive Loop
while
(
1
)
{
//3) Reset the "size" attribute of the packet's data descriptor to the data
length in bytes that can be received at the application level, i.e.
the size of the receive buffer
data_des.size
=
MESH_MTU
;
//4) Reset the additional option received in the previous packet, if any
recv_opt.type = 0;
//5) Receive the packet
recv_ret =esp_mesh_recv(
&
src,
&
data_des,RECV_TIMEOUT,
&
recv_flag,
&
recv_opt,1)
//6) Check the result of the receive operation
if
(
recv_ret
!=
ESP_OK
)
{
/* Possible error recovery operations */
}
else
{
//7) If there were no errors in the receive, parse the received data
if
(((recv_flag>>3)&1)||(recv_opt.type == MESH_OPT_RECV_DS_ADDR))
{
/* The data in the packet originated from an external host */
}
else
{
/* The data in the packet originated from
another node inside the mesh network */
}
}
}
}
Retrieve the number of packets pending to be sent in the mesh stack's sending queues
The packets pending to be sent by a node are organized into multiple queues in the mesh stack, where the
number of packets pending in each queue can be retrieved by calling the following function:
//file esp_mesh.h
typedef
struct
//Number of packets pending to be sent in
{
each of the mesh stack's sending queues
int
to_parent
;
//Parent sending queue
int
to_parent_p2p
;
//Parent (P2P) sending queue
int
to_child
;
//Child sending queue
int to_child_p2p;//Child (P2P) sending queue
int mgmt;//Management sending queue
int broadcast;//Broadcast and multicast sending queue
} mesh_tx_pending_t;
esp_err_t esp_mesh_get_tx_pending(mesh_tx_pending_t* pending)
ESP-IDF Mesh Stack
Driver Module
Page 66
void
appDriver
()
{
mesh_tx_pending_t
tx_pending
;
//Stores the number of packets pending to be
sent in each of the mesh stack's queues
ESP_ERROR_CHECK(esp_mesh_get_tx_pending(
&
tx_pending));
}
Retrieve the number of packets pending to be received at the application level
The packets pending to be received at the application level are organized in the mesh stack into a queue
relative to the packets destined for the node and into a special TODS queue exclusive to the root node
relative to the packets that are to be forwarded to the external DS.
//file esp_mesh.h
typedef
struct //Number of packets pending to be received at the application
{ level in each of the mesh stack's receiving queues
int toSelf;//Receiving queue of packets destined for the node
int
toDS
;
//Receiving queue of packets destined for the external DS
(root node only)
}
mesh_rx_pending_t
;
esp_err_t esp_mesh_get_rx_pending(mesh_rx_pending_t* pending)
void
appDriver
()
{
mesh_rx_pending_t
rx_pending
;
//Stores the number of packets pending to
be received at the application level
ESP_ERROR_CHECK
(
esp_mesh_get_rx_pending
(
&
rx_pending
));
}
ESP-IDF Mesh Stack
Driver Module
Page 67
Mesh Root
-
specific API
The following API can be called only on the root node of a mesh network, and so should be used in root driver
components only.
Receive a packet destined for the external DS at the application level
The packets destined for a remote host can be received at the application level on the root node by calling
the following function, which is for the most part analogous to the
esp_mesh_recv()
function:
//file esp_mesh.h
esp_err_t esp_mesh_recv_toDS(mesh_addr_t* from,
mesh_addr_t* to,
mesh_data_t* data,
int timeout_ms,
int* flag,
mesh_opt_t opt[],
int
opt_count
)
Where note that in the received packet's flag the MESH_DATA_TODS bit will always be set and that currently
no packet additional option is involved or of use in sending a packet towards an external host.
void ReceiverDSDriver()
{
esp_err_t recv_ret; //Used to store the result of the recv()
uint8_t recv_buffer[MESH_MTU]; //Receive Buffer (MESH_MTU = 1500 bytes)
mesh_addr_t src = {0}; //Received packet's source address(node's MAC)
mesh_addr_t dest; //Packet's destination address (IPv4:PORT)
mesh_data_t data_des = {0}; //Received packet's data descriptor
int recv_flag = 0; //Received packet's flag
mesh_opt_t recv_opt; //Received packet's additional option
uint8_t opt_val[50];
//Received packet's additional option's value
//1) Set the address of the receive buffer
data_des.data =recv_buffer;
//2) Set the address of the buffer where to store the value of an additional
option in the received packet (if any)
recv_opt.val =opt_val;
ESP-IDF Mesh Stack
Driver Module
Page 68
//Receive Loop
while
(1)
{
//3) Reset the "size" attribute to the size of the receive buffer
data_des.size =MESH_MTU;
//4) Reset the additional option received in the previous packet
recv_opt.type = 0;
//5) Receive the packet whose data is destined for an external host
recv_ret =esp_mesh_recv_toDS(
&
src,
&
dest,
&
data_des,RECV_TIMEOUT,
&
recv_flag,
&
recv_opt,1)
//6) Check the result of the receive operation
if
(recv_ret != ESP_OK)
{
/* Possible error recovery operations */
}
else
{
//7) If there were no errors in the receive, parse the received
data and forward it to the external router via IP sockets
}
}
}
Once the data destined for a remote host has been received on the root node at the application level, it can
be forwarded to the external router by using IP sockets, where a complete mechanism allowing a full
bidirectional communication between the nodes in the mesh network and the external IP network, with the
possibility of maintaining active sockets on the root node on behalf of the other nodes, requires additional
communication protocols inside the network and possibly a Port Address Translation (PAT) mechanism on
the root, topics that are beyond the purposes of this guide.
Trigger a New Root Election
(Self
-
Organized Network Only)
In a self-organized network once a node has been elected root, apart from the root self-healing and root
conflicts resolution procedures the node will remain the root indefinetely, even if other nodes with a higher
RSSI with the router connect to the network or if its own RSSI with the router degrades.
From here a good practice, should the root node's RSSI with the router fall below an acceptable threshold, is
for it to trigger a new root node election, which is obtained by calling the following function:
//file esp_mesh.h
typedef
union //Root switch request type-specific configuration
{
int attempts; //Maximum rounds in the new root election (default = 15)
mesh_addr_t rc_addr; //The address of a node appointed to be the next
} mesh_rc_config_t; root by the current root node (UNIMPLEMENTED)
typedef
struct //Root switch request configuration
{
bool is_rc_specified;//Root switch request type where:
- false: A new root election is triggered (default)
- true: The new root node is appointed directly by
the current root (UNIMPLEMENTED)
mesh_rc_config_t config; //Root switch request type-specific configuration
float percentage;//The election's voting percentage threshold for a
node to proclaim itself root (default = 0.9)
} mesh_vote_t;
ESP-IDF Mesh Stack
Driver Module
Page 69
esp_err_t esp_mesh_waive_root(const mesh_vote_t* vote, int reason)
Where the "
reason
" argument currently allows as value only the "MESH_VOTE_REASON_INITIATED" constant.
Once the function returns successfully a new root election will start in the mesh network, at the end of which
if a new candidate root is found such node will send the current root a root switch request (triggering the
MESH_EVENT_ROOT_SWITCH_REQ event), which in turn will disconnect from the router, search for a
preferred parent to connect to, and send back to the new root candidate a root switch acknowledgment
(triggering the MESH_EVENT_ROOT_SWITCH_ACK event), which in turn will disconnect from its current parent
and attempt to connect to the router as the new root.
Note that if no candidate root is found at the end of the election or if it coincides with the current root node,
no root switch will occur.
void rootWaiveDriver()
{
EventBits_t eventbits;//Used to check the flags of the mesh event group
mesh_vote_t vote_settings;//Used to store the new root election settings
wifi_ap_record_t routerAP;//Used to store information on the router's AP
the root's station interface is connected to
//Initialize the new root election settings
vote_settings.is_rc_specified =false;//Define the root switch request type
(currently only false is supported,
i.e. trigger a new root election)
vote_settings.config.attempts =ROOT_ELECTION_MAX_ROUNDS; //Set the election
maximum rounds
vote_settings.percentage =ROOT_ELECTION_THRESHOLD; //Set the root voting
percentage threshold
//New root election trigger cycle
while
(mesh_state.my_type == MESH_ROOT) //While the node is the root
{
ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(
&
routerAP));//Retrieve information
on the router's AP
eventbits =xEventGroupGetBits(mesh_event_group); //Retrieve the value of
the mesh event group
if
((routerAP.rssi <= ROOT_MIN_RSSI)&& //If the RSSI with the router falls
((eventbits>>2)&1)&&((eventbits>>4)&1)) below its minimum threshold, the
node has children connected
(MESH_CHILD) and a new root
election is not already in
progress (MESH_VOTE)
ESP_ERROR_CHECK(esp_mesh_waive_root(
&
vote_settings, //Start a new election
MESH_VOTE_REASON_INITIATED);
vTaskDelay(WAIVE_CHECK_INTERVAL/portTICK_PERIOD_MS); //Await a predefined
} interval before
checking again
}
ESP-IDF Mesh Stack
Driver Module
Page 70
Mesh Network Status API
Retrieve the number of nodes in the mesh network
//file esp_mesh.h
int esp_mesh_get_total_node_num(void)
void
appDriver
()
{
int
tot_nodes
=
esp_mesh_get_total_node_num
();
}
Routing Table API
A node's routing table is comprised of its station MAC address and the station MAC addresses of all its descendants
in the mesh network, where as previously discussed in the mesh packets routing algorithm a node also keeps track
of the children through which each of its descendants is reachable.
Retrieve the number of entries in a node's routing table
//file esp_mesh.h
int esp_mesh_get_routing_table_size(void)
void appDriver()
{
int rtable_num =esp_mesh_get_routing_table_size();
}
Where note that the number of entries in the root node's routing table represents the total number of nodes
in the mesh network ( = esp_mesh_get_total_node_num()), and the minimum size of a node's routing table
is 1, which occurs for a node with no children connected and so with only its own station MAC address in its
routing table.
ESP-IDF Mesh Stack
Driver Module
Page 71
Retrieve the entries of a node's routing table
//file esp_mesh.h
esp_err_t esp_mesh_get_routing_table(mesh_addr_t* rtable,
int
rtable_size
,
int* rtable_num)
void appDriver()
{
mesh_addr_t* rtable; //Used to store dynamically the node's routing table
int rtable_num; //Used to store the number of entries
in the node's routing table
//Retrieve the number of entries in the node's routing table
rtable_num = esp_mesh_get_routing_table_size();
//Dynamically allocate the memory required to store the node's routing table
rtable = (mesh_addr_t*)malloc(rtable_entries*6);
//Retrieve the node's routing table
ESP_ERROR_CHECK(esp_mesh_get_routing_table(rtable,rtable_entries*6,
&
rtable_entries
));
free(rtable); //Release the dynamic memory used
to store the node's routing table
}
Note that the memory where to store the node's routing table should be allocated dynamically, where it's size
can be retrieved beforehand by using the esp_mesh_get_routing_table_size() function described earlier.
ESP-IDF Mesh Stack
Driver Module
Page 72
Retrieve the number of entries in the sub
-
routing table relative to a node's child
The number of entries in the sub
-
routing table relative to a node's child, which is equal to the number of
entries in the node's child routing table, can be retrieved by calling the following function:
//file esp_mesh.h
esp_err_t esp_mesh_get_subnet_nodes_num(const mesh_addr_t* child,
int*
child_rtable_num
)
Where note that the list of children connected to a node with their MAC addresses can be retrieved by using
the
esp_wifi_ap_get_sta_list()
function described earlier in the MESH_EVENT_CHILD_DISCONNECTED
event specific handler.
void appDriver()
{
mesh_addr_t child; //Used to store the MAC address of a specific child
int child_rtable_num; //Used to store the number of entries in the
sub-routing table relative to the node's child
wifi_sta_list_t children; //Used to store information on the clients
(children) connected to the node
ESP_ERROR_CHECK(esp_wifi_ap_get_sta_list(&children)); //Retrieve information
on the connected
children nodes
memcpy(child.addr,clients.sta[CHILD_INDEX].mac,6); //Copy the MAC address
of a specific child
//Retrieve the number of entries in the
sub-routing table relative to the node's child
ESP_ERROR_CHECK(esp_mesh_get_subnet_nodes_num(&child,&child_rtable_num));
}
ESP-IDF Mesh Stack
Driver Module
Page 73
Retrieve the entries of the sub-routing table relative to a node's child
The entries of the sub-routing table relative to a node's child, which correspond to the entries in the node's
child routing table, can be retrieved by calling the following function:
//file esp_mesh.h
esp_err_t esp_mesh_get_subnet_nodes_list(const mesh_addr_t* child,
const mesh_addr_t*
child_rtable,
int child_rtable_num)
Where again the list of children connected to a node with their MAC addresses can be retrieved via the
esp_wifi_ap_get_sta_list()
function, and the memory where to store the node's child sub
-
routing table
should be allocated dynamically, where its size can be retrieved beforehand by using the
esp_mesh_get_subnet_nodes_num() function described earlier.
void
appDriver
()
{
mesh_addr_t child;
mesh_addr_t*
child_rtable
;
int child_rtable_num;
wifi_sta_list_t children;
ESP_ERROR_CHECK(esp_wifi_ap_get_sta_list(
&
children)); //Retrieve information
on the connected
memcpy(child.addr,clients.sta[CHILD_INDEX].mac,6); //Copy the MAC address
of a specific child
//Retrieve the number of entries in the
sub-routing table relative to the node's child
ESP_ERROR_CHECK
(
esp_mesh_get_subnet_nodes_num
(
&
child
,
&
child_rtable_num
));
//Dynamically allocate the memory required to store
the sub-routing table relative to the node's child
child_rtable
= (
mesh_addr_t*
)
malloc
(
child_rtable_entries
*
6
);
//Retrieve the entries of the sub-routing table relative to the node's child
ESP_ERROR_CHECK(esp_mesh_get_subnet_nodes_list(
&
child,child_rtable,
child_rtable_entries));
free(child_rtable); //Release the dynamic memory used to store the
sub-routing table relative to the node's child
}
ESP-IDF Mesh Stack
Driver Module
Page 74
Node Mesh Status API
The following API allows to retrieve information on the mesh status of a node directly from the mesh stack, where
note that by using the mesh_status_t struct and the mesh events specific handlers proposed in this guide their
use in an application is not necessary.
Retrieve a node's parent BSSID
//file esp_mesh.h
esp_err_t esp_mesh_get_parent_bssid(mesh_addr_t* bssid)
void
appDriver
()
{
mesh_addr_t parent; //Used to store the BSSID of the node's parent
ESP_ERROR_CHECK(esp_mesh_get_parent_bssid(
&
parent));
}
Note that the BSSID of a node's parent changes only when the node connects to a new parent in the mesh
network (MESH_EVENT_PARENT_CONNECTED t,
info.connected.bssid
).
Check whether a node is the root node
//file esp_mesh.h
bool esp_mesh_is_root(void)
Note that a node's type changes to root only when it connects to a new parent (the router) and is on the first
layer of the mesh network (MESH_EVENT_PARENT_CONNECTED, info.self_layer == 1).
void appDriver()
{
if
(esp_mesh_is_root())
/* The node is the root node */
else
/* The node is not the root node */
}
ESP-IDF Mesh Stack
Driver Module
Page 75
Retrieve a node's type
//file esp_mesh.h
mesh_type_t esp_mesh_get_type(void)
void appDriver()
{
mesh_type_t my_type =esp_mesh_get_type();
}
Note that a node's type can change only:
When the node connects to a new parent (MESH_EVENT_PARENT_CONNECTED) depending on its layer
(info.self_layer) and the mesh network's maximum layer (MESH_MAX_LAYER).
When a network topology change affects the node's ancestors (MESH_EVENT_LAYER_CHANGE),
depending on its new layer (info.new_layer) and the mesh network's maximum layer
(MESH_MAX_LAYER).
Retrieve a node's layer in the mesh network
//file esp_mesh.h
int esp_mesh_get_layer(void)
void appDriver()
{
int my_layer =esp_mesh_get_layer();
}
Note that the layer of a node in the mesh network can change only:
When the node connects to a new parent (MESH_EVENT_PARENT_CONNECTED, info.self_layer).
When a network topology change affects the node's ancestors (MESH_EVENT_LAYER_CHANGE,
info.new_layer).
ESP-IDF Mesh Stack
Driver Module
Page 76
Mesh Configuration Retrieval API
The following API allow to retrieve the configuration of the mesh stack on a node:
Base Mesh Configuration
Retrieve a node's base mesh configuration
As a reminder, a node's base mesh configuration consists of:
The Mesh Network Identifier (MID) of the mesh network the node will attempt to join or create.
The Mesh Wi-Fi Channel, representing the Wi-Fi channel used by the node to communicate over the
mesh network.
The address of the Mesh Events General Handler function.
The Mesh SoftAP Settings, consisting of the password used by the node to connect to a parent and to
accept children connections and the maximum number of connected children for the node.
The Mesh Router Settings, consisting of the router's AP SSID, SSID length, password and the specific
BSSID of the router to connect to.
The cryptographic algorithm used by the node to crypt the Mesh IE in its Wi-Fi beacon frames.
From here such information can be retrieved by calling the following function:
//file esp_mesh.h
esp_err_t esp_mesh_get_config(mesh_cfg_t config)
void appDriver()
{
mesh_cfg_t bmesh_conf;
ESP_ERROR_CHECK(esp_mesh_get_config(
&
bmesh_conf);
}
ESP-IDF Mesh Stack
Driver Module
Page 77
Retrieve a node's Fixed Root setting
//file esp_mesh.h
bool esp_mesh_is_root_fixed(void)
void appDriver()
{
if
(esp_mesh_get_self_organized())
/* Self-Organized or Fixed-Root networking
depending on the fixed-root setting */
else
/* Manual Networking */
}
Where note that disabled self-organized networking features correspond to a manual mesh network, while
enabled self-organized networking features correspond either to a self-organized or a fixed-root network
depending on the node's fixed root setting.
Where note that a disabled fixed root setting corresponds to a self-organized mesh network, while an
enabled fixed root setting corresponds either to a fixed-root or a manual network depending on whether the
self-organized networking features on the node are enabled or not.
Mesh Organization Mode
Check whether the self-organized networking features are enabled on a node
//file esp_mesh.h
bool esp_mesh_get_self_organized(void)
void appDriver()
{
if
(esp_mesh_is_root_fixed())
/* Fixed-Root or Manual Networking depending if
the self-networking features are enabled or not */
else
/* Self-Organized Networking */
}
ESP-IDF Mesh Stack
Driver Module
Page 78
Retrieve a node's mesh maximum layer setting
//file esp_mesh.h
int esp_mesh_get_max_layer(void)
void appDriver()
{
int mesh_max_layer = esp_mesh_get_max_layer();
}
Retrieve a node's mesh maximum capacity setting
//file esp_mesh.h
int esp_mesh_get_capacity_num(void)
void appDriver()
{
int mesh_max_capacity = esp_mesh_get_capacity_num();
}
Mesh Topology Settings
void appDriver()
{
if
(esp_mesh_get_self_organized())
if
(esp_mesh_is_root_fixed())
/* Fixed-Root Networking */
else
/* Self-Organized Networking */
else
/* Manual Networking */
}
From here, the mesh organization mode used on a node can be retrieved by jointly using the previous two
functions as follows:
ESP-IDF Mesh Stack
Driver Module
Page 79
Mesh Self-Organized Root Settings
Retrieve a node's root election voting percentage threshold
//file esp_mesh.h
float esp_mesh_get_vote_percentage(void)
void appDriver()
{
float voting_threshold = esp_mesh_get_vote_percentage();
}
Retrieve a node's minimum number of rounds in a root election
//file esp_mesh_internal.h
esp_err_t esp_mesh_get_attempts(mesh_attempts_t* attempts)
void appDriver()
{
mesh_attempts_t self_attempts;
ESP_ERROR_CHECK(esp_mesh_get_attempts(
&
vote_turns);
//From here the "scan" member of the mesh_attempts_t struct
holds the node's minimum number of rounds in a root election
}
ESP-IDF Mesh Stack
Driver Module
Page 80
void appDriver()
{
int root_healing_delay = esp_mesh_get_root_healing_delay();
}
Retrieve a node's root self
-
healing delay
//file esp_mesh.h
int esp_mesh_get_root_healing_delay(void)
Other Root Node Settings
Retrieve the size of the root node's receiving queue for packets destined for the external DS
(TODS queue)
//file esp_mesh.h
int esp_mesh_get_xon_qsize(void)
void appDriver()
{
int TODS_queue_size = esp_mesh_get_xon_qsize();
}
Retrieve the root node's station IP configuration
//File tcpip_adapter.h
esp_err_t tcpip_adapter_get_ip_info(tcpip_adapter_if_t tcpip_if,
tcpip_adapter_ip_info_t* ip_info)
ESP-IDF Mesh Stack
Driver Module
Page 81
void appDriver()
{
tcpip_adapter_ip_info_t ipinfo; //Used to store an interface's
IP configuration
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA,&ipinfo));
}
Retrieve the address of a DNS server set on the root node's Station interface
//File tcpip_adapter.h
esp_err_t tcpip_adapter_get_dns_info(tcpip_adapter_if_t tcpip_if,
tcpip_adapter_dns_type_t type,
tcpip_adapter_dns_info_t* addr)
void appDriver()
{
tcpip_adapter_dns_type_t dns_primary; //Used to store an interface's
tcpip_adapter_dns_type_t dns_secondary;
DNS Server address
ESP_ERROR_CHECK(tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA, //Station
TCPIP_ADAPTER_DNS_MAIN, Primary
&
dns_primary)); DNS Server
ESP_ERROR_CHECK(tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA, //Station
TCPIP_ADAPTER_DNS_BACKUP,Secondary
&
dns_secondary)); DNS Server
}
ESP-IDF Mesh Stack
Driver Module
Page 82
Retrieve a node's mesh children disassociation delay
//file esp_mesh.h
int esp_mesh_get_ap_assoc_expire(void)
void appDriver()
{
int children_disassoc_delay = esp_mesh_get_ap_assoc_expire();
}
Mesh Additional SoftAP Settings
Retrieve a node's Mesh SoftAP Authmode
//file esp_mesh.h
wifi_auth_mode_t esp_mesh_get_ap_authmode(void)
Where remember that a node's mesh SoftAP authmode differs in general from the authentication mode used
on the node's SoftAP interface (which currently is always left open).
void appDriver()
{
wifi_authmode_t mesh_softap_authmode = esp_mesh_get_ap_authmode();
}
ESP-IDF Mesh Stack
Driver Module
Page 83
Mesh Node-specific Settings
Check whether a node belongs to a specific mesh group
//file esp_mesh.h
bool esp_mesh_is_my_group(const mesh_addr_t* gid)
void appDriver()
{
mesh_addr_t mesh_group;
memcpy(mesh_group.addr,GROUP_GID,6); //Set the GID of the group to check
if
(esp_mesh_is_my_group(
&
group_id)) for membership of the node
/* The node belongs to such mesh group */
else
/* The node doesn't belong to such mesh group */
}
Retrieve the mesh groups a node belongs to
//file esp_mesh.h
esp_err_t esp_mesh_get_group_list(mesh_addr_t* gids,int num)
void appDriver()
{
mesh_addr_t node_groups[MESH_MAX_GROUPS]; //MESH_MAX_GROUPS represents
the maximum number of mesh
groups a node can belong to
ESP_ERROR_CHECK(esp_mesh_get_group_list(
&
node_groups,MESH_MAX_GROUPS));
}
ESP-IDF Mesh Stack
Driver Module
Page 84
Remove a node from a set of mesh groups
//file esp_mesh.h
esp_err_t esp_mesh_delete_group_id(const mesh_addr_t* gids,
int
num
)
void appDriver()
{
mesh_addr_t mesh_groups[NUM_GROUPS]; //NUM_GROUPS represents the number of
mesh groups to remove the node from
for
(int i=0;i<NUM_GROUPS;i++) //Set the mesh groups to remove the node from
memcpy(mesh_groups.addr,GROUPi_GID,6);
ESP_ERROR_CHECK(esp_mesh_delete_group_id(&mesh_groups,NUM_GROUPS));
}
Retrieve the MAC address of a node's Wi
-
Fi Interface mode
(Station or SoftAP)
//File esp_wifi.h
esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t* mac))
void appDriver()
{
uint8_t ifx_mac[6]; //Array where to store the node's interface MAC address
ESP_ERROR_CHECK(esp_wifi_get_mac(ESP_IF_WIFI_STA,
&
mac); //Station MAC address
}
ESP-IDF Mesh Stack
Driver Module
Page 85
Mesh Stop API
Disable mesh networking on a node
//file esp_mesh.h
esp_err_t esp_mesh_stop(void)
Calling this function causes the node to:
Deinitialize the mesh IE.
Disconnect from its parent, if any (without raising the MESH_EVENT_PARENT_DISCONNECTED event)
Disassociate all its children nodes, if any (without raising MESH_EVENT_CHILD_DISCONNECTED events)
Delete all the packets sending and receiving queues.
Stop the mesh network management service (raising the MESH_EVENT_STOPPED event)
Unregister the Mesh Events General Handler function.
Release the data structures used for mesh networking in the mesh stack.
Restore the Wi-Fi SoftAP interface to its default settings.
void appDriver()
{
ESP_ERROR_CHECK(esp_mesh_stop());
}
Deinitialize the mesh stack on a node
//file esp_mesh.h
esp_err_t esp_mesh_deinit(void)
void appDriver()
{
ESP_ERROR_CHECK(esp_mesh_deinit());
}
Calling this function causes the disabling on mesh networking as with the esp_mesh_stop() function plus the
release of all remaining resources in the node's mesh stack.
ESP-IDF Mesh Stack
Driver Module
Page 86

Navigation menu