Merge branch 'feat/example_connect_thread_v5_2' into 'release/v5.2'

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

See merge request espressif/esp-idf!36092
This commit is contained in:
Shu Chen
2025-01-05 10:58:24 +08:00
11 changed files with 360 additions and 7 deletions

View File

@ -22,6 +22,9 @@ if(CONFIG_EXAMPLE_CONNECT_ETHERNET)
list(APPEND srcs "eth_connect.c")
endif()
if(CONFIG_EXAMPLE_CONNECT_THREAD)
list(APPEND srcs "thread_connect.c")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
@ -34,3 +37,7 @@ endif()
if(CONFIG_EXAMPLE_CONNECT_ETHERNET)
idf_component_optional_requires(PRIVATE esp_eth)
endif()
if(CONFIG_EXAMPLE_CONNECT_THREAD)
idf_component_optional_requires(PRIVATE openthread)
endif()

View File

@ -7,7 +7,7 @@ menu "Example Connection Configuration"
depends on !IDF_TARGET_LINUX && SOC_WIFI_SUPPORTED
default y
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
@ -121,7 +121,7 @@ menu "Example Connection Configuration"
depends on !IDF_TARGET_LINUX
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 Ethernet
if EXAMPLE_CONNECT_ETHERNET
@ -306,13 +306,83 @@ menu "Example Connection Configuration"
Set PHY address according your board schematic.
endif # EXAMPLE_CONNECT_ETHERNET
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
depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET || EXAMPLE_CONNECT_THREAD
bool "Obtain IPv6 address"
default y
select LWIP_IPV6

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_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_THREAD
example_print_all_netif_ips(EXAMPLE_NETIF_DESC_THREAD);
#endif
return ESP_OK;
}

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);

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)
#endif
/**

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

@ -13,6 +13,8 @@
depends_filepatterns:
- examples/common_components/iperf/*
- examples/common_components/iperf/**/*
- 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