Merge branch 'example/multi_interface' into 'master'

feat(example): Add tcp client example for multiple interface.

See merge request espressif/esp-idf!6918
This commit is contained in:
David Čermák 2020-05-18 19:40:40 +08:00
commit a3520970f6
18 changed files with 582 additions and 68 deletions

View file

@ -402,6 +402,22 @@ esp_err_t esp_netif_set_old_ip_info(esp_netif_t *esp_netif, const esp_netif_ip_i
*/
int esp_netif_get_netif_impl_index(esp_netif_t *esp_netif);
/**
* @brief Get net interface name from network stack implementation
*
* @note This name could be used in `setsockopt()` to bind socket with appropriate interface
*
* @param[in] esp_netif Handle to esp-netif instance
* @param[out] name Interface name as specified in underlying TCP/IP stack. Note that the
* actual name will be copied to the specified buffer, which must be allocated to hold
* maximum interface name size (6 characters for lwIP)
*
* @return
* - ESP_OK
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS
*/
esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char* name);
/**
* @}
*/
@ -648,6 +664,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
*

View file

@ -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;
@ -1715,4 +1736,15 @@ int esp_netif_get_netif_impl_index(esp_netif_t *esp_netif)
return netif_get_index(esp_netif->lwip_netif);
}
esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char* name)
{
ESP_LOGD(TAG, "%s esp_netif:%p", __func__, esp_netif);
if (esp_netif == NULL || esp_netif->lwip_netif == NULL) {
return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
}
netif_index_to_name(netif_get_index(esp_netif->lwip_netif), name);
return ESP_OK;
}
#endif /* CONFIG_ESP_NETIF_TCPIP_LWIP */

View file

@ -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"

View file

