ESP IDF Mesh Stack Pratical Guide Practical
User Manual:
Open the PDF directly: View PDF .
Page Count: 88
Download | |
Open PDF In Browser | View PDF |
ESP-IDF Mesh Stack Practical Guide ESP-IDF V3.1 ESP-IDF Mesh Stack Practical Guide 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 BertiniLicensed 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. Requirements 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) 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 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: Setup Module The Setup Module, whose purpose is to carry out the setup process of mesh networking on the device, consists of the following components: • 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. Driver Module 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. 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. ESP-IDF Mesh Stack Introduction Page 2 Code Premises Required Headers 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 //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 { SELF_ORGANIZED, FIXED_ROOT, MANUAL_NETWORKING } mesh_org_t; //Mesh Organization Mode //Self-Organized Networking //Fixed-Root Networking //Manual Networking typedef struct { uint8_t mid[6]; uint8_t channel; mesh_org_t org; uint8_t root_addr[6]; uint8_t parent_addr[6]; mesh_type_t my_type; int8_t my_layer; } mesh_status_t; //Describes the status of a node in the mesh network //Mesh Network Identifier (MID) //Mesh Wi-Fi Channel //Mesh Organization Mode //Root node's SoftAP MAC address //Node's parent SoftAP MAC address //Node's type //Node's layer in the mesh network Global Variables The following global variables are used in this guide for developing a mesh application: EventGroupHandle_t mesh_event_group; /*-- Mesh Setup Flags #define MESH_ON #define MESH_PARENT #define MESH_CHILD #define MESH_TODS #define MESH_VOTE ESP-IDF Mesh Stack --*/ BIT0 BIT1 BIT2 BIT3 BIT4 //Mesh Event Group Handler //Whether mesh networking is enabled on the node or not //Whether the node is connected to a parent or not //Whether the node has children connected or not //Whether the external DS is reachable or not //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) Introduction Page 3 /*-- Driver Components Executions Flags --*/ #define MESH_DRIVER_INTERNAL BIT5 #define MESH_DRIVER_EXTERNAL BIT6 #define MESH_DRIVER_ROOT BIT7 mesh_status_t mesh_state; //Indicate whether each driver component is running or not (where here a single component for each of the three driver categories is used) //Whether the internal drivers are running or not //Whether the external drivers are running or not //Whether the root drivers are running or not //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 Setup Module 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. Mesh Initializer Function 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, Wi-Fi Stack Configuration 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. • 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). From here the steps required to configure the Wi-Fi stack for the purposes of mesh networking are: ESP-IDF Mesh Stack Mesh Initializer Function Page 5 1) Initialize the system flash storage 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 2) Initialize the TCP/LwIP Stack 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) 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. esp_err_t mesh_init() { … tcpip_adapter_init(); … } //Initialize the TCP/LwIP stack 3) Devolve the Handling of Wi-Fi Events 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 4) Initialize the Wi-Fi Stack 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 int int int int int int int int int int int } wifi_init_config_t; tx_buf_type; static_tx_buf_num; dynamic_tx_buf_num; csi_enable; ampdu_rx_enable; ampdu_tx_enable; nvs_enable; nano_enable; tx_ba_win; rx_ba_win; wifi_task_core_id; magic; //Wi-Fi TX buffer type //Wi-Fi static TX buffer number //Wi-Fi dynamic TX buffer number //Wi-Fi CSI enable flag //Wi-Fi AMPDU RX enable flag //Wi-Fi AMPDU TX enable flag //Wi-Fi NVS flash enable flag //printf/scan family enable flag //Wi-Fi Block Ack TX window size //Wi-Fi Block Ack RX window size //Wi-Fi Task Core ID //Wi-Fi init magic number 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 5) Disable Station DHCP Client and SoftAP DHCP Server 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 { TCPIP_ADAPTER_IF_STA = 0, TCPIP_ADAPTER_IF_AP, TCPIP_ADAPTER_IF_ETH, TCPIP_ADAPTER_IF_MAX, } tcpip_adapter_if_t; //TCP/IP interface enumerates //Station interface //SoftAP interface //Ethernet interface 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 6) Enable the Wi-Fi Interface 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()); … } ESP-IDF Mesh Stack //Enable the Wi-Fi interface 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: 1) Initialize the Mesh Stack 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) 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_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 A flag representing whether the node is connected to a parent or not A flag representing whether the node has children connected or not A flag representing whether the external DS is accessible or not A flag representing whether a new root election is currently in progress in the mesh network or not (that we'll use as an active low flag, i.e. 0 = election in progress, 1 = no election) ESP-IDF Mesh Stack Mesh Initializer Function (MESH_ON) (MESH_PARENT) (MESH_CHILD) (MESH_TODS) (MESH_VOTE) Page 11 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 being executed or not • A flag representing whether the external driver component is currently being executed or not • A flag representing whether the root driver component is currently being executed or not (MESH_DRIVER_INTERNAL) (MESH_DRIVER_EXTERNAL) (MESH_DRIVER_ROOT) 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) 3) Set the Base Mesh Configuration 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_addr_t mesh_id; uint8_t channel; mesh_event_cb_t event_cb; mesh_ap_cfg_t mesh_ap; mesh_router_t router; const mesh_crypto_funcs_t* } mesh_cfg_t; //Mesh Base Configuration //Mesh Network Identifier (MID) //Mesh Wi-Fi Channel (1-14) //Mesh Events General Handler's address //Mesh SoftAP Settings //Mesh Router Settings crypto_funcs; //Mesh packets cryptographic algorithm 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: //Destination Address (IPv4:PORT) of a mesh typedef struct packet destined outside the mesh network { //Destination IP ip4_addr_t ip4; //Destination port uint16_t port; } __attribute__((packed)) mip_t; //("__attribute__((packed))" is for the byte alignment of the in-memory representation of the struct) ESP-IDF Mesh Stack Mesh Initializer Function Page 12 typedef union { uint8_t addr[6]; mip_t mip; } mesh_addr_t; //Used to store an address in mesh networking //Depending on the context may represent a Mesh Network Identifier (MID), a node's MAC address or a Mesh Group Identifier (GID) //Destination address of a mesh packet destined for 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 { uint8_t password[64]; uint8_t max_connection; } mesh_ap_cfg_t; //Base Mesh SoftAP Settings //Required from nodes to connect to a parent node //Maximum number of connected children for each 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 { uint8_t ssid[32]; uint8_t ssid_len; uint8_t password[64]; uint8_t bssid[6]; } mesh_router_t; ESP-IDF Mesh Stack //Mesh Router Settings //Router's AP SSID //Router's AP SSID length //Router's AP password //Router's AP BSSID (required only if the root node must connect to a router with a specific BSSID) 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. 4) Mesh Shared Settings (optional) 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: 1- Self-Organized Network (default) 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 2- Fixed-Root Network 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 selforganized 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 3- Manual Networking 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 selfhealing 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: L max nodes = i=1 C(i-1) 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 selforganized 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 • 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 { int scan; int vote; int fail; int monitor_ie; //Self-Organized implicit actions' number of attempts //Minimum root election rounds (default=10) <---//Maximum self-healing root election rounds (default=15) //Maximum parent reconnection tries before switching to another parent (default=120) //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_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 ESP-IDF Mesh Stack Mesh Initializer Function Page 22 • 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. esp_err_t mesh_init() { … //Set the root node healing delay ESP_ERROR_CHECK(esp_mesh_set_root_healing_delay(MESH_ROOT_HEALING_DELAY)); … } Other Root Node Settings • 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_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)); … } 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 { WIFI_AUTH_OPEN = 0, WIFI_AUTH_WEP, WIFI_AUTH_WPA_PSK, WIFI_AUTH_WPA2_PSK, WIFI_AUTH_WPA_WPA2_PSK, WIFI_AUTH_WPA2_ENTERPRISE, WIFI_AUTH_MAX } wifi_auth_mode_t; //SoftAP authmode enumerates //Open (no authentication) //WEP (buggy, avoid) //WPA_PSK //WPA2_PSK //WPA_WPA2_PSK //WPA2_ENTERPRISE //File esp_mesh.h esp_err_t esp_mesh_set_ap_authmode(wifi_authmode_t 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_err_t mesh_init() { … //Set Mesh SoftAP Authmode ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(MESH_SOFTAP_AUTHMODE)); … } ESP-IDF Mesh Stack Mesh Initializer Function Page 24 • 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. esp_err_t mesh_init() { … //Set the mesh children disassociation delay ESP_ERROR_CHECK(esp_mesh_set_ap_assoc_expire(CHILD_DISASSOCIATION_DELAY)); … } 5) Mesh Node-specific Settings (optional) 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 { ESP_IF_WIFI_STA = 0, ESP_IF_WIFI_AP, ESP_IF_ETH, ESP_IF_MAX } esp_interface_t; //Networking interface enumerates //Wi-Fi station interface (or mode) //Wi-Fi softAP interface (or mode) //Ethernet interface //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 a custom MAC address for if(MESH_STATION_USE_CUSTOM_MAC) { 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 a custom MAC address for if(MESH_SOFTAP_USE_CUSTOM_MAC) { the softAP interface is used memcpy(mac,MESH_SOFTAP_CUSTOM_MAC,6); ESP_ERROR_CHECK(esp_wifi_set_mac(ESP_IF_WIFI_AP,mac)); } … } 6) Enable Mesh Networking 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()); return ESP_OK; } //Enable Mesh Networking //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 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 Events IDs /*-- 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; } mesh_event_scan_done_t; //The number of APs that were found in the Wi-Fi scan /*=========================== MESH_EVENT_PARENT_CONNECTED ===========================*/ //File esp_event.h (automatically included by the previous headers) typedef struct { uint8_t ssid[32]; uint8_t ssid_len; uint8_t bssid[6]; uint8_t channel; wifi_auth_mode_t authmode; } system_event_sta_connected_t; //SYSTEM_EVENT_STA_CONNECTED event-specific info //SSID of the AP the station connected to //SSID length of the AP the station connected to //BSSID of the AP the station connected to //Wi-Fi channel of the AP the station connected to //The authmode used by AP the station connected to //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 //SYSTEM_EVENT_STA_DISCONNECTED event-specific info typedef struct { 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; } mesh_event_no_parent_found_t; ESP-IDF Mesh Stack //The total number of scans performed searching for a viable parent for the node Mesh Events Handling Page 34 Mesh Network Update Events /*=========================== MESH_EVENT_CHILD_CONNECTED ===========================*/ //File esp_event.h //SYSTEM_EVENT_AP_STACONNECTED event-specific info typedef struct { 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 //SYSTEM_EVENT_AP_STADISCONNECTED event-specific info typedef struct { 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; } mesh_event_root_fixed_t; ESP-IDF Mesh Stack //The parent's and thus the node's new value of the fixed root setting Mesh Events Handling Page 35 /*============================= MESH_EVENT_TODS_STATE =============================*/ //File esp_mesh.h typedef enum { MESH_TODS_REACHABLE, MESH_TODS_UNREACHABLE, } mesh_event_toDS_state_t; //Whether the external DS is currently accessible or not //External DS accessible //External DS not accessible /*============================= 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; } mesh_event_layer_change_t; //The new node's layer in the mesh network /*============================ 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 //SYSTEM_EVENT_STA_GOT_IP event-specific info typedef struct { 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 //Mesh Events additional information union typedef 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; } mesh_event_t; ESP-IDF Mesh Stack //Summary struct passed by the mesh stack to the Mesh Events General Handler //Event ID //Event additional information (if applicable) 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; 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: 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; 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 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: void mesh_EVENTi_handler(EVENTi_t* info) { /* Specific handler logic */ return; } 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. 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; type depending on it having children else mesh_state->my_type = MESH_IDLE; 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 (no root election) return; } /* 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 //Prevents an error from happening later should the node if(MESH_ROOT_IPDYNAMIC) 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: ESP-IDF Mesh Stack Mesh Events Specific Handlers Page 40 //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) 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 { uint8_t ssid[32]; uint8_t password[64]; bool bssid_set; uint8_t bssid[6]; uint8_t channel; … } wifi_sta_config_t; typedef union { wifi_sta_config_t sta; wifi_ap_config_t ap; } wifi_config_t; ESP-IDF Mesh Stack //Station Mode Configuration //SSID of the AP to connect to //Password of the AP to connect to //If set, connect only to the AP with the following specific BSSID //Specific BSSID of the AP to connect to //AP's channel /* Other station mode-related members */ //Holds a Wi-Fi interface mode configuration (Station OR SoftAP) //Station Mode Configuration <--//SoftAP Mode Configuration Mesh Events Specific Handlers Page 41 //File esp_mesh.h typedef enum { MESH_IDLE, MESH_ROOT, MESH_NODE, MESH_LEAF, } mesh_type_t; //Mesh node types //Idle Node //Root Node //Intermediate Parent Node //Leaf Node 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) 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 //Wi-Fi Scan types typedef enum { 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 { uint32_t min; uint32_t max; } wifi_active_scan_time_t; //Active scan time per Wi-Fi channel //The minimum active scan time per Wi-Fi channel (default = 120ms) //The maximum active scan time per Wi-Fi channel (default = 120ms, must be <=1500ms) typedef struct { wifi_active_scan_time_t active; uint32_t passive; } wifi_scan_time_t; ESP-IDF Mesh Stack //Scan time per Wi-Fi channel //Active scan time per Wi-Fi channel //Passive scan time per Wi-Fi channel (must be <=1500ms) Mesh Events Specific Handlers Page 42 typedef struct { uint8_t* ssid; uint8_t* bssid; uint8_t channel; //Wi-Fi Scan configuration //Whether to scan for an AP with a specific SSID only //Whether to scan for an AP with a specific BSSID only //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 //Further actions depend on the Mesh switch(mesh_state.org) { Organization Mode set on the node //For Self-Organized networking, case SELF_ORGANIZED: no further action is required break; 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 the node is the fixed-root, it must attempt if(IS_NODE_FIXED_ROOT) { to directly connect to the external router strcpy((char*)parent_ap.sta.ssid,MESH_ROUTER_SSID); strcpy((char*)parent_ap.sta.password,MESH_ROUTER_PASSWORD); if(MESH_ROUTER_SPECIFIC_BSSID) { parent_ap.sta.bssid_set = true; 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)); } //If the node is NOT the fixed root, no further action is required break; 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() { mesh_reset_status(); //Reset the node's status xEventGroupClearBits(mesh_event_group,MESH_ON); //Clear the MESH_ON flag if(/* unintentional */) //If the disabling was unintentional, ESP_ERROR_CHECK(esp_mesh_start()); try to reenable mesh networking 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: //Node's Mesh IE (extract) typedef struct { 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() function described previously with the following considerations: Connecting to a Node: • 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) 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 = 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)); //Otherwise perform a new Wi-Fi scan after a defined time interval else { 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 the node is the root, an IP configuration if(mesh_state.my_layer == 1) { 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 //Otherwise if a static IP configuration is used, apply it else { //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; //LEAF node (my_layer == MESH_MAX_LAYER) else 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 applicationlevel, 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 the node is root, inform if(mesh_state.my_type == MESH_ROOT) { 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 (selforganized 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 Network Update Events 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 applicationlevel 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 it was the last child if(children.num == 0) xEventGroupClearBits(mesh_event_group,MESH_CHILD); to disconnect, clear the MESH_CHILD flag return; } 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 //Information on a specific client (child) typedef struct { connected to the node's SoftAP interface uint8_t mac[6]; //Client's (child's) MAC address … } wifi_sta_info_t; //Information on the clients (children) typedef struct { 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; ESP-IDF Mesh Stack Mesh Events Specific Handlers Page 52 //File esp_wifi.h esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t* stalist) 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 in the node's mesh status return; } 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) { //Change the node's mesh organization mode accordingly if(info->is_fixed) 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 the external DS is reachable if(*info == MESH_TODS_REACHABLE) { xEventGroupSetBits(mesh_event_group,MESH_TODS); //Set the MESH_TODS flag startMeshExternalDrivers(); //Start external driver components } //If the external DS is NOT reachable else 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 (for it's active low) return; } 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); return; } //Set the MESH_VOTE flag (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 external DS is accessible return; } 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 the node is the current root if(mesh_state.my_type == MESH_ROOT) ESP_ERROR_CHECK(esp_mesh_post_toDS_state(false)); //Inform the mesh network that the external DS is no longer return; } 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 root to intermediate parent return; } 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 root to intermediate parent return; } ESP-IDF Mesh Stack Mesh Events Specific Handlers Page 57 Driver Module 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 } … 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) #define MESH_DATA_FROMDS (0x04) #define MESH_DATA_TODS (0x08) #define MESH_DATA_NONBLOCK (0x10) #define MESH_DATA_DROP (0x20) #define MESH_DATA_GROUP (0x40) ESP-IDF Mesh Stack //Denotes that the packet is destined for a non-root node inside the mesh network //Denotes that the packet contains data which originated from an external host //Denotes that the packet is destined for an external host //Denotes that the non-blocking variant of the esp_mesh_send() or esp_mesh_recv() function must be used to send/receive the packet //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 //Denotes that the packet is destined for all nodes belonging to a certain mesh group (multicast) 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, } mesh_proto_t; //Identifies the protocol the payload of the mesh packet belongs to //Binary Data (default) //HTTP protocol //JSON protocol //MQTT protocol typedef enum { MESH_TOS_P2P, MESH_TOS_E2E, MESH_TOS_DEF, } mesh_tos_t; //Defines the type of service that must or was used to deliver the mesh packet //Reliable point-to-point transmission (default) //Reliable end-to-end transmission (currently unimplemented) //Unreliable transmission typedef struct { uint8_t* data; uint16_t size; mesh_proto_t proto; mesh_tos_t tos; } mesh_data_t; //Mesh Packet Data Descriptor //Pointer to the send/receive buffer //Size of the data to send/receive or that has been received //The protocol the payload of the mesh packet belongs to //The type of service that must or 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 { uint8_t type; uint8_t* val; //Mesh Packet Additional Option Descriptor //The additional option's type (either of the above) //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: 1) 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. 2) 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). 3) 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. Notes on Mesh Communication • 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. ESP-IDF Mesh Stack Driver Module Page 62 • 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) 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; uint8_t send_buffer[MESH_MPS]; mesh_addr_t dest; mesh_data_t data_des; int send_flag = 0; mesh_opt_t send_opt; uint8_t opt_val[50]; //Used to store the result of the send() //Send Buffer (MESH_MPS = 1472 bytes) //Packet's destination address //Packet's data descriptor //Packet's flag //Possible additional send option //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; data_des.size = sizeof(PACKET_CONTENTS); data_des.proto = PACKET_DATA_PROTOCOL; data_des.tos = PACKET_TYPE_OF_SERVICE; ESP-IDF Mesh Stack Driver Module //Address of the sending buffer //Size of the packet's payload //Payload's protocol //Packet's type of service Page 63 //3) Send Flag Initialization //If we want to use the non-blocking if(SEND_NONBLOCK) send_flag += MESH_DATA_NONBLOCK; version of the esp_mesh_send() function //If the packet to send is not of critical if(SEND_NONCRITICAL) 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; uint8_t recv_buffer[MESH_MTU]; mesh_addr_t src = {0}; mesh_data_t data_des = {0}; int recv_flag = 0; mesh_opt_t recv_opt; uint8_t opt_val[50]; //Used to store the result of the recv() //Receive Buffer (MESH_MTU = 1500 bytes) //Received packet's source address //Received packet's data descriptor //Received packet's flag //Received packet's additional option //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 { int to_parent; int to_parent_p2p; int to_child; int to_child_p2p; int mgmt; int broadcast; } mesh_tx_pending_t; //Number of packets pending to be sent in each of the mesh stack's sending queues //Parent sending queue //Parent (P2P) sending queue //Child sending queue //Child (P2P) sending queue //Management sending queue //Broadcast and multicast sending queue 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 { int toSelf; int toDS; //Number of packets pending to be received at the application level in each of the mesh stack's receiving queues //Receiving queue of packets destined for the node //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; uint8_t recv_buffer[MESH_MTU]; mesh_addr_t src = {0}; mesh_addr_t dest; mesh_data_t data_des = {0}; int recv_flag = 0; mesh_opt_t recv_opt; uint8_t opt_val[50]; //Used to store the result of the recv() //Receive Buffer (MESH_MTU = 1500 bytes) //Received packet's source address(node's MAC) //Packet's destination address (IPv4:PORT) //Received packet's data descriptor //Received packet's flag //Received packet's additional option //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 { int attempts; mesh_addr_t rc_addr; } mesh_rc_config_t; typedef struct { bool is_rc_specified; //Root switch request type-specific configuration //Maximum rounds in the new root election (default = 15) //The address of a node appointed to be the next root by the current root node (UNIMPLEMENTED) //Root switch request configuration //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; mesh_vote_t vote_settings; wifi_ap_record_t routerAP; //Used to check the flags of the mesh event group //Used to store the new root election settings //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 (currently only i.e. trigger a vote_settings.config.attempts = ROOT_ELECTION_MAX_ROUNDS; vote_settings.percentage = ROOT_ELECTION_THRESHOLD; } switch request type false is supported, new root election) //Set the election maximum rounds //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) 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. void appDriver() { … int rtable_num = esp_mesh_get_routing_table_size(); … } 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) 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. 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 } 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; int child_rtable_num; //Used to store the MAC address of a specific child //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)); memcpy(child.addr,clients.sta[CHILD_INDEX].mac,6); //Retrieve information on the connected //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) 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). 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)); … } • 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) 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). void appDriver() { … mesh_type_t my_type = esp_mesh_get_type(); … } • Retrieve a node's layer in the mesh network //file esp_mesh.h int esp_mesh_get_layer(void) 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). void appDriver() { … int my_layer = esp_mesh_get_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 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) 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. void appDriver() { … if(esp_mesh_get_self_organized()) /* Self-Organized or Fixed-Root networking depending on the fixed-root setting */ else /* Manual Networking */ … } • Retrieve a node's Fixed Root setting //file esp_mesh.h bool esp_mesh_is_root_fixed(void) 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. 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 From here, the mesh organization mode used on a node can be retrieved by jointly using the previous two functions as follows: void appDriver() { … if(esp_mesh_get_self_organized()) if(esp_mesh_is_root_fixed()) /* Fixed-Root Networking */ else /* Self-Organized Networking */ else /* Manual Networking */ … } Mesh Topology Settings • 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(); … } 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 • Retrieve a node's root self-healing delay //file esp_mesh.h int esp_mesh_get_root_healing_delay(void) void appDriver() { … int root_healing_delay = esp_mesh_get_root_healing_delay(); … } 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 Server &dns_primary)); ESP_ERROR_CHECK(tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA, //Station TCPIP_ADAPTER_DNS_BACKUP,Secondary DNS Server &dns_secondary)); … } ESP-IDF Mesh Stack Driver Module Page 82 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(); … } • 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(); … } 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 for membership of the node if(esp_mesh_is_my_group(&group_id)) /* 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 //Set the mesh groups to remove the node from for(int i=0;i
Source Exif Data:File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.7 Linearized : No Page Mode : UseNone Has XFA : No Page Count : 88 XMP Toolkit : XMP Core 4.4.0-soda Format : application/pdf Creator Tool : PDF Architect 6 Modify Date : 2018:11:27 09:15:13+01:00 Create Date : 2018:11:27 08:01:33+01:00 Title : ESP-IDF Mesh Stack Pratical Guide Creator : Riccardo Bertini Producer : Nuance PDF Create Author : Riccardo BertiniEXIF Metadata provided by EXIF.tools