Merge branch 'feat/example_connect_thread_v5_4' into 'release/v5.4'

feat(protocol_examples_common): Add Thread connect to support Thread for the protocol examples(v5.4)

See merge request espressif/esp-idf!36090
This commit is contained in:
Shu Chen 2025-01-07 10:34:12 +08:00
commit 89e47be331
12 changed files with 372 additions and 10 deletions

View File

@ -22,6 +22,10 @@ if(CONFIG_EXAMPLE_CONNECT_ETHERNET)
list(APPEND srcs "eth_connect.c")
endif()
if(CONFIG_EXAMPLE_CONNECT_THREAD)
list(APPEND srcs "thread_connect.c")
endif()
if(CONFIG_EXAMPLE_CONNECT_PPP)
list(APPEND srcs "ppp_connect.c")
endif()
@ -29,7 +33,7 @@ endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
PRIV_REQUIRES esp_netif driver esp_wifi vfs console esp_eth)
PRIV_REQUIRES esp_netif driver esp_wifi vfs console esp_eth openthread)
if(CONFIG_EXAMPLE_PROVIDE_WIFI_CONSOLE_CMD)
idf_component_optional_requires(PRIVATE console)
@ -39,6 +43,10 @@ if(CONFIG_EXAMPLE_CONNECT_ETHERNET)
idf_component_optional_requires(PUBLIC esp_eth)
endif()
if(CONFIG_EXAMPLE_CONNECT_THREAD)
idf_component_optional_requires(PRIVATE openthread)
endif()
if(CONFIG_EXAMPLE_CONNECT_PPP)
idf_component_optional_requires(PRIVATE esp_tinyusb espressif__esp_tinyusb)
endif()

View File

@ -7,7 +7,7 @@ menu "Example Connection Configuration"
depends on !IDF_TARGET_LINUX && (SOC_WIFI_SUPPORTED || ESP_WIFI_REMOTE_ENABLED || ESP_HOST_WIFI_ENABLED)
default y if SOC_WIFI_SUPPORTED
help
Protocol examples can use Wi-Fi and/or Ethernet to connect to the network.
Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network.
Choose this option to connect with WiFi
if EXAMPLE_CONNECT_WIFI
@ -119,9 +119,9 @@ menu "Example Connection Configuration"
config EXAMPLE_CONNECT_ETHERNET
bool "connect using Ethernet interface"
depends on !IDF_TARGET_LINUX
default y if !EXAMPLE_CONNECT_WIFI
default y if !EXAMPLE_CONNECT_WIFI && !EXAMPLE_CONNECT_THREAD
help
Protocol examples can use Wi-Fi and/or Ethernet to connect to the network.
Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network.
Choose this option to connect with Ethernet
if EXAMPLE_CONNECT_ETHERNET
@ -381,13 +381,83 @@ menu "Example Connection Configuration"
endif # EXAMPLE_CONNECT_PPP
config EXAMPLE_CONNECT_THREAD
bool "Connect using Thread interface"
depends on !IDF_TARGET_LINUX && OPENTHREAD_ENABLED
default y if SOC_IEEE802154_SUPPORTED
select EXAMPLE_CONNECT_IPV6
help
Protocol examples can use Wi-Fi, Ethernet and/or Thread to connect to the network.
Choose this option to connect with Thread.
The operational active dataset of the Thread network can be configured in openthread
component at '->Components->OpenThread->Thread Core Features->Thread Operational Dataset'
if EXAMPLE_CONNECT_THREAD
config EXAMPLE_THREAD_TASK_STACK_SIZE
int "Example Thread task stack size"
default 8192
help
Thread task stack size
menu "Radio Spinel Options"
depends on OPENTHREAD_RADIO_SPINEL_UART || OPENTHREAD_RADIO_SPINEL_SPI
config EXAMPLE_THREAD_UART_RX_PIN
depends on OPENTHREAD_RADIO_SPINEL_UART
int "Uart Rx Pin"
default 17
config EXAMPLE_THREAD_UART_TX_PIN
depends on OPENTHREAD_RADIO_SPINEL_UART
int "Uart Tx pin"
default 18
config EXAMPLE_THREAD_UART_BAUD
depends on OPENTHREAD_RADIO_SPINEL_UART
int "Uart baud rate"
default 460800
config EXAMPLE_THREAD_UART_PORT
depends on OPENTHREAD_RADIO_SPINEL_UART
int "Uart port"
default 1
config EXAMPLE_THREAD_SPI_CS_PIN
depends on OPENTHREAD_RADIO_SPINEL_SPI
int "SPI CS Pin"
default 10
config EXAMPLE_THREAD_SPI_SCLK_PIN
depends on OPENTHREAD_RADIO_SPINEL_SPI
int "SPI SCLK Pin"
default 12
config EXAMPLE_THREAD_SPI_MISO_PIN
depends on OPENTHREAD_RADIO_SPINEL_SPI
int "SPI MISO Pin"
default 13
config EXAMPLE_THREAD_SPI_MOSI_PIN
depends on OPENTHREAD_RADIO_SPINEL_SPI
int "SPI MOSI Pin"
default 11
config EXAMPLE_THREAD_SPI_INTR_PIN
depends on OPENTHREAD_RADIO_SPINEL_SPI
int "SPI Interrupt Pin"
default 8
endmenu
endif
config EXAMPLE_CONNECT_IPV4
bool
depends on LWIP_IPV4
default n if EXAMPLE_CONNECT_THREAD
default y
config EXAMPLE_CONNECT_IPV6
depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET || EXAMPLE_CONNECT_PPP
depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET || EXAMPLE_CONNECT_PPP || EXAMPLE_CONNECT_THREAD
bool "Obtain IPv6 address"
default y
select LWIP_IPV6

