initial commit

This commit is contained in:
valentineautos
2025-12-05 09:22:55 +00:00
parent 46949642a2
commit 16ffc2ab10
4033 changed files with 980542 additions and 0 deletions

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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(pppos_host)

View File

@@ -0,0 +1,9 @@
# Client side demo of ESP-PPP-Link
This is a basic demo of using esp-mqtt library, but connects to the internet using a PPPoS client. To run this example, you would need a PPP server that provides connectivity to the MQTT broker used in this example (by default a public broker accessible on the internet).
If configured, this example could also run a ping session and an iperf console.
The PPP server could be a Linux computer with `pppd` service or an ESP32 acting like a connection gateway with PPPoS server (see the "slave" project).

View File

@@ -0,0 +1,6 @@
if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
set(wifi_over_channels channel_wifi_station.c)
endif()
idf_component_register(SRCS app_main.c register_iperf.c
${wifi_over_channels}
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,119 @@
menu "Example Configuration"
config EXAMPLE_GLOBAL_DNS
hex "Set global DNS server"
range 0 0xFFFFFFFF
default 0x08080808
help
Global DNS server address.
config EXAMPLE_MQTT
bool "Run mqtt example"
default y
help
Run MQTT client after startup.
config EXAMPLE_BROKER_URL
string "Broker URL"
depends on EXAMPLE_MQTT
default "mqtt://mqtt.eclipseprojects.io"
help
URL of the broker to connect to.
config EXAMPLE_IPERF
bool "Run iperf"
default y
help
Init and run iperf console.
config EXAMPLE_SPI_HOST
int "SPI Host"
depends on EPPP_LINK_DEVICE_SPI
default 1
range 0 2
help
SPI host to use (SPI1_HOST=0, SPI2_HOST=1, SPI3_HOST=2).
config EXAMPLE_SPI_MOSI_PIN
int "MOSI Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 23
range 0 39
help
Pin number of SPI MOSI.
config EXAMPLE_SPI_MISO_PIN
int "MISO Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 19
range 0 39
help
Pin number of SPI MISO.
config EXAMPLE_SPI_SCLK_PIN
int "SCLK Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 18
range 0 39
help
Pin number of SPI SCLK.
config EXAMPLE_SPI_CS_PIN
int "CS Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 5
range 0 39
help
Pin number of SPI CS.
config EXAMPLE_SPI_INTR_PIN
int "Interrupt Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 17
range 0 39
help
Pin number of SPI interrupt.
config EXAMPLE_SPI_FREQUENCY
int "SPI Frequency (Hz)"
depends on EPPP_LINK_DEVICE_SPI
default 4000000
range 100000 80000000
help
SPI frequency in Hz.
config EXAMPLE_UART_TX_PIN
int "TXD Pin Number"
depends on EPPP_LINK_DEVICE_UART
default 17
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 18
range 0 31
help
Pin number of UART RX.
config EXAMPLE_UART_BAUDRATE
int "Baudrate"
depends on EPPP_LINK_DEVICE_UART
default 921600
range 0 4000000
help
Baudrate used by the PPP over UART
config EXAMPLE_WIFI_OVER_EPPP_CHANNEL
bool "Use WiFi over EPPP channel"
default n
depends on EPPP_LINK_CHANNELS_SUPPORT && ESP_WIFI_REMOTE_ENABLED
help
Enable this option to use WiFi over EPPP channel.
If this option is enabled, the example will only start the Wi-Fi driver,
but the Wi-Fi netif will reside on client's end and will channel
the Rx and Tx data via EPPP channels.
endmenu

View File

@@ -0,0 +1,167 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "eppp_link.h"
#include "esp_log.h"
#include "esp_check.h"
#include "mqtt_client.h"
#include "console_ping.h"
void register_iperf(void);
static const char *TAG = "eppp_host_example";
#if CONFIG_EXAMPLE_MQTT
static void mqtt_event_handler(void *args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
}
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
static void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = "mqtt://mqtt.eclipseprojects.io",
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client);
}
#endif // MQTT
void station_over_eppp_channel(void *arg);
void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* Sets up the default EPPP-connection
*/
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
#if CONFIG_EPPP_LINK_DEVICE_SPI
config.transport = EPPP_TRANSPORT_SPI;
config.spi.is_master = true;
config.spi.host = CONFIG_EXAMPLE_SPI_HOST;
config.spi.mosi = CONFIG_EXAMPLE_SPI_MOSI_PIN;
config.spi.miso = CONFIG_EXAMPLE_SPI_MISO_PIN;
config.spi.sclk = CONFIG_EXAMPLE_SPI_SCLK_PIN;
config.spi.cs = CONFIG_EXAMPLE_SPI_CS_PIN;
config.spi.intr = CONFIG_EXAMPLE_SPI_INTR_PIN;
config.spi.freq = CONFIG_EXAMPLE_SPI_FREQUENCY;
#elif CONFIG_EPPP_LINK_DEVICE_UART
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;
#elif CONFIG_EPPP_LINK_DEVICE_ETH
config.transport = EPPP_TRANSPORT_ETHERNET;
#else
config.transport = EPPP_TRANSPORT_SDIO;
config.sdio.is_host = true;
#endif
esp_netif_t *eppp_netif = eppp_connect(&config);
if (eppp_netif == NULL) {
ESP_LOGE(TAG, "Failed to connect");
return ;
}
// Setup global DNS
esp_netif_dns_info_t dns;
dns.ip.u_addr.ip4.addr = esp_netif_htonl(CONFIG_EXAMPLE_GLOBAL_DNS);
dns.ip.type = ESP_IPADDR_TYPE_V4;
ESP_ERROR_CHECK(esp_netif_set_dns_info(eppp_netif, ESP_NETIF_DNS_MAIN, &dns));
// Initialize console REPL
ESP_ERROR_CHECK(console_cmd_init());
#if CONFIG_EXAMPLE_IPERF
register_iperf();
printf("\n =======================================================\n");
printf(" | Steps to Test EPPP-host bandwidth |\n");
printf(" | |\n");
printf(" | 1. Wait for the ESP32 to get an IP |\n");
printf(" | 2. Server: 'iperf -u -s -i 3' (on host) |\n");
printf(" | 3. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n");
printf(" | |\n");
printf(" =======================================================\n\n");
#endif // CONFIG_EXAMPLE_IPERF
// Register the ping command
ESP_ERROR_CHECK(console_cmd_ping_register());
// start console REPL
ESP_ERROR_CHECK(console_cmd_start());
#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
station_over_eppp_channel(eppp_netif);
#endif
#if CONFIG_EXAMPLE_MQTT
mqtt_app_start();
#endif
}

