feat(eppp): Added support for SPI transport

This commit is contained in:
David Cermak
2023-12-22 20:03:01 +01:00
parent ad27414a64
commit 18f845275f
19 changed files with 1192 additions and 431 deletions

View File

@ -49,3 +49,7 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
### console_cmd_wifi
* Brief introduction [README](components/console_cmd_wifi/README.md)
### ESP PPP Link (eppp)
* Brief introduction [README](components/eppp_link/README.md)

View File

@ -32,19 +32,4 @@ menu "eppp_link"
Size of the Tx packet queue.
You can decrease the number for slower bit rates.
config EPPP_LINK_SERVER_IP
hex "Server IP address"
range 0 0xFFFFFFFF
default 0xc0a80b01
help
Preferred IP address of the server side.
config EPPP_LINK_CLIENT_IP
hex "Client IP address"
range 0 0xFFFFFFFF
default 0xc0a80b02
help
Preferred IP address of the client side.
endmenu

View File

@ -1,6 +1,14 @@
# ESP PPP Link component (eppp_link)
The component provides a general purpose connectivity engine between two micro-controllers, one acting as PPP server (slave), the other one as PPP client (host). Typical application is a WiFi connectivity provider for chips that do not have WiFi:
The component provides a general purpose connectivity engine between two microcontrollers, one acting as PPP server (slave), the other one as PPP client (host).
This component could be used for extending network using physical serial connection. Applications could vary from providing PRC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi
## Typical application
Using this component we can construct a WiFi connectivity gateway on PPP channel. The below diagram depicts an application where
PPP server is running on a WiFi capable chip with NAPT module translating packets between WiFi and PPPoS interface.
We usually call this node a SLAVE microcontroller. The "HOST" microcontroller runs PPP client and connects only to the serial line,
brings in the WiFi connectivity from the "SLAVE" microcontroller.
```
SLAVE micro HOST micro
@ -17,16 +25,17 @@ The component provides a general purpose connectivity engine between two micro-c
* `eppp_connect()` -- Simplified API. Provides the initialization, starts the task and blocks until we're connected
* `eppp_client_init()` -- Initialization of the client. Need to run only once.
* `eppp_client_start()` -- Starts the connection, could be called after startup or whenever a connection is lost
* `eppp_client_perform()` -- Perform one iteration of the PPP task (need to be called regularly in task-less configuration)
### Server
* `eppp_listen()` -- Simplified API. Provides the initialization, starts the task and blocks until the client connects
* `eppp_server_init()` -- Initialization of the server. Need to run only once.
* `eppp_server_start()` -- (Re)starts the connection, should be called after startup or whenever a connection is lost
* `eppp_server_perform()` -- Perform one iteration of the PPP task (need to be called regularly in task-less configuration)
### Manual actions
* `eppp_init()` -- Initializes one endpoint (client/server).
* `eppp_deinit()` -- Destroys the endpoint
* `eppp_netif_start()` -- Starts the network, could be called after startup or whenever a connection is lost
* `eppp_netif_stop()` -- Stops the network
* `eppp_perform()` -- Perform one iteration of the PPP task (need to be called regularly in task-less configuration)
## Throughput
@ -34,10 +43,10 @@ Tested with WiFi-NAPT example, no IRAM optimizations
### UART @ 3Mbauds
* TCP - 2Mbits
* UDP - 2Mbits
* TCP - 2Mbits/s
* UDP - 2Mbits/s
### SPI @ 40MHz
### SPI @ 20MHz
* TCP - 6Mbits
* UDP - 10Mbits
* TCP - 6Mbits/s
* UDP - 10Mbits/s

File diff suppressed because it is too large Load Diff

View File

@ -20,24 +20,34 @@ menu "Example Configuration"
help
URL of the broker to connect to.
config EXAMPLE_ICMP_PING
bool "Run ping example"
default y
help
Ping configured address after startup.
config EXAMPLE_PING_ADDR
hex "Ping IPv4 address"
depends on EXAMPLE_ICMP_PING
range 0 0xFFFFFFFF
default 0x08080808
help
Address to send ping requests.
config EXAMPLE_IPERF
bool "Run iperf"
default y
help
Init and run iperf console.
config EXAMPLE_UART_TX_PIN
int "TXD Pin Number"
depends on EPPP_LINK_DEVICE_UART
default 10
range 0 31
help
Pin number of UART TX.
config EXAMPLE_UART_RX_PIN
int "RXD Pin Number"
depends on EPPP_LINK_DEVICE_UART
default 11
range 0 31
help
Pin number of UART RX.
config EXAMPLE_UART_BAUDRATE
int "Baudrate"
depends on EPPP_LINK_DEVICE_UART
default 2000000
range 0 4000000
help
Baudrate used by the PPP over UART
endmenu

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -13,11 +13,9 @@
#include "esp_event.h"
#include "esp_netif.h"
#include "eppp_link.h"
#include "lwip/sockets.h"
#include "esp_log.h"
#include "mqtt_client.h"
#include "ping/ping_sock.h"
#include "esp_console.h"
#include "console_ping.h"
void register_iperf(void);
@ -80,7 +78,7 @@ static void mqtt_event_handler(void *args, esp_event_base_t base, int32_t event_
static void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = CONFIG_EXAMPLE_BROKER_URL,
.broker.address.uri = "mqtt://mqtt.eclipseprojects.io",
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
@ -90,43 +88,6 @@ static void mqtt_app_start(void)
}
#endif // MQTT
#if CONFIG_EXAMPLE_ICMP_PING
static void test_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%" PRId32 "bytes from %s icmp_seq=%d ttl=%d time=%" PRId32 " ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void test_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
{
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("%" PRId32 " packets transmitted, %" PRId32 " received, time %" PRId32 "ms\n", transmitted, received, total_time_ms);
}
#endif // PING
void app_main(void)
{
@ -140,7 +101,16 @@ void app_main(void)
/* Sets up the default EPPP-connection
*/
esp_netif_t *eppp_netif = eppp_connect();
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
#if CONFIG_EPPP_LINK_DEVICE_SPI
config.transport = EPPP_TRANSPORT_SPI;
#else
config.transport = EPPP_TRANSPORT_UART;
config.uart.tx_io = CONFIG_EXAMPLE_UART_TX_PIN;
config.uart.rx_io = CONFIG_EXAMPLE_UART_RX_PIN;
config.uart.baud = CONFIG_EXAMPLE_UART_BAUDRATE;
#endif
esp_netif_t *eppp_netif = eppp_connect(&config);
if (eppp_netif == NULL) {
ESP_LOGE(TAG, "Failed to connect");
return ;
@ -152,13 +122,6 @@ void app_main(void)
ESP_ERROR_CHECK(esp_netif_set_dns_info(eppp_netif, ESP_NETIF_DNS_MAIN, &dns));
#if CONFIG_EXAMPLE_IPERF
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();
repl_config.prompt = "iperf>";
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
register_iperf();
printf("\n =======================================================\n");
@ -174,28 +137,15 @@ void app_main(void)
printf(" | |\n");
printf(" =======================================================\n\n");
#endif // CONFIG_EXAMPLE_IPERF
// Initialize console REPL
ESP_ERROR_CHECK(console_cmd_init());
// Register the ping command
ESP_ERROR_CHECK(console_cmd_ping_register());
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(repl));
#endif
#if CONFIG_EXAMPLE_ICMP_PING
ip_addr_t target_addr = { .type = IPADDR_TYPE_V4, .u_addr.ip4.addr = esp_netif_htonl(CONFIG_EXAMPLE_PING_ADDR) };
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
ping_config.timeout_ms = 2000;
ping_config.interval_ms = 20,
ping_config.target_addr = target_addr;
ping_config.count = 100; // ping in infinite mode
/* set callback functions */
esp_ping_callbacks_t cbs;
cbs.on_ping_success = test_on_ping_success;
cbs.on_ping_timeout = test_on_ping_timeout;
cbs.on_ping_end = test_on_ping_end;
esp_ping_handle_t ping;
esp_ping_new_session(&ping_config, &cbs, &ping);
/* start ping */
esp_ping_start(ping);
#endif // PING
ESP_ERROR_CHECK(console_cmd_start());
#if CONFIG_EXAMPLE_MQTT
mqtt_app_start();

View File

@ -2,3 +2,5 @@ dependencies:
espressif/eppp_link:
version: "*"
override_path: "../../.."
console_cmd_ping:
version: "*"

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -46,18 +46,14 @@ static struct {
static int ppp_cmd_iperf(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
iperf_cfg_t cfg;
// ethernet iperf only support IPV4 address
iperf_cfg_t cfg = {.type = IPERF_IP_TYPE_IPV4};
if (nerrors != 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 0;
}
memset(&cfg, 0, sizeof(cfg));
// ethernet iperf only support IPV4 address
cfg.type = IPERF_IP_TYPE_IPV4;
/* iperf -a */
if (iperf_args.abort->count != 0) {
iperf_stop();

View File

@ -3,6 +3,6 @@
#
CONFIG_UART_ISR_IN_IRAM=y
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_LWIP_PPP_DEBUG_ON=y
CONFIG_EPPP_LINK_DEVICE_SPI=y

View File

@ -18,4 +18,28 @@ menu "Example Configuration"
help
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
config EXAMPLE_UART_TX_PIN
int "TXD Pin Number"
depends on EPPP_LINK_DEVICE_UART
default 11
range 0 31
help
Pin number of UART TX.
config EXAMPLE_UART_RX_PIN
int "RXD Pin Number"
depends on EPPP_LINK_DEVICE_UART
default 10
range 0 31
help
Pin number of UART RX.
config EXAMPLE_UART_BAUDRATE
int "Baudrate"
depends on EPPP_LINK_DEVICE_UART
default 2000000
range 0 4000000
help
Baudrate used by the PPP over UART
endmenu

View File

@ -1,12 +1,11 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
@ -15,9 +14,6 @@
#include "nvs_flash.h"
#include "eppp_link.h"
#include "lwip/err.h"
#include "lwip/sys.h"
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
@ -123,7 +119,16 @@ void app_main(void)
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
esp_netif_t *eppp_netif = eppp_listen();
eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG();
#if CONFIG_EPPP_LINK_DEVICE_SPI
config.transport = EPPP_TRANSPORT_SPI;
#else
config.transport = EPPP_TRANSPORT_UART;
config.uart.tx_io = CONFIG_EXAMPLE_UART_TX_PIN;
config.uart.rx_io = CONFIG_EXAMPLE_UART_RX_PIN;
config.uart.baud = CONFIG_EXAMPLE_UART_BAUDRATE;
#endif
esp_netif_t *eppp_netif = eppp_listen(&config);
if (eppp_netif == NULL) {
ESP_LOGE(TAG, "Failed to setup connection");
return ;

View File

@ -1,9 +1,111 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
esp_netif_t *eppp_connect(void);
#define EPPP_DEFAULT_SERVER_IP() ESP_IP4TOADDR(192, 168, 11, 1)
#define EPPP_DEFAULT_CLIENT_IP() ESP_IP4TOADDR(192, 168, 11, 2)
esp_netif_t *eppp_listen(void);
#define EPPP_DEFAULT_CONFIG(our_ip, their_ip) { \
.transport = EPPP_TRANSPORT_UART, \
.spi = { \
.host = 1, \
.mosi = 11, \
.miso = 13, \
.sclk = 12, \
.cs = 10, \
.intr = 2, \
.freq = 16*1000*1000, \
.input_delay_ns = 0, \
.cs_ena_pretrans = 0, \
.cs_ena_posttrans = 0, \
}, \
.uart = { \
.port = 1, \
.baud = 921600, \
.tx_io = 25, \
.rx_io = 26, \
.queue_size = 16, \
.rx_buffer_size = 1024, \
}, \
. task = { \
.run_task = true, \
.stack_size = 4096, \
.priority = 8, \
}, \
. ppp = { \
.our_ip4_addr.addr = our_ip, \
.their_ip4_addr.addr = their_ip, \
} \
}
#define EPPP_DEFAULT_SERVER_CONFIG() EPPP_DEFAULT_CONFIG(EPPP_DEFAULT_SERVER_IP(), EPPP_DEFAULT_CLIENT_IP())
#define EPPP_DEFAULT_CLIENT_CONFIG() EPPP_DEFAULT_CONFIG(EPPP_DEFAULT_CLIENT_IP(), EPPP_DEFAULT_SERVER_IP())
typedef enum eppp_type {
EPPP_SERVER,
EPPP_CLIENT,
} eppp_type_t;
typedef enum eppp_transport {
EPPP_TRANSPORT_UART,
EPPP_TRANSPORT_SPI,
} eppp_transport_t;
typedef struct eppp_config_t {
eppp_transport_t transport;
struct eppp_config_spi_s {
int host;
int mosi;
int miso;
int sclk;
int cs;
int intr;
int freq;
int input_delay_ns;
int cs_ena_pretrans;
int cs_ena_posttrans;
} spi;
struct eppp_config_uart_s {
int port;
int baud;
int tx_io;
int rx_io;
int queue_size;
int rx_buffer_size;
} uart;
struct eppp_config_task_s {
bool run_task;
int stack_size;
int priority;
} task;
struct eppp_config_pppos_s {
esp_ip4_addr_t our_ip4_addr;
esp_ip4_addr_t their_ip4_addr;
} ppp;
} eppp_config_t;
esp_netif_t *eppp_connect(eppp_config_t *config);
esp_netif_t *eppp_listen(eppp_config_t *config);
void eppp_close(esp_netif_t *netif);
esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config);
void eppp_deinit(esp_netif_t *netif);
esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_timeout_ms);
esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms);
esp_err_t eppp_netif_start(esp_netif_t *netif);
esp_err_t eppp_perform(esp_netif_t *netif);

View File

@ -0,0 +1,7 @@
# The following four 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(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/tools/unit-test-app/components)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_app)

View File

@ -0,0 +1,73 @@
# Test application running both server and client on the same device
Need to connect client's Tx to server's Rx and vice versa:
GPIO25 - GPIO4
GPIO26 - GPIO5
We wait for the connection and then we start pinging the client's address on server's netif.
## Example of output:
```
I (393) eppp_test_app: [APP] Startup..
I (393) eppp_test_app: [APP] Free memory: 296332 bytes
I (393) eppp_test_app: [APP] IDF version: v5.3-dev-1154-gf14d9e7431-dirty
I (423) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
I (423) uart: queue free spaces: 16
I (433) eppp_link: Waiting for IP address
I (433) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
I (443) uart: queue free spaces: 16
I (443) eppp_link: Waiting for IP address
I (6473) esp-netif_lwip-ppp: Connected
I (6513) eppp_link: Got IPv4 event: Interface "pppos_client" address: 192.168.11.2
I (6523) esp-netif_lwip-ppp: Connected
I (6513) eppp_link: Connected!
I (6523) eppp_link: Got IPv4 event: Interface "pppos_server" address: 192.168.11.1
I (6553) main_task: Returned from app_main()
64bytes from 192.168.11.2 icmp_seq=1 ttl=255 time=18 ms
64bytes from 192.168.11.2 icmp_seq=2 ttl=255 time=19 ms
64bytes from 192.168.11.2 icmp_seq=3 ttl=255 time=19 ms
64bytes from 192.168.11.2 icmp_seq=4 ttl=255 time=20 ms
64bytes from 192.168.11.2 icmp_seq=5 ttl=255 time=19 ms
64bytes from 192.168.11.2 icmp_seq=6 ttl=255 time=19 ms
64bytes from 192.168.11.2 icmp_seq=7 ttl=255 time=19 ms
From 192.168.11.2 icmp_seq=8 timeout // <-- Disconnected Tx-Rx wires
From 192.168.11.2 icmp_seq=9 timeout
```
## Test cases
This test app exercises these methods of setting up server-client connection:
* simple blocking API (eppp_listen() <--> eppp_connect()): Uses network events internally and waits for connection
* simplified non-blocking API (eppp_open(EPPP_SERVER, ...) <--> eppp_open(EPPP_SERVER, ...) ): Uses events internally, optionally waits for connecting
* manual API (eppp_init(), eppp_netif_start(), eppp_perform()): User to manually drive Rx task
- Note that the ping test for this test case takes longer, since we call perform for both server and client from one task, for example:
```
TEST(eppp_test, open_close_taskless)I (28562) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
I (28572) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
Note: esp_netif_init() has been called. Until next reset, TCP/IP task will periodicially allocate memory and consume CPU time.
I (28602) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
I (28612) uart: queue free spaces: 16
I (28612) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
I (28622) uart: queue free spaces: 16
I (28642) esp-netif_lwip-ppp: Connected
I (28642) esp-netif_lwip-ppp: Connected
I (28642) test: Got IPv4 event: Interface "pppos_server(EPPP0)" address: 192.168.11.1
I (28642) esp-netif_lwip-ppp: Connected
I (28652) test: Got IPv4 event: Interface "pppos_client(EPPP1)" address: 192.168.11.2
I (28662) esp-netif_lwip-ppp: Connected
64bytes from 192.168.11.2 icmp_seq=1 ttl=255 time=93 ms
64bytes from 192.168.11.2 icmp_seq=2 ttl=255 time=98 ms
64bytes from 192.168.11.2 icmp_seq=3 ttl=255 time=99 ms
64bytes from 192.168.11.2 icmp_seq=4 ttl=255 time=99 ms
64bytes from 192.168.11.2 icmp_seq=5 ttl=255 time=99 ms
5 packets transmitted, 5 received, time 488ms
I (29162) esp-netif_lwip-ppp: User interrupt
I (29162) test: Disconnected interface "pppos_client(EPPP1)"
I (29172) esp-netif_lwip-ppp: User interrupt
I (29172) test: Disconnected interface "pppos_server(EPPP0)"
MALLOC_CAP_8BIT usage: Free memory delta: 0 Leak threshold: -64
MALLOC_CAP_32BIT usage: Free memory delta: 0 Leak threshold: -64
PASS
```

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS app_main.c
INCLUDE_DIRS "."
REQUIRES test_utils
PRIV_REQUIRES unity nvs_flash esp_netif driver esp_event)

View File

@ -0,0 +1,344 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "eppp_link.h"
#include "lwip/sockets.h"
#include "esp_log.h"
#include "ping/ping_sock.h"
#include "driver/uart.h"
#include "test_utils.h"
#include "unity.h"
#include "test_utils.h"
#include "unity_fixture.h"
#include "memory_checks.h"
#include "lwip/sys.h"
#define CLIENT_INFO_CONNECTED BIT0
#define CLIENT_INFO_DISCONNECT BIT1
#define CLIENT_INFO_CLOSED BIT2
#define PING_SUCCEEDED BIT3
#define PING_FAILED BIT4
#define STOP_WORKER_TASK BIT5
#define WORKER_TASK_STOPPED BIT6
TEST_GROUP(eppp_test);
TEST_SETUP(eppp_test)
{
// Perform some open/close operations to disregard lazy init one-time allocations
// LWIP: core protection mutex
sys_arch_protect();
sys_arch_unprotect(0);
// UART: install and delete both drivers to disregard potential leak in allocated interrupt slot
TEST_ESP_OK(uart_driver_install(UART_NUM_1, 256, 0, 0, NULL, 0));
TEST_ESP_OK(uart_driver_delete(UART_NUM_1));
TEST_ESP_OK(uart_driver_install(UART_NUM_2, 256, 0, 0, NULL, 0));
TEST_ESP_OK(uart_driver_delete(UART_NUM_2));
// PING: used for timestamps
struct timeval time;
gettimeofday(&time, NULL);
test_utils_record_free_mem();
TEST_ESP_OK(test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL));
}
TEST_TEAR_DOWN(eppp_test)
{
test_utils_finish_and_evaluate_leaks(32, 64);
}
static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
{
EventGroupHandle_t event = args;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("%" PRId32 " packets transmitted, %" PRId32 " received, time %" PRId32 "ms\n", transmitted, received, total_time_ms);
if (transmitted == received) {
xEventGroupSetBits(event, PING_SUCCEEDED);
} else {
xEventGroupSetBits(event, PING_FAILED);
}
}
static void test_on_ping_success(esp_ping_handle_t hdl, void *args)
{
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%" PRId32 "bytes from %s icmp_seq=%d ttl=%d time=%" PRId32 " ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
struct client_info {
esp_netif_t *netif;
EventGroupHandle_t event;
};
static void open_client_task(void *ctx)
{
struct client_info *info = ctx;
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
config.uart.port = UART_NUM_2;
config.uart.tx_io = 4;
config.uart.rx_io = 5;
info->netif = eppp_connect(&config);
xEventGroupSetBits(info->event, CLIENT_INFO_CONNECTED);
// wait for disconnection trigger
EventBits_t bits = xEventGroupWaitBits(info->event, CLIENT_INFO_DISCONNECT, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & CLIENT_INFO_DISCONNECT, CLIENT_INFO_DISCONNECT);
eppp_close(info->netif);
xEventGroupSetBits(info->event, CLIENT_INFO_CLOSED);
vTaskDelete(NULL);
}
TEST(eppp_test, init_deinit)
{
// Init and deinit server size
eppp_config_t config = EPPP_DEFAULT_CONFIG(0, 0);
esp_netif_t *netif = eppp_init(EPPP_SERVER, &config);
TEST_ASSERT_NOT_NULL(netif);
eppp_deinit(netif);
netif = NULL;
// Init and deinit client size
netif = eppp_init(EPPP_CLIENT, &config);
TEST_ASSERT_NOT_NULL(netif);
eppp_deinit(netif);
}
static EventBits_t ping_test(uint32_t addr, esp_netif_t *netif, EventGroupHandle_t event)
{
ip_addr_t target_addr = { .type = IPADDR_TYPE_V4, .u_addr.ip4.addr = addr };
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
ping_config.interval_ms = 100;
ping_config.target_addr = target_addr;
ping_config.interface = esp_netif_get_netif_impl_index(netif);
esp_ping_callbacks_t cbs = { .cb_args = event, .on_ping_end = test_on_ping_end, .on_ping_success = test_on_ping_success };
esp_ping_handle_t ping;
esp_ping_new_session(&ping_config, &cbs, &ping);
esp_ping_start(ping);
// Wait for the client thread closure and delete locally created objects
EventBits_t bits = xEventGroupWaitBits(event, PING_SUCCEEDED | PING_FAILED, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
esp_ping_stop(ping);
esp_ping_delete_session(ping);
return bits;
}
TEST(eppp_test, open_close)
{
test_case_uses_tcpip();
eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG();
struct client_info client = { .netif = NULL, .event = xEventGroupCreate()};
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ASSERT_NOT_NULL(client.event);
// Need to connect the client in a separate thread, as the simplified API blocks until connection
xTaskCreate(open_client_task, "client_task", 4096, &client, 5, NULL);
// Now start the server
esp_netif_t *eppp_server = eppp_listen(&config);
// Wait for the client to connect
EventBits_t bits = xEventGroupWaitBits(client.event, CLIENT_INFO_CONNECTED, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & CLIENT_INFO_CONNECTED, CLIENT_INFO_CONNECTED);
// Check that both server and client are valid netif pointers
TEST_ASSERT_NOT_NULL(eppp_server);
TEST_ASSERT_NOT_NULL(client.netif);
// Now that we're connected, let's try to ping clients address
bits = ping_test(config.ppp.their_ip4_addr, eppp_server, client.event);
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
// Trigger client disconnection and close the server
xEventGroupSetBits(client.event, CLIENT_INFO_DISCONNECT);
eppp_close(eppp_server);
// Wait for the client thread closure and delete locally created objects
bits = xEventGroupWaitBits(client.event, CLIENT_INFO_CLOSED, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & CLIENT_INFO_CLOSED, CLIENT_INFO_CLOSED);
TEST_ESP_OK(esp_event_loop_delete_default());
vEventGroupDelete(client.event);
// wait for the lwip sockets to close cleanly
vTaskDelay(pdMS_TO_TICKS(1000));
}
static void on_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
{
EventGroupHandle_t event = arg;
if (base == IP_EVENT && event_id == IP_EVENT_PPP_GOT_IP) {
ip_event_got_ip_t *e = (ip_event_got_ip_t *)data;
esp_netif_t *netif = e->esp_netif;
ESP_LOGI("test", "Got IPv4 event: Interface \"%s(%s)\" address: " IPSTR, esp_netif_get_desc(netif),
esp_netif_get_ifkey(netif), IP2STR(&e->ip_info.ip));
if (strcmp("pppos_server", esp_netif_get_desc(netif)) == 0) {
xEventGroupSetBits(event, 1 << EPPP_SERVER);
} else if (strcmp("pppos_client", esp_netif_get_desc(netif)) == 0) {
xEventGroupSetBits(event, 1 << EPPP_CLIENT);
}
} else if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) {
esp_netif_t **netif = data;
ESP_LOGI("test", "Disconnected interface \"%s(%s)\"", esp_netif_get_desc(*netif), esp_netif_get_ifkey(*netif));
if (strcmp("pppos_server", esp_netif_get_desc(*netif)) == 0) {
xEventGroupSetBits(event, 1 << EPPP_SERVER);
} else if (strcmp("pppos_client", esp_netif_get_desc(*netif)) == 0) {
xEventGroupSetBits(event, 1 << EPPP_CLIENT);
}
}
}
TEST(eppp_test, open_close_nonblocking)
{
test_case_uses_tcpip();
EventGroupHandle_t event = xEventGroupCreate();
eppp_config_t server_config = EPPP_DEFAULT_SERVER_CONFIG();
TEST_ESP_OK(esp_event_loop_create_default());
// Open the server size
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_event, event));
esp_netif_t *eppp_server = eppp_open(EPPP_SERVER, &server_config, 0);
TEST_ASSERT_NOT_NULL(eppp_server);
// Open the client size
eppp_config_t client_config = EPPP_DEFAULT_SERVER_CONFIG();
client_config.uart.port = UART_NUM_2;
client_config.uart.tx_io = 4;
client_config.uart.rx_io = 5;
esp_netif_t *eppp_client = eppp_open(EPPP_CLIENT, &client_config, 0);
TEST_ASSERT_NOT_NULL(eppp_client);
const EventBits_t wait_bits = (1 << EPPP_SERVER) | (1 << EPPP_CLIENT);
EventBits_t bits = xEventGroupWaitBits(event, wait_bits, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & wait_bits, wait_bits);
// Now that we're connected, let's try to ping clients address
bits = ping_test(server_config.ppp.their_ip4_addr, eppp_server, event);
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
// stop network for both client and server
eppp_netif_stop(eppp_client, 0); // ignore result, since we're not waiting for clean close
eppp_close(eppp_server);
eppp_close(eppp_client); // finish client close
TEST_ESP_OK(esp_event_loop_delete_default());
vEventGroupDelete(event);
// wait for the lwip sockets to close cleanly
vTaskDelay(pdMS_TO_TICKS(1000));
}
struct worker {
esp_netif_t *eppp_server;
esp_netif_t *eppp_client;
EventGroupHandle_t event;
};
static void worker_task(void *ctx)
{
struct worker *info = ctx;
while (1) {
eppp_perform(info->eppp_server);
eppp_perform(info->eppp_client);
if (xEventGroupGetBits(info->event) & STOP_WORKER_TASK) {
break;
}
}
xEventGroupSetBits(info->event, WORKER_TASK_STOPPED);
vTaskDelete(NULL);
}
TEST(eppp_test, open_close_taskless)
{
test_case_uses_tcpip();
struct worker info = { .event = xEventGroupCreate() };
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_event, info.event));
TEST_ESP_OK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_event, info.event));
// Create server
eppp_config_t server_config = EPPP_DEFAULT_SERVER_CONFIG();
info.eppp_server = eppp_init(EPPP_SERVER, &server_config);
TEST_ASSERT_NOT_NULL(info.eppp_server);
// Create client
eppp_config_t client_config = EPPP_DEFAULT_CLIENT_CONFIG();
client_config.uart.port = UART_NUM_2;
client_config.uart.tx_io = 4;
client_config.uart.rx_io = 5;
info.eppp_client = eppp_init(EPPP_CLIENT, &client_config);
TEST_ASSERT_NOT_NULL(info.eppp_client);
// Start workers
xTaskCreate(worker_task, "worker", 4096, &info, 5, NULL);
// Start network
TEST_ESP_OK(eppp_netif_start(info.eppp_server));
TEST_ESP_OK(eppp_netif_start(info.eppp_client));
const EventBits_t wait_bits = (1 << EPPP_SERVER) | (1 << EPPP_CLIENT);
EventBits_t bits = xEventGroupWaitBits(info.event, wait_bits, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & wait_bits, wait_bits);
xEventGroupClearBits(info.event, wait_bits);
// Now that we're connected, let's try to ping clients address
bits = ping_test(server_config.ppp.their_ip4_addr, info.eppp_server, info.event);
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
// stop network for both client and server, we won't wait for completion so expecting ESP_FAIL
TEST_ASSERT_EQUAL(eppp_netif_stop(info.eppp_client, 0), ESP_FAIL);
TEST_ASSERT_EQUAL(eppp_netif_stop(info.eppp_server, 0), ESP_FAIL);
// and wait for completion
bits = xEventGroupWaitBits(info.event, wait_bits, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & wait_bits, wait_bits);
// now stop the worker
xEventGroupSetBits(info.event, STOP_WORKER_TASK);
bits = xEventGroupWaitBits(info.event, WORKER_TASK_STOPPED, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
TEST_ASSERT_EQUAL(bits & WORKER_TASK_STOPPED, WORKER_TASK_STOPPED);
// and destroy objects
eppp_deinit(info.eppp_server);
eppp_deinit(info.eppp_client);
TEST_ESP_OK(esp_event_loop_delete_default());
vEventGroupDelete(info.event);
// wait for the lwip sockets to close cleanly
vTaskDelay(pdMS_TO_TICKS(1000));
}
TEST_GROUP_RUNNER(eppp_test)
{
RUN_TEST_CASE(eppp_test, init_deinit)
RUN_TEST_CASE(eppp_test, open_close)
RUN_TEST_CASE(eppp_test, open_close_nonblocking)
RUN_TEST_CASE(eppp_test, open_close_taskless)
}
void app_main(void)
{
UNITY_MAIN(eppp_test);
}

View File

@ -0,0 +1,4 @@
dependencies:
espressif/eppp_link:
version: "*"
override_path: "../../.."

View File

@ -0,0 +1,12 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.3.0 Project Minimal Configuration
#
CONFIG_UART_ISR_IN_IRAM=y
CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=0
CONFIG_FREERTOS_UNICORE=y
CONFIG_HEAP_TRACING_STANDALONE=y
CONFIG_HEAP_TRACING_STACK_DEPTH=6
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_LWIP_PPP_DEBUG_ON=y
CONFIG_UNITY_ENABLE_FIXTURE=y