Skip to content

Commit

Permalink
feat(common_examples): Add common examples aplicable for all phys
Browse files Browse the repository at this point in the history
  • Loading branch information
bogdankolendovskyy committed Jun 7, 2024
1 parent 2ca2628 commit c09c4d9
Show file tree
Hide file tree
Showing 38 changed files with 644 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand All @@ -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.*='
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ GPATH
**/test_apps/**/sdkconfig
**/test_apps/**/sdkconfig.old

# Example project files
**/examples/**/sdkconfig
**/examples/**/sdkconfig.old
**/examples/**/build
**/examples/**/managed_components
# Project files
**/sdkconfig
**/sdkconfig.old
**/build
**/managed_components

# Doc build artifacts
docs/_build/
Expand Down
8 changes: 8 additions & 0 deletions common_examples/iperf/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
12 changes: 12 additions & 0 deletions common_examples/iperf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Iperf example

This example 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.
2 changes: 2 additions & 0 deletions common_examples/iperf/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "iperf.c"
INCLUDE_DIRS ".")
7 changes: 7 additions & 0 deletions common_examples/iperf/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions common_examples/iperf/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dependencies:
espressif/ethernet_init:
override_path: '../../../ethernet_init/'
espressif/iperf-cmd: "^0.1.1"
114 changes: 114 additions & 0 deletions common_examples/iperf/main/iperf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_console.h"
#include "esp_log.h"
#include "ethernet_init.h"
#include "iperf_cmd.h"
#include "sdkconfig.h"

static const char *TAG = "iperf_example";

static void start_dhcp_server_after_connection(void *arg, esp_event_base_t base, int32_t id, void *event_data)
{
esp_netif_t *eth_netif = esp_netif_next_unsafe(NULL);
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
while (eth_netif != NULL) {
esp_eth_handle_t eth_handle_for_current_netif = esp_netif_get_io_driver(eth_netif);
if (memcmp(&eth_handle, &eth_handle_for_current_netif, sizeof(esp_eth_handle_t)) == 0) {
esp_netif_dhcpc_stop(eth_netif);
esp_netif_dhcps_start(eth_netif);
}
eth_netif = esp_netif_next_unsafe(eth_netif);
}
}

void app_main(void)
{
uint8_t eth_port_cnt = 0;
char if_key_str[10];
char if_desc_str[10];
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(&eth_handles, &eth_port_cnt);

#if CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER
esp_netif_ip_info_t *ip_infos;

ip_infos = calloc(eth_port_cnt, sizeof(esp_netif_ip_info_t));

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,
.route_prio = 50
};
cfg = (esp_netif_config_t) {
.base = &eth_netif_cfg,
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
};

for (uint8_t i = 0; i < eth_port_cnt; i++) {
sprintf(if_key_str, "ETH_S%d", i);
sprintf(if_desc_str, "eth%d", i);

esp_netif_ip_info_t ip_info_i = {
.ip = {.addr = ESP_IP4TOADDR(192, 168, i, 1)},
.netmask = {.addr = ESP_IP4TOADDR(255, 255, 255, 0)},
.gw = {.addr = ESP_IP4TOADDR(192, 168, i, 1)}
};
ip_infos[i] = ip_info_i;

eth_netif_cfg.if_key = if_key_str;
eth_netif_cfg.if_desc = if_desc_str;
eth_netif_cfg.route_prio -= i * 5;
eth_netif_cfg.ip_info = &(ip_infos[i]);
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])));
}
esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, start_dhcp_server_after_connection, NULL);
ESP_LOGI(TAG, "--------");
for (uint8_t i = 0; i < eth_port_cnt; i++) {
esp_eth_start(eth_handles[i]);
ESP_LOGI(TAG, "Network Interface %d: " IPSTR, i, IP2STR(&ip_infos[i].ip));
}
ESP_LOGI(TAG, "--------");
#else
if (eth_port_cnt == 1) {
// Use default config when using one interface
eth_netif_cfg = *(ESP_NETIF_BASE_DEFAULT_ETH);
} else {
// Set config to support multiple interfaces
eth_netif_cfg = (esp_netif_inherent_config_t) ESP_NETIF_INHERENT_DEFAULT_ETH();
}
cfg = (esp_netif_config_t) {
.base = &eth_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])));
esp_eth_start(eth_handles[i]);
}
#endif
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);
}
1 change: 1 addition & 0 deletions common_examples/iperf/sdkconfig.defaults.client
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER=n
1 change: 1 addition & 0 deletions common_examples/iperf/sdkconfig.defaults.server
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER=y
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ dependencies:
ethernet_init:
version: '*'
# For local development use the local copy of the component
override_path: '../../../'
override_path: '../../../ethernet_init/'
8 changes: 8 additions & 0 deletions common_examples/tcp_client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
34 changes: 34 additions & 0 deletions common_examples/tcp_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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 set the `Server IP address`.