View File

@@ -0,0 +1,185 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "eppp_link.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_wifi_remote.h"
#define CHAT_CHANNEL 1
#define WIFI_CHANNEL 2
typedef enum {
UNKNOWN,
HELLO,
START,
ERROR,
} state_t;
typedef struct context {
eppp_channel_fn_t transmit;
EventGroupHandle_t flags;
state_t state;
esp_netif_t *eppp;
} context_t;
#define HELLO_BIT BIT0
#define START_BIT BIT1
#define CONNECT_BIT BIT2
#define SERVER_UP_BIT BIT3
#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | SERVER_UP_BIT)
static uint8_t s_wifi_mac_addr[6] = { 0 };
static const char *TAG = "eppp_host_example_with_channels";
esp_netif_t* esp_wifi_remote_create_default_sta(void);
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "IP event_handler: event_base=%s event_id=%d", event_base, event_id);
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
}
}
esp_err_t esp_wifi_remote_get_mac(wifi_interface_t ifx, uint8_t mac[6])
{
if (ifx != WIFI_IF_STA) {
return ESP_ERR_INVALID_STATE;
}
for (int i = 0; i < sizeof(s_wifi_mac_addr); i++) {
if (s_wifi_mac_addr[i] == 0) {
return ESP_ERR_INVALID_STATE;
}
}
memcpy(mac, s_wifi_mac_addr, sizeof(s_wifi_mac_addr));
return ESP_OK;
}
static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len)
{
context_t *ctx = eppp_get_context(netif);
if (nr == CHAT_CHANNEL) {
ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer);
const char hello[] = "Hello client";
const char mac[] = "MAC: ";
const char connected[] = "Connected";
const char server_up[] = "Server up";
size_t mac_len = 5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */;
if (len == sizeof(server_up) && memcmp(buffer, server_up, len) == 0) {
if (ctx->state == UNKNOWN) {
ESP_LOGI(TAG, "Server is up");
ctx->state = HELLO;
} else {
ESP_LOGE(TAG, "Received server up in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
xEventGroupSetBits(ctx->flags, SERVER_UP_BIT);
} else if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) {
if (ctx->state == HELLO) {
xEventGroupSetBits(ctx->flags, HELLO_BIT);
} else {
ESP_LOGE(TAG, "Received hello in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
} else if (len == mac_len && memcmp(buffer, mac, 5) == 0) {
if (ctx->state == HELLO) {
uint8_t mac_addr[6];
sscanf((char *)buffer + 5, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8,
&mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]);
ESP_LOGI(TAG, "Parsed MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
memcpy(s_wifi_mac_addr, mac_addr, sizeof(s_wifi_mac_addr));
xEventGroupSetBits(ctx->flags, START_BIT);
} else {
ESP_LOGE(TAG, "Received MAC in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
} else if (len == sizeof(connected) && memcmp(buffer, connected, len) == 0) {
if (ctx->state == START) {
xEventGroupSetBits(ctx->flags, CONNECT_BIT);
} else {
ESP_LOGE(TAG, "Received connected in unexpected state %d", ctx->state);
ctx->state = ERROR;
}
}
} else if (nr == WIFI_CHANNEL) {
ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len);
ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE);
return esp_wifi_remote_channel_rx(ctx->eppp, buffer, NULL, len);
} else {
ESP_LOGE(TAG, "Incorrect channel number %d", nr);
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t wifi_transmit(void *h, void *buffer, size_t len)
{
esp_netif_t *eppp = (esp_netif_t *)h;
context_t *ctx = eppp_get_context(eppp);
ESP_LOG_BUFFER_HEXDUMP("wifi-transmit", buffer, len, ESP_LOG_VERBOSE);
return ctx->transmit(eppp, WIFI_CHANNEL, buffer, len);
}
void esp_netif_destroy_wifi_remote(void *esp_netif);
void station_over_eppp_channel(void *arg)
{
__attribute__((unused)) esp_err_t ret;
esp_netif_t *wifi = NULL;
context_t ctx = {
.transmit = NULL,
.flags = NULL,
.state = UNKNOWN,
.eppp = (esp_netif_t *)arg
};
ESP_GOTO_ON_FALSE(ctx.eppp != NULL, ESP_FAIL, err, TAG, "Incorrect EPPP netif");
ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group");
ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels");
ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set");
ESP_GOTO_ON_ERROR(esp_wifi_remote_channel_set(WIFI_IF_STA, ctx.eppp, wifi_transmit), err, TAG, "Failed to set wifi channel tx function");
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, &ctx);
while (1) {
EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000));
if (bits & HELLO_BIT) {
ESP_LOGI(TAG, "Hello done");
if (wifi == NULL) {
wifi = esp_wifi_remote_create_default_sta();
}
const char command[] = "Get MAC";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)command, sizeof(command));
} else if (bits & START_BIT) {
ctx.state = START;
ESP_LOGI(TAG, "Starting WIFI");
esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_START, NULL, 0, 0);
} else if (bits & CONNECT_BIT) {
ESP_LOGI(TAG, "WIFI connected");
esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_CONNECTED, NULL, 0, 0);
} else if ((bits & SERVER_UP_BIT) == SERVER_UP_BIT || ctx.state != START) {
if (ctx.state == ERROR) {
esp_netif_destroy_wifi_remote(wifi);
wifi = NULL;
ESP_LOGI(TAG, "WiFi netif has been destroyed");
}
const char hello[] = "Hello server";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello));
ctx.state = HELLO;
}
}
err:
vTaskDelete(NULL);
}

