From 06711c7c367cc89b3525e01c5dfe490edfe64c5a Mon Sep 17 00:00:00 2001 From: David Cermak Date: Thu, 23 Apr 2020 16:23:46 +0200 Subject: [PATCH] examples: common connect component to use both interfaces at once --- components/esp_netif/include/esp_netif.h | 11 + components/esp_netif/lwip/esp_netif_lwip.c | 29 ++- .../Kconfig.projbuild | 26 +-- .../protocol_examples_common/connect.c | 192 +++++++++++++----- .../include/protocol_examples_common.h | 14 ++ examples/protocols/README.md | 4 +- 6 files changed, 208 insertions(+), 68 deletions(-) diff --git a/components/esp_netif/include/esp_netif.h b/components/esp_netif/include/esp_netif.h index 22874808f..d0cda153a 100644 --- a/components/esp_netif/include/esp_netif.h +++ b/components/esp_netif/include/esp_netif.h @@ -648,6 +648,17 @@ esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if */ esp_err_t esp_netif_get_ip6_global(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6); +/** + * @brief Get all IPv6 addresses of the specified interface + * + * @param[in] esp_netif Handle to esp-netif instance + * @param[out] if_ip6 Array of IPv6 addresses will be copied to the argument + * + * @return + * number of returned IPv6 addresses + */ +int esp_netif_get_all_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[]); + /** * @brief Sets IPv4 address to the specified octets * diff --git a/components/esp_netif/lwip/esp_netif_lwip.c b/components/esp_netif/lwip/esp_netif_lwip.c index 684902183..0d57b6648 100644 --- a/components/esp_netif/lwip/esp_netif_lwip.c +++ b/components/esp_netif/lwip/esp_netif_lwip.c @@ -1446,7 +1446,7 @@ static esp_err_t esp_netif_create_ip6_linklocal_api(esp_netif_api_msg_t *msg) { esp_netif_t *esp_netif = msg->esp_netif; - ESP_LOGD(TAG, "%s esp-netif:%p", __func__, esp_netif); + ESP_LOGV(TAG, "%s esp-netif:%p", __func__, esp_netif); struct netif *p_netif = esp_netif->lwip_netif; if (p_netif != NULL && netif_is_up(p_netif)) { @@ -1462,7 +1462,7 @@ esp_err_t esp_netif_create_ip6_linklocal(esp_netif_t *esp_netif) _RUN_IN_LWIP_TA esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6) { - ESP_LOGD(TAG, "%s esp-netif:%p", __func__, esp_netif); + ESP_LOGV(TAG, "%s esp-netif:%p", __func__, esp_netif); if (esp_netif == NULL || if_ip6 == NULL || esp_netif->is_ppp_netif) { return ESP_ERR_ESP_NETIF_INVALID_PARAMS; @@ -1479,7 +1479,7 @@ esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if esp_err_t esp_netif_get_ip6_global(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6) { - ESP_LOGD(TAG, "%s esp-netif:%p", __func__, esp_netif); + ESP_LOGV(TAG, "%s esp-netif:%p", __func__, esp_netif); if (esp_netif == NULL || if_ip6 == NULL) { return ESP_ERR_ESP_NETIF_INVALID_PARAMS; @@ -1496,10 +1496,31 @@ esp_err_t esp_netif_get_ip6_global(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip } } } - + return ESP_FAIL; } +int esp_netif_get_all_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[]) +{ + ESP_LOGV(TAG, "%s esp-netif:%p", __func__, esp_netif); + + if (esp_netif == NULL || if_ip6 == NULL) { + return 0; + } + + int addr_count = 0; + struct netif *p_netif = esp_netif->lwip_netif; + + if (p_netif != NULL && netif_is_up(p_netif)) { + for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (!ip_addr_cmp(&p_netif->ip6_addr[i], IP6_ADDR_ANY)) { + memcpy(&if_ip6[addr_count++], &p_netif->ip6_addr[i], sizeof(ip6_addr_t)); + } + } + } + return addr_count; +} + esp_netif_flags_t esp_netif_get_flags(esp_netif_t *esp_netif) { return esp_netif->flags; diff --git a/examples/common_components/protocol_examples_common/Kconfig.projbuild b/examples/common_components/protocol_examples_common/Kconfig.projbuild index f47b8a6af..eccf77889 100644 --- a/examples/common_components/protocol_examples_common/Kconfig.projbuild +++ b/examples/common_components/protocol_examples_common/Kconfig.projbuild @@ -1,18 +1,11 @@ menu "Example Connection Configuration" - choice EXAMPLE_CONNECT_INTERFACE - prompt "Connect using" - default EXAMPLE_CONNECT_WIFI + + config EXAMPLE_CONNECT_WIFI + bool "connect using WiFi interface" + default y help - Protocol examples can use Wi-Fi or Ethernet to connect to the network. - Choose which interface to use. - - config EXAMPLE_CONNECT_WIFI - bool "Wi-Fi" - - config EXAMPLE_CONNECT_ETHERNET - bool "Ethernet" - - endchoice + Protocol examples can use Wi-Fi and/or Ethernet to connect to the network. + Choose this option to connect with WiFi if EXAMPLE_CONNECT_WIFI config EXAMPLE_WIFI_SSID @@ -29,6 +22,13 @@ menu "Example Connection Configuration" Can be left blank if the network has no security set. endif + config EXAMPLE_CONNECT_ETHERNET + bool "connect using Ethernet interface" + default n + help + Protocol examples can use Wi-Fi and/or Ethernet to connect to the network. + Choose this option to connect with Ethernet + if EXAMPLE_CONNECT_ETHERNET choice EXAMPLE_USE_ETHERNET prompt "Ethernet Type" diff --git a/examples/common_components/protocol_examples_common/connect.c b/examples/common_components/protocol_examples_common/connect.c index e845837cb..653a43ea0 100644 --- a/examples/common_components/protocol_examples_common/connect.c +++ b/examples/common_components/protocol_examples_common/connect.c @@ -25,11 +25,9 @@ #include "lwip/err.h" #include "lwip/sys.h" -#define GOT_IPV4_BIT BIT(0) -#define GOT_IPV6_BIT BIT(1) - #ifdef CONFIG_EXAMPLE_CONNECT_IPV6 -#define CONNECTED_BITS (GOT_IPV4_BIT | GOT_IPV6_BIT) +#define MAX_IP6_ADDRS_PER_NETIF (5) +#define NR_OF_IP_ADDRESSES_TO_WAIT_FOR (s_active_interfaces*2) #if defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK) #define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_LINK_LOCAL @@ -42,12 +40,12 @@ #endif // if-elif CONFIG_EXAMPLE_CONNECT_IPV6_PREF_... #else -#define CONNECTED_BITS (GOT_IPV4_BIT) +#define NR_OF_IP_ADDRESSES_TO_WAIT_FOR (s_active_interfaces) #endif -static EventGroupHandle_t s_connect_event_group; +static int s_active_interfaces = 0; +static xSemaphoreHandle s_semph_get_ip_addrs; static esp_ip4_addr_t s_ip_addr; -static const char *s_connection_name; static esp_netif_t *s_example_esp_netif = NULL; #ifdef CONFIG_EXAMPLE_CONNECT_IPV6 @@ -66,19 +64,72 @@ static const char *s_ipv6_addr_types[] = { static const char *TAG = "example_connect"; -/* set up connection, Wi-Fi or Ethernet */ -static void start(void); +#if CONFIG_EXAMPLE_CONNECT_WIFI +static esp_netif_t* wifi_start(void); +static void wifi_stop(void); +#endif +#if CONFIG_EXAMPLE_CONNECT_ETHERNET +static esp_netif_t* eth_start(void); +static void eth_stop(void); +#endif + +/** + * @brief Checks the netif description if it contains specified prefix. + * All netifs created withing common connect component are prefixed with the module TAG, + * so it returns true if the specified netif is owned by this module + */ +static bool is_our_netif(const char *prefix, esp_netif_t *netif) +{ + return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix)-1) == 0; +} + +/* set up connection, Wi-Fi and/or Ethernet */ +static void start(void) +{ + +#if CONFIG_EXAMPLE_CONNECT_WIFI + s_example_esp_netif = wifi_start(); + s_active_interfaces++; +#endif + +#if CONFIG_EXAMPLE_CONNECT_ETHERNET + s_example_esp_netif = eth_start(); + s_active_interfaces++; +#endif + +#if CONFIG_EXAMPLE_CONNECT_WIFI && CONFIG_EXAMPLE_CONNECT_ETHERNET + /* if both intefaces at once, clear out to indicate that multiple netifs are active */ + s_example_esp_netif = NULL; +#endif + + s_semph_get_ip_addrs = xSemaphoreCreateCounting(NR_OF_IP_ADDRESSES_TO_WAIT_FOR, 0); +} /* tear down connection, release resources */ -static void stop(void); +static void stop(void) +{ +#if CONFIG_EXAMPLE_CONNECT_WIFI + wifi_stop(); + s_active_interfaces--; +#endif + +#if CONFIG_EXAMPLE_CONNECT_ETHERNET + eth_stop(); + s_active_interfaces--; +#endif +} static void on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { - ESP_LOGI(TAG, "Got IP event!"); ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + if (!is_our_netif(TAG, event->esp_netif)) { + ESP_LOGW(TAG, "Got IPv4 from another interface \"%s\": ignored", esp_netif_get_desc(event->esp_netif)); + return; + } + ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr)); - xEventGroupSetBits(s_connect_event_group, GOT_IPV4_BIT); + xSemaphoreGive(s_semph_get_ip_addrs); } #ifdef CONFIG_EXAMPLE_CONNECT_IPV6 @@ -87,15 +138,16 @@ static void on_got_ipv6(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; - if (event->esp_netif != s_example_esp_netif) { - ESP_LOGD(TAG, "Got IPv6 from another netif: ignored"); + if (!is_our_netif(TAG, event->esp_netif)) { + ESP_LOGW(TAG, "Got IPv6 from another netif: ignored"); return; } esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); - ESP_LOGI(TAG, "Got IPv6 address: " IPV6STR ", type: %s", IPV62STR(event->ip6_info.ip), s_ipv6_addr_types[ipv6_type]); + ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif), + IPV62STR(event->ip6_info.ip), s_ipv6_addr_types[ipv6_type]); if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) { memcpy(&s_ipv6_addr, &event->ip6_info.ip, sizeof(s_ipv6_addr)); - xEventGroupSetBits(s_connect_event_group, GOT_IPV6_BIT); + xSemaphoreGive(s_semph_get_ip_addrs); } } @@ -103,32 +155,47 @@ static void on_got_ipv6(void *arg, esp_event_base_t event_base, esp_err_t example_connect(void) { - if (s_connect_event_group != NULL) { + if (s_semph_get_ip_addrs != NULL) { return ESP_ERR_INVALID_STATE; } - s_connect_event_group = xEventGroupCreate(); start(); ESP_ERROR_CHECK(esp_register_shutdown_handler(&stop)); - ESP_LOGI(TAG, "Waiting for IP"); - xEventGroupWaitBits(s_connect_event_group, CONNECTED_BITS, true, true, portMAX_DELAY); - ESP_LOGI(TAG, "Connected to %s", s_connection_name); - ESP_LOGI(TAG, "IPv4 address: " IPSTR, IP2STR(&s_ip_addr)); + ESP_LOGI(TAG, "Waiting for IP(s)"); + for (int i=0; idel(s_phy)); ESP_ERROR_CHECK(s_mac->del(s_mac)); - esp_netif_destroy(s_example_esp_netif); + esp_netif_destroy(eth_netif); s_example_esp_netif = NULL; } @@ -334,3 +413,18 @@ esp_netif_t *get_example_netif(void) { return s_example_esp_netif; } + +esp_netif_t *get_example_netif_from_desc(const char *desc) +{ + esp_netif_t *netif = NULL; + char *expected_desc; + asprintf(&expected_desc, "%s: %s", TAG, desc); + while ((netif = esp_netif_next(netif)) != NULL) { + if (strcmp(esp_netif_get_desc(netif), expected_desc) == 0) { + free(expected_desc); + return netif; + } + } + free(expected_desc); + return netif; +} diff --git a/examples/common_components/protocol_examples_common/include/protocol_examples_common.h b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h index 98f67e348..859264df0 100644 --- a/examples/common_components/protocol_examples_common/include/protocol_examples_common.h +++ b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h @@ -57,8 +57,22 @@ esp_err_t example_configure_stdin_stdout(void); /** * @brief Returns esp-netif pointer created by example_connect() * + * @note If multiple interfaces active at once, this API return NULL + * In that case the get_example_netif_from_desc() should be used + * to get esp-netif pointer based on interface description */ esp_netif_t *get_example_netif(void); + +/** + * @brief Returns esp-netif pointer created by example_connect() described by + * the supplied desc field + * + * @param desc Textual interface of created network interface, for example "sta" + * indicate default WiFi station, "eth" default Ethernet interface. + * + */ +esp_netif_t *get_example_netif_from_desc(const char *desc); + #ifdef __cplusplus } #endif diff --git a/examples/protocols/README.md b/examples/protocols/README.md index 3e8570622..701daebf9 100644 --- a/examples/protocols/README.md +++ b/examples/protocols/README.md @@ -8,13 +8,13 @@ See the [README.md](../README.md) file in the upper level [examples](../) direct ### About the `example_connect()` Function -Protocols examples use a simple helper function, `example_connect()`, to establish Wi-Fi or Ethernet connection. This function is implemented in [examples/common_components/protocol_examples/common/connect.c](../common_components/protocol_examples_common/connect.c), and has a very simple behavior: block until connection is established and IP address is obtained, then return. This function is used to reduce the amount of boilerplate and to keep the example code focused on the protocol or library being demonstrated. +Protocols examples use a simple helper function, `example_connect()`, to establish Wi-Fi and/or Ethernet connection. This function is implemented in [examples/common_components/protocol_examples/common/connect.c](../common_components/protocol_examples_common/connect.c), and has a very simple behavior: block until connection is established and IP address is obtained, then return. This function is used to reduce the amount of boilerplate and to keep the example code focused on the protocol or library being demonstrated. The simple `example_connect()` function does not handle timeouts, does not gracefully handle various error conditions, and is only suited for use in examples. When developing real applications, this helper function needs to be replaced with full Wi-Fi / Ethernet connection handling code. Such code can be found in [examples/wifi/getting_started/](../wifi/getting_started) and [examples/ethernet/basic/](../ethernet/basic) examples. ### Configuring the Example -To configure the example to use Wi-Fi or Ethernet connection, open the project configuration menu (`idf.py menuconfig`) and navigate to "Example Connection Configuration" menu. Select either "Wi-Fi" or "Ethernet" in the "Connect using" choice. +To configure the example to use Wi-Fi, Ethernet or both connections, open the project configuration menu (`idf.py menuconfig`) and navigate to "Example Connection Configuration" menu. Select either "Wi-Fi" or "Ethernet" or both in the "Connect using" choice. When connecting using Wi-Fi, enter SSID and password of your Wi-Fi access point into the corresponding fields. If connecting to an open Wi-Fi network, keep the password field empty.