From 1ea002eb5dd03ef51bbc0b344c0a03c1a948e8b0 Mon Sep 17 00:00:00 2001 From: Bogdan Kolendovskyy Date: Mon, 15 Apr 2024 12:15:27 +0200 Subject: [PATCH] feat(common_examples): Add common examples aplicable for all phys --- ...it_build.yml => common_examples_build.yml} | 4 +- .gitignore | 22 ++++ common_examples/iperf/CMakeLists.txt | 8 ++ common_examples/iperf/README.md | 12 ++ common_examples/iperf/main/CMakeLists.txt | 2 + common_examples/iperf/main/Kconfig.projbuild | 7 ++ common_examples/iperf/main/idf_component.yml | 5 + common_examples/iperf/main/iperf.c | 79 ++++++++++++ .../iperf/sdkconfig.defaults.client | 1 + .../iperf/sdkconfig.defaults.server | 1 + .../simple-ethernet/CMakeLists.txt | 0 .../simple-ethernet/main/CMakeLists.txt | 0 .../simple-ethernet/main/idf_component.yml | 2 +- .../simple-ethernet/main/simple-ethernet.c | 0 .../simple-ethernet/sdkconfig.defaults.dm9051 | 0 .../sdkconfig.defaults.dp83848 | 0 .../simple-ethernet/sdkconfig.defaults.ip101 | 0 .../sdkconfig.defaults.ksz80xx | 0 .../sdkconfig.defaults.ksz8851snl | 0 .../sdkconfig.defaults.lan867x | 0 .../sdkconfig.defaults.lan87xx | 0 .../sdkconfig.defaults.rtl8201 | 0 .../simple-ethernet/sdkconfig.defaults.w5500 | 0 common_examples/tcp_client/CMakeLists.txt | 8 ++ common_examples/tcp_client/README.md | 20 ++++ .../tcp_client/main/CMakeLists.txt | 2 + .../tcp_client/main/Kconfig.projbuild | 7 ++ .../tcp_client/main/idf_component.yml | 7 ++ common_examples/tcp_client/main/tcp_client.c | 102 ++++++++++++++++ common_examples/tcp_client/tcp_server.py | 40 +++++++ common_examples/tcp_server/CMakeLists.txt | 10 ++ common_examples/tcp_server/README.md | 20 ++++ .../tcp_server/main/CMakeLists.txt | 2 + .../tcp_server/main/Kconfig.projbuild | 7 ++ .../tcp_server/main/idf_component.yml | 7 ++ common_examples/tcp_server/main/tcp_server.c | 113 ++++++++++++++++++ common_examples/tcp_server/tcp_client.py | 53 ++++++++ ethernet_init/idf_component.yml | 4 +- 38 files changed, 541 insertions(+), 4 deletions(-) rename .github/workflows/{ethernet_init_build.yml => common_examples_build.yml} (84%) create mode 100644 common_examples/iperf/CMakeLists.txt create mode 100644 common_examples/iperf/README.md create mode 100644 common_examples/iperf/main/CMakeLists.txt create mode 100644 common_examples/iperf/main/Kconfig.projbuild create mode 100644 common_examples/iperf/main/idf_component.yml create mode 100644 common_examples/iperf/main/iperf.c create mode 100644 common_examples/iperf/sdkconfig.defaults.client create mode 100644 common_examples/iperf/sdkconfig.defaults.server rename {ethernet_init/examples => common_examples}/simple-ethernet/CMakeLists.txt (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/main/CMakeLists.txt (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/main/idf_component.yml (76%) rename {ethernet_init/examples => common_examples}/simple-ethernet/main/simple-ethernet.c (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.dm9051 (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.dp83848 (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.ip101 (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.ksz80xx (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.ksz8851snl (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.lan867x (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.lan87xx (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.rtl8201 (100%) rename {ethernet_init/examples => common_examples}/simple-ethernet/sdkconfig.defaults.w5500 (100%) create mode 100644 common_examples/tcp_client/CMakeLists.txt create mode 100644 common_examples/tcp_client/README.md create mode 100644 common_examples/tcp_client/main/CMakeLists.txt create mode 100644 common_examples/tcp_client/main/Kconfig.projbuild create mode 100644 common_examples/tcp_client/main/idf_component.yml create mode 100644 common_examples/tcp_client/main/tcp_client.c create mode 100644 common_examples/tcp_client/tcp_server.py create mode 100644 common_examples/tcp_server/CMakeLists.txt create mode 100644 common_examples/tcp_server/README.md create mode 100644 common_examples/tcp_server/main/CMakeLists.txt create mode 100644 common_examples/tcp_server/main/Kconfig.projbuild create mode 100644 common_examples/tcp_server/main/idf_component.yml create mode 100644 common_examples/tcp_server/main/tcp_server.c create mode 100644 common_examples/tcp_server/tcp_client.py diff --git a/.github/workflows/ethernet_init_build.yml b/.github/workflows/common_examples_build.yml similarity index 84% rename from .github/workflows/ethernet_init_build.yml rename to .github/workflows/common_examples_build.yml index 3172454..d5e9b25 100644 --- a/.github/workflows/ethernet_init_build.yml +++ b/.github/workflows/common_examples_build.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: idf_ver: ["latest"] - example: ["simple-ethernet"] + example: ["simple-ethernet", "iperf", "tcp_server"] idf_target: ["esp32"] runs-on: ubuntu-20.04 container: espressif/idf:${{ matrix.idf_ver }} @@ -23,5 +23,5 @@ jobs: run: | . ${IDF_PATH}/export.sh pip install idf-build-apps - cd $GITHUB_WORKSPACE/esp_eth_drivers/ethernet_init/examples/${{ matrix.example }} + cd $GITHUB_WORKSPACE/esp_eth_drivers/common_examples/${{ matrix.example }} idf-build-apps build -p . --target ${{ matrix.idf_target }} --build-dir build_@t_@w --config 'sdkconfig.defaults.*=' \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1b8630d..2e9d4c2 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,28 @@ GPATH **/examples/**/sdkconfig.old **/examples/**/build **/examples/**/managed_components +common_examples/**/sdkconfig +common_examples/**/sdkconfig.old +common_examples/**/build +common_examples/**/managed_components + +# Project files +**/sdkconfig +**/sdkconfig.old +**/build +**/managed_components + +# Project files +**/sdkconfig +**/sdkconfig.old +**/build +**/managed_components + +# Project files +**/sdkconfig +**/sdkconfig.old +**/build +**/managed_components # Doc build artifacts docs/_build/ diff --git a/common_examples/iperf/CMakeLists.txt b/common_examples/iperf/CMakeLists.txt new file mode 100644 index 0000000..2aca2cc --- /dev/null +++ b/common_examples/iperf/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(iperf) diff --git a/common_examples/iperf/README.md b/common_examples/iperf/README.md new file mode 100644 index 0000000..7f3f5a0 --- /dev/null +++ b/common_examples/iperf/README.md @@ -0,0 +1,12 @@ +# Iperf example + +This exmaple provides a simple way to measure network performance using iperf. + +## About the example +The example uses `iperf` and `iperf-cmd` components for an iperf implementation and provides command line interface for it. It provides DHCP server functionality for connecting to another ESP32, instead of a PC. + +## Configuring the example +Using `idf.py menuconfig` set up the Ethernet configuration, in the `Example option` you can enable DHCP server with `Act as a DHCP server`. This will make ESP32 run an instance of DHCP server per interface assignign IP addresses in the subnet 192.168.1.0/24. + +## Running the example +You will see `esp>` prompt appear in ESP32 console. Run `iperf -h` to see iperf command options. diff --git a/common_examples/iperf/main/CMakeLists.txt b/common_examples/iperf/main/CMakeLists.txt new file mode 100644 index 0000000..2302295 --- /dev/null +++ b/common_examples/iperf/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "iperf.c" + INCLUDE_DIRS ".") diff --git a/common_examples/iperf/main/Kconfig.projbuild b/common_examples/iperf/main/Kconfig.projbuild new file mode 100644 index 0000000..6550057 --- /dev/null +++ b/common_examples/iperf/main/Kconfig.projbuild @@ -0,0 +1,7 @@ +menu "Example options" + config EXAMPLE_ACT_AS_DHCP_SERVER + bool "Act as a DHCP server" + default n + help + Set ESP32 to act as DHCP server instead of as a client. +endmenu \ No newline at end of file diff --git a/common_examples/iperf/main/idf_component.yml b/common_examples/iperf/main/idf_component.yml new file mode 100644 index 0000000..dc782d2 --- /dev/null +++ b/common_examples/iperf/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + espressif/ethernet_init: + override_path: '../../../ethernet_init/' + espressif/iperf-cmd: "^0.1.1" + espressif/iperf: "^0.1.1" diff --git a/common_examples/iperf/main/iperf.c b/common_examples/iperf/main/iperf.c new file mode 100644 index 0000000..8c69912 --- /dev/null +++ b/common_examples/iperf/main/iperf.c @@ -0,0 +1,79 @@ +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_console.h" +#include "ethernet_init.h" +#include "iperf_cmd.h" +#include "sdkconfig.h" + +esp_netif_ip_info_t ip_info = { + .ip = {.addr = ESP_IP4TOADDR(192, 168, 1, 1)}, + .netmask = {.addr = ESP_IP4TOADDR(255, 255, 255, 0)}, + .gw = {.addr = ESP_IP4TOADDR(192, 168, 1, 1)} +}; + +static void start_dhcp_server_at_connection(void *esp_netif, esp_event_base_t base, int32_t event_id, void *data) +{ + esp_netif_dhcpc_stop(esp_netif); + esp_netif_set_ip_info(esp_netif, &ip_info); + esp_netif_dhcps_start(esp_netif); +} + +void app_main(void) +{ + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles; + esp_netif_config_t cfg; + esp_netif_inherent_config_t eth_netif_cfg; + esp_netif_init(); + esp_event_loop_create_default(); + ethernet_init_all(ð_handles, ð_port_cnt); + +#if CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER + // Config for ESP32 to act as DHCP server + eth_netif_cfg = (esp_netif_inherent_config_t) { + .get_ip_event = IP_EVENT_ETH_GOT_IP, + .lost_ip_event = 0, + .flags = ESP_NETIF_DHCP_SERVER, + .ip_info = &ip_info, + .if_key = "ETH_DHCPS", + .if_desc = "eth", + .route_prio = 50 + }; +#else + if (eth_port_cnt == 1) { + // Use default config when using one interface + eth_netif_cfg = *(ESP_NETIF_BASE_DEFAULT_ETH); + } else { + // Set behavioral config to support multiple interfaces + eth_netif_cfg = (esp_netif_inherent_config_t) ESP_NETIF_INHERENT_DEFAULT_ETH(); + } +#endif + char if_key_str[10]; + char if_desc_str[10]; + cfg = (esp_netif_config_t) { + .base = ð_netif_cfg, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + }; + for (int i = 0; i < eth_port_cnt; i++) { + sprintf(if_key_str, "ETH_%d", i); + sprintf(if_desc_str, "eth%d", i); + eth_netif_cfg.if_key = if_key_str; + eth_netif_cfg.if_desc = if_desc_str; + eth_netif_cfg.route_prio -= i * 5; + esp_netif_t *eth_netif = esp_netif_new(&cfg); + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[i]))); + +#if CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER + esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, start_dhcp_server_at_connection, eth_netif); +#endif + + esp_eth_start(eth_handles[i]); + } + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + esp_console_new_repl_uart(&uart_config, &repl_config, &repl); + app_register_iperf_commands(); + esp_console_start_repl(repl); +} diff --git a/common_examples/iperf/sdkconfig.defaults.client b/common_examples/iperf/sdkconfig.defaults.client new file mode 100644 index 0000000..7ba958c --- /dev/null +++ b/common_examples/iperf/sdkconfig.defaults.client @@ -0,0 +1 @@ +CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER=n \ No newline at end of file diff --git a/common_examples/iperf/sdkconfig.defaults.server b/common_examples/iperf/sdkconfig.defaults.server new file mode 100644 index 0000000..b01535a --- /dev/null +++ b/common_examples/iperf/sdkconfig.defaults.server @@ -0,0 +1 @@ +CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER=y diff --git a/ethernet_init/examples/simple-ethernet/CMakeLists.txt b/common_examples/simple-ethernet/CMakeLists.txt similarity index 100% rename from ethernet_init/examples/simple-ethernet/CMakeLists.txt rename to common_examples/simple-ethernet/CMakeLists.txt diff --git a/ethernet_init/examples/simple-ethernet/main/CMakeLists.txt b/common_examples/simple-ethernet/main/CMakeLists.txt similarity index 100% rename from ethernet_init/examples/simple-ethernet/main/CMakeLists.txt rename to common_examples/simple-ethernet/main/CMakeLists.txt diff --git a/ethernet_init/examples/simple-ethernet/main/idf_component.yml b/common_examples/simple-ethernet/main/idf_component.yml similarity index 76% rename from ethernet_init/examples/simple-ethernet/main/idf_component.yml rename to common_examples/simple-ethernet/main/idf_component.yml index 62a3d86..cb1c895 100644 --- a/ethernet_init/examples/simple-ethernet/main/idf_component.yml +++ b/common_examples/simple-ethernet/main/idf_component.yml @@ -4,4 +4,4 @@ dependencies: ethernet_init: version: '*' # For local development use the local copy of the component - override_path: '../../../' \ No newline at end of file + override_path: '../../../ethernet_init/' \ No newline at end of file diff --git a/ethernet_init/examples/simple-ethernet/main/simple-ethernet.c b/common_examples/simple-ethernet/main/simple-ethernet.c similarity index 100% rename from ethernet_init/examples/simple-ethernet/main/simple-ethernet.c rename to common_examples/simple-ethernet/main/simple-ethernet.c diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.dm9051 b/common_examples/simple-ethernet/sdkconfig.defaults.dm9051 similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.dm9051 rename to common_examples/simple-ethernet/sdkconfig.defaults.dm9051 diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.dp83848 b/common_examples/simple-ethernet/sdkconfig.defaults.dp83848 similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.dp83848 rename to common_examples/simple-ethernet/sdkconfig.defaults.dp83848 diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.ip101 b/common_examples/simple-ethernet/sdkconfig.defaults.ip101 similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.ip101 rename to common_examples/simple-ethernet/sdkconfig.defaults.ip101 diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.ksz80xx b/common_examples/simple-ethernet/sdkconfig.defaults.ksz80xx similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.ksz80xx rename to common_examples/simple-ethernet/sdkconfig.defaults.ksz80xx diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.ksz8851snl b/common_examples/simple-ethernet/sdkconfig.defaults.ksz8851snl similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.ksz8851snl rename to common_examples/simple-ethernet/sdkconfig.defaults.ksz8851snl diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.lan867x b/common_examples/simple-ethernet/sdkconfig.defaults.lan867x similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.lan867x rename to common_examples/simple-ethernet/sdkconfig.defaults.lan867x diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.lan87xx b/common_examples/simple-ethernet/sdkconfig.defaults.lan87xx similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.lan87xx rename to common_examples/simple-ethernet/sdkconfig.defaults.lan87xx diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.rtl8201 b/common_examples/simple-ethernet/sdkconfig.defaults.rtl8201 similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.rtl8201 rename to common_examples/simple-ethernet/sdkconfig.defaults.rtl8201 diff --git a/ethernet_init/examples/simple-ethernet/sdkconfig.defaults.w5500 b/common_examples/simple-ethernet/sdkconfig.defaults.w5500 similarity index 100% rename from ethernet_init/examples/simple-ethernet/sdkconfig.defaults.w5500 rename to common_examples/simple-ethernet/sdkconfig.defaults.w5500 diff --git a/common_examples/tcp_client/CMakeLists.txt b/common_examples/tcp_client/CMakeLists.txt new file mode 100644 index 0000000..9daa0ea --- /dev/null +++ b/common_examples/tcp_client/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(tcp_client) diff --git a/common_examples/tcp_client/README.md b/common_examples/tcp_client/README.md new file mode 100644 index 0000000..1ed02ad --- /dev/null +++ b/common_examples/tcp_client/README.md @@ -0,0 +1,20 @@ +# TCP client example + +This example provides a basic implementation of TCP client for ESP32 with drivers from this repository + +## About the example +TCP client is designed to periodically transmit to the server, and accept responses, which are printed to the console. + +## Configuring the example +Configure the example using `idf.py menuconfig`, according to your setup, provide settings for Ethernet initialization. Next, go to `Example options` and input the IP address of your PC in the local network to which ESP32 is connected. If you want to connect this to another ESP32 device running `tcp_server` example, make sure to the the `Server IP address` to 196.168.1.1 + +## Running the example + +After you obtain an IP address you will see a message reading `TCP client is started, waiting for the server to accept a connection.`. + +If you are connecting the device to a PC - start the server with `tcp_server.py`. The command to do so is `tcp_server.py IP` to start listening on the specified IP address. + +**Important**: use the IP address of your PC in the local network to which ESP32 is connected. It **must** match the IP address you've set in the `Example options`. + + +You will see incoming messages in ESP32 console or as an as output of `tcp_server.py` if you have ran it. \ No newline at end of file diff --git a/common_examples/tcp_client/main/CMakeLists.txt b/common_examples/tcp_client/main/CMakeLists.txt new file mode 100644 index 0000000..9ff632b --- /dev/null +++ b/common_examples/tcp_client/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "tcp_client.c" + INCLUDE_DIRS ".") diff --git a/common_examples/tcp_client/main/Kconfig.projbuild b/common_examples/tcp_client/main/Kconfig.projbuild new file mode 100644 index 0000000..e02c8fe --- /dev/null +++ b/common_examples/tcp_client/main/Kconfig.projbuild @@ -0,0 +1,7 @@ +menu "Example options" + config EXAMPLE_SERVER_IP_ADDRESS + string "Server IP address" + default "192.168.1.1" + help + TCP server IP. +endmenu \ No newline at end of file diff --git a/common_examples/tcp_client/main/idf_component.yml b/common_examples/tcp_client/main/idf_component.yml new file mode 100644 index 0000000..cb1c895 --- /dev/null +++ b/common_examples/tcp_client/main/idf_component.yml @@ -0,0 +1,7 @@ +dependencies: + idf: + version: '>=5.0' + ethernet_init: + version: '*' + # For local development use the local copy of the component + override_path: '../../../ethernet_init/' \ No newline at end of file diff --git a/common_examples/tcp_client/main/tcp_client.c b/common_examples/tcp_client/main/tcp_client.c new file mode 100644 index 0000000..935a4f9 --- /dev/null +++ b/common_examples/tcp_client/main/tcp_client.c @@ -0,0 +1,102 @@ +#include +#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 "ethernet_init.h" +#include "lwip/sockets.h" +#include "sdkconfig.h" + +#define SOCKET_PORT 5000 +#define SOCKET_MAX_LENGTH 128 + +static const char *TAG = "tcp_client"; +static SemaphoreHandle_t x_got_ip_semaphore; + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *data) +{ + ip_event_got_ip_t *event = (ip_event_got_ip_t *) data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + xSemaphoreGive(x_got_ip_semaphore); +} + +void app_main(void) +{ + // Create default event loop that running in background + ESP_ERROR_CHECK(esp_event_loop_create_default()); + // Initialize semaphore + x_got_ip_semaphore = xSemaphoreCreateBinary(); + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles; + char if_key_str[10]; + char if_desc_str[10]; + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + esp_netif_init(); + // Register user defined event handers + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); + // Create instance(s) of esp-netif for Ethernet(s) + if (eth_port_cnt == 1) { + // Use ESP_NETIF_DEFAULT_ETH when just one Ethernet interface is used and you don't need to modify + // default esp-netif configuration parameters. + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *eth_netif = esp_netif_new(&cfg); + // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); + } else { + // Use ESP_NETIF_INHERENT_DEFAULT_ETH when multiple Ethernet interfaces are used and so you need to modify + // esp-netif configuration parameters for each interface (name, priority, etc.). + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); + esp_netif_config_t cfg_spi = { + .base = &esp_netif_config, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + }; + + for (int i = 0; i < eth_port_cnt; i++) { + sprintf(if_key_str, "ETH_%d", i); + sprintf(if_desc_str, "eth%d", i); + esp_netif_config.if_key = if_key_str; + esp_netif_config.if_desc = if_desc_str; + esp_netif_config.route_prio -= i * 5; + esp_netif_t *eth_netif = esp_netif_new(&cfg_spi); + + // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[i]))); + esp_eth_start(eth_handles[i]); + } + } + // Wait until IP address is assigned to this device + xSemaphoreTake(x_got_ip_semaphore, portMAX_DELAY); + ESP_LOGI(TAG, "TCP client is started, waiting for the server to accept a connection."); + int client_fd; + struct sockaddr_in server; + char rxbuffer[SOCKET_MAX_LENGTH] = {0}; + char txbuffer[SOCKET_MAX_LENGTH] = {0}; + client_fd = socket(AF_INET, SOCK_STREAM, 0); + server.sin_family = AF_INET; + server.sin_port = htons(SOCKET_PORT); + server.sin_addr.s_addr = inet_addr(CONFIG_EXAMPLE_SERVER_IP_ADDRESS); + ESP_ERROR_CHECK(connect(client_fd, (struct sockaddr *)&server, sizeof(struct sockaddr))); + int transmission_cnt = 0; + while (1) { + snprintf(txbuffer, SOCKET_MAX_LENGTH, "Transmission #%d. Hello from ESP32 TCP client", ++transmission_cnt); + ESP_LOGI(TAG, "Transmitting: \"%s\"", txbuffer); + write(client_fd, txbuffer, SOCKET_MAX_LENGTH); + read(client_fd, rxbuffer, SOCKET_MAX_LENGTH); + ESP_LOGI(TAG, "Received \"%s\"", rxbuffer); + memset(txbuffer, 0, SOCKET_MAX_LENGTH); + memset(rxbuffer, 0, SOCKET_MAX_LENGTH); + vTaskDelay(pdMS_TO_TICKS(500)); + } +} diff --git a/common_examples/tcp_client/tcp_server.py b/common_examples/tcp_client/tcp_server.py new file mode 100644 index 0000000..c85c5f8 --- /dev/null +++ b/common_examples/tcp_client/tcp_server.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +import socket +import argparse +import logging +import signal + +parser = argparse.ArgumentParser(description='Serve TCP connection using berkley sockets and wait for connections', epilog='Part of the tcp_client example for esp_eth_drivers') +parser.add_argument('ip') +args = parser.parse_args() + +SOCKET_PORT = 5000 + +# setup sigint handler +signal.signal(signal.SIGINT, lambda s, f : exit(0)) + +logger = logging.getLogger("tcp_server") +logging.basicConfig(format="%(name)s :: %(levelname)-8s :: %(message)s", level=logging.DEBUG) +logger.info("Listening on %s:%d", args.ip, SOCKET_PORT) + +# init server +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.bind((args.ip, 5000)) +# listen for incoming connections +sock.listen(1) + +counter = 1 +while True: + conn, address = sock.accept() + logger.debug("Accepted connection from %s:%d", address[0], address[1]) + while True: + try: + data = conn.recv(128).decode() + except ConnectionAbortedError: + logger.info("Connection closed by client") + break + logger.debug("Received: \"%s\"", data) + msg = f"Transmission {counter}: Hello from Python" + logger.debug("Transmitting: \"%s\"", msg) + conn.sendall(str.encode(msg)) + counter += 1 diff --git a/common_examples/tcp_server/CMakeLists.txt b/common_examples/tcp_server/CMakeLists.txt new file mode 100644 index 0000000..af72d7f --- /dev/null +++ b/common_examples/tcp_server/CMakeLists.txt @@ -0,0 +1,10 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +set(COMPONENT main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(tcp_server) diff --git a/common_examples/tcp_server/README.md b/common_examples/tcp_server/README.md new file mode 100644 index 0000000..ceaeb05 --- /dev/null +++ b/common_examples/tcp_server/README.md @@ -0,0 +1,20 @@ +# TCP server example + +This example provides a basic implementation of TCP server for ESP32 with drivers from this repository. + +## About the example +TCP server is designed to accept transmissions from the client, print them to the console and respond with another message "Transmission #XX. Hello from ESP32 TCP server". + +## Configuring the example +Configure the example using `idf.py menuconfig`, according to your setup, provide settings for Ethernet initialization. If you want to connect to another ESP32, set the +`Act as DHCP server` option in the `Example options`, the IP address of the device will be 192.168.1.1. + +## Running the example +To transmit data between your PC and ESP32 you need to wait until it obtains an IP address from DHCP server and run tcp client script. + +If you are connecting the device to a PC you will need to run a client script - the minimal command to do it is `tcp_client.py IP` and it will run until the script is stopped. Additional parameters are: +* `-c COUNT` to set the amount of transmission after which the client stops transmitting, terminates the connection and presents stats +* `-t TIME` to set periods between transmissions (default: 500ms) +* `-s` to run silently, without printing debug messages. In this mode count is set to 10 if not specified otherwise + +You will see the ouput both in ESP32's console, and as the output of `tcp_client.py` if you have ran it. \ No newline at end of file diff --git a/common_examples/tcp_server/main/CMakeLists.txt b/common_examples/tcp_server/main/CMakeLists.txt new file mode 100644 index 0000000..c8a8d16 --- /dev/null +++ b/common_examples/tcp_server/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "tcp_server.c" + INCLUDE_DIRS ".") diff --git a/common_examples/tcp_server/main/Kconfig.projbuild b/common_examples/tcp_server/main/Kconfig.projbuild new file mode 100644 index 0000000..c577730 --- /dev/null +++ b/common_examples/tcp_server/main/Kconfig.projbuild @@ -0,0 +1,7 @@ +menu "Example options" + config EXAMPLE_ACT_AS_DHCP_SERVER + bool "Act sa DHCP server" + default n + help + Set ESP32 to act as DHCP server instead of as a client. +endmenu \ No newline at end of file diff --git a/common_examples/tcp_server/main/idf_component.yml b/common_examples/tcp_server/main/idf_component.yml new file mode 100644 index 0000000..cb1c895 --- /dev/null +++ b/common_examples/tcp_server/main/idf_component.yml @@ -0,0 +1,7 @@ +dependencies: + idf: + version: '>=5.0' + ethernet_init: + version: '*' + # For local development use the local copy of the component + override_path: '../../../ethernet_init/' \ No newline at end of file diff --git a/common_examples/tcp_server/main/tcp_server.c b/common_examples/tcp_server/main/tcp_server.c new file mode 100644 index 0000000..f09be4f --- /dev/null +++ b/common_examples/tcp_server/main/tcp_server.c @@ -0,0 +1,113 @@ +#include +#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 "ethernet_init.h" +#include "lwip/sockets.h" + +#define SOCKET_PORT 5000 +#define SOCKET_MAX_LENGTH 128 +static const char *TAG = "tcp_server"; + +esp_netif_ip_info_t ip_info = { + .ip = {.addr = ESP_IP4TOADDR(192, 168, 1, 1)}, + .netmask = {.addr = ESP_IP4TOADDR(255, 255, 255, 0)}, + .gw = {.addr = ESP_IP4TOADDR(192, 168, 1, 1)} +}; + +static void start_dhcp_server_at_connection(void *esp_netif, esp_event_base_t base, int32_t event_id, void *data) +{ + esp_netif_dhcpc_stop(esp_netif); + esp_netif_set_ip_info(esp_netif, &ip_info); + esp_netif_dhcps_start(esp_netif); +} + + +void app_main(void) +{ + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles; + esp_netif_config_t cfg; + esp_netif_inherent_config_t eth_netif_cfg; + esp_netif_init(); + esp_event_loop_create_default(); + ethernet_init_all(ð_handles, ð_port_cnt); + +#if CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER + // Config for ESP32 to act as DHCP server + eth_netif_cfg = (esp_netif_inherent_config_t) { + .get_ip_event = IP_EVENT_ETH_GOT_IP, + .lost_ip_event = 0, + .flags = ESP_NETIF_DHCP_SERVER, + .ip_info = &ip_info, + .if_key = "ETH_DHCPS", + .if_desc = "eth", + .route_prio = 50 + }; +#else + if (eth_port_cnt == 1) { + // Use default config when using one interface + eth_netif_cfg = *(ESP_NETIF_BASE_DEFAULT_ETH); + } else { + // Set behavioral config to support multiple interfaces + eth_netif_cfg = (esp_netif_inherent_config_t) ESP_NETIF_INHERENT_DEFAULT_ETH(); + } +#endif + char if_key_str[10]; + char if_desc_str[10]; + cfg = (esp_netif_config_t) { + .base = ð_netif_cfg, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + }; + for (int i = 0; i < eth_port_cnt; i++) { + sprintf(if_key_str, "ETH_%d", i); + sprintf(if_desc_str, "eth%d", i); + eth_netif_cfg.if_key = if_key_str; + eth_netif_cfg.if_desc = if_desc_str; + eth_netif_cfg.route_prio -= i * 5; + esp_netif_t *eth_netif = esp_netif_new(&cfg); + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[i]))); + +#if CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER + esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, start_dhcp_server_at_connection, eth_netif); +#endif + + esp_eth_start(eth_handles[i]); + } + int server_fd, client_fd; + struct sockaddr_in server, client; + char rxbuffer[SOCKET_MAX_LENGTH] = {0}; + char txbuffer[SOCKET_MAX_LENGTH] = {0}; + server_fd = socket(AF_INET, SOCK_STREAM, 0); + + server.sin_family = AF_INET; + server.sin_port = htons(SOCKET_PORT); + server.sin_addr.s_addr = htonl(INADDR_ANY); + + bind(server_fd, (struct sockaddr *) &server, sizeof(server)); + listen(server_fd, 1); + int transmission_cnt = 0; + while (1) { + socklen_t client_len = sizeof(client); + client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len); + if (client_fd == -1) { + ESP_LOGE(TAG, "An error occured when acceptig a connection (errno: %d)", errno); + } + while (1) { + int read = recv(client_fd, rxbuffer, SOCKET_MAX_LENGTH, 0); + if (read == -1) { + ESP_LOGE(TAG, "An error occured when receiving data (errno: %d)", errno); + } else if (!read) { + break; // done reading + } + ESP_LOGI(TAG, "Received \"%s\"", rxbuffer); + snprintf(txbuffer, SOCKET_MAX_LENGTH, "Transmission #%d. Hello from ESP32 TCP server", ++transmission_cnt); + ESP_LOGI(TAG, "Transmitting: \"%s\"", txbuffer); + send(client_fd, txbuffer, read, 0); + } + } +} diff --git a/common_examples/tcp_server/tcp_client.py b/common_examples/tcp_server/tcp_client.py new file mode 100644 index 0000000..e559f9b --- /dev/null +++ b/common_examples/tcp_server/tcp_client.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +import socket +import argparse +import logging +import time +import signal + +parser = argparse.ArgumentParser(description='Establish TCP connection to a server using berkley sockets and periodically send transmission expecting echo', epilog='Part of the tcp_server example for esp_eth_drivers') +parser.add_argument('ip') +parser.add_argument('-t', '--time', type=int, default=500, help='Period of transmission (ms)') +parser.add_argument('-c', '--count', type=int, help='How many transmissions to perform (default: no limit)') +parser.add_argument('-s', '--silent', action='store_true', help="Do not log transmissions, just print out the result, automatically limits count to 10") +args = parser.parse_args() + +SOCKET_PORT = 5000 + +# setup sigint handler +signal.signal(signal.SIGINT, lambda s, f : exit(0)) + +logger = logging.getLogger("tcp_client") +logging.basicConfig(format="%(name)s :: %(levelname)-8s :: %(message)s", level=logging.DEBUG) +if args.silent: + logger.setLevel(logging.INFO) + args.count = 10 +logger.info("Transmitting to %s:%d every %d ms", args.ip, SOCKET_PORT, args.time) + +# init socket connection +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.settimeout(10) +try: + sock.connect((args.ip, args.port)) +except TimeoutError: + logger.error("Couldn't establish connection due to timeout") + exit(1) +# do transmissions +counter = 1 +receives = 0 +timeouts = 0 +while args.count == None or counter <= args.count: + msg = f"Transmission {counter}: Hello from Python" + logger.debug("Transmitting: \"%s\"", msg) + sock.sendall(str.encode(msg)) + try: + data = sock.recv(128) + receives+=1 + except TimeoutError: + logger.error("Timeout, no echo received") + timeouts+=1 + logger.debug("Received: \"%s\"", data.decode()) + counter+=1 + time.sleep(args.time * 0.001) +sock.close() +logger.info("Performed %d transmissions, received %d replies with %d timeouts (%.2f%% success rate)", args.count, receives, timeouts, (1 - timeouts/receives)*100.0) \ No newline at end of file diff --git a/ethernet_init/idf_component.yml b/ethernet_init/idf_component.yml index c145b0d..c13057a 100644 --- a/ethernet_init/idf_component.yml +++ b/ethernet_init/idf_component.yml @@ -1,6 +1,8 @@ dependencies: idf: version: '>=5.1' +examples: + - path: ../common_examples/ description: This component initializes Ethernet driver based on Espressif IoT Development Framework Configuration. url: https://github.com/espressif/esp-eth-drivers/tree/master/ethernet_init -version: 0.2.1 +version: 0.2.2