From 547210f7a5f37d5a577f4fd489f6b77f53f05e1d Mon Sep 17 00:00:00 2001 From: Liu Han Date: Wed, 4 Dec 2019 18:27:28 +0800 Subject: [PATCH] socket-example: Add tcp client example for multiple interfaces --- .../tcp_client_multi_net/CMakeLists.txt | 10 ++ .../sockets/tcp_client_multi_net/Makefile | 11 ++ .../sockets/tcp_client_multi_net/README.md | 133 ++++++++++++++++ .../tcp_client_multi_net/main/CMakeLists.txt | 2 + .../main/Kconfig.projbuild | 16 ++ .../tcp_client_multi_net/main/component.mk | 4 + .../main/tcp_client_multiple.c | 144 ++++++++++++++++++ .../tcp_client_multi_net/sdkconfig.defaults | 2 + 8 files changed, 322 insertions(+) create mode 100644 examples/protocols/sockets/tcp_client_multi_net/CMakeLists.txt create mode 100644 examples/protocols/sockets/tcp_client_multi_net/Makefile create mode 100644 examples/protocols/sockets/tcp_client_multi_net/README.md create mode 100644 examples/protocols/sockets/tcp_client_multi_net/main/CMakeLists.txt create mode 100644 examples/protocols/sockets/tcp_client_multi_net/main/Kconfig.projbuild create mode 100644 examples/protocols/sockets/tcp_client_multi_net/main/component.mk create mode 100644 examples/protocols/sockets/tcp_client_multi_net/main/tcp_client_multiple.c create mode 100644 examples/protocols/sockets/tcp_client_multi_net/sdkconfig.defaults diff --git a/examples/protocols/sockets/tcp_client_multi_net/CMakeLists.txt b/examples/protocols/sockets/tcp_client_multi_net/CMakeLists.txt new file mode 100644 index 000000000..423899f2e --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/CMakeLists.txt @@ -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) diff --git a/examples/protocols/sockets/tcp_client_multi_net/Makefile b/examples/protocols/sockets/tcp_client_multi_net/Makefile new file mode 100644 index 000000000..1505a97ad --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/Makefile @@ -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 + diff --git a/examples/protocols/sockets/tcp_client_multi_net/README.md b/examples/protocols/sockets/tcp_client_multi_net/README.md new file mode 100644 index 000000000..0217fce72 --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/README.md @@ -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). diff --git a/examples/protocols/sockets/tcp_client_multi_net/main/CMakeLists.txt b/examples/protocols/sockets/tcp_client_multi_net/main/CMakeLists.txt new file mode 100644 index 000000000..65a87f685 --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "tcp_client_multiple.c" + INCLUDE_DIRS ".") diff --git a/examples/protocols/sockets/tcp_client_multi_net/main/Kconfig.projbuild b/examples/protocols/sockets/tcp_client_multi_net/main/Kconfig.projbuild new file mode 100644 index 000000000..af1a70552 --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/main/Kconfig.projbuild @@ -0,0 +1,16 @@ +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 + +endmenu diff --git a/examples/protocols/sockets/tcp_client_multi_net/main/component.mk b/examples/protocols/sockets/tcp_client_multi_net/main/component.mk new file mode 100644 index 000000000..a98f634ea --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/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/protocols/sockets/tcp_client_multi_net/main/tcp_client_multiple.c b/examples/protocols/sockets/tcp_client_multi_net/main/tcp_client_multiple.c new file mode 100644 index 000000000..f69262f2f --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/main/tcp_client_multiple.c @@ -0,0 +1,144 @@ +/* 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 "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 +#include +#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) +{ + esp_netif_ip_info_t ip; + 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 local IP of the network interface */ + 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; + } + + /* 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); +} diff --git a/examples/protocols/sockets/tcp_client_multi_net/sdkconfig.defaults b/examples/protocols/sockets/tcp_client_multi_net/sdkconfig.defaults new file mode 100644 index 000000000..148809bf1 --- /dev/null +++ b/examples/protocols/sockets/tcp_client_multi_net/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_EXAMPLE_CONNECT_WIFI=y +CONFIG_EXAMPLE_CONNECT_ETHERNET=y