View File

@@ -0,0 +1,6 @@
dependencies:
console_cmd_ping:
version: '*'
espressif/eppp_link:
version: '*'
espressif/iperf-cmd: ^0.1.1

View File

@@ -0,0 +1,179 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "sys/socket.h" // for INADDR_ANY
#include "esp_netif.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_console.h"
#include "esp_event.h"
#include "esp_bit_defs.h"
#include "argtable3/argtable3.h"
#include "iperf.h"
#include "sdkconfig.h"
/* "iperf" command */
static struct {
struct arg_str *ip;
struct arg_lit *server;
struct arg_lit *udp;
struct arg_lit *version;
struct arg_int *port;
struct arg_int *length;
struct arg_int *interval;
struct arg_int *time;
struct arg_int *bw_limit;
struct arg_lit *abort;
struct arg_end *end;
} iperf_args;
static int ppp_cmd_iperf(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
// 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;
}
/* iperf -a */
if (iperf_args.abort->count != 0) {
iperf_stop();
return 0;
}
if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) {
ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode");
return 0;
}
/* iperf -s */
if (iperf_args.ip->count == 0) {
cfg.flag |= IPERF_FLAG_SERVER;
}
/* iperf -c SERVER_ADDRESS */
else {
cfg.destination_ip4 = esp_ip4addr_aton(iperf_args.ip->sval[0]);
cfg.flag |= IPERF_FLAG_CLIENT;
}
if (iperf_args.length->count == 0) {
cfg.len_send_buf = 0;
} else {
cfg.len_send_buf = iperf_args.length->ival[0];
}
cfg.source_ip4 = INADDR_ANY;
/* iperf -u */
if (iperf_args.udp->count == 0) {
cfg.flag |= IPERF_FLAG_TCP;
} else {
cfg.flag |= IPERF_FLAG_UDP;
}
/* iperf -p */
if (iperf_args.port->count == 0) {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = IPERF_DEFAULT_PORT;
} else {
if (cfg.flag & IPERF_FLAG_SERVER) {
cfg.sport = iperf_args.port->ival[0];
cfg.dport = IPERF_DEFAULT_PORT;
} else {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = iperf_args.port->ival[0];
}
}
/* iperf -i */
if (iperf_args.interval->count == 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
} else {
cfg.interval = iperf_args.interval->ival[0];
if (cfg.interval <= 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
}
}
/* iperf -t */
if (iperf_args.time->count == 0) {
cfg.time = IPERF_DEFAULT_TIME;
} else {
cfg.time = iperf_args.time->ival[0];
if (cfg.time <= cfg.interval) {
cfg.time = cfg.interval;
}
}
/* iperf -b */
if (iperf_args.bw_limit->count == 0) {
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
} else {
cfg.bw_lim = iperf_args.bw_limit->ival[0];
if (cfg.bw_lim <= 0) {
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
}
}
printf("mode=%s-%s sip=" IPSTR ":%" PRIu16 ", dip=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ":%" PRIu16 ", interval=%" PRIu32 ", time=%" PRIu32 "\r\n",
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
(uint16_t) cfg.source_ip4 & 0xFF,
(uint16_t) (cfg.source_ip4 >> 8) & 0xFF,
(uint16_t) (cfg.source_ip4 >> 16) & 0xFF,
(uint16_t) (cfg.source_ip4 >> 24) & 0xFF,
cfg.sport,
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
cfg.interval, cfg.time);
iperf_start(&cfg);
return 0;
}
void register_iperf(void)
{
iperf_args.ip = arg_str0("c", "client", "<ip>",
"run in client mode, connecting to <host>");
iperf_args.server = arg_lit0("s", "server", "run in server mode");
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
iperf_args.version = arg_lit0("V", "ipv6_domain", "use IPV6 address rather than IPV4");
iperf_args.port = arg_int0("p", "port", "<port>",
"server port to listen on/connect to");
iperf_args.length = arg_int0("l", "len", "<length>", "set read/write buffer size");
iperf_args.interval = arg_int0("i", "interval", "<interval>",
"seconds between periodic bandwidth reports");
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
iperf_args.bw_limit = arg_int0("b", "bandwidth", "<bandwidth>", "bandwidth to send at in Mbits/sec");
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
iperf_args.end = arg_end(1);
const esp_console_cmd_t iperf_cmd = {
.command = "iperf",
.help = "iperf command",
.hint = NULL,
.func = &ppp_cmd_iperf,
.argtable = &iperf_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
}

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_EPPP_LINK_DEVICE_SPI=y
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
CONFIG_EPPP_LINK_USES_PPP=y

View File

@@ -0,0 +1,3 @@
CONFIG_IDF_TARGET="esp32c3"
CONFIG_EPPP_LINK_DEVICE_UART=y
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y

View File

@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=y

View File

@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_EPPP_LINK_DEVICE_SDIO=y

View File

@@ -0,0 +1 @@
CONFIG_EPPP_LINK_DEVICE_SPI=y

View File

@@ -0,0 +1 @@
CONFIG_EPPP_LINK_DEVICE_UART=y

View File

@@ -0,0 +1 @@
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n

View File

@@ -0,0 +1,6 @@
# 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(pppos_slave)

View File

@@ -0,0 +1,7 @@
# Wi-Fi station to PPPoS server
This example demonstrate using NAPT to bring connectivity from WiFi station to PPPoS server.
This example expect a PPPoS client to connect to the server and use the connectivity.
The client could be a Linux computer with `pppd` service or another microcontroller with PPP client (or another ESP32 with not WiFi interface)

View File

@@ -0,0 +1,6 @@
if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
set(wifi_over_channels channel_wifi_station.c)
endif()
idf_component_register(SRCS eppp_slave.c
${wifi_over_channels}
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,111 @@
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config ESP_MAXIMUM_RETRY
int "Maximum retry"
default 5
help
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
config EXAMPLE_SPI_HOST
int "SPI Host"
depends on EPPP_LINK_DEVICE_SPI
default 1
range 0 2
help
SPI host to use (SPI1_HOST=0, SPI2_HOST=1, SPI3_HOST=2).
config EXAMPLE_SPI_MOSI_PIN
int "MOSI Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 23
range 0 39
help
Pin number of SPI MOSI.
config EXAMPLE_SPI_MISO_PIN
int "MISO Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 19
range 0 39
help
Pin number of SPI MISO.
config EXAMPLE_SPI_SCLK_PIN
int "SCLK Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 18
range 0 39
help
Pin number of SPI SCLK.
config EXAMPLE_SPI_CS_PIN
int "CS Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 5
range 0 39
help
Pin number of SPI CS.
config EXAMPLE_SPI_INTR_PIN
int "Interrupt Pin Number"
depends on EPPP_LINK_DEVICE_SPI
default 17
range 0 39
help
Pin number of SPI interrupt.
config EXAMPLE_SPI_FREQUENCY
int "SPI Frequency (Hz)"
depends on EPPP_LINK_DEVICE_SPI
default 1000000
range 100000 80000000
help
SPI frequency in Hz.
config EXAMPLE_UART_TX_PIN
int "TXD Pin Number"
depends on EPPP_LINK_DEVICE_UART
default 18
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 17
range 0 31
help
Pin number of UART RX.
config EXAMPLE_UART_BAUDRATE
int "Baudrate"
depends on EPPP_LINK_DEVICE_UART
default 921600
range 0 4000000
help
Baudrate used by the PPP over UART
config EXAMPLE_WIFI_OVER_EPPP_CHANNEL
bool "Use WiFi over EPPP channel"
default n
depends on EPPP_LINK_CHANNELS_SUPPORT
help
Enable this option to use WiFi over EPPP channel.
If this option is enabled, the example will only start the Wi-Fi driver,
but the Wi-Fi netif will reside on client's end and will channel
the Rx and Tx data via EPPP channels.
endmenu

View File

@@ -0,0 +1,169 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "eppp_link.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_private/wifi.h"
#define CHAT_CHANNEL 1
#define WIFI_CHANNEL 2
typedef enum {
UNKNOWN,
HELLO,
START,
ERROR,
} state_t;
typedef struct context {
eppp_channel_fn_t transmit;
EventGroupHandle_t flags;
state_t state;
esp_netif_t *eppp;
} context_t;
#define HELLO_BIT BIT0
#define START_BIT BIT1
#define CONNECT_BIT BIT2
#define DISCONNECT_BIT BIT3
#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | DISCONNECT_BIT)
static const char *TAG = "eppp_host_example_with_channels";
static context_t *s_eppp_channel_ctx = NULL;
static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len)
{
context_t *ctx = eppp_get_context(netif);
if (nr == CHAT_CHANNEL) {
ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer);
const char hello[] = "Hello server";
const char mac[] = "Get MAC";
if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) {
if (ctx->state == HELLO) {
xEventGroupSetBits(ctx->flags, HELLO_BIT);
} else {
ctx->state = ERROR;
}
} else if (len == sizeof(mac) && memcmp(buffer, mac, 5) == 0) {
if (ctx->state == HELLO) {
xEventGroupSetBits(ctx->flags, START_BIT);
} else {
ctx->state = ERROR;
}
}
} else if (nr == WIFI_CHANNEL) {
ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len);
ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE);
return esp_wifi_internal_tx(WIFI_IF_STA, buffer, len);
} else {
ESP_LOGE(TAG, "Incorrect channel number %d", nr);
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t wifi_receive(void *buffer, uint16_t len, void *eb)
{
s_eppp_channel_ctx->transmit(s_eppp_channel_ctx->eppp, WIFI_CHANNEL, buffer, len);
esp_wifi_internal_free_rx_buffer(eb);
return ESP_OK;
}
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
context_t *ctx = arg;
ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%d", event_base, event_id);
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "WIFI start event");
esp_wifi_connect();
xEventGroupSetBits(ctx->flags, CONNECT_BIT);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "connect to the AP fail");
xEventGroupSetBits(ctx->flags, DISCONNECT_BIT);
}
}
static void init_wifi_driver(context_t *ctx)
{
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
event_handler, ctx));
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_ESP_WIFI_SSID,
.password = CONFIG_ESP_WIFI_PASSWORD,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
}
void station_over_eppp_channel(void *arg)
{
__attribute__((unused)) esp_err_t ret;
context_t ctx = {
.transmit = NULL,
.flags = NULL,
.state = UNKNOWN,
.eppp = (esp_netif_t *)arg
};
ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group");
ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels");
ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set");
init_wifi_driver(&ctx);
while (1) {
EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000));
if (bits & HELLO_BIT) {
ESP_LOGI(TAG, "Hello from client received");
const char hello[] = "Hello client";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello));
} else if (bits & START_BIT) {
ctx.state = START;
ESP_LOGI(TAG, "Starting WIFI");
uint8_t mac[6];
if (esp_wifi_get_mac(WIFI_IF_STA, mac) != ESP_OK) {
ESP_LOGE(TAG, "esp_wifi_get_mac failed");
ctx.state = ERROR;
continue;
}
char mac_data[5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */];
sprintf(mac_data, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
ESP_LOGI(TAG, "Sending MAC: %.*s", (int)sizeof(mac_data), mac_data);
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)mac_data, sizeof(mac_data));
ret = esp_wifi_start();
ESP_LOGI(TAG, "WIFI start result: %d", ret);
s_eppp_channel_ctx = &ctx;
esp_wifi_internal_reg_rxcb(WIFI_IF_STA, wifi_receive);
} else if (bits & CONNECT_BIT) {
ESP_LOGI(TAG, "WIFI connected");
const char connected[] = "Connected";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)connected, sizeof(connected));
} else if (bits & DISCONNECT_BIT) {
const char disconnected[] = "Disconnected";
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)disconnected, sizeof(disconnected));
} else if (ctx.state != START) {
ctx.state = HELLO;
const char up[] = "Server up";
esp_wifi_disconnect();
esp_wifi_stop();
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)up, sizeof(up));
}
}
err:
vTaskDelete(NULL);
}

