From bcee69ae06a17ff55f74120fe6605b6a65475d99 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 19 Feb 2020 16:56:07 +0100 Subject: [PATCH 1/2] esp-netif: Enable easier configuration of custom DHCP server address --- components/esp_netif/esp_netif_defaults.c | 12 +++--------- components/esp_netif/include/esp_netif_ip_addr.h | 6 ++++++ components/esp_wifi/include/esp_mesh.h | 2 ++ components/lwip/port/esp32/netif/wlanif.c | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/components/esp_netif/esp_netif_defaults.c b/components/esp_netif/esp_netif_defaults.c index 4dd497648..af71eaef6 100644 --- a/components/esp_netif/esp_netif_defaults.c +++ b/components/esp_netif/esp_netif_defaults.c @@ -24,12 +24,6 @@ // - default init / create functions for basic default interfaces // -#define IP4TOUINT32(a,b,c,d) (((uint32_t)((a) & 0xffU) << 24) | \ - ((uint32_t)((b) & 0xffU) << 16) | \ - ((uint32_t)((c) & 0xffU) << 8) | \ - (uint32_t)((d) & 0xffU)) - -#define IP4TOADDR(a,b,c,d) esp_netif_htonl(IP4TOUINT32(a, b, c, d)) // @@ -44,7 +38,7 @@ const esp_netif_inherent_config_t _g_esp_netif_inherent_eth_config = ESP_NETIF_I const esp_netif_inherent_config_t _g_esp_netif_inherent_ppp_config = ESP_NETIF_INHERENT_DEFAULT_PPP(); const esp_netif_ip_info_t _g_esp_netif_soft_ap_ip = { - .ip = { .addr = IP4TOADDR( 192, 168, 4, 1) }, - .gw = { .addr = IP4TOADDR( 192, 168, 4, 1) }, - .netmask = { .addr = IP4TOADDR( 255, 255, 255, 0) }, + .ip = { .addr = ESP_IP4TOADDR( 192, 168, 4, 1) }, + .gw = { .addr = ESP_IP4TOADDR( 192, 168, 4, 1) }, + .netmask = { .addr = ESP_IP4TOADDR( 255, 255, 255, 0) }, }; diff --git a/components/esp_netif/include/esp_netif_ip_addr.h b/components/esp_netif/include/esp_netif_ip_addr.h index e673b399b..92f374e1b 100644 --- a/components/esp_netif/include/esp_netif_ip_addr.h +++ b/components/esp_netif/include/esp_netif_ip_addr.h @@ -78,6 +78,12 @@ extern "C" { #define ESP_IPADDR_TYPE_V6 6U #define ESP_IPADDR_TYPE_ANY 46U +#define ESP_IP4TOUINT32(a,b,c,d) (((uint32_t)((a) & 0xffU) << 24) | \ + ((uint32_t)((b) & 0xffU) << 16) | \ + ((uint32_t)((c) & 0xffU) << 8) | \ + (uint32_t)((d) & 0xffU)) + +#define ESP_IP4TOADDR(a,b,c,d) esp_netif_htonl(ESP_IP4TOUINT32(a, b, c, d)) struct esp_ip6_addr { uint32_t addr[4]; diff --git a/components/esp_wifi/include/esp_mesh.h b/components/esp_wifi/include/esp_mesh.h index 6b17a60ce..12632e4fa 100644 --- a/components/esp_wifi/include/esp_mesh.h +++ b/components/esp_wifi/include/esp_mesh.h @@ -224,6 +224,8 @@ typedef enum { MESH_PROTO_HTTP, /**< HTTP protocol */ MESH_PROTO_JSON, /**< JSON format */ MESH_PROTO_MQTT, /**< MQTT protocol */ + MESH_PROTO_AP, + MESH_PROTO_STA, } mesh_proto_t; /** diff --git a/components/lwip/port/esp32/netif/wlanif.c b/components/lwip/port/esp32/netif/wlanif.c index 1e1fe1daa..1555b0ce9 100644 --- a/components/lwip/port/esp32/netif/wlanif.c +++ b/components/lwip/port/esp32/netif/wlanif.c @@ -54,6 +54,7 @@ #include "esp_netif_net_stack.h" #include "esp_compiler.h" +#if !ESP_L2_TO_L3_COPY /** * @brief Free resources allocated in L2 layer * @@ -65,6 +66,7 @@ static void lwip_netif_wifi_free_rx_buffer(struct netif *netif, void *buf) esp_netif_t *esp_netif = esp_netif_get_handle_from_netif_impl(netif); esp_netif_free_rx_buffer(esp_netif, buf); } +#endif /** * In this function, the hardware should be initialized. @@ -177,14 +179,12 @@ wlanif_input(void *h, void *buffer, size_t len, void* eb) #if (ESP_L2_TO_L3_COPY == 1) p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { -// esp_wifi_internal_free_rx_buffer(eb); esp_netif_free_rx_buffer(esp_netif, eb); return; } p->l2_owner = NULL; memcpy(p->payload, buffer, len); esp_netif_free_rx_buffer(esp_netif, eb); -// esp_wifi_internal_free_rx_buffer(eb); #else p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); if (p == NULL){ From 9e58d94a42f432da54046df93e26f63aa9076cd7 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 13 Feb 2020 15:51:24 +0100 Subject: [PATCH 2/2] esp-mesh: Added example showing internal IP capable mesh network --- components/esp_wifi/include/esp_mesh.h | 4 +- .../mesh/ip_internal_network/CMakeLists.txt | 6 + examples/mesh/ip_internal_network/Makefile | 9 + examples/mesh/ip_internal_network/README.md | 81 +++ .../ip_internal_network/main/CMakeLists.txt | 4 + .../main/Kconfig.projbuild | 94 ++++ .../ip_internal_network/main/component.mk | 4 + .../main/include/mesh_netif.h | 77 +++ .../mesh/ip_internal_network/main/mesh_main.c | 426 ++++++++++++++++ .../ip_internal_network/main/mesh_netif.c | 480 ++++++++++++++++++ .../mesh/ip_internal_network/main/mqtt_app.c | 80 +++ .../mesh/ip_internal_network/partitions.csv | 5 + .../ip_internal_network/sdkconfig.defaults | 7 + 13 files changed, 1275 insertions(+), 2 deletions(-) create mode 100644 examples/mesh/ip_internal_network/CMakeLists.txt create mode 100644 examples/mesh/ip_internal_network/Makefile create mode 100644 examples/mesh/ip_internal_network/README.md create mode 100644 examples/mesh/ip_internal_network/main/CMakeLists.txt create mode 100644 examples/mesh/ip_internal_network/main/Kconfig.projbuild create mode 100644 examples/mesh/ip_internal_network/main/component.mk create mode 100644 examples/mesh/ip_internal_network/main/include/mesh_netif.h create mode 100644 examples/mesh/ip_internal_network/main/mesh_main.c create mode 100644 examples/mesh/ip_internal_network/main/mesh_netif.c create mode 100644 examples/mesh/ip_internal_network/main/mqtt_app.c create mode 100644 examples/mesh/ip_internal_network/partitions.csv create mode 100644 examples/mesh/ip_internal_network/sdkconfig.defaults diff --git a/components/esp_wifi/include/esp_mesh.h b/components/esp_wifi/include/esp_mesh.h index 12632e4fa..ea9899302 100644 --- a/components/esp_wifi/include/esp_mesh.h +++ b/components/esp_wifi/include/esp_mesh.h @@ -224,8 +224,8 @@ typedef enum { MESH_PROTO_HTTP, /**< HTTP protocol */ MESH_PROTO_JSON, /**< JSON format */ MESH_PROTO_MQTT, /**< MQTT protocol */ - MESH_PROTO_AP, - MESH_PROTO_STA, + MESH_PROTO_AP, /**< IP network mesh communication of node's AP inteface */ + MESH_PROTO_STA, /**< IP network mesh communication of node's STA inteface */ } mesh_proto_t; /** diff --git a/examples/mesh/ip_internal_network/CMakeLists.txt b/examples/mesh/ip_internal_network/CMakeLists.txt new file mode 100644 index 000000000..96c6da397 --- /dev/null +++ b/examples/mesh/ip_internal_network/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ip_internal_network) diff --git a/examples/mesh/ip_internal_network/Makefile b/examples/mesh/ip_internal_network/Makefile new file mode 100644 index 000000000..ab2f5cc42 --- /dev/null +++ b/examples/mesh/ip_internal_network/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ip_internal_network + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/mesh/ip_internal_network/README.md b/examples/mesh/ip_internal_network/README.md new file mode 100644 index 000000000..004084053 --- /dev/null +++ b/examples/mesh/ip_internal_network/README.md @@ -0,0 +1,81 @@ +# Mesh IP Internal Networking example + +This example demonstrates how to use mesh to create a IP capable sub-network. +As a demonstration all nodes publish their IP and their internal mesh layer to MQTT broker +and uses internal communication at the same time + +## Functionality + +This example uses experimental NAT feature to translate addresses/ports from an internal subnet, that is created +by the root node running a DHCP server. At the same time, the nodes communicate using low level mesh send/receive +API to exchange data, such as routing table from root to all nodes and an event notification from one node +to all other nodes in the mesh. As a demonstration, the same event is also published at the mqtt broker +on a subscribed topic, so both internal mesh_recv() notification as well as mqtt data event are to be received. + +### Hardware Required + +This example can be executed on any platform board, the only required interface is WiFi and connection to internet. + +### Configure the project + +Open the project configuration menu (`idf.py menuconfig`) to configure the mesh network channel, router SSID, router password and mesh softAP settings. + +### Build and Flash + +Build the project and flash it to multiple boards forming a mesh network, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +### Output sample from mesh node +``` +I (7749) mesh_main: state:0 +I (7749) mesh_main: root address:24:0a:c4:09:88:5d +I (7899) wifi:AP's beacon interval = 307200 us, DTIM period = 2 +I (8809) mesh_main: IP:10.0.0.3 +I (8819) mesh_main: Tried to publish layer:2 IP:10.0.0.3 +I (8819) mesh_mqtt: Other event id:7 +I (9189) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000 +I (9469) mesh_mqtt: MQTT_EVENT_CONNECTED +I (9569) mesh_mqtt: sent publish returned msg_id=42728 +I (9839) mesh_mqtt: MQTT_EVENT_SUBSCRIBED, msg_id=60327 +I (9919) mesh_mqtt: MQTT_EVENT_PUBLISHED, msg_id=42728 +... +I (1218786) MESH_MQTT: sent publish returned msg_id=13978 +W (1218836) mesh_main: Key pressed! +I (1218836) MESH_MQTT: sent publish returned msg_id=15808 +I (1218846) mesh_main: Sending to [0] 30:ae:a4:80:5b:18: sent with err code: 0 +I (1218906) MESH_MQTT: MQTT_EVENT_PUBLISHED, msg_id=13978 +I (1219016) MESH_MQTT: MQTT_EVENT_PUBLISHED, msg_id=15808 +I (1219366) MESH_MQTT: MQTT_EVENT_DATA +TOPIC=/topic/ip_mesh/key_pressed +DATA=24:0a:c4:09:88:5c +I (1220036) mesh_main: Received Routing table [0] 30:ae:a4:80:5b:18 +I (1220036) mesh_main: Received Routing table [1] 24:0a:c4:09:88:5c +``` + +### Output sample from the root node +``` +I (11957) mesh_main: state:0 +I (11967) mesh_main: root address:24:0a:c4:09:88:5d +I (11967) wifi:AP's beacon interval = 102400 us, DTIM period = 3 +I (12767) esp_netif_handlers: sta ip: 192.168.2.3, mask: 255.255.255.0, gw: 192.168.2.1 +I (12767) mesh_main: IP:192.168.2.3 +... +I (1253864) mesh_main: Received Routing table [0] 30:ae:a4:80:5b:18 +I (1253864) mesh_main: Received Routing table [1] 24:0a:c4:09:88:5c +I (1253874) mesh_main: Sending routing table to [0] 30:ae:a4:80:5b:18: sent with err code: 0 +I (1253884) mesh_main: Sending routing table to [1] 24:0a:c4:09:88:5c: sent with err code: 0 +I (1253974) MESH_MQTT: MQTT_EVENT_PUBLISHED, msg_id=18126 +W (1254714) mesh_main: Keypressed detected on node: 24:0a:c4:09:88:5c +I (1254814) MESH_MQTT: MQTT_EVENT_DATA +TOPIC=/topic/ip_mesh/key_pressed +DATA=24:0a:c4:09:88:5c +``` diff --git a/examples/mesh/ip_internal_network/main/CMakeLists.txt b/examples/mesh/ip_internal_network/main/CMakeLists.txt new file mode 100644 index 000000000..06403090a --- /dev/null +++ b/examples/mesh/ip_internal_network/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "mesh_main.c" + "mesh_netif.c" + "mqtt_app.c" + INCLUDE_DIRS "." "include") diff --git a/examples/mesh/ip_internal_network/main/Kconfig.projbuild b/examples/mesh/ip_internal_network/main/Kconfig.projbuild new file mode 100644 index 000000000..f0d29f381 --- /dev/null +++ b/examples/mesh/ip_internal_network/main/Kconfig.projbuild @@ -0,0 +1,94 @@ +menu "Example Configuration" + + config MESH_CHANNEL + int "channel" + range 0 14 + default 0 + help + mesh network channel. + + config MESH_ROUTER_SSID + string "Router SSID" + default "ROUTER_SSID" + help + Router SSID. + + config MESH_ROUTER_PASSWD + string "Router password" + default "ROUTER_PASSWD" + help + Router password. + + choice + bool "Mesh AP Authentication Mode" + default WIFI_AUTH_WPA2_PSK + help + Authentication mode. + + config WIFI_AUTH_OPEN + bool "WIFI_AUTH_OPEN" + config WIFI_AUTH_WPA_PSK + bool "WIFI_AUTH_WPA_PSK" + config WIFI_AUTH_WPA2_PSK + bool "WIFI_AUTH_WPA2_PSK" + config WIFI_AUTH_WPA_WPA2_PSK + bool "WIFI_AUTH_WPA_WPA2_PSK" + endchoice + + config MESH_AP_AUTHMODE + int + default 0 if WIFI_AUTH_OPEN + default 2 if WIFI_AUTH_WPA_PSK + default 3 if WIFI_AUTH_WPA2_PSK + default 4 if WIFI_AUTH_WPA_WPA2_PSK + help + Mesh AP authentication mode. + + config MESH_AP_PASSWD + string "Mesh AP Password" + default "MAP_PASSWD" + help + Mesh AP password. + + config MESH_AP_CONNECTIONS + int "Mesh AP Connections" + range 1 10 + default 6 + help + The number of stations allowed to connect in. + + config MESH_MAX_LAYER + int "Mesh Max Layer" + range 1 25 + default 6 + help + Max layer allowed in mesh network. + + config MESH_ROUTE_TABLE_SIZE + int "Mesh Routing Table Size" + range 1 300 + default 50 + help + The number of devices over the network(max: 300). + + config MESH_USE_GLOBAL_DNS_IP + bool "Use global DNS IP" + default n + help + Use fixed DNS server IP address. If enabled, root node + advertises the specified DNS address in DHCP packets. + By default it uses DNS record received from the router. + + config MESH_GLOBAL_DNS_IP + hex "Global DNS" + depends on MESH_USE_GLOBAL_DNS_IP + default 0x08080808 + help + The IP address of global DNS server that is used + for internal IP subnet formed by the mesh network + if MESH_USE_GLOBAL_DNS_IP is enabled. + Note: The IP address is in platform (not network) + format. + +endmenu + diff --git a/examples/mesh/ip_internal_network/main/component.mk b/examples/mesh/ip_internal_network/main/component.mk new file mode 100644 index 000000000..a98f634ea --- /dev/null +++ b/examples/mesh/ip_internal_network/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/mesh/ip_internal_network/main/include/mesh_netif.h b/examples/mesh/ip_internal_network/main/include/mesh_netif.h new file mode 100644 index 000000000..98ddc0a47 --- /dev/null +++ b/examples/mesh/ip_internal_network/main/include/mesh_netif.h @@ -0,0 +1,77 @@ +/* Mesh IP Internal Networking Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#pragma once + +/******************************************************* + * Macros + *******************************************************/ +#define MAC_ADDR_LEN (6u) +#define MAC_ADDR_EQUAL(a, b) (0 == memcmp(a, b, MAC_ADDR_LEN)) + +/******************************************************* + * Type Definitions + *******************************************************/ +typedef void (mesh_raw_recv_cb_t)(mesh_addr_t *from, mesh_data_t *data); + +/******************************************************* + * Function Declarations + *******************************************************/ + +/** + * @brief Initializes netifs in a default way before knowing if we are going to be a root + * + * @param cb callback receive function for mesh raw packets + * + * @return ESP_OK on success + */ +esp_err_t mesh_netifs_init(mesh_raw_recv_cb_t *cb); + +/** + * @brief Destroy the netifs and related structures + * + * @return ESP_OK on success + */ +esp_err_t mesh_netifs_destroy(void); + +/** + * @brief Start the mesh netifs based on the configuration (root/node) + * + * @return ESP_OK on success + */ +esp_err_t mesh_netifs_start(bool is_root); + +/** + * @brief Stop the netifs and reset to the default mode + * + * @return ESP_OK on success + */ +esp_err_t mesh_netifs_stop(void); + +/** + * @brief Start the netif for root AP + * + * Note: The AP netif needs to be started separately after root received + * an IP address from the router so the DNS address could be used for dhcps + * + * @param is_root must be true, ignored otherwise + * @param dns_addr DNS address to use in DHCP server running on roots AP + * + * @return ESP_OK on success + */ +esp_err_t mesh_netif_start_root_ap(bool is_root, uint32_t dns_addr); + +/** + * @brief Returns MAC address of the station interface + * + * Used mainly for checking node addresses of the peers in routing table + * to avoid sending data to oneself + * + * @return Pointer to MAC address + */ +uint8_t* mesh_netif_get_station_mac(void); diff --git a/examples/mesh/ip_internal_network/main/mesh_main.c b/examples/mesh/ip_internal_network/main/mesh_main.c new file mode 100644 index 000000000..6282c1aa8 --- /dev/null +++ b/examples/mesh/ip_internal_network/main/mesh_main.c @@ -0,0 +1,426 @@ +/* Mesh Internal Communication Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "esp_wifi.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_mesh.h" +#include "nvs_flash.h" +#include "mesh_netif.h" +#include "driver/gpio.h" +#include "freertos/semphr.h" + +/******************************************************* + * Macros + *******************************************************/ +#define EXAMPLE_BUTTON_GPIO 0 + +// commands for internal mesh communication: +// , where CMD is one character, payload is variable dep. on command +#define CMD_KEYPRESSED 0x55 +// CMD_KEYPRESSED: payload is always 6 bytes identifying address of node sending keypress event +#define CMD_ROUTE_TABLE 0x56 +// CMD_KEYPRESSED: payload is a multiple of 6 listing addresses in a routing table +/******************************************************* + * Constants + *******************************************************/ +static const char *MESH_TAG = "mesh_main"; +static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x76}; + +/******************************************************* + * Variable Definitions + *******************************************************/ +static bool is_running = true; +static mesh_addr_t mesh_parent_addr; +static int mesh_layer = -1; +static esp_ip4_addr_t s_current_ip; +static mesh_addr_t s_route_table[CONFIG_MESH_ROUTE_TABLE_SIZE]; +static int s_route_table_size = 0; +static SemaphoreHandle_t s_route_table_lock = NULL; +static uint8_t s_mesh_tx_payload[CONFIG_MESH_ROUTE_TABLE_SIZE*6+1]; + + +/******************************************************* + * Function Declarations + *******************************************************/ +// interaction with public mqtt broker +void mqtt_app_start(void); +void mqtt_app_publish(char* topic, char *publish_string); + +/******************************************************* + * Function Definitions + *******************************************************/ + +static void initialise_button(void) +{ + gpio_config_t io_conf = {0}; + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.pin_bit_mask = BIT64(EXAMPLE_BUTTON_GPIO); + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = 1; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); +} + +void static recv_cb(mesh_addr_t *from, mesh_data_t *data) +{ + if (data->data[0] == CMD_ROUTE_TABLE) { + int size = data->size - 1; + if (s_route_table_lock == NULL || size%6 != 0) { + ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unexpected size"); + return; + } + xSemaphoreTake(s_route_table_lock, portMAX_DELAY); + s_route_table_size = size / 6; + for (int i=0; i < s_route_table_size; ++i) { + ESP_LOGI(MESH_TAG, "Received Routing table [%d] " + MACSTR, i, MAC2STR(data->data + 6*i + 1)); + } + memcpy(&s_route_table, data->data + 1, size); + xSemaphoreGive(s_route_table_lock); + } else if (data->data[0] == CMD_KEYPRESSED) { + if (data->size != 7) { + ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unexpected size"); + return; + } + ESP_LOGW(MESH_TAG, "Keypressed detected on node: " + MACSTR, MAC2STR(data->data + 1)); + } else { + ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unknown command"); + } +} + +static void check_button(void* args) +{ + static bool old_level = true; + bool new_level; + bool run_check_button = true; + initialise_button(); + while (run_check_button) { + new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO); + if (!new_level && old_level) { + if (s_route_table_size && !esp_mesh_is_root()) { + ESP_LOGW(MESH_TAG, "Key pressed!"); + mesh_data_t data; + uint8_t *my_mac = mesh_netif_get_station_mac(); + uint8_t data_to_send[6+1] = { CMD_KEYPRESSED, }; + esp_err_t err; + char print[6*3+1]; // MAC addr size + terminator + memcpy(data_to_send + 1, my_mac, 6); + data.size = 7; + data.proto = MESH_PROTO_BIN; + data.tos = MESH_TOS_P2P; + data.data = data_to_send; + snprintf(print, sizeof(print),MACSTR, MAC2STR(my_mac)); + mqtt_app_publish("/topic/ip_mesh/key_pressed", print); + xSemaphoreTake(s_route_table_lock, portMAX_DELAY); + for (int i = 0; i < s_route_table_size; i++) { + if (MAC_ADDR_EQUAL(s_route_table[i].addr, my_mac)) { + continue; + } + err = esp_mesh_send(&s_route_table[i], &data, MESH_DATA_P2P, NULL, 0); + ESP_LOGI(MESH_TAG, "Sending to [%d] " + MACSTR ": sent with err code: %d", i, MAC2STR(s_route_table[i].addr), err); + } + xSemaphoreGive(s_route_table_lock); + } + } + old_level = new_level; + vTaskDelay(50 / portTICK_PERIOD_MS); + } + vTaskDelete(NULL); + +} + + +void esp_mesh_mqtt_task(void *arg) +{ + is_running = true; + char *print; + mesh_data_t data; + esp_err_t err; + mqtt_app_start(); + while (is_running) { + asprintf(&print, "layer:%d IP:" IPSTR, esp_mesh_get_layer(), IP2STR(&s_current_ip)); + ESP_LOGI(MESH_TAG, "Tried to publish %s", print); + mqtt_app_publish("/topic/ip_mesh", print); + free(print); + if (esp_mesh_is_root()) { + esp_mesh_get_routing_table((mesh_addr_t *) &s_route_table, + CONFIG_MESH_ROUTE_TABLE_SIZE * 6, &s_route_table_size); + data.size = s_route_table_size * 6 + 1; + data.proto = MESH_PROTO_BIN; + data.tos = MESH_TOS_P2P; + s_mesh_tx_payload[0] = CMD_ROUTE_TABLE; + memcpy(s_mesh_tx_payload + 1, s_route_table, s_route_table_size*6); + data.data = s_mesh_tx_payload; + for (int i = 0; i < s_route_table_size; i++) { + err = esp_mesh_send(&s_route_table[i], &data, MESH_DATA_P2P, NULL, 0); + ESP_LOGI(MESH_TAG, "Sending routing table to [%d] " + MACSTR ": sent with err code: %d", i, MAC2STR(s_route_table[i].addr), err); + } + } + vTaskDelay(2 * 1000 / portTICK_RATE_MS); + } + vTaskDelete(NULL); +} + +esp_err_t esp_mesh_comm_mqtt_task_start(void) +{ + static bool is_comm_mqtt_task_started = false; + + s_route_table_lock = xSemaphoreCreateMutex(); + + if (!is_comm_mqtt_task_started) { + xTaskCreate(esp_mesh_mqtt_task, "mqtt task", 3072, NULL, 5, NULL); + xTaskCreate(check_button, "check button task", 3072, NULL, 5, NULL); + is_comm_mqtt_task_started = true; + } + return ESP_OK; +} + +void mesh_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + mesh_addr_t id = {0,}; + static uint8_t last_layer = 0; + + switch (event_id) { + case MESH_EVENT_STARTED: { + esp_mesh_get_id(&id); + ESP_LOGI(MESH_TAG, "ID:"MACSTR"", MAC2STR(id.addr)); + mesh_layer = esp_mesh_get_layer(); + } + break; + case MESH_EVENT_STOPPED: { + ESP_LOGI(MESH_TAG, ""); + mesh_layer = esp_mesh_get_layer(); + } + break; + case MESH_EVENT_CHILD_CONNECTED: { + mesh_event_child_connected_t *child_connected = (mesh_event_child_connected_t *)event_data; + ESP_LOGI(MESH_TAG, "aid:%d, "MACSTR"", + child_connected->aid, + MAC2STR(child_connected->mac)); + } + break; + case MESH_EVENT_CHILD_DISCONNECTED: { + mesh_event_child_disconnected_t *child_disconnected = (mesh_event_child_disconnected_t *)event_data; + ESP_LOGI(MESH_TAG, "aid:%d, "MACSTR"", + child_disconnected->aid, + MAC2STR(child_disconnected->mac)); + } + break; + case MESH_EVENT_ROUTING_TABLE_ADD: { + mesh_event_routing_table_change_t *routing_table = (mesh_event_routing_table_change_t *)event_data; + ESP_LOGW(MESH_TAG, "add %d, new:%d", + routing_table->rt_size_change, + routing_table->rt_size_new); + } + break; + case MESH_EVENT_ROUTING_TABLE_REMOVE: { + mesh_event_routing_table_change_t *routing_table = (mesh_event_routing_table_change_t *)event_data; + ESP_LOGW(MESH_TAG, "remove %d, new:%d", + routing_table->rt_size_change, + routing_table->rt_size_new); + } + break; + case MESH_EVENT_NO_PARENT_FOUND: { + mesh_event_no_parent_found_t *no_parent = (mesh_event_no_parent_found_t *)event_data; + ESP_LOGI(MESH_TAG, "scan times:%d", + no_parent->scan_times); + } + /* TODO handler for the failure */ + break; + case MESH_EVENT_PARENT_CONNECTED: { + mesh_event_connected_t *connected = (mesh_event_connected_t *)event_data; + esp_mesh_get_id(&id); + mesh_layer = connected->self_layer; + memcpy(&mesh_parent_addr.addr, connected->connected.bssid, 6); + ESP_LOGI(MESH_TAG, + "layer:%d-->%d, parent:"MACSTR"%s, ID:"MACSTR"", + last_layer, mesh_layer, MAC2STR(mesh_parent_addr.addr), + esp_mesh_is_root() ? "" : + (mesh_layer == 2) ? "" : "", MAC2STR(id.addr)); + last_layer = mesh_layer; + mesh_netifs_start(esp_mesh_is_root()); + } + break; + case MESH_EVENT_PARENT_DISCONNECTED: { + mesh_event_disconnected_t *disconnected = (mesh_event_disconnected_t *)event_data; + ESP_LOGI(MESH_TAG, + "reason:%d", + disconnected->reason); + mesh_layer = esp_mesh_get_layer(); + mesh_netifs_stop(); + } + break; + case MESH_EVENT_LAYER_CHANGE: { + mesh_event_layer_change_t *layer_change = (mesh_event_layer_change_t *)event_data; + mesh_layer = layer_change->new_layer; + ESP_LOGI(MESH_TAG, "layer:%d-->%d%s", + last_layer, mesh_layer, + esp_mesh_is_root() ? "" : + (mesh_layer == 2) ? "" : ""); + last_layer = mesh_layer; + } + break; + case MESH_EVENT_ROOT_ADDRESS: { + mesh_event_root_address_t *root_addr = (mesh_event_root_address_t *)event_data; + ESP_LOGI(MESH_TAG, "root address:"MACSTR"", + MAC2STR(root_addr->addr)); + } + break; + case MESH_EVENT_VOTE_STARTED: { + mesh_event_vote_started_t *vote_started = (mesh_event_vote_started_t *)event_data; + ESP_LOGI(MESH_TAG, + "attempts:%d, reason:%d, rc_addr:"MACSTR"", + vote_started->attempts, + vote_started->reason, + MAC2STR(vote_started->rc_addr.addr)); + } + break; + case MESH_EVENT_VOTE_STOPPED: { + ESP_LOGI(MESH_TAG, ""); + break; + } + case MESH_EVENT_ROOT_SWITCH_REQ: { + mesh_event_root_switch_req_t *switch_req = (mesh_event_root_switch_req_t *)event_data; + ESP_LOGI(MESH_TAG, + "reason:%d, rc_addr:"MACSTR"", + switch_req->reason, + MAC2STR( switch_req->rc_addr.addr)); + } + break; + case MESH_EVENT_ROOT_SWITCH_ACK: { + /* new root */ + mesh_layer = esp_mesh_get_layer(); + esp_mesh_get_parent_bssid(&mesh_parent_addr); + ESP_LOGI(MESH_TAG, "layer:%d, parent:"MACSTR"", mesh_layer, MAC2STR(mesh_parent_addr.addr)); + } + break; + case MESH_EVENT_TODS_STATE: { + mesh_event_toDS_state_t *toDs_state = (mesh_event_toDS_state_t *)event_data; + ESP_LOGI(MESH_TAG, "state:%d", *toDs_state); + } + break; + case MESH_EVENT_ROOT_FIXED: { + mesh_event_root_fixed_t *root_fixed = (mesh_event_root_fixed_t *)event_data; + ESP_LOGI(MESH_TAG, "%s", + root_fixed->is_fixed ? "fixed" : "not fixed"); + } + break; + case MESH_EVENT_ROOT_ASKED_YIELD: { + mesh_event_root_conflict_t *root_conflict = (mesh_event_root_conflict_t *)event_data; + ESP_LOGI(MESH_TAG, + ""MACSTR", rssi:%d, capacity:%d", + MAC2STR(root_conflict->addr), + root_conflict->rssi, + root_conflict->capacity); + } + break; + case MESH_EVENT_CHANNEL_SWITCH: { + mesh_event_channel_switch_t *channel_switch = (mesh_event_channel_switch_t *)event_data; + ESP_LOGI(MESH_TAG, "new channel:%d", channel_switch->channel); + } + break; + case MESH_EVENT_SCAN_DONE: { + mesh_event_scan_done_t *scan_done = (mesh_event_scan_done_t *)event_data; + ESP_LOGI(MESH_TAG, "number:%d", + scan_done->number); + } + break; + case MESH_EVENT_NETWORK_STATE: { + mesh_event_network_state_t *network_state = (mesh_event_network_state_t *)event_data; + ESP_LOGI(MESH_TAG, "is_rootless:%d", + network_state->is_rootless); + } + break; + case MESH_EVENT_STOP_RECONNECTION: { + ESP_LOGI(MESH_TAG, ""); + } + break; + case MESH_EVENT_FIND_NETWORK: { + mesh_event_find_network_t *find_network = (mesh_event_find_network_t *)event_data; + ESP_LOGI(MESH_TAG, "new channel:%d, router BSSID:"MACSTR"", + find_network->channel, MAC2STR(find_network->router_bssid)); + } + break; + case MESH_EVENT_ROUTER_SWITCH: { + mesh_event_router_switch_t *router_switch = (mesh_event_router_switch_t *)event_data; + ESP_LOGI(MESH_TAG, "new router:%s, channel:%d, "MACSTR"", + router_switch->ssid, router_switch->channel, MAC2STR(router_switch->bssid)); + } + break; + default: + ESP_LOGI(MESH_TAG, "unknown id:%d", event_id); + break; + } +} + +void ip_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; + ESP_LOGI(MESH_TAG, "IP:" IPSTR, IP2STR(&event->ip_info.ip)); + s_current_ip.addr = event->ip_info.ip.addr; +#if !CONFIG_MESH_USE_GLOBAL_DNS_IP + esp_netif_t *netif = event->esp_netif; + esp_netif_dns_info_t dns; + ESP_ERROR_CHECK(esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns)); + mesh_netif_start_root_ap(esp_mesh_is_root(), dns.ip.u_addr.ip4.addr); +#endif + esp_mesh_comm_mqtt_task_start(); +} + + +void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + /* tcpip initialization */ + ESP_ERROR_CHECK(esp_netif_init()); + /* event initialization */ + ESP_ERROR_CHECK(esp_event_loop_create_default()); + /* crete network interfaces for mesh (only station instance saved for further manipulation, soft AP instance ignored */ + ESP_ERROR_CHECK(mesh_netifs_init(recv_cb)); + + /* wifi initialization */ + wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&config)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH)); + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); + ESP_ERROR_CHECK(esp_wifi_start()); + /* mesh initialization */ + ESP_ERROR_CHECK(esp_mesh_init()); + ESP_ERROR_CHECK(esp_event_handler_register(MESH_EVENT, ESP_EVENT_ANY_ID, &mesh_event_handler, NULL)); + ESP_ERROR_CHECK(esp_mesh_set_max_layer(CONFIG_MESH_MAX_LAYER)); + ESP_ERROR_CHECK(esp_mesh_set_vote_percentage(1)); + ESP_ERROR_CHECK(esp_mesh_set_ap_assoc_expire(10)); + mesh_cfg_t cfg = MESH_INIT_CONFIG_DEFAULT(); + /* mesh ID */ + memcpy((uint8_t *) &cfg.mesh_id, MESH_ID, 6); + /* router */ + cfg.channel = CONFIG_MESH_CHANNEL; + cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID); + memcpy((uint8_t *) &cfg.router.ssid, CONFIG_MESH_ROUTER_SSID, cfg.router.ssid_len); + memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD, + strlen(CONFIG_MESH_ROUTER_PASSWD)); + /* mesh softAP */ + ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE)); + cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS; + memcpy((uint8_t *) &cfg.mesh_ap.password, CONFIG_MESH_AP_PASSWD, + strlen(CONFIG_MESH_AP_PASSWD)); + ESP_ERROR_CHECK(esp_mesh_set_config(&cfg)); + /* mesh start */ + ESP_ERROR_CHECK(esp_mesh_start()); + ESP_LOGI(MESH_TAG, "mesh starts successfully, heap:%d, %s\n", esp_get_free_heap_size(), + esp_mesh_is_root_fixed() ? "root fixed" : "root not fixed"); +} diff --git a/examples/mesh/ip_internal_network/main/mesh_netif.c b/examples/mesh/ip_internal_network/main/mesh_netif.c new file mode 100644 index 000000000..d2efd7e56 --- /dev/null +++ b/examples/mesh/ip_internal_network/main/mesh_netif.c @@ -0,0 +1,480 @@ +/* Mesh IP Internal Communication Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include "esp_wifi.h" +#include "esp_netif.h" +#include "esp_log.h" +#include +#include "esp_mesh.h" +#include "lwip/lwip_napt.h" +#include "esp_wifi_netif.h" +#include "mesh_netif.h" + +/******************************************************* + * Macros + *******************************************************/ +#define RX_SIZE (1560) + +#if CONFIG_MESH_USE_GLOBAL_DNS_IP +#define DNS_IP_ADDR CONFIG_MESH_GLOBAL_DNS_IP +#endif + +/******************************************************* + * Type Definitions + *******************************************************/ +typedef struct mesh_netif_driver* mesh_netif_driver_t; + +typedef struct mesh_netif_driver { + esp_netif_driver_base_t base; + uint8_t sta_mac_addr[MAC_ADDR_LEN]; +}* mesh_netif_driver_t; + +/******************************************************* + * Constants + *******************************************************/ +static const char* TAG = "mesh_netif"; +const esp_netif_ip_info_t g_mesh_netif_subnet_ip = { // mesh subnet IP info + .ip = { .addr = ESP_IP4TOADDR( 10, 0, 0, 1) }, + .gw = { .addr = ESP_IP4TOADDR( 10, 0, 0, 1) }, + .netmask = { .addr = ESP_IP4TOADDR( 255, 255, 0, 0) }, +}; + +/******************************************************* + * Variable Definitions + *******************************************************/ +static esp_netif_t *netif_sta = NULL; +static esp_netif_t *netif_ap = NULL; +static bool receive_task_is_running = false; +static mesh_addr_t s_route_table[CONFIG_MESH_ROUTE_TABLE_SIZE] = { 0 }; +static mesh_raw_recv_cb_t *s_mesh_raw_recv_cb = NULL; + +/******************************************************* + * Function Definitions + *******************************************************/ +// setup DHCP server's DNS OFFER +// +static esp_err_t set_dhcps_dns(esp_netif_t *netif, uint32_t addr) +{ + esp_netif_dns_info_t dns; + dns.ip.u_addr.ip4.addr = addr; + dns.ip.type = IPADDR_TYPE_V4; + dhcps_offer_t dhcps_dns_value = OFFER_DNS; + ESP_ERROR_CHECK(esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value))); + ESP_ERROR_CHECK(esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns)); + return ESP_OK; +} + +// Receive task +// +static void receive_task(void *arg) +{ + esp_err_t err; + mesh_addr_t from; + int flag = 0; + mesh_data_t data; + static uint8_t rx_buf[RX_SIZE] = { 0, }; + + ESP_LOGD(TAG, "Receiving task started"); + while (receive_task_is_running) { + data.data = rx_buf; + data.size = RX_SIZE; + err = esp_mesh_recv(&from, &data, portMAX_DELAY, &flag, NULL, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Received with err code %d %s", err, esp_err_to_name(err)); + continue; + } + if (data.proto == MESH_PROTO_BIN && s_mesh_raw_recv_cb) { + s_mesh_raw_recv_cb(&from, &data); + } + if (esp_mesh_is_root()) { + if (data.proto == MESH_PROTO_AP) { + ESP_LOGD(TAG, "Root received: from: " MACSTR " to " MACSTR " size: %d", + MAC2STR((uint8_t*)data.data) ,MAC2STR((uint8_t*)(data.data+6)), data.size); + if (netif_ap) { + // actual receive to TCP/IP stack + esp_netif_receive(netif_ap, data.data, data.size, NULL); + } + } else if (data.proto == MESH_PROTO_STA) { + ESP_LOGE(TAG, "Root station Should never receive data from mesh!"); + } + } else { + if (data.proto == MESH_PROTO_AP) { + ESP_LOGD(TAG, "Node AP should never receive data from mesh"); + } else if (data.proto == MESH_PROTO_STA) { + ESP_LOGD(TAG, "Node received: from: " MACSTR " to " MACSTR " size: %d", + MAC2STR((uint8_t*)data.data) ,MAC2STR((uint8_t*)(data.data+6)), data.size); + if (netif_sta) { + // actual receive to TCP/IP stack + esp_netif_receive(netif_sta, data.data, data.size, NULL); + } + } + } + } + vTaskDelete(NULL); + +} + +// Free RX buffer (not used as the buffer is static) +// +static void mesh_free(void *h, void* buffer) +{ + free(buffer); +} + +// Transmit function variants +// +static esp_err_t mesh_netif_transmit_from_root_ap(void *h, void *buffer, size_t len) +{ + // Use only to transmit data from root AP to node's AP + static const uint8_t eth_broadcast[MAC_ADDR_LEN] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; + int route_table_size = 0; + mesh_netif_driver_t mesh_driver = h; + mesh_addr_t dest_addr; + mesh_data_t data; + ESP_LOGD(TAG, "Sending to node: " MACSTR ", size: %d" ,MAC2STR((uint8_t*)buffer), len); + memcpy(dest_addr.addr, buffer, MAC_ADDR_LEN); + data.data = buffer; + data.size = len; + data.proto = MESH_PROTO_STA; // sending from root AP -> Node's STA + data.tos = MESH_TOS_P2P; + if (MAC_ADDR_EQUAL(dest_addr.addr, eth_broadcast)) { + ESP_LOGD(TAG, "Broadcasting!"); + esp_mesh_get_routing_table((mesh_addr_t *) &s_route_table, + CONFIG_MESH_ROUTE_TABLE_SIZE * 6, &route_table_size); + for (int i = 0; i < route_table_size; i++) { + if (MAC_ADDR_EQUAL(s_route_table[i].addr, mesh_driver->sta_mac_addr)) { + ESP_LOGD(TAG, "That was me, skipping!"); + continue; + } + ESP_LOGD(TAG, "Broadcast: Sending to [%d] " MACSTR, i, MAC2STR(s_route_table[i].addr)); + esp_err_t err = esp_mesh_send(&s_route_table[i], &data, MESH_DATA_P2P, NULL, 0); + if (ESP_OK != err) { + ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err)); + } + } + } else { + // Standard P2P + esp_err_t err = esp_mesh_send(&dest_addr, &data, MESH_DATA_P2P, NULL, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err)); + return err; + } + } + return ESP_OK; +} + +// Construct and Destruct functions +// + +static esp_err_t mesh_netif_transmit_from_node_sta(void *h, void *buffer, size_t len) +{ + mesh_data_t data; + ESP_LOGD(TAG, "Sending to root, dest addr: " MACSTR ", size: %d" ,MAC2STR((uint8_t*)buffer), len); + data.data = buffer; + data.size = len; + data.proto = MESH_PROTO_AP; // Node's station transmits data to root's AP + data.tos = MESH_TOS_P2P; + esp_err_t err = esp_mesh_send(NULL, &data, MESH_DATA_TODS, NULL, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err)); + } + return err; +} + +static esp_err_t mesh_driver_start_root_ap(esp_netif_t * esp_netif, void * args) +{ + mesh_netif_driver_t driver = args; + driver->base.netif = esp_netif; + esp_netif_driver_ifconfig_t driver_ifconfig = { + .handle = driver, + .transmit = mesh_netif_transmit_from_root_ap, + .driver_free_rx_buffer = mesh_free + }; + + return esp_netif_set_driver_config(esp_netif, &driver_ifconfig); +} + +static esp_err_t mesh_driver_start_node_sta(esp_netif_t * esp_netif, void * args) +{ + mesh_netif_driver_t driver = args; + driver->base.netif = esp_netif; + esp_netif_driver_ifconfig_t driver_ifconfig = { + .handle = driver, + .transmit = mesh_netif_transmit_from_node_sta, + .driver_free_rx_buffer = mesh_free + }; + + return esp_netif_set_driver_config(esp_netif, &driver_ifconfig); +} + + +void mesh_delete_if_driver(mesh_netif_driver_t driver) +{ + // Stop the task once both drivers are removed + // receive_task_is_running = true; + free(driver); +} + +mesh_netif_driver_t mesh_create_if_driver(bool is_ap, bool is_root) +{ + mesh_netif_driver_t driver = calloc(1, sizeof(struct mesh_netif_driver)); + if (driver == NULL) { + ESP_LOGE(TAG, "No memory to create a wifi interface handle"); + return NULL; + } + if (is_ap && is_root) { + driver->base.post_attach = mesh_driver_start_root_ap; + } else if (!is_ap && !is_root) { + driver->base.post_attach = mesh_driver_start_node_sta; + } else { + return NULL; + } + + if (!receive_task_is_running) { + receive_task_is_running = true; + xTaskCreate(receive_task, "netif rx task", 3072, NULL, 5, NULL); + } + + // save station mac address to exclude it from routing-table on broadcast + esp_wifi_get_mac(ESP_IF_WIFI_STA, driver->sta_mac_addr); + + return driver; +} + +esp_err_t mesh_netifs_destroy(void) +{ + receive_task_is_running = false; + return ESP_OK; +} + +static void mesh_netif_init_station(void) +{ + // By default create a station that would connect to AP (expecting root to connect to external network) + esp_netif_config_t cfg_sta = ESP_NETIF_DEFAULT_WIFI_STA(); + netif_sta = esp_netif_new(&cfg_sta); + assert(netif_sta); + ESP_ERROR_CHECK(esp_netif_attach_wifi_station(netif_sta)); + ESP_ERROR_CHECK(esp_wifi_set_default_wifi_sta_handlers()); +} + + +// Init by default for both potential root and node +// +esp_err_t mesh_netifs_init(mesh_raw_recv_cb_t *cb) +{ + mesh_netif_init_station(); + s_mesh_raw_recv_cb = cb; + return ESP_OK; + +} + +/** + * @brief Starts AP esp-netif link over mesh (root's AP on mesh) + */ +static esp_err_t start_mesh_link_ap(void) +{ + uint8_t mac[MAC_ADDR_LEN]; + esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); + esp_netif_set_mac(netif_ap, mac); + esp_netif_action_start(netif_ap, NULL, 0, NULL); + return ESP_OK; +} + +/** + * @brief Starts station link over wifi (root node to the router) + */ +static esp_err_t start_wifi_link_sta(void) +{ + uint8_t mac[6]; + esp_wifi_get_mac(ESP_IF_WIFI_STA, mac); + esp_err_t ret; + void *driver = esp_netif_get_io_driver(netif_sta); + if ((ret = esp_wifi_register_if_rxcb(driver, esp_netif_receive, netif_sta)) != ESP_OK) { + ESP_LOGE(TAG, "esp_wifi_register_if_rxcb for if=%p failed with %d", driver, ret); + return ESP_FAIL; + } + esp_netif_set_mac(netif_sta, mac); + esp_netif_action_start(netif_sta, NULL, 0, NULL); + return ESP_OK; +} + +/** + * @brief Starts station link over mesh (node to root over mesh) + */ +static esp_err_t start_mesh_link_sta(void) +{ + uint8_t mac[MAC_ADDR_LEN]; + esp_wifi_get_mac(ESP_IF_WIFI_STA, mac); + esp_netif_set_mac(netif_sta, mac); + esp_netif_action_start(netif_sta, NULL, 0, NULL); + esp_netif_action_connected(netif_sta, NULL, 0, NULL); + return ESP_OK; +} + +/** + * @brief Creates esp-netif for AP interface over mesh + * + * @return Pointer to esp-netif instance + */ +static esp_netif_t* create_mesh_link_ap(void) +{ + esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP(); + base_cfg.if_desc = "mesh_link_ap"; + base_cfg.ip_info = &g_mesh_netif_subnet_ip; + + esp_netif_config_t cfg = { + .base = &base_cfg, + .driver = NULL, + .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP }; + esp_netif_t * netif = esp_netif_new(&cfg); + assert(netif); + return netif; +} + +/** + * @brief Creates esp-netif for station interface over mesh + * + * @note Interface needs to be started (later) using the above APIs + * based on the actual configuration root/node, + * since root connects normally over wifi + * + * @return Pointer to esp-netif instance + */ +static esp_netif_t* create_mesh_link_sta(void) +{ + esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); + base_cfg.if_desc = "mesh_link_sta"; + + esp_netif_config_t cfg = { + .base = &base_cfg, + .driver = NULL, + .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA }; + esp_netif_t * netif = esp_netif_new(&cfg); + assert(netif); + return netif; +} + +esp_err_t mesh_netif_start_root_ap(bool is_root, uint32_t addr) +{ + if (is_root) { + if (netif_ap) { + esp_netif_action_disconnected(netif_ap, NULL, 0, NULL); + mesh_delete_if_driver(esp_netif_get_io_driver(netif_ap)); + esp_netif_destroy(netif_ap); + netif_ap = NULL; + } + netif_ap = create_mesh_link_ap(); + mesh_netif_driver_t driver = mesh_create_if_driver(true, true); + if (driver == NULL) { + ESP_LOGE(TAG, "Failed to create wifi interface handle"); + return ESP_FAIL; + } + esp_netif_attach(netif_ap, driver); + set_dhcps_dns(netif_ap, addr); + start_mesh_link_ap(); + ip_napt_enable(g_mesh_netif_subnet_ip.ip.addr, 1); + } + return ESP_OK; +} + +esp_err_t mesh_netifs_start(bool is_root) +{ + if (is_root) { + // ROOT: need both sta should use standard wifi, AP mesh link netif + + // Root: Station + if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) { + ESP_LOGI(TAG, "Already wifi station, no need to do anything"); + } else if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "mesh_link_sta") == 0) { + esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); + mesh_delete_if_driver(esp_netif_get_io_driver(netif_sta)); + esp_netif_destroy(netif_sta); + mesh_netif_init_station(); + } else if (netif_sta == NULL) { + mesh_netif_init_station(); + } + + // Root: AP is initialized only if GLOBAL DNS configured + // (otherwise have to wait until the actual DNS record received from the router) +#if CONFIG_MESH_USE_GLOBAL_DNS_IP + mesh_netif_start_root_ap(true, htonl(DNS_IP_ADDR)); +#endif + + } else { + // NODE: create only STA in form of mesh link + if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "mesh_link_sta") == 0) { + ESP_LOGI(TAG, "Already mesh link station, no need to do anything"); + return ESP_OK; + } + if (netif_sta) { + esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); + // should remove the actual driver + if (strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) { + ESP_LOGI(TAG, "It was a wifi station removing stuff"); + esp_wifi_clear_default_wifi_driver_and_handlers(netif_sta); + } + esp_netif_destroy(netif_sta); + + } + netif_sta = create_mesh_link_sta(); + // now we create a mesh driver and attach it to the existing netif + mesh_netif_driver_t driver = mesh_create_if_driver(false, false); + if (driver == NULL) { + ESP_LOGE(TAG, "Failed to create wifi interface handle"); + return ESP_FAIL; + } + esp_netif_attach(netif_sta, driver); + start_mesh_link_sta(); + // If we have a AP on NODE -> stop and remove it! + if (netif_ap) { + esp_netif_action_disconnected(netif_ap, NULL, 0, NULL); + mesh_delete_if_driver(esp_netif_get_io_driver(netif_ap)); + esp_netif_destroy(netif_ap); + netif_ap = NULL; + } + + } + return ESP_OK; +} + +esp_err_t mesh_netifs_stop(void) +{ + if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "sta") == 0 && netif_ap == NULL) { + return ESP_OK; + } + + if (netif_sta) { + if (strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) { + esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); + esp_netif_action_stop(netif_sta, NULL, 0, NULL); + esp_wifi_clear_default_wifi_driver_and_handlers(netif_sta); + } else { + esp_netif_action_disconnected(netif_sta, NULL, 0, NULL); + mesh_delete_if_driver(esp_netif_get_io_driver(netif_sta)); + } + esp_netif_destroy(netif_sta); + netif_sta = NULL; + } + + if (netif_ap) { + esp_netif_action_disconnected(netif_ap, NULL, 0, NULL); + mesh_delete_if_driver(esp_netif_get_io_driver(netif_ap)); + esp_netif_destroy(netif_ap); + netif_ap = NULL; + } + // reserve the default (STA gets ready to become root) + mesh_netif_init_station(); + start_wifi_link_sta(); + return ESP_OK; +} + +uint8_t* mesh_netif_get_station_mac(void) +{ + mesh_netif_driver_t mesh = esp_netif_get_io_driver(netif_sta); + return mesh->sta_mac_addr; +} diff --git a/examples/mesh/ip_internal_network/main/mqtt_app.c b/examples/mesh/ip_internal_network/main/mqtt_app.c new file mode 100644 index 000000000..abc8ac95c --- /dev/null +++ b/examples/mesh/ip_internal_network/main/mqtt_app.c @@ -0,0 +1,80 @@ +/* Mesh IP Internal Networking Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "esp_log.h" +#include "esp_system.h" +#include "esp_netif.h" +#include "esp_tls.h" + +#include "mqtt_client.h" + +static const char *TAG = "mesh_mqtt"; +static esp_mqtt_client_handle_t s_client = NULL; + +static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) +{ + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + if (esp_mqtt_client_subscribe(s_client, "/topic/ip_mesh/key_pressed", 0) < 0) { + // Disconnect to retry the subscribe after auto-reconnect timeout + esp_mqtt_client_disconnect(s_client); + } + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic); + ESP_LOGI(TAG, "DATA=%.*s", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + default: + ESP_LOGI(TAG, "Other event id:%d", event->event_id); + break; + } + return ESP_OK; +} + +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { + ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id); + mqtt_event_handler_cb(event_data); +} + +void mqtt_app_publish(char* topic, char *publish_string) +{ + if (s_client) { + int msg_id = esp_mqtt_client_publish(s_client, topic, publish_string, 0, 1, 0); + ESP_LOGI(TAG, "sent publish returned msg_id=%d", msg_id); + } +} + +void mqtt_app_start(void) +{ + esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtt://mqtt.eclipse.org", + }; + + s_client = esp_mqtt_client_init(&mqtt_cfg); + esp_mqtt_client_register_event(s_client, ESP_EVENT_ANY_ID, mqtt_event_handler, s_client); + esp_mqtt_client_start(s_client); +} diff --git a/examples/mesh/ip_internal_network/partitions.csv b/examples/mesh/ip_internal_network/partitions.csv new file mode 100644 index 000000000..d01414b8a --- /dev/null +++ b/examples/mesh/ip_internal_network/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1200000, diff --git a/examples/mesh/ip_internal_network/sdkconfig.defaults b/examples/mesh/ip_internal_network/sdkconfig.defaults new file mode 100644 index 000000000..27b8cb3cc --- /dev/null +++ b/examples/mesh/ip_internal_network/sdkconfig.defaults @@ -0,0 +1,7 @@ + +CONFIG_LWIP_L2_TO_L3_COPY=y +CONFIG_LWIP_IP_FORWARD=y +CONFIG_LWIP_IPV4_NAPT=y +CONFIG_LWIP_TCP_MSS=624 +CONFIG_LWIP_TCP_OVERSIZE_MSS=y +