diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index 7d7c0ff0ab..99ed9fd364 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -6,6 +6,28 @@ menu "OpenThread" help Select this option to enable OpenThread and show the submenu with OpenThread configuration choices. + menu "Thread Task Parameters" + depends on OPENTHREAD_ENABLED + + config OPENTHREAD_TASK_NAME + string "OpenThread task name" + default "ot_main" + help + The OpenThread task name. + + config OPENTHREAD_TASK_SIZE + int "Size of OpenThread task" + default 8192 + help + The size in bytes of OpenThread task. + + config OPENTHREAD_TASK_PRIORITY + int "Priority of OpenThread task" + default 5 + help + The priority of OpenThread task. + endmenu + menu "Thread Version Message" depends on OPENTHREAD_ENABLED @@ -23,10 +45,18 @@ menu "OpenThread" endmenu menu "Thread Console" - depends on OPENTHREAD_ENABLED + config OPENTHREAD_CONSOLE_ENABLE + bool "Enable OpenThread console" + depends on OPENTHREAD_ENABLED + default y + help + Enable the OpenThread-specific console provided by the SDK. This only controls whether + the SDK sets up a dedicated console for OpenThread. Even if disabled, the default + ESP-IDF console (if initialized elsewhere) can still be used independently. choice OPENTHREAD_CONSOLE_TYPE prompt "OpenThread console type" + depends on OPENTHREAD_CONSOLE_ENABLE default OPENTHREAD_CONSOLE_TYPE_UART help Select OpenThread console type diff --git a/components/openthread/include/esp_openthread.h b/components/openthread/include/esp_openthread.h index a3f0528f93..ce96d18656 100644 --- a/components/openthread/include/esp_openthread.h +++ b/components/openthread/include/esp_openthread.h @@ -7,6 +7,7 @@ #pragma once #include "esp_err.h" +#include "esp_netif.h" #include "esp_openthread_types.h" #include "openthread/dataset.h" #include "openthread/error.h" @@ -89,6 +90,29 @@ otInstance *esp_openthread_get_instance(void); */ esp_err_t esp_openthread_mainloop_exit(void); +/** + * @brief Starts the full OpenThread stack and create a handle task. + * + * @note The OpenThread instance will also be initialized in this function. + * + * @param[in] config The OpenThread platform configuration. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if already initialized + * + */ +esp_err_t esp_openthread_start(esp_openthread_platform_config_t *config); + +/** + * @brief This function performs OpenThread stack and platform driver deinitialization and delete the handle task. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if Thread is already active + * + */ +esp_err_t esp_openthread_stop(void); + #ifdef __cplusplus } // end of extern "C" #endif diff --git a/components/openthread/private_include/esp_openthread_ncp.h b/components/openthread/private_include/esp_openthread_ncp.h index 507d305daa..65053bb56a 100644 --- a/components/openthread/private_include/esp_openthread_ncp.h +++ b/components/openthread/private_include/esp_openthread_ncp.h @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include +#include #if CONFIG_OPENTHREAD_NCP_VENDOR_HOOK @@ -15,3 +16,13 @@ #define SPINEL_PROP_VENDOR_ESP_COEX_EVENT (SPINEL_PROP_VENDOR_ESP__BEGIN + 3) #endif + +#ifdef __cplusplus +extern "C" { +#endif + +void otAppNcpInit(otInstance *aInstance); + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_openthread_platform.h b/components/openthread/private_include/esp_openthread_platform.h index 3999322a8d..582599f5d7 100644 --- a/components/openthread/private_include/esp_openthread_platform.h +++ b/components/openthread/private_include/esp_openthread_platform.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/openthread/src/esp_openthread.cpp b/components/openthread/src/esp_openthread.cpp index d816cd121d..e779959d25 100644 --- a/components/openthread/src/esp_openthread.cpp +++ b/components/openthread/src/esp_openthread.cpp @@ -4,12 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "sdkconfig.h" #include "esp_openthread.h" #include "esp_check.h" +#include "esp_heap_caps.h" #include "esp_openthread_border_router.h" #include "esp_openthread_common_macro.h" +#include "esp_openthread_cli.h" #include "esp_openthread_dns64.h" #include "esp_openthread_lock.h" +#include "esp_openthread_ncp.h" +#include "esp_openthread_netif_glue.h" #include "esp_openthread_platform.h" #include "esp_openthread_sleep.h" #include "esp_openthread_state.h" @@ -18,15 +23,19 @@ #include "freertos/FreeRTOS.h" #include "lwip/dns.h" #include "openthread/instance.h" +#include "openthread/logging.h" #include "openthread/netdata.h" #include "openthread/tasklet.h" #include "openthread/thread.h" +#include #if CONFIG_OPENTHREAD_FTD #include "openthread/dataset_ftd.h" #endif static bool s_ot_mainloop_running = false; +static SemaphoreHandle_t s_ot_syn_semaphore = NULL; +static TaskHandle_t s_ot_task_handle = NULL; static int hex_digit_to_int(char hex) { @@ -223,3 +232,81 @@ esp_err_t esp_openthread_deinit(void) otInstanceFinalize(esp_openthread_get_instance()); return esp_openthread_platform_deinit(); } + +static void ot_task_worker(void *aContext) +{ + esp_openthread_platform_config_t* config = (esp_openthread_platform_config_t *)aContext; + // Initialize the OpenThread stack + ESP_ERROR_CHECK(esp_openthread_init(config)); + +#if CONFIG_OPENTHREAD_FTD || CONFIG_OPENTHREAD_MTD + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); + esp_netif_t *openthread_netif = esp_netif_new(&cfg); + assert(openthread_netif != NULL); + ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(config))); +#endif + +#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC + // The OpenThread log level directly matches ESP log level + (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); +#endif // CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC + +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_init(); + esp_openthread_cli_console_command_register(); +#endif // CONFIG_OPENTHREAD_CLI + + xSemaphoreGive(s_ot_syn_semaphore); +#if CONFIG_OPENTHREAD_RADIO + otAppNcpInit(esp_openthread_get_instance()); +#endif + + // Run the main loop + esp_openthread_launch_mainloop(); + +#if CONFIG_OPENTHREAD_RADIO + ESP_LOGE(OT_PLAT_LOG_TAG, "RCP deinitialization is not supported for now"); + assert(false); +#endif +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_console_command_unregister(); +#endif // CONFIG_OPENTHREAD_CLI + +#if CONFIG_OPENTHREAD_FTD || CONFIG_OPENTHREAD_MTD + // Clean up + esp_openthread_netif_glue_deinit(); + esp_netif_destroy(openthread_netif); +#endif + + ESP_ERROR_CHECK(esp_openthread_deinit()); + + xSemaphoreGive(s_ot_syn_semaphore); + vTaskDelay(portMAX_DELAY); +} + +esp_err_t esp_openthread_start(esp_openthread_platform_config_t *config) +{ + ESP_RETURN_ON_FALSE(s_ot_syn_semaphore == NULL, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "OpenThread has been initialized"); + s_ot_syn_semaphore = xSemaphoreCreateBinary(); + ESP_RETURN_ON_FALSE(s_ot_syn_semaphore != NULL, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Failed to create s_ot_syn_semaphore"); + assert(xTaskCreate(ot_task_worker, CONFIG_OPENTHREAD_TASK_NAME, CONFIG_OPENTHREAD_TASK_SIZE, config, CONFIG_OPENTHREAD_TASK_PRIORITY, &s_ot_task_handle) == pdPASS); + xSemaphoreTake(s_ot_syn_semaphore, portMAX_DELAY); + return ESP_OK; +} + +esp_err_t esp_openthread_stop(void) +{ + ESP_RETURN_ON_FALSE(s_ot_syn_semaphore != NULL, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "OpenThread is not initialized"); + esp_openthread_lock_acquire(portMAX_DELAY); + otInstance *instance = esp_openthread_get_instance(); + bool is_thread_not_active = (otThreadGetDeviceRole(instance) == OT_DEVICE_ROLE_DISABLED && otIp6IsEnabled(instance) == false); + esp_openthread_lock_release(); + ESP_RETURN_ON_FALSE(is_thread_not_active, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Thread interface is still active"); + esp_openthread_mainloop_exit(); + xSemaphoreTake(s_ot_syn_semaphore, portMAX_DELAY); + vTaskDelete(s_ot_task_handle); + vSemaphoreDelete(s_ot_syn_semaphore); + s_ot_task_handle = NULL; + s_ot_syn_semaphore = NULL; + return ESP_OK; +}