View File

@@ -0,0 +1,167 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_check.h"
#include "nvs_flash.h"
#include "eppp_link.h"
#include "inttypes.h"
static const char *TAG = "eppp_slave";
#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%" PRIi32, event_base, event_id);
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "WIFI start event");
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG, "connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void init_network_interface(void)
{
s_wifi_event_group = xEventGroupCreate();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_ESP_WIFI_SSID,
.password = CONFIG_ESP_WIFI_PASSWORD,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
#else
// If the SoC does not have WiFi capabilities, we can initialize a different network interface, this function is a placeholder for that purpose.
// This function is also a no-op if EXAMPLE_WIFI_OVER_EPPP_CHANNEL==1, since the Wi-Fi network interface will live on the other peer (on the host side).
void init_network_interface(void)
{
}
#endif // SoC WiFi capable chip || WiFi over EPPP channel
void station_over_eppp_channel(void *arg);
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
init_network_interface(); // WiFi station if withing SoC capabilities (otherwise a placeholder)
eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG();
#if CONFIG_EPPP_LINK_DEVICE_SPI
config.transport = EPPP_TRANSPORT_SPI;
config.spi.is_master = false;
config.spi.host = CONFIG_EXAMPLE_SPI_HOST;
config.spi.mosi = CONFIG_EXAMPLE_SPI_MOSI_PIN;
config.spi.miso = CONFIG_EXAMPLE_SPI_MISO_PIN;
config.spi.sclk = CONFIG_EXAMPLE_SPI_SCLK_PIN;
config.spi.cs = CONFIG_EXAMPLE_SPI_CS_PIN;
config.spi.intr = CONFIG_EXAMPLE_SPI_INTR_PIN;
config.spi.freq = CONFIG_EXAMPLE_SPI_FREQUENCY;
#elif CONFIG_EPPP_LINK_DEVICE_UART
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;
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
config.transport = EPPP_TRANSPORT_SDIO;
#endif // transport device
esp_netif_t *eppp_netif = eppp_listen(&config);
if (eppp_netif == NULL) {
ESP_LOGE(TAG, "Failed to setup connection");
return ;
}
#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
station_over_eppp_channel(eppp_netif);
#else
ESP_ERROR_CHECK(esp_netif_napt_enable(eppp_netif));
#endif // CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
}

View File

@@ -0,0 +1,3 @@
dependencies:
espressif/eppp_link:
version: '*'

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s2"
CONFIG_EPPP_LINK_DEVICE_SPI=y
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32c2"
CONFIG_EPPP_LINK_DEVICE_UART=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_EPPP_LINK_USES_PPP=y

View File

@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=y

View File

@@ -0,0 +1 @@
CONFIG_LWIP_IPV6=n

View File

@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32c6"
CONFIG_EPPP_LINK_DEVICE_SDIO=y

View File

@@ -0,0 +1 @@
CONFIG_EPPP_LINK_DEVICE_SPI=y

View File

@@ -0,0 +1 @@
CONFIG_EPPP_LINK_DEVICE_UART=y

View File

@@ -0,0 +1,3 @@
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096