@ -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; i<NR_OF_IP_ADDRESSES_TO_WAIT_FOR; ++i) {
xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY);
}
// iterate over active interfaces, and print out IPs of "our" netifs
esp_netif_t *netif = NULL;
esp_netif_ip_info_t ip;
esp_ip6_addr_t ip6[MAX_IP6_ADDRS_PER_NETIF];
for (int i=0; i<esp_netif_get_nr_of_ifs(); ++i) {
netif = esp_netif_next(netif);
if (is_our_netif(TAG, netif)) {
ESP_LOGI(TAG, "Connected to %s", esp_netif_get_desc(netif));
ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip));
ESP_LOGI(TAG, "- IPv4 address: " IPSTR, IP2STR(&ip.ip));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
ESP_LOGI(TAG, "IPv6 address: " IPV6STR, IPV62STR(s_ipv6_addr));
int ip6_addrs = esp_netif_get_all_ip6(netif, ip6);
for (int j=0; j< ip6_addrs; ++j) {
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j]));
ESP_LOGI(TAG, "- IPv6 address: " IPV6STR ", type: %s", IPV62STR(ip6[j]), s_ipv6_addr_types[ipv6_type]);
}
#endif
}
}
return ESP_OK;
}
esp_err_t example_disconnect(void)
{
if (s_connect_event_group == NULL) {
if (s_semph_get_ip_addrs == NULL) {
return ESP_ERR_INVALID_STATE;
}
vEventGroupDelete(s_connect_event_group);
s_connect_event_group = NULL;
vSemaphoreDelete(s_semph_get_ip_addrs);
s_semph_get_ip_addrs = NULL;
stop();
ESP_LOGI(TAG, "Disconnected from %s", s_connection_name);
s_connection_name = NULL;
return ESP_OK;
}
@ -155,22 +222,22 @@ static void on_wifi_connect(void *esp_netif, esp_event_base_t event_base,
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
static void start(void)
static esp_netif_t* wifi_start(void)
{
char *desc;
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_WIFI_STA();
esp_netif_t *netif = esp_netif_new(&netif_config);
assert(netif);
esp_netif_attach_wifi_station(netif);
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
// Prefix the interface description with the module TAG
// Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask)
asprintf(&desc, "%s: %s", TAG, esp_netif_config.if_desc);
esp_netif_config.if_desc = desc;
esp_netif_config.route_prio = 128;
esp_netif_t *netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config);
free(desc);
esp_wifi_set_default_wifi_sta_handlers();
s_example_esp_netif = netif;
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
@ -190,11 +257,12 @@ static void start(void)
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_connect());
s_connection_name = CONFIG_EXAMPLE_WIFI_SSID;
return netif;
}
static void stop(void)
static void wifi_stop(void)
{
esp_netif_t *wifi_netif = get_example_netif_from_desc("sta");
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect));
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
@ -207,8 +275,8 @@ static void stop(void)
}
ESP_ERROR_CHECK(err);
ESP_ERROR_CHECK(esp_wifi_deinit());
ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(s_example_esp_netif));
esp_netif_destroy(s_example_esp_netif);
ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(wifi_netif));
esp_netif_destroy(wifi_netif);
s_example_esp_netif = NULL;
}
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
@ -238,12 +306,22 @@ static esp_eth_mac_t *s_mac = NULL;
static esp_eth_phy_t *s_phy = NULL;
static void *s_eth_glue = NULL;
static void start(void)
static esp_netif_t* eth_start(void)
{
esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_ETH();
char *desc;
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();
// Prefix the interface description with the module TAG
// Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask)
asprintf(&desc, "%s: %s", TAG, esp_netif_config.if_desc);
esp_netif_config.if_desc = desc;
esp_netif_config.route_prio = 64;
esp_netif_config_t netif_config = {
.base = &esp_netif_config,
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
};
esp_netif_t *netif = esp_netif_new(&netif_config);
assert(netif);
s_example_esp_netif = netif;
free(desc);
// Set default handlers to process TCP/IP stuffs
ESP_ERROR_CHECK(esp_eth_set_default_handlers(netif));
// Register user defined event handers
@ -307,11 +385,12 @@ static void start(void)
s_eth_glue = esp_eth_new_netif_glue(s_eth_handle);
esp_netif_attach(netif, s_eth_glue);
esp_eth_start(s_eth_handle);
s_connection_name = "Ethernet";
return netif;
}
static void stop(void)
static void eth_stop(void)
{
esp_netif_t *eth_netif = get_example_netif_from_desc("eth");
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, &on_got_ip));
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6));
@ -319,12 +398,12 @@ static void stop(void)
#endif
ESP_ERROR_CHECK(esp_eth_stop(s_eth_handle));
ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue));
ESP_ERROR_CHECK(esp_eth_clear_default_handlers(s_example_esp_netif));
ESP_ERROR_CHECK(esp_eth_clear_default_handlers(eth_netif));
ESP_ERROR_CHECK(esp_eth_driver_uninstall(s_eth_handle));
ESP_ERROR_CHECK(s_phy->del(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;
}

View file

@ -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

View file

@ -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.

View file

@ -1,4 +1,5 @@
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23

View file

@ -1,4 +1,5 @@
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23

View file

@ -1,6 +1,7 @@
CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23

View file

@ -1,6 +1,7 @@
CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23

View file

@ -0,0 +1,10 @@
# 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.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tcp_client_multiple)

View file

@ -0,0 +1,11 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := tcp_client_multiple
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,133 @@
# Multiple Ethernet Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates basic usage of Ethernet interface and WiFi station together. The workflow of the example could be as follow:
1. Connects to both WiFi and Ethernet using common-connect component
2. Starts two tasks, one for each interface to resolve configured host name and connect to it periodically
3. Connection to host endpoint is handled by:
- creating a socket as TCP client
- binding it to the related interface (Ethernet of WiFi)
- send and receive a trivial HTTP request and response
If you have a new multiple interface application to go (for example, connect to IoT cloud via Ethernet and WiFi), try this as a basic template, then add your own code.
## How to use example
### Hardware Required
To run this example, you need to have one ESP32 development board integrated with an Ethernet interface, for example, ESP32-Ethernet-Kit, or just connect your ESP32-DevkitC board to a breakout board which features RMII Ethernet PHY.
### Configure the project
Enter project configuration by `idf.py menuconfig` (or `make menuconfig` if using legacy GNU Make build system) and navigate into:
* `Example Connection Configuration` menu to choose the connection details:
- Enter SSID and password for WiFi connection
- Set Ethernet type and configuration for Ethernet connection
- Note that the project is preconfigured to have both WiFi and Ethernet interface enabled by default
- See the [README.md](../../README.md) for more details about common example connection component
* `Example Configuration` menu:
- Set host name and port for the tcp_client to connect to
### Build and Flash
Run `idf.py -p PORT flash monitor` to build and flash the project..
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```bash
I (695) example_connect: Connecting to DavidsAP...
I (795) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
I (795) wifi:mode : sta (30:ae:a4:c6:b4:f8)
W (795) event: handler already registered, overwriting
I (815) esp_eth.netif.glue: 30:ae:a4:c6:b4:fb
I (815) esp_eth.netif.glue: ethernet attached to netif
I (825) example_connect: Waiting for IP(s)
I (1525) wifi:new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1
I (2295) wifi:state: init -> auth (b0)
I (2295) wifi:state: auth -> assoc (0)
I (2305) wifi:state: assoc -> run (10)
I (2315) wifi:connected with DavidsAP, aid = 2, channel 6, BW20, bssid = 16:f7:28:37:58:36
I (2315) wifi:security type: 3, phy: bgn, rssi: -35
I (2315) wifi:pm start, type: 1
I (2325) wifi:AP's beacon interval = 102400 us, DTIM period = 3
I (3125) esp_netif_handlers: example_connect: sta ip: 192.168.2.15, mask: 255.255.255.0, gw: 192.168.2.1
I (3125) example_connect: Interface desciption example_connect: sta
I (3135) example_connect: Interface "example_connect: sta" got IPv4 address: 192.168.2.15
I (3625) example_connect: Interface "example_connect: sta" got IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4f8, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (4825) example_connect: Ethernet Link Up
I (5625) esp_netif_handlers: example_connect: eth ip: 192.168.32.148, mask: 255.255.252.0, gw: 192.168.32.3
I (5625) example_connect: Interface desciption example_connect: eth
I (5635) example_connect: Interface "example_connect: eth" got IPv4 address: 192.168.32.148
I (6625) example_connect: Interface "example_connect: eth" got IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4fb, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6625) example_connect: Connected to example_connect: eth
I (6635) example_connect: - IPv4 address: 192.168.32.148
I (6635) example_connect: - IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4fbtype: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6645) example_connect: Connected to example_connect: sta
I (6655) example_connect: - IPv4 address: 192.168.2.15
I (6655) example_connect: - IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fec6:b4f8type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6675) example_connect: Connected to Ethernet
I (6675) tcp_client_multiple: netif described as "sta" corresponds to esp-netif ptr:0x3ffba3ac
I (6675) tcp_client_multiple: netif described as "eth" corresponds to esp-netif ptr:0x3ffc608c
I (6895) tcp_client_multiple: "example_connect: eth" Socket created
I (6895) tcp_client_multiple: "example_connect: sta" Socket created
I (6895) tcp_client_multiple: "example_connect: eth" Successfully connected
I (6905) tcp_client_multiple: "example_connect: sta" Successfully connected
I (6965) tcp_client_multiple: "example_connect: eth" Received Data 127 bytes
I (6965) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:58 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (6965) tcp_client_multiple: "example_connect: sta" Received Data 127 bytes
I (6985) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:58 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (7675) tcp_client_multiple: "example_connect: eth" Socket created
I (7675) tcp_client_multiple: "example_connect: eth" Successfully connected
I (7695) tcp_client_multiple: "example_connect: sta" Socket created
I (7705) tcp_client_multiple: "example_connect: sta" Successfully connected
I (7735) tcp_client_multiple: "example_connect: eth" Received Data 127 bytes
I (7735) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:59 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (7955) tcp_client_multiple: "example_connect: sta" Received Data 127 bytes
I (7955) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:02:59 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (8445) tcp_client_multiple: "example_connect: eth" Socket created
I (8445) tcp_client_multiple: "example_connect: eth" Successfully connected
I (8505) tcp_client_multiple: "example_connect: eth" Received Data 127 bytes
I (8505) tcp_client_multiple: HTTP/1.1 200 OK
Date: Thu, 23 Apr 2020 07:03:00 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html;
I (8675) tcp_client_multiple: "example_connect: sta" Socket created
```
## Troubleshooting
* When connecting using Ethernet, please consult troubleshooting described in [Ethernet common readme](../../../ethernet/README.md)
or [Ethernet documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html).
If using Ethernet for the first time, it is recommended to start with the [Ethernet example readme](../../../ethernet/basic/README.md), which contains instructions for connecting and configuring the PHY.
Once Ethernet example obtains IP address successfully, proceed to this example.
* When connecting using Wi-Fi, please refer to the WiFi examples in [examples/wifi/getting_started/](../wifi/getting_started).

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "tcp_client_multiple.c"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,24 @@
menu "Example Configuration"
config EXAMPLE_HOST_NAME
string "Host Name"
default "baidu.com"
help
host name
config EXAMPLE_HOST_PORT
int "Host Port"
default 80
range 0 65535
help
host port
config EXAMPLE_BIND_SOCKET_TO_NETIF_NAME
bool "Bind to interface by its name"
default y
help
By default example uses setsockopt() to bind the tcp-client's socket
to specific interface. Setting this option to true demonstrates binding
the socket to the local ip address of the network interface.
endmenu

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,157 @@
/* 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 <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include <sys/socket.h>
#include <netdb.h>
#include "protocol_examples_common.h"
static const char *TAG = "tcp_client_multiple";
#define HOST_NAME CONFIG_EXAMPLE_HOST_NAME
#define HOST_IP_PORT CONFIG_EXAMPLE_HOST_PORT
static const char *payload = "GET / HTTP/1.1\r\n\r\n";
static void app_multiple_handle(esp_ip4_addr_t *ip4_addr, esp_netif_t *esp_netif)
{
char rx_buffer[128] = {0};
const char *netif_name = esp_netif_get_desc(esp_netif);
/* Create a socket */
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to create socket: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
ESP_LOGI(TAG, "\"%s\" Socket created", netif_name);
/* Bind the socket to an interface (based on example config option)
* - using netif local IP address
* - using netif name
*/
#if CONFIG_EXAMPLE_BIND_SOCKET_TO_NETIF_NAME
struct ifreq ifr;
esp_netif_get_netif_impl_name(esp_netif, ifr.ifr_name);
int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
#else
esp_netif_ip_info_t ip;
memset(&ip, 0, sizeof(esp_netif_ip_info_t));
ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif, &ip));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(0);
addr.sin_addr.s_addr = ip.ip.addr;
int ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to bind socket: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
#endif /* CONFIG_EXAMPLE_BIND_SOCKET_TO_NETIF_NAME */
/* Connect to the host by the network interface */
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = ip4_addr->addr;
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(HOST_IP_PORT);
ret = connect(sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (ret != 0) {
ESP_LOGE(TAG, "\"%s\" Socket unable to connect: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
ESP_LOGI(TAG, "\"%s\" Successfully connected", netif_name);
ret = send(sock, payload, strlen(payload), 0);
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Error occured during sending: errno %d", netif_name, errno);
goto app_multiple_handle_fail;
}
ret = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Error occured during receiving: errno %d", netif_name, errno);
} else if (ret > 0){
rx_buffer[ret] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "\"%s\" Received Data %d bytes", netif_name, ret);
ESP_LOGI(TAG, "%s", rx_buffer);
} else {
ESP_LOGE(TAG, "\"%s\" Closed connection during receiving", netif_name);
}
app_multiple_handle_fail:
close(sock);
}
static void app_connection_task(void *pvParameters)
{
esp_ip4_addr_t ip4_addr;
const char *netif_desc = pvParameters;
esp_netif_t *netif = get_example_netif_from_desc(netif_desc);
ESP_LOGD(TAG, "netif described as \"%s\" corresponds to esp-netif ptr:%p", netif_desc, netif);
while(netif) {
/* Wait for the host name to get */
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
int err = getaddrinfo(HOST_NAME, NULL, &hints, &res);
if(err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
break;
}
memcpy(&ip4_addr, &((struct sockaddr_in *)(res->ai_addr))->sin_addr, sizeof(ip4_addr));
freeaddrinfo(res);
/* Connect the host using the corresponding network interface */
app_multiple_handle(&ip4_addr, netif);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
ESP_LOGE(TAG, "%s with netif desc:%s Failed! exiting", __func__, netif_desc);
vTaskDelete(NULL);
}
void app_main(void)
{
//Initialize NVS
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_netif_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
xTaskCreate(&app_connection_task, "app_ethernet_task", 4096, "eth", 5, NULL);
xTaskCreate(&app_connection_task, "app_wifi_task", 4096, "sta", 5, NULL);
}

View file

@ -0,0 +1,2 @@
CONFIG_EXAMPLE_CONNECT_WIFI=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y