Merge branch 'feat/common_connect_ppp' into 'master'

feat(examples): Add PPP to common connection component

See merge request espressif/esp-idf!26315
This commit is contained in:
David Čermák 2023-10-31 18:59:38 +08:00
commit 283a3d64b9
8 changed files with 414 additions and 1 deletions

View File

@ -22,6 +22,10 @@ if(CONFIG_EXAMPLE_CONNECT_ETHERNET)
list(APPEND srcs "eth_connect.c")
endif()
if(CONFIG_EXAMPLE_CONNECT_PPP)
list(APPEND srcs "ppp_connect.c")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
@ -34,3 +38,7 @@ endif()
if(CONFIG_EXAMPLE_CONNECT_ETHERNET)
idf_component_optional_requires(PRIVATE esp_eth)
endif()
if(CONFIG_EXAMPLE_CONNECT_PPP)
idf_component_optional_requires(PRIVATE esp_tinyusb espressif__esp_tinyusb)
endif()

View File

@ -306,16 +306,80 @@ menu "Example Connection Configuration"
Set PHY address according your board schematic.
endif # EXAMPLE_CONNECT_ETHERNET
config EXAMPLE_CONNECT_PPP
bool "connect using Point to Point interface"
select LWIP_PPP_SUPPORT
help
Protocol examples can use PPP connection over serial line.
Choose this option to connect to the ppp server running
on your laptop over a serial line (either UART or USB ACM)
if EXAMPLE_CONNECT_PPP
choice EXAMPLE_CONNECT_PPP_DEVICE
prompt "Choose PPP device"
default EXAMPLE_CONNECT_PPP_DEVICE_USB
help
Select which peripheral to use to connect to the PPP server.
config EXAMPLE_CONNECT_PPP_DEVICE_USB
bool "USB"
depends on SOC_USB_OTG_SUPPORTED
select TINYUSB_CDC_ENABLED
help
Use USB ACM device.
config EXAMPLE_CONNECT_PPP_DEVICE_UART
bool "UART"
help
Use UART.
endchoice
menu "UART Configuration"
depends on EXAMPLE_CONNECT_PPP_DEVICE_UART
config EXAMPLE_CONNECT_UART_TX_PIN
int "TXD Pin Number"
default 4
range 0 31
help
Pin number of UART TX.
config EXAMPLE_CONNECT_UART_RX_PIN
int "RXD Pin Number"
default 5
range 0 31
help
Pin number of UART RX.
config EXAMPLE_CONNECT_UART_BAUDRATE
int "UART Baudrate"
default 115200
range 9600 3000000
help
Baudrate of the UART device
endmenu
config EXAMPLE_PPP_CONN_MAX_RETRY
int "Maximum retry"
default 6
help
Set the Maximum retry to avoid station reconnecting if the pppd
is not available
endif # EXAMPLE_CONNECT_PPP
config EXAMPLE_CONNECT_IPV4
bool
depends on LWIP_IPV4
default y
config EXAMPLE_CONNECT_IPV6
depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET
depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET || EXAMPLE_CONNECT_PPP
bool "Obtain IPv6 address"
default y
select LWIP_IPV6
select LWIP_PPP_ENABLE_IPV6 if EXAMPLE_CONNECT_PPP
help
By default, examples will wait until IPv4 and IPv6 local link addresses are obtained.
Disable this option if the network does not support IPv6.

View File

