From 00d7c408489a6cca973376b637c71d786724d9b9 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 26 Apr 2023 19:10:16 +0200 Subject: [PATCH] feat(examples): Add multiple netif demo: eth+wifi+PPP --- .../esp_netif/multiple_netifs/CMakeLists.txt | 6 + examples/esp_netif/multiple_netifs/README.md | 25 +++ .../multiple_netifs/main/CMakeLists.txt | 2 + .../multiple_netifs/main/Kconfig.projbuild | 42 ++++ .../multiple_netifs/main/check_connection.c | 113 ++++++++++ .../multiple_netifs/main/ethernet_netif.c | 137 ++++++++++++ .../multiple_netifs/main/idf_component.yml | 4 + .../multiple_netifs/main/iface_info.h | 26 +++ .../multiple_netifs/main/multi_netif_main.c | 129 +++++++++++ .../multiple_netifs/main/ppp_connect.c | 210 ++++++++++++++++++ .../multiple_netifs/main/wifi_connect.c | 128 +++++++++++ .../multiple_netifs/sdkconfig.defaults | 2 + 12 files changed, 824 insertions(+) create mode 100644 examples/esp_netif/multiple_netifs/CMakeLists.txt create mode 100644 examples/esp_netif/multiple_netifs/README.md create mode 100644 examples/esp_netif/multiple_netifs/main/CMakeLists.txt create mode 100644 examples/esp_netif/multiple_netifs/main/Kconfig.projbuild create mode 100644 examples/esp_netif/multiple_netifs/main/check_connection.c create mode 100644 examples/esp_netif/multiple_netifs/main/ethernet_netif.c create mode 100644 examples/esp_netif/multiple_netifs/main/idf_component.yml create mode 100644 examples/esp_netif/multiple_netifs/main/iface_info.h create mode 100644 examples/esp_netif/multiple_netifs/main/multi_netif_main.c create mode 100644 examples/esp_netif/multiple_netifs/main/ppp_connect.c create mode 100644 examples/esp_netif/multiple_netifs/main/wifi_connect.c create mode 100644 examples/esp_netif/multiple_netifs/sdkconfig.defaults diff --git a/examples/esp_netif/multiple_netifs/CMakeLists.txt b/examples/esp_netif/multiple_netifs/CMakeLists.txt new file mode 100644 index 000000000..3d86dc916 --- /dev/null +++ b/examples/esp_netif/multiple_netifs/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following 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.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(multiple_netifs) diff --git a/examples/esp_netif/multiple_netifs/README.md b/examples/esp_netif/multiple_netifs/README.md new file mode 100644 index 000000000..09ba1a1b9 --- /dev/null +++ b/examples/esp_netif/multiple_netifs/README.md @@ -0,0 +1,25 @@ +| Supported Targets | ESP32 | +| ----------------- | ----- | + +# Multiple Interface example + +## Overview + +This example demonstrates working with multiple different interfaces with different priorities. It creates these interfaces and tries to connect: +* WiFi Station +* Ethernet using ESP32 internal ethernet driver +* PPPoS over cellular modem + +## How to use example + +* Set the priorities and the host name for the example to ICMP ping. +* The example will initialize all interfaces +* The example will start looping and checking connectivity to the host name + * It prints the default interface and ping output + * It tries to reconfigure DNS server if host name resolution fails + * It tries to manually change the default interface if connection fails + +### Hardware Required + +To run this example, it's recommended that you have an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html). +You would also need a modem connected to the board using UART interface. diff --git a/examples/esp_netif/multiple_netifs/main/CMakeLists.txt b/examples/esp_netif/multiple_netifs/main/CMakeLists.txt new file mode 100644 index 000000000..1d2d3000b --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS ethernet_netif.c multi_netif_main.c wifi_connect.c check_connection.c ppp_connect.c + INCLUDE_DIRS ".") diff --git a/examples/esp_netif/multiple_netifs/main/Kconfig.projbuild b/examples/esp_netif/multiple_netifs/main/Kconfig.projbuild new file mode 100644 index 000000000..7ad68cf71 --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/Kconfig.projbuild @@ -0,0 +1,42 @@ +menu "Connection Configuration" + + config ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + config ESP_MAXIMUM_RETRY + int "Maximum retry" + default 0 + help + Set the Maximum retry to avoid station reconnecting. Set to 0 to keep retrying indefinitely. + + config EXAMPLE_MODEM_PPP_APN + string "Set MODEM APN" + default "default.apn" + help + Set APN (Access Point Name), a logical name to choose data network + + config EXAMPLE_MODEM_UART_TX_PIN + int "TXD Pin Number" + default 15 + range 0 31 + help + Pin number of UART TX. + + config EXAMPLE_MODEM_UART_RX_PIN + int "RXD Pin Number" + default 14 + range 0 31 + help + Pin number of UART RX. + + +endmenu diff --git a/examples/esp_netif/multiple_netifs/main/check_connection.c b/examples/esp_netif/multiple_netifs/main/check_connection.c new file mode 100644 index 000000000..64c2e21ca --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/check_connection.c @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Checks network connectivity by pinging configured host + + 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 "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "ping/ping_sock.h" +#include "lwip/netdb.h" +#include "esp_log.h" + +#define SUCCESS (1) +#define FAIL (2) + +static const char *TAG = "check_connection"; + +static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) +{ + uint8_t ttl; + uint16_t seqno; + uint32_t elapsed_time, recv_len; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); + ESP_LOGI(TAG, "%" PRIu32 " bytes from %s icmp_seq=%d ttl=%d time=%" PRIu32 " ms", + recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time); +} + +static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) +{ + uint16_t seqno; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + ESP_LOGE(TAG, "From %s icmp_seq=%d timeout", inet_ntoa(target_addr.u_addr.ip4), seqno); +} + +static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) +{ + EventGroupHandle_t events = args; + ip_addr_t target_addr; + uint32_t transmitted; + uint32_t received; + uint32_t total_time_ms; + esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted)); + esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); + uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100); + if (IP_IS_V4(&target_addr)) { + ESP_LOGI(TAG, "\n--- %s ping statistics ---", inet_ntoa(*ip_2_ip4(&target_addr))); + } else { + ESP_LOGI(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr))); + } + ESP_LOGI(TAG, "%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %"PRIu32"ms\n", + transmitted, received, loss, total_time_ms); + xEventGroupSetBits(events, received == 0 ? FAIL : SUCCESS); +} + +esp_err_t check_connectivity(const char *host) +{ + EventGroupHandle_t events = xEventGroupCreate(); + + ip_addr_t target_addr; + struct addrinfo hint; + struct addrinfo *res = NULL; + memset(&hint, 0, sizeof(hint)); + memset(&target_addr, 0, sizeof(target_addr)); + /* convert domain name to IP address */ + if (getaddrinfo(host, NULL, &hint, &res) != 0) { + ESP_LOGE(TAG, "ping: unknown host %s\n", host); + return ESP_ERR_NOT_FOUND; + } + if (res->ai_family == AF_INET) { + struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr; + inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4); + } else { + struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr; + inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6); + } + freeaddrinfo(res); + + esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG(); + config.target_addr = target_addr; + + esp_ping_callbacks_t cbs = { + .on_ping_success = cmd_ping_on_ping_success, + .on_ping_timeout = cmd_ping_on_ping_timeout, + .on_ping_end = cmd_ping_on_ping_end, + .cb_args = events + }; + esp_ping_handle_t ping; + esp_ping_new_session(&config, &cbs, &ping); + esp_ping_start(ping); + vTaskDelay(pdMS_TO_TICKS(config.count * config.interval_ms)); + EventBits_t bits = xEventGroupWaitBits(events, FAIL | SUCCESS, pdFALSE, pdFALSE, portMAX_DELAY); + + vEventGroupDelete(events); + esp_ping_delete_session(ping); + + return bits == SUCCESS ? ESP_OK : ESP_FAIL; +} diff --git a/examples/esp_netif/multiple_netifs/main/ethernet_netif.c b/examples/esp_netif/multiple_netifs/main/ethernet_netif.c new file mode 100644 index 000000000..2b6276e2a --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/ethernet_netif.c @@ -0,0 +1,137 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Ethernet Basic Initialization + + 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_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "iface_info.h" + +static const char *TAG = "ethernet_connect"; + +struct eth_info_t { + iface_info_t parent; + esp_eth_handle_t eth_handle; + esp_eth_netif_glue_handle_t glue; + esp_eth_mac_t *mac; + esp_eth_phy_t *phy; +}; + +static void eth_event_handler(void *args, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + uint8_t mac_addr[6] = {0}; + /* we can get the ethernet driver handle from event data */ + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + struct eth_info_t *eth_info = args; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + eth_info->parent.connected = true; + break; + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + eth_info->parent.connected = false; + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + +static void got_ip_event_handler(void *args, 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; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + struct eth_info_t *eth_info = args; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "MASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "GW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + + for (int i = 0; i < 2; ++i) { + esp_netif_get_dns_info(eth_info->parent.netif, i, ð_info->parent.dns[i]); + ESP_LOGI(TAG, "DNS %i:" IPSTR, i, IP2STR(ð_info->parent.dns[i].ip.u_addr.ip4)); + } + ESP_LOGI(TAG, "~~~~~~~~~~~"); +} + + +static void teardown_eth(iface_info_t *info) +{ + struct eth_info_t *eth_info = __containerof(info, struct eth_info_t, parent); + + esp_eth_stop(eth_info->eth_handle); + esp_eth_del_netif_glue(eth_info->glue); + esp_eth_driver_uninstall(eth_info->eth_handle); + eth_info->phy->del(eth_info->phy); + eth_info->mac->del(eth_info->mac); + esp_netif_destroy(eth_info->parent.netif); + free(eth_info); +} + +iface_info_t *setup_eth(int prio) +{ + struct eth_info_t *eth_info = malloc(sizeof(struct eth_info_t)); + assert(eth_info); + eth_info->parent.teardown = teardown_eth; + eth_info->parent.name = "Ethernet"; + + // Init common MAC and PHY configs to default + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + + // Use internal ESP32's ethernet + eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); + eth_info->mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); + eth_info->phy = esp_eth_phy_new_ip101(&phy_config); + // Init Ethernet driver to default and install it + esp_eth_config_t config = ETH_DEFAULT_CONFIG(eth_info->mac, eth_info->phy); + ESP_ERROR_CHECK(esp_eth_driver_install(&config, ð_info->eth_handle)); + + // Create an instance of esp-netif for Ethernet + esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_ETH(); + base_netif_cfg.route_prio = prio; + esp_netif_config_t cfg = { + .base = &base_netif_cfg, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH, + }; + eth_info->parent.netif = esp_netif_new(&cfg); + eth_info->glue = esp_eth_new_netif_glue(eth_info->eth_handle); + // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_netif_attach(eth_info->parent.netif, eth_info->glue )); + + // Register user defined event handers + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_info)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_info)); + + // Start Ethernet driver state machine + ESP_ERROR_CHECK(esp_eth_start(eth_info->eth_handle)); + + return ð_info->parent; +} diff --git a/examples/esp_netif/multiple_netifs/main/idf_component.yml b/examples/esp_netif/multiple_netifs/main/idf_component.yml new file mode 100644 index 000000000..186d064b4 --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/idf_component.yml @@ -0,0 +1,4 @@ +dependencies: + espressif/esp_modem: + version: "^1.0.0" + override_path: "../../../../components/esp_modem" diff --git a/examples/esp_netif/multiple_netifs/main/iface_info.h b/examples/esp_netif/multiple_netifs/main/iface_info.h new file mode 100644 index 000000000..d3457eb9e --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/iface_info.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Common interface info + + 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 +#include "esp_netif.h" + +struct iface_info_t { + esp_netif_t *netif; + esp_netif_dns_info_t dns[2]; + void (*teardown)(struct iface_info_t *); + const char *name; + bool connected; +}; + +typedef struct iface_info_t iface_info_t; diff --git a/examples/esp_netif/multiple_netifs/main/multi_netif_main.c b/examples/esp_netif/multiple_netifs/main/multi_netif_main.c new file mode 100644 index 000000000..32be8b91b --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/multi_netif_main.c @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* Multiple Network Interface 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 +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_netif.h" +#include "esp_event.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "nvs_flash.h" +#include "iface_info.h" + +iface_info_t *setup_eth(int prio); +iface_info_t *setup_wifi(int prio); +iface_info_t *setup_ppp(int prio); +esp_err_t check_connectivity(const char *host); + +#define HOST "www.espressif.com" +#define ETH_PRIO 200 +#define WIFI_PRIO 100 +#define PPP_PRIO 50 + +static const char *TAG = "app_main"; + +static ssize_t get_default(iface_info_t *list[], size_t num) +{ + esp_netif_t *default_netif = esp_netif_get_default_netif(); + if (default_netif == NULL) { + ESP_LOGE(TAG, "default netif is NULL!"); + return -1; + } + ESP_LOGI(TAG, "Default netif: %s", esp_netif_get_desc(default_netif)); + + for (int i = 0; i < num; ++i) { + if (list[i] && list[i]->netif == default_netif) { + ESP_LOGI(TAG, "Default interface: %s", list[i]->name); + return i; + } + } + // not found + return -2; +} + +void app_main(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_netif_init()); + // Create default event loop that running in background + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + // all interfaces + iface_info_t *ifaces[] = { + setup_eth(ETH_PRIO), + setup_wifi(WIFI_PRIO), + setup_ppp(PPP_PRIO), + }; + size_t num_of_ifaces = sizeof(ifaces) / sizeof(ifaces[0]); + + while (true) { + dns_clear_cache(); + vTaskDelay(pdMS_TO_TICKS(2000)); + ssize_t i = get_default(ifaces, num_of_ifaces); + if (i == -1) { // default netif is NULL, probably all interfaces are down -> retry + continue; + } else if (i < 0) { + break; // some other error, exit + } + + esp_err_t connect_status = check_connectivity(HOST); + if (connect_status == ESP_OK) { + // connectivity ok + continue; + } + if (connect_status == ESP_ERR_NOT_FOUND) { + // set the default DNS info to global DNS server list + for (int j = 0; j < 2; ++j) { + esp_netif_dns_info_t dns_info; + esp_netif_get_dns_info(ifaces[i]->netif, j, &dns_info); + if (memcmp(&dns_info.ip, &ifaces[i]->dns[j].ip, sizeof(esp_ip_addr_t)) == 0) { + connect_status = ESP_FAIL; + } else { + esp_netif_set_dns_info(ifaces[i]->netif, j, &ifaces[i]->dns[j]); + ESP_LOGI(TAG, "Reconfigured DNS%i=" IPSTR, j, IP2STR(&ifaces[i]->dns[j].ip.u_addr.ip4)); + } + } + } + if (connect_status == ESP_FAIL) { + ESP_LOGE(TAG, "No connection via the default netif!"); + // try to switch interfaces manually + // WARNING: Once we set_default_netif() manually, we disable the automatic prio-routing + int next = (i + 1) % num_of_ifaces; + while (ifaces[i] != ifaces[next]) { + if (ifaces[next]->connected) { + ESP_LOGE(TAG, "Trying another interface: %s", ifaces[next]->name); + esp_netif_set_default_netif(ifaces[next]->netif); + break; + } + ++next; + next = next % num_of_ifaces; + } + } + } + + ESP_LOGI(TAG, "Stop and cleanup all interfaces"); + for (int i = 0; i < num_of_ifaces; ++i) { + if (ifaces[i]) { + ifaces[i]->teardown(ifaces[i]); + } + } +} diff --git a/examples/esp_netif/multiple_netifs/main/ppp_connect.c b/examples/esp_netif/multiple_netifs/main/ppp_connect.c new file mode 100644 index 000000000..e1f4c1b43 --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/ppp_connect.c @@ -0,0 +1,210 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* PPPoS Initialization + + 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 "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_netif.h" +#include "esp_netif_ppp.h" +#include "mqtt_client.h" +#include "esp_modem_api.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "iface_info.h" + +struct ppp_info_t { + iface_info_t parent; + esp_modem_dce_t *dce; + bool stop_task; +}; + +static const int CONNECT_BIT = BIT0; +static const char *TAG = "pppos_connect"; +static EventGroupHandle_t event_group = NULL; + + +static void on_ppp_changed(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + ESP_LOGI(TAG, "PPP state changed event %" PRIu32, event_id); + if (event_id == NETIF_PPP_ERRORUSER) { + struct ppp_info_t *ppp_info = arg; + esp_netif_t *netif = event_data; + ESP_LOGI(TAG, "User interrupted event from netif:%p", netif); + ppp_info->parent.connected = false; + } +} + +static void on_ip_event(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + ESP_LOGD(TAG, "IP event! %" PRIu32, event_id); + struct ppp_info_t *ppp_info = arg; + + if (event_id == IP_EVENT_PPP_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + + ESP_LOGI(TAG, "Modem Connect to PPP Server"); + ESP_LOGI(TAG, "~~~~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip)); + ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask)); + ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw)); + for (int i = 0; i < 2; ++i) { + esp_netif_get_dns_info(ppp_info->parent.netif, i, &ppp_info->parent.dns[i]); + ESP_LOGI(TAG, "DNS %i:" IPSTR, i, IP2STR(&ppp_info->parent.dns[i].ip.u_addr.ip4)); + } + ESP_LOGI(TAG, "~~~~~~~~~~~~~~"); + xEventGroupSetBits(event_group, CONNECT_BIT); + ppp_info->parent.connected = true; + + ESP_LOGI(TAG, "GOT ip event!!!"); + } else if (event_id == IP_EVENT_PPP_LOST_IP) { + ESP_LOGI(TAG, "Modem Disconnect from PPP Server"); + ppp_info->parent.connected = false; + } else if (event_id == IP_EVENT_GOT_IP6) { + ESP_LOGI(TAG, "GOT IPv6 event!"); + ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; + ESP_LOGI(TAG, "Got IPv6 address " IPV6STR, IPV62STR(event->ip6_info.ip)); + } +} + + +static void teardown_ppp(iface_info_t *info) +{ + struct ppp_info_t *ppp_info = __containerof(info, struct ppp_info_t, parent); + + esp_netif_action_disconnected(ppp_info->parent.netif, 0, 0, 0); + esp_netif_action_stop(ppp_info->parent.netif, 0, 0, 0); + esp_err_t err = esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_COMMAND); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err); + return; + } + esp_modem_destroy(ppp_info->dce); + vEventGroupDelete(event_group); + ppp_info->stop_task = true; + free(info); +} + +static void ppp_task(void *args) +{ + struct ppp_info_t *ppp_info = args; + int backoff_time = 15000; + const int max_backoff = 60000; + esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN); + esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); + dte_config.uart_config.tx_io_num = CONFIG_EXAMPLE_MODEM_UART_TX_PIN; + dte_config.uart_config.rx_io_num = CONFIG_EXAMPLE_MODEM_UART_RX_PIN; + + ppp_info->dce = esp_modem_new(&dte_config, &dce_config, ppp_info->parent.netif); + + int rssi, ber; + esp_err_t ret = esp_modem_get_signal_quality(ppp_info->dce, &rssi, &ber); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_modem_get_signal_quality failed with %d %s", ret, esp_err_to_name(ret)); + goto failed; + } + ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber); + ret = esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_DATA); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_DATA) failed with %d", ret); + goto failed; + } + +failed: + +#define CONTINUE_LATER() backoff_time *= 2; \ + if (backoff_time > max_backoff) { backoff_time = max_backoff; } \ + continue; + + // now let's keep retrying + while (!ppp_info->stop_task) { + vTaskDelay(pdMS_TO_TICKS(backoff_time)); + if (ppp_info->parent.connected) { + backoff_time = 5000; + continue; + } + // try if the modem got stuck in data mode + ESP_LOGI(TAG, "Trying to Sync with modem"); + ret = esp_modem_sync(ppp_info->dce); + if (ret != ESP_OK) { + ESP_LOGI(TAG, "Switching to command mode"); + esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_COMMAND); + ESP_LOGI(TAG, "Retry sync 3 times"); + for (int i = 0; i < 3; ++i) { + ret = esp_modem_sync(ppp_info->dce); + if (ret == ESP_OK) { + break; + } + vTaskDelay(pdMS_TO_TICKS(1000)); + } + if (ret != ESP_OK) { + CONTINUE_LATER(); + } + } + ESP_LOGI(TAG, "Manual hang-up before reconnecting"); + ret = esp_modem_at(ppp_info->dce, "ATH", NULL, 2000); + if (ret != ESP_OK) { + CONTINUE_LATER(); + } + ret = esp_modem_get_signal_quality(ppp_info->dce, &rssi, &ber); + if (ret != ESP_OK) { + CONTINUE_LATER(); + } + ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber); + ret = esp_modem_set_mode(ppp_info->dce, ESP_MODEM_MODE_DATA); + if (ret != ESP_OK) { + CONTINUE_LATER(); + } + } + +#undef CONTINUE_LATER + + vTaskDelete(NULL); +} + +iface_info_t *setup_ppp(int prio) +{ + struct ppp_info_t *ppp_info = calloc(1, sizeof(struct ppp_info_t)); + assert(ppp_info); + ppp_info->parent.teardown = teardown_ppp; + ppp_info->parent.name = "Modem"; + event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, ppp_info)); + ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, ppp_info)); + + esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP(); + base_netif_cfg.route_prio = prio; + esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg, + .stack = ESP_NETIF_NETSTACK_DEFAULT_PPP + }; + + ppp_info->parent.netif = esp_netif_new(&netif_ppp_config); + if (ppp_info->parent.netif == NULL) { + goto err; + } + if (xTaskCreate(ppp_task, "ppp_retry_task", 4096, ppp_info, 5, NULL) != pdTRUE) { + goto err; + } + + ESP_LOGI(TAG, "Waiting for IP address"); + xEventGroupWaitBits(event_group, CONNECT_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(10000)); + + return &ppp_info->parent; + +err: + + teardown_ppp(&ppp_info->parent); + return NULL; +} diff --git a/examples/esp_netif/multiple_netifs/main/wifi_connect.c b/examples/esp_netif/multiple_netifs/main/wifi_connect.c new file mode 100644 index 000000000..31f3a587d --- /dev/null +++ b/examples/esp_netif/multiple_netifs/main/wifi_connect.c @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* WiFi connection + + 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 "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "iface_info.h" + +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + + +static const char *TAG = "wifi_connect"; +static int s_retry_num = 0; +static EventGroupHandle_t s_wifi_event_group; + +static void event_handler(void *args, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + struct iface_info_t *wifi_info = args; + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + wifi_info->connected = false; + if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY || CONFIG_ESP_MAXIMUM_RETRY == 0) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(TAG, "connect to the AP fail"); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + + ESP_LOGI(TAG, "WiFi station Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "MASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "GW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + + for (int i = 0; i < 2; ++i) { + esp_netif_get_dns_info(wifi_info->netif, i, &wifi_info->dns[i]); + ESP_LOGI(TAG, "DNS %i:" IPSTR, i, IP2STR(&wifi_info->dns[i].ip.u_addr.ip4)); + } + ESP_LOGI(TAG, "~~~~~~~~~~~"); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + wifi_info->connected = true; + } +} + +static void teardown_wifi(iface_info_t *info) +{ + esp_netif_action_disconnected(info->netif, 0, 0, 0); + esp_netif_action_stop(info->netif, 0, 0, 0); + esp_wifi_stop(); + esp_wifi_deinit(); + free(info); +} + +iface_info_t *setup_wifi(int prio) +{ + struct iface_info_t *wifi_info = malloc(sizeof(iface_info_t)); + assert(wifi_info); + wifi_info->teardown = teardown_wifi; + wifi_info->name = "WiFi station"; + s_wifi_event_group = xEventGroupCreate(); + + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); + esp_netif_config.route_prio = prio; + wifi_info->netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config); + esp_wifi_set_default_wifi_sta_handlers(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, wifi_info)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, wifi_info)); + + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_ESP_WIFI_SSID, + .password = CONFIG_ESP_WIFI_PASSWORD, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_ERROR_CHECK(esp_wifi_start() ); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection or a failure (connection failed or a timeout) */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, (WIFI_CONNECTED_BIT | WIFI_FAIL_BIT), pdFALSE, pdFALSE, pdMS_TO_TICKS(5000)); + + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", + CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", + CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); + teardown_wifi(wifi_info); + wifi_info = NULL; + } else if (CONFIG_ESP_MAXIMUM_RETRY == 0) { + ESP_LOGI(TAG, "No connection at the moment, will keep retrying..."); + } else { + ESP_LOGE(TAG, "Failed to connect withing specified timeout"); + teardown_wifi(wifi_info); + wifi_info = NULL; + } + return wifi_info; +} diff --git a/examples/esp_netif/multiple_netifs/sdkconfig.defaults b/examples/esp_netif/multiple_netifs/sdkconfig.defaults new file mode 100644 index 000000000..3c08a9a30 --- /dev/null +++ b/examples/esp_netif/multiple_netifs/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_LWIP_PPP_SUPPORT=y +CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y