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 Apr 19, 2024
1 parent a631cfe commit d299f03
Show file tree
Hide file tree
Showing 37 changed files with 512 additions and 3 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.*='
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ GPATH
**/examples/**/sdkconfig.old
**/examples/**/build
**/examples/**/managed_components
common_examples/**/sdkconfig
common_examples/**/sdkconfig.old
common_examples/**/build
common_examples/**/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)
14 changes: 14 additions & 0 deletions common_examples/iperf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 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 providing 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` setup the Ethernet configuration, in `Example option` you can enable DHCP server.

**Important**: If DHCP server functionality is enabled only first PHY can be used. In DHCP client mode you can use any available.

## 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 sa DHCP server"
default n
help
Set ESP32 to act as DHCP server instead of as a client.
endmenu
5 changes: 5 additions & 0 deletions common_examples/iperf/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dependencies:
espressif/ethernet_init:
override_path: '../../../ethernet_init/'
espressif/iperf-cmd: "^0.1.1"
espressif/iperf: "^0.1.1"
91 changes: 91 additions & 0 deletions common_examples/iperf/main/iperf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#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"

static void my_event_connected_handler(void *esp_netif, esp_event_base_t base, int32_t event_id, void *data)
{
esp_netif_dhcps_start(esp_netif);
}


void app_main(void)
{
uint8_t eth_port_cnt = 0;
esp_eth_handle_t *eth_handles;
esp_netif_init();
esp_event_loop_create_default();
ethernet_init_all(&eth_handles, &eth_port_cnt);

//esp_netif_config_t cfg;
#if CONFIG_EXAMPLE_ACT_AS_DHCP_SERVER
// Act as DHCP 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, 255)}
};
const esp_netif_inherent_config_t eth_behav_cfg = {
.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
};
esp_netif_config_t cfg = { .base = &eth_behav_cfg, .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH };

esp_netif_t *eth_netif = esp_netif_new(&cfg);
esp_eth_handle_t eth_handle = eth_handles[0];
esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]));

esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, my_event_connected_handler, eth_netif);
esp_netif_dhcpc_stop(eth_netif);
esp_netif_set_ip_info(eth_netif, &ip_info);
esp_eth_start(eth_handle);
#else
// Act as DHCP client, usual behaviour
char if_key_str[10];
char if_desc_str[10];
// 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]);
}
}
#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)
20 changes: 20 additions & 0 deletions common_examples/tcp_client/README.md
Original file line number Diff line number Diff line change
@@ -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 networkto wich ESP32 is connected.

## Running the example

Ater you obtain an IP address you will see message reading `TCP client is started, waiting for the server to accept a connection.`.

Start the server with `tcp_server.py`. Command to do so is `tcp_server.py IP` to start listening on specified IP address.

**Important**: use the IP address of your PC in the local network to whic ESP32 is connected. It **must** match the IP address you've set in `Example options`.


You will see incoming messages as output of `tcp_server.py`.
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.0"
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/'
102 changes: 102 additions & 0 deletions common_examples/tcp_client/main/tcp_client.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#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 xGotIpSemaphore;

/** 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(xGotIpSemaphore);
}

void app_main(void)
{
// Create default event loop that running in background
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Initialize semaphore
xGotIpSemaphore = 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(xGotIpSemaphore, 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);
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));
}
}
40 changes: 40 additions & 0 deletions common_examples/tcp_client/tcp_server.py
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions common_examples/tcp_server/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_server)
18 changes: 18 additions & 0 deletions common_examples/tcp_server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 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 client, print it to 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.

## 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.
Minimal command to do it is `tcp_client.py IP` and it will run until script is stopped. Additional parameters are:
* `-c COUNT` to set amount of transmission after which client stops transmitting, terminates 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 incoming messages in ESP32's console, and server's answers as output of `tcp_client.py`
Loading

0 comments on commit d299f03

Please sign in to comment.