View File

@ -4,7 +4,7 @@ This component implements the most common connection methods for ESP32 boards. I
## How to use this component
Choose the preferred interface (WiFi, Ethernet, PPPoS) to connect to the network and configure the interface.
Choose the preferred interface (WiFi, Ethernet, Thread, 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.
@ -23,6 +23,14 @@ Choose WiFi connection method (for chipsets that support it) and configure basic
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.
### Thread
Choose Thread connection if your board supports IEEE802.15.4 native radio or works with [OpenThread RCP](../../openthread/ot_rcp/README.md). You can configure the Thread network at menuconfig '->Components->OpenThread->Thread Core Features->Thread Operational Dataset'.
If the Thread end-device joins a Thread network with a Thread Border Router that has the NAT64 feature enabled, the end-device can access the Internet with the standard DNS APIs after configuring the following properties:
* Enable DNS64 client ('->Components->OpenThread->Thread Core Features->Enable DNS64 client')
* Enable custom DNS external resolve Hook ('->Components->LWIP->Hooks->DNS external resolve Hook->Custom implementation')
### 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) ).

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -35,7 +35,7 @@ const char *example_ipv6_addr_types_to_str[6] = {
/**
* @brief Checks the netif description if it contains specified prefix.
* All netifs created withing common connect component are prefixed with the module TAG,
* All netifs created within common connect component are prefixed with the module TAG,
* so it returns true if the specified netif is owned by this module
*/
bool example_is_our_netif(const char *prefix, esp_netif_t *netif)
@ -61,7 +61,7 @@ static esp_err_t print_all_ips_tcpip(void* ctx)
while ((netif = esp_netif_next_unsafe(netif)) != NULL) {
if (example_is_our_netif(prefix, netif)) {
ESP_LOGI(TAG, "Connected to %s", esp_netif_get_desc(netif));
#if CONFIG_LWIP_IPV4
#if CONFIG_EXAMPLE_CONNECT_IPV4
esp_netif_ip_info_t ip;
ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip));
@ -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_THREAD
if (example_thread_connect() != ESP_OK) {
return ESP_FAIL;
}
ESP_ERROR_CHECK(esp_register_shutdown_handler(&example_thread_shutdown));
#endif
#if CONFIG_EXAMPLE_CONNECT_PPP
if (example_ppp_connect() != ESP_OK) {
return ESP_FAIL;
@ -116,6 +122,10 @@ esp_err_t example_connect(void)
example_print_all_netif_ips(EXAMPLE_NETIF_DESC_STA);
#endif
#if CONFIG_EXAMPLE_CONNECT_THREAD
example_print_all_netif_ips(EXAMPLE_NETIF_DESC_THREAD);
#endif
#if CONFIG_EXAMPLE_CONNECT_PPP
example_print_all_netif_ips(EXAMPLE_NETIF_DESC_PPP);
#endif

View File

@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Private Funtions of protocol example common */
/* Private Functions of protocol example common */
#pragma once
@ -45,6 +45,8 @@ void example_wifi_shutdown(void);
esp_err_t example_wifi_connect(void);
void example_ethernet_shutdown(void);
esp_err_t example_ethernet_connect(void);
void example_thread_shutdown(void);
esp_err_t example_thread_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_THREAD
#define EXAMPLE_NETIF_DESC_THREAD "example_netif_thread"
#endif
#if CONFIG_EXAMPLE_CONNECT_PPP
#define EXAMPLE_NETIF_DESC_PPP "example_netif_ppp"
#endif
@ -74,6 +78,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_THREAD
#define EXAMPLE_INTERFACE get_example_netif_from_desc(EXAMPLE_NETIF_DESC_THREAD)
#define get_example_netif() get_example_netif_from_desc(EXAMPLE_NETIF_DESC_THREAD)
#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)

View File

@ -0,0 +1,115 @@
/*
* Thread configurations for protocol examples
*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include <sdkconfig.h>
#include <esp_openthread_types.h>
#ifdef CONFIG_OPENTHREAD_RADIO_NATIVE
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = RADIO_MODE_NATIVE, \
}
#elif defined(CONFIG_OPENTHREAD_RADIO_SPINEL_UART)
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = RADIO_MODE_UART_RCP, \
.radio_uart_config = \
{ \
.port = CONFIG_EXAMPLE_THREAD_UART_PORT, \
.uart_config = \
{ \
.baud_rate = CONFIG_EXAMPLE_THREAD_UART_BAUD, \
.data_bits = UART_DATA_8_BITS, \
.parity = UART_PARITY_DISABLE, \
.stop_bits = UART_STOP_BITS_1, \
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \
.rx_flow_ctrl_thresh = 0, \
.source_clk = UART_SCLK_DEFAULT, \
}, \
.rx_pin = CONFIG_EXAMPLE_THREAD_UART_RX_PIN, \
.tx_pin = CONFIG_EXAMPLE_THREAD_UART_TX_PIN, \
}, \
}
#elif defined(CONFIG_OPENTHREAD_RADIO_SPINEL_SPI)
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = RADIO_MODE_SPI_RCP, \
.radio_spi_config = \
{ \
.host_device = SPI2_HOST, \
.dma_channel = 2, \
.spi_interface = \
{ \
.mosi_io_num = CONFIG_EXAMPLE_THREAD_SPI_MOSI_PIN, \
.miso_io_num = CONFIG_EXAMPLE_THREAD_SPI_MISO_PIN, \
.sclk_io_num = CONFIG_EXAMPLE_THREAD_SPI_SCLK_PIN, \
.quadwp_io_num = -1, \
.quadhd_io_num = -1, \
}, \
.spi_device = \
{ \
.cs_ena_pretrans = 2, \
.input_delay_ns = 100, \
.mode = 0, \
.clock_speed_hz = 2500 * 1000, \
.spics_io_num = CONFIG_EXAMPLE_THREAD_SPI_CS_PIN, \
.queue_size = 5, \
}, \
.intr_pin = CONFIG_EXAMPLE_THREAD_SPI_INTR_PIN, \
}, \
}
#else
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = RADIO_MODE_TREL, \
}
#endif
#if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \
.host_uart_config = \
{ \
.port = 0, \
.uart_config = \
{ \
.baud_rate = 115200, \
.data_bits = UART_DATA_8_BITS, \
.parity = UART_PARITY_DISABLE, \
.stop_bits = UART_STOP_BITS_1, \
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \
.rx_flow_ctrl_thresh = 0, \
.source_clk = UART_SCLK_DEFAULT, \
}, \
.rx_pin = UART_PIN_NO_CHANGE, \
.tx_pin = UART_PIN_NO_CHANGE, \
}, \
}
#elif CONFIG_OPENTHREAD_CONSOLE_TYPE_USB_SERIAL_JTAG
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, \
.host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), \
}
#else
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_NONE, \
}
#endif
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
{ \
.storage_partition_name = "nvs", \
.netif_queue_size = 10, \
.task_queue_size = 10, \
}

View File

@ -0,0 +1,130 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "esp_err.h"
#include "esp_event.h"
#include "esp_event_base.h"
#include "esp_vfs_eventfd.h"
#include "example_common_private.h"
#include "protocol_examples_common.h"
#include "protocol_examples_thread_config.h"
#include "esp_log.h"
#include <string.h>
#include <esp_openthread_cli.h>
#include <esp_openthread_lock.h>
#include <esp_openthread_netif_glue.h>
#include <esp_openthread_types.h>
#include <esp_openthread.h>
#include <openthread/dataset.h>
#include <openthread/logging.h>
static TaskHandle_t s_ot_task_handle = NULL;
static esp_netif_t *s_openthread_netif = NULL;
static SemaphoreHandle_t s_semph_thread_attached = NULL;
static SemaphoreHandle_t s_semph_thread_set_dns_server = NULL;
static const char *TAG = "example_connect";
static void thread_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id,
void* event_data)
{
if (event_base == OPENTHREAD_EVENT) {
if (event_id == OPENTHREAD_EVENT_ATTACHED) {
xSemaphoreGive(s_semph_thread_attached);
} else if (event_id == OPENTHREAD_EVENT_SET_DNS_SERVER) {
xSemaphoreGive(s_semph_thread_set_dns_server);
}
}
}
static void ot_task_worker(void *aContext)
{
esp_openthread_platform_config_t config = {
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
};
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_OPENTHREAD();
esp_netif_config.if_desc = EXAMPLE_NETIF_DESC_THREAD;
esp_netif_config_t cfg = {
.base = &esp_netif_config,
.stack = &g_esp_netif_netstack_default_openthread,
};
s_openthread_netif = esp_netif_new(&cfg);
assert(s_openthread_netif != NULL);
// Initialize the OpenThread stack
ESP_ERROR_CHECK(esp_openthread_init(&config));
ESP_ERROR_CHECK(esp_netif_attach(s_openthread_netif, esp_openthread_netif_glue_init(&config)));
esp_openthread_lock_acquire(portMAX_DELAY);
(void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
esp_openthread_cli_init();
esp_openthread_cli_create_task();
otOperationalDatasetTlvs dataset;
otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset);
if (error != OT_ERROR_NONE) {
ESP_ERROR_CHECK(esp_openthread_auto_start(NULL));
} else {
ESP_ERROR_CHECK(esp_openthread_auto_start(&dataset));
}
esp_openthread_lock_release();
// Run the main loop
esp_openthread_launch_mainloop();
// Clean up
esp_openthread_netif_glue_deinit();
esp_netif_destroy(s_openthread_netif);
esp_vfs_eventfd_unregister();
vTaskDelete(NULL);
}
/* tear down connection, release resources */
void example_thread_shutdown(void)
{
vTaskDelete(s_ot_task_handle);
esp_openthread_netif_glue_deinit();
esp_netif_destroy(s_openthread_netif);
esp_vfs_eventfd_unregister();
vSemaphoreDelete(s_semph_thread_set_dns_server);
vSemaphoreDelete(s_semph_thread_attached);
}
esp_err_t example_thread_connect(void)
{
s_semph_thread_attached = xSemaphoreCreateBinary();
if (s_semph_thread_attached == NULL) {
return ESP_ERR_NO_MEM;
}
s_semph_thread_set_dns_server = xSemaphoreCreateBinary();
if (s_semph_thread_set_dns_server == NULL) {
vSemaphoreDelete(s_semph_thread_attached);
return ESP_ERR_NO_MEM;
}
// 4 eventfds might be used for Thread
// * netif
// * ot task queue
// * radio driver
// * border router
esp_vfs_eventfd_config_t eventfd_config = {
.max_fds = 4,
};
esp_vfs_eventfd_register(&eventfd_config);
ESP_ERROR_CHECK(esp_event_handler_register(OPENTHREAD_EVENT, ESP_EVENT_ANY_ID, thread_event_handler, NULL));
if (xTaskCreate(ot_task_worker, "ot_br_main", CONFIG_EXAMPLE_THREAD_TASK_STACK_SIZE, NULL, 5, &s_ot_task_handle) != pdPASS) {
vSemaphoreDelete(s_semph_thread_attached);
vSemaphoreDelete(s_semph_thread_set_dns_server);
ESP_LOGE(TAG, "Failed to create openthread task");
return ESP_FAIL;
}
xSemaphoreTake(s_semph_thread_attached, portMAX_DELAY);
// Wait 1s for the Thread device to set its DNS server with the NAT64 prefix.
if (xSemaphoreTake(s_semph_thread_set_dns_server, 1000 / portTICK_PERIOD_MS) != pdPASS) {
ESP_LOGW(TAG, "DNS server is not set for the Thread device, might be unable to access the Internet");
}
return ESP_OK;
}

View File

@ -11,6 +11,8 @@
- lwip
- openthread
depends_filepatterns:
- examples/common_components/protocol_examples_common/*
- examples/common_components/protocol_examples_common/**/*
- examples/openthread/*
- examples/openthread/**/*

View File

@ -47,6 +47,9 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y
CONFIG_MDNS_MULTIPLE_INSTANCE=y
# end of mDNS
# Example connect
CONFIG_EXAMPLE_CONNECT_THREAD=n
#
# ESP System Settings
#

View File

@ -41,3 +41,6 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y
# Configurations for optimizing the size of firmware
#
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
# Example connect
CONFIG_EXAMPLE_CONNECT_THREAD=n

View File

@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32h2"
CONFIG_MBEDTLS_CMAC_C=y
CONFIG_OPENTHREAD_ENABLED=y
CONFIG_OPENTHREAD_DNS64_CLIENT=y