### Determining the server IP address
If you are running a TCP server on your PC using `tcp_server.py` - run a command to see your IP:
* Windows - `ipconfig /all`
* macOS/Linux - `ifconfig`

If you are running a TCP server on another ESP32 along with a DHCP server - see the output of your server for lines such as:
```
I (6349) tcp_server: --------
I (6359) tcp_server: Network Interface 0: 192.168.0.1
I (6359) tcp_server: Network Interface 1: 192.168.1.1
I (6369) tcp_server: --------
```
It will list all your network interfaces and the IP address assigned to it. Depending on the interface you are connected to - set the `Server IP address` accordingly.

## 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 the ESP32 console or as an output of `tcp_server.py` if you are connecting the ESP32 to your PC and running a TCP server using a script.
2 changes: 2 additions & 0 deletions common_examples/tcp_client/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "tcp_client.c"
INCLUDE_DIRS ".")
7 changes: 7 additions & 0 deletions common_examples/tcp_client/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions common_examples/tcp_client/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -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/'
128 changes: 128 additions & 0 deletions common_examples/tcp_client/main/tcp_client.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#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 "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(&eth_handles, &eth_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 has started, waiting for the server to accept a connection.");
int client_fd, ret;
struct sockaddr_in server;
char rxbuffer[SOCKET_MAX_LENGTH] = {0};
char txbuffer[SOCKET_MAX_LENGTH] = {0};
client_fd = socket(AF_INET, SOCK_STREAM, 0);
if (client_fd == -1) {
ESP_LOGE(TAG, "Could not create the socket (errno: %d)", errno);
goto err;
}
server.sin_family = AF_INET;
server.sin_port = htons(SOCKET_PORT);
server.sin_addr.s_addr = inet_addr(CONFIG_EXAMPLE_SERVER_IP_ADDRESS);
ret = connect(client_fd, (struct sockaddr *)&server, sizeof(struct sockaddr));
if (ret == -1) {
ESP_LOGE(TAG, "An error has occurred while connecting to the server (errno: %d)", errno);
goto err;
}
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);
ret = send(client_fd, txbuffer, SOCKET_MAX_LENGTH, 0);
if (ret == -1) {
ESP_LOGE(TAG, "An error has occurred while sending data (errno: %d)", errno);
break;
}
ret = recv(client_fd, rxbuffer, SOCKET_MAX_LENGTH, 0);
if (ret == -1) {
ESP_LOGE(TAG, "An error has occurred while receiving data (errno: %d)", errno);
} else if (ret == 0) {
break; // done reading
}
ESP_LOGI(TAG, "Received \"%s\"", rxbuffer);
memset(txbuffer, 0, SOCKET_MAX_LENGTH);
memset(rxbuffer, 0, SOCKET_MAX_LENGTH);
vTaskDelay(pdMS_TO_TICKS(500));
}
return;
err:
close(client_fd);
ESP_LOGI(TAG, "Program was stopped because an error occured");
}
Loading

0 comments on commit c09c4d9

Please sign in to comment.