diff --git a/examples/common_components/protocol_examples_common/CMakeLists.txt b/examples/common_components/protocol_examples_common/CMakeLists.txt index 4f9ea953cf46..a5711e23b9e0 100644 --- a/examples/common_components/protocol_examples_common/CMakeLists.txt +++ b/examples/common_components/protocol_examples_common/CMakeLists.txt @@ -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() diff --git a/examples/common_components/protocol_examples_common/Kconfig.projbuild b/examples/common_components/protocol_examples_common/Kconfig.projbuild index 7eb2a8fd6f8c..41f112b64b1d 100644 --- a/examples/common_components/protocol_examples_common/Kconfig.projbuild +++ b/examples/common_components/protocol_examples_common/Kconfig.projbuild @@ -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 diff --git a/examples/common_components/protocol_examples_common/connect.c b/examples/common_components/protocol_examples_common/connect.c index 6abc2a3d24c5..89317d0bbc0e 100644 --- a/examples/common_components/protocol_examples_common/connect.c +++ b/examples/common_components/protocol_examples_common/connect.c @@ -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; } diff --git a/examples/common_components/protocol_examples_common/include/example_common_private.h b/examples/common_components/protocol_examples_common/include/example_common_private.h index b85d26d6360c..df786d851f91 100644 --- a/examples/common_components/protocol_examples_common/include/example_common_private.h +++ b/examples/common_components/protocol_examples_common/include/example_common_private.h @@ -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); diff --git a/examples/common_components/protocol_examples_common/include/protocol_examples_common.h b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h index f8c54f558bef..99eea9e57257 100644 --- a/examples/common_components/protocol_examples_common/include/protocol_examples_common.h +++ b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h @@ -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 /** diff --git a/examples/common_components/protocol_examples_common/include/protocol_examples_thread_config.h b/examples/common_components/protocol_examples_common/include/protocol_examples_thread_config.h new file mode 100644 index 000000000000..532f828b65fa --- /dev/null +++ b/examples/common_components/protocol_examples_common/include/protocol_examples_thread_config.h @@ -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 + +#include + +#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, \ + } diff --git a/examples/common_components/protocol_examples_common/thread_connect.c b/examples/common_components/protocol_examples_common/thread_connect.c new file mode 100644 index 000000000000..f4dae26e56fb --- /dev/null +++ b/examples/common_components/protocol_examples_common/thread_connect.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/examples/openthread/.build-test-rules.yml b/examples/openthread/.build-test-rules.yml index fe987c1cae7c..a7e57a4fc373 100644 --- a/examples/openthread/.build-test-rules.yml +++ b/examples/openthread/.build-test-rules.yml @@ -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/**/* diff --git a/examples/openthread/ot_br/sdkconfig.defaults b/examples/openthread/ot_br/sdkconfig.defaults index 619ab0f7e78d..2b17031c2bfb 100644 --- a/examples/openthread/ot_br/sdkconfig.defaults +++ b/examples/openthread/ot_br/sdkconfig.defaults @@ -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 # diff --git a/examples/openthread/ot_trel/sdkconfig.defaults b/examples/openthread/ot_trel/sdkconfig.defaults index 99f65081b92b..07de5fdbf960 100644 --- a/examples/openthread/ot_trel/sdkconfig.defaults +++ b/examples/openthread/ot_trel/sdkconfig.defaults @@ -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 diff --git a/examples/protocols/sntp/sdkconfig.defaults.esp32h2 b/examples/protocols/sntp/sdkconfig.defaults.esp32h2 new file mode 100644 index 000000000000..326bcafa2505 --- /dev/null +++ b/examples/protocols/sntp/sdkconfig.defaults.esp32h2 @@ -0,0 +1,4 @@ +CONFIG_IDF_TARGET="esp32h2" +CONFIG_MBEDTLS_CMAC_C=y +CONFIG_OPENTHREAD_ENABLED=y +CONFIG_OPENTHREAD_DNS64_CLIENT=y