@ -0,0 +1,58 @@
# protocol_example_connect
This component implements the most common connection methods for ESP32 boards. It should be used mainly in examples of ESP-IDF to demonstrate functionality of network protocols and other libraries, that need the connection step as a prerequisite.
## How to use this component
Choose the preferred interface (WiFi, Ethernet, PPPoS) to connect to the network and configure the interface.
It is possible to enable multiple interfaces simultaneously making the connection phase to block until all the chosen interfaces acquire IP addresses.
It is also possible to disable all interfaces, skipping the connection phase altogether.
### WiFi
Choose WiFi connection method (for chipsets that support it) and configure basic WiFi connection properties:
* WiFi SSID
* WiFI password
* Maximum connection retry (connection would be aborted if it doesn't succeed after specified number of retries)
* WiFi scan method (including RSSI and authorization mode threshold)
### Ethernet
Choose Ethernet connection if your board supports it. The most common settings is using Espressif Ethernet Kit, which is also the recommended HW for this selection. You can also select an SPI ethernet device (if your chipset doesn't support internal EMAC or if you prefer). It is also possible to use OpenCores Ethernet MAC if you're running the example under QEMU.
### PPP
Point to point connection method creates a simple IP tunnel to the counterpart device (running PPP server), typically a Linux machine with pppd service. We currently support only PPP over Serial (using UART or USB CDC). This is useful for simple testing of networking layers, but with some additional configuration on the server side, we could simulate standard model of internet connectivity. The PPP server could be also represented by a cellular modem device with pre-configured connectivity and already switched to PPP mode (this setup is not very flexible though, so we suggest using a standard modem library implementing commands and modes, e.g. [esp_modem](https://components.espressif.com/component/espressif/esp_modem) ).
> [!Note]
> Note that if you choose USB device, you have to manually add a dependency on `esp_tinyusb` component. This step is necessary to keep the `protocol_example_connect` component simple and dependency free. Please run this command from your project location to add the dependency:
> ```bash
> idf.py add-dependency espressif/esp_tinyusb^1
> ```
#### Setup a PPP server
Connect the board using UART or USB and note the device name, which would be typically:
* `/dev/ttyACMx` for USB devices
* `/dev/ttyUSBx` for UART devices
Run the pppd server:
```bash
sudo pppd /dev/ttyACM0 115200 192.168.11.1:192.168.11.2 ms-dns 8.8.8.8 modem local noauth debug nocrtscts nodetach +ipv6
```
Please update the parameters with the correct serial device, baud rate, IP addresses, DNS server, use `+ipv6` if `EXAMPLE_CONNECT_IPV6=y`.
#### Connection to outside
In order to access other network endpoints, we have to configure some IP/translation rules. The easiest method is to setup a masquerade of the PPPD created interface (`ppp0`) to your default networking interface (`${ETH0}`). Here is an example of such rule:
```bash
sudo iptables -t nat -A POSTROUTING -o ${ETH0} -j MASQUERADE
sudo iptables -A FORWARD -i ${ETH0} -o ppp0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i ppp0 -o ${ETH0} -j ACCEPT
```

View File

@ -101,6 +101,12 @@ esp_err_t example_connect(void)
}
ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_wifi_shutdown));
#endif
#if CONFIG_EXAMPLE_CONNECT_PPP
if (example_ppp_connect() != ESP_OK) {
return ESP_FAIL;
}
ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_ppp_shutdown));
#endif
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
example_print_all_netif_ips(EXAMPLE_NETIF_DESC_ETH);
@ -110,6 +116,10 @@ esp_err_t example_connect(void)
example_print_all_netif_ips(EXAMPLE_NETIF_DESC_STA);
#endif
#if CONFIG_EXAMPLE_CONNECT_PPP
example_print_all_netif_ips(EXAMPLE_NETIF_DESC_PPP);
#endif
return ESP_OK;
}

View File

@ -45,6 +45,9 @@ void example_wifi_shutdown(void);
esp_err_t example_wifi_connect(void);
void example_ethernet_shutdown(void);
esp_err_t example_ethernet_connect(void);
esp_err_t example_ppp_connect(void);
void example_ppp_start(void);
void example_ppp_shutdown(void);

View File

@ -31,6 +31,10 @@ extern "C" {
#define EXAMPLE_NETIF_DESC_ETH "example_netif_eth"
#endif
#if CONFIG_EXAMPLE_CONNECT_PPP
#define EXAMPLE_NETIF_DESC_PPP "example_netif_ppp"
#endif
/* Example default interface, prefer the ethernet one if running in example-test (CI) configuration */
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_ETH)
@ -38,6 +42,9 @@ extern "C" {
#elif CONFIG_EXAMPLE_CONNECT_WIFI
#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_STA)
#define get_example_netif() get_example_netif_from_desc(EXAMPLE_NETIF_DESC_STA)
#elif CONFIG_EXAMPLE_CONNECT_PPP
#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_PPP)
#define get_example_netif() get_example_netif_from_desc(EXAMPLE_NETIF_DESC_PPP)
#endif
/**

View File

@ -0,0 +1,260 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include <stdint.h>
#include "sdkconfig.h"
#include "protocol_examples_common.h"
#include "example_common_private.h"
#if CONFIG_EXAMPLE_CONNECT_PPP
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
static int s_itf;
static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE];
#else // DEVICE is UART
#include "driver/uart.h"
#define BUF_SIZE (1024)
static bool s_stop_task = false;
#endif // CONNECT_PPP_DEVICE
static const char *TAG = "example_connect_ppp";
static int s_retry_num = 0;
static EventGroupHandle_t s_event_group = NULL;
static esp_netif_t *s_netif;
static const int GOT_IPV4 = BIT0;
static const int CONNECTION_FAILED = BIT1;
#if CONFIG_EXAMPLE_CONNECT_IPV6
static const int GOT_IPV6 = BIT2;
#define CONNECT_BITS (GOT_IPV4|GOT_IPV6|CONNECTION_FAILED)
#else
#define CONNECT_BITS (GOT_IPV4|CONNECTION_FAILED)
#endif
static esp_err_t transmit(void *h, void *buffer, size_t len)
{
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE);
#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB
tinyusb_cdcacm_write_queue(s_itf, buffer, len);
tinyusb_cdcacm_write_flush(s_itf, 0);
#else // DEVICE_UART
uart_write_bytes(UART_NUM_1, buffer, len);
#endif // CONNECT_PPP_DEVICE
return ESP_OK;
}
static esp_netif_driver_ifconfig_t driver_cfg = {
.handle = (void *)1, // singleton driver, just to != NULL
.transmit = transmit,
};
const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg;
static void on_ip_event(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_id == IP_EVENT_PPP_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
if (!example_is_our_netif(EXAMPLE_NETIF_DESC_PPP, event->esp_netif)) {
return;
}
esp_netif_t *netif = event->esp_netif;
esp_netif_dns_info_t dns_info;
ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info);
ESP_LOGI(TAG, "Main DNS server : " IPSTR, IP2STR(&dns_info.ip.u_addr.ip4));
xEventGroupSetBits(s_event_group, GOT_IPV4);
#if CONFIG_EXAMPLE_CONNECT_IPV6
} else if (event_id == IP_EVENT_GOT_IP6) {
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
if (!example_is_our_netif(EXAMPLE_NETIF_DESC_PPP, event->esp_netif)) {
return;
}
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip);
ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif),
IPV62STR(event->ip6_info.ip), example_ipv6_addr_types_to_str[ipv6_type]);
if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) {
xEventGroupSetBits(s_event_group, GOT_IPV6);
}
#endif
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
ESP_LOGI(TAG, "Disconnect from PPP Server");
s_retry_num++;
if (s_retry_num > CONFIG_EXAMPLE_PPP_CONN_MAX_RETRY) {
ESP_LOGE(TAG, "PPP Connection failed %d times, stop reconnecting.", s_retry_num);
xEventGroupSetBits(s_event_group, CONNECTION_FAILED);
} else {
ESP_LOGI(TAG, "PPP Connection failed %d times, try to reconnect.", s_retry_num);
esp_netif_action_start(s_netif, 0, 0, 0);
esp_netif_action_connected(s_netif, 0, 0, 0);
}
}
}
#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB
static void cdc_rx_callback(int itf, cdcacm_event_t *event)
{
size_t rx_size = 0;
if (itf != s_itf) {
// Not our channel
return;
}
esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
if (ret == ESP_OK) {
ESP_LOG_BUFFER_HEXDUMP(TAG, buf, rx_size, ESP_LOG_VERBOSE);
// pass the received data to the network interface
esp_netif_receive(s_netif, buf, rx_size, NULL);
} else {
ESP_LOGE(TAG, "Read error");
}
}
static void line_state_changed(int itf, cdcacm_event_t *event)
{
s_itf = itf; // use this channel for the netif communication
ESP_LOGI(TAG, "Line state changed on channel %d", itf);
}
#else // DEVICE is UART
static void ppp_task(void *args)
{
uart_config_t uart_config = {};
uart_config.baud_rate = CONFIG_EXAMPLE_CONNECT_UART_BAUDRATE;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_DEFAULT;
QueueHandle_t event_queue;
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, BUF_SIZE, 0, 16, &event_queue, 0));
ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, CONFIG_EXAMPLE_CONNECT_UART_TX_PIN, CONFIG_EXAMPLE_CONNECT_UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_set_rx_timeout(UART_NUM_1, 1));
char *buffer = (char*)malloc(BUF_SIZE);
uart_event_t event;
esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, esp_netif_action_connected, s_netif);
esp_netif_action_start(s_netif, 0, 0, 0);
esp_netif_action_connected(s_netif, 0, 0, 0);
while (!s_stop_task) {
xQueueReceive(event_queue, &event, pdMS_TO_TICKS(1000));
if (event.type == UART_DATA) {
size_t len;
uart_get_buffered_data_len(UART_NUM_1, &len);
if (len) {
len = uart_read_bytes(UART_NUM_1, buffer, BUF_SIZE, 0);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE);
esp_netif_receive(s_netif, buffer, len, NULL);
}
} else {
ESP_LOGW(TAG, "Received UART event: %d", event.type);
}
}
free(buffer);
vTaskDelete(NULL);
}
#endif // CONNECT_PPP_DEVICE
esp_err_t example_ppp_connect(void)
{
ESP_LOGI(TAG, "Start example_connect.");
#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB
ESP_LOGI(TAG, "USB initialization");
const tinyusb_config_t tusb_cfg = {
.device_descriptor = NULL,
.string_descriptor = NULL,
.external_phy = false,
.configuration_descriptor = NULL,
};
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
tinyusb_config_cdcacm_t acm_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = TINYUSB_CDC_ACM_0,
.callback_rx = &cdc_rx_callback,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = NULL,
.callback_line_coding_changed = NULL
};
ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg));
/* the second way to register a callback */
ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback(
TINYUSB_CDC_ACM_0,
CDC_EVENT_LINE_STATE_CHANGED,
&line_state_changed));
#endif // CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB
s_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL));
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP();
base_netif_cfg.if_desc = EXAMPLE_NETIF_DESC_PPP;
esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg,
.driver = ppp_driver_cfg,
.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP
};
s_netif = esp_netif_new(&netif_ppp_config);
assert(s_netif);
#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_USB
esp_netif_action_start(s_netif, 0, 0, 0);
esp_netif_action_connected(s_netif, 0, 0, 0);
#else // DEVICE is UART
s_stop_task = false;
if (xTaskCreate(ppp_task, "ppp connect", 4096, NULL, 5, NULL) != pdTRUE) {
ESP_LOGE(TAG, "Failed to create a ppp connection task");
return ESP_FAIL;
}
#endif // CONNECT_PPP_DEVICE
ESP_LOGI(TAG, "Waiting for IP address");
EventBits_t bits = xEventGroupWaitBits(s_event_group, CONNECT_BITS, pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & CONNECTION_FAILED) {
ESP_LOGE(TAG, "Connection failed!");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Connected!");
return ESP_OK;
}
void example_ppp_shutdown(void)
{
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event));
#if CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_UART
s_stop_task = true;
vTaskDelay(pdMS_TO_TICKS(1000)); // wait for the ppp task to stop
#endif
esp_netif_action_disconnected(s_netif, 0, 0, 0);
vEventGroupDelete(s_event_group);
esp_netif_action_stop(s_netif, 0, 0, 0);
esp_netif_destroy(s_netif);
s_netif = NULL;
s_event_group = NULL;
}
#endif // CONFIG_EXAMPLE_CONNECT_PPP

View File

@ -0,0 +1,3 @@
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_CONNECT_PPP=y
CONFIG_EXAMPLE_CONNECT_PPP_DEVICE_UART=y