From 2b9d6c8820f82697459eee7b85af87f3f709ce47 Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Fri, 30 May 2025 19:57:02 +0800 Subject: [PATCH 1/4] feat(openthread): optimize trel reception --- components/openthread/Kconfig | 8 + .../private_include/esp_openthread_common.h | 15 ++ .../private_include/esp_openthread_radio.h | 21 +++ .../src/port/esp_openthread_radio.c | 8 +- .../openthread/src/port/esp_openthread_trel.c | 163 +++++++++++------- examples/openthread/ot_br/main/esp_ot_br.c | 19 +- .../openthread/ot_trel/main/esp_ot_trel.c | 3 +- 7 files changed, 164 insertions(+), 73 deletions(-) create mode 100644 components/openthread/private_include/esp_openthread_common.h diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index 54ecce875a..e87e108048 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -130,6 +130,14 @@ menu "OpenThread" default 12390 help Configure the port number of TREL service. + + config OPENTHREAD_TREL_BUFFER_SIZE + int "The receive buffer size of openthread trel" + depends on OPENTHREAD_RADIO_TREL + range 10 255 + default 50 + help + Configure the receive buffer size of TREL service. endmenu menu "Thread 15.4 Radio Link" diff --git a/components/openthread/private_include/esp_openthread_common.h b/components/openthread/private_include/esp_openthread_common.h new file mode 100644 index 0000000000..321b3c8e07 --- /dev/null +++ b/components/openthread/private_include/esp_openthread_common.h @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +typedef struct { + uint8_t head; + uint8_t tail; + atomic_uint_fast8_t used; +} esp_openthread_circular_queue_info_t; diff --git a/components/openthread/private_include/esp_openthread_radio.h b/components/openthread/private_include/esp_openthread_radio.h index 4b9aa4555f..ae7550471b 100644 --- a/components/openthread/private_include/esp_openthread_radio.h +++ b/components/openthread/private_include/esp_openthread_radio.h @@ -84,6 +84,27 @@ void esp_openthread_set_coex_config(esp_ieee802154_coex_config_t config); * */ esp_ieee802154_coex_config_t esp_openthread_get_coex_config(void); + +/** + * @brief This function updates the TREL fds and timeouts to the main loop. + * + * @param[inout] mainloop The main loop context. + * + */ +void esp_openthread_trel_update(esp_openthread_mainloop_context_t *mainloop); + +/** + * @brief This function performs the OpenThread TREL process. + * + * @param[in] instance The OpenThread instance. + * @param[in] mainloop The main loop context. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failure + * + */ +esp_err_t esp_openthread_trel_process(otInstance *aInstance, const esp_openthread_mainloop_context_t *mainloop); #endif #ifdef __cplusplus diff --git a/components/openthread/src/port/esp_openthread_radio.c b/components/openthread/src/port/esp_openthread_radio.c index 5fa6205213..f4841cde45 100644 --- a/components/openthread/src/port/esp_openthread_radio.c +++ b/components/openthread/src/port/esp_openthread_radio.c @@ -4,7 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include #include "esp_openthread_radio.h" #include "esp_err.h" @@ -13,6 +12,7 @@ #include "esp_ieee802154.h" #include "esp_ieee802154_types.h" #include "esp_mac.h" +#include "esp_openthread_common.h" #include "esp_openthread_common_macro.h" #include "esp_openthread_platform.h" #include "esp_openthread_types.h" @@ -52,12 +52,6 @@ typedef struct { uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; } esp_openthread_radio_tx_psdu; -typedef struct { - uint8_t head; - uint8_t tail; - atomic_uint_fast8_t used; -} esp_openthread_circular_queue_info_t; - static otRadioFrame s_transmit_frame; static esp_openthread_radio_tx_psdu s_transmit_psdu; diff --git a/components/openthread/src/port/esp_openthread_trel.c b/components/openthread/src/port/esp_openthread_trel.c index 3c56e7a1c1..eae1c04219 100644 --- a/components/openthread/src/port/esp_openthread_trel.c +++ b/components/openthread/src/port/esp_openthread_trel.c @@ -18,10 +18,12 @@ #include "esp_netif_ip_addr.h" #include "esp_openthread.h" #include "esp_openthread_border_router.h" +#include "esp_openthread_common.h" #include "esp_openthread_common_macro.h" #include "esp_openthread_lock.h" +#include "esp_openthread_platform.h" #include "esp_openthread_radio.h" -#include "esp_openthread_task_queue.h" +#include "esp_vfs_eventfd.h" #include "lwip/pbuf.h" #include "lwip/tcpip.h" #include "lwip/udp.h" @@ -54,6 +56,10 @@ typedef struct { static ot_trel_t s_ot_trel = {CONFIG_OPENTHREAD_TREL_PORT, NULL}; static bool s_is_service_registered = false; +static ot_trel_recv_task_t s_trel_receive_buffer[CONFIG_OPENTHREAD_TREL_BUFFER_SIZE]; +static esp_openthread_circular_queue_info_t s_recv_queue = {.head = 0, .tail = 0, .used = 0}; +static const char *s_trel_workflow = "trel"; +static int s_trel_event_fd = -1; static void trel_browse_notifier(mdns_result_t *result) { @@ -89,55 +95,27 @@ static void trel_browse_notifier(mdns_result_t *result) } } -static void trel_recv_task(void *ctx) -{ - esp_err_t ret = ESP_OK; - OT_UNUSED_VARIABLE(ret); - ot_trel_recv_task_t *task_ctx = (ot_trel_recv_task_t *)ctx; - struct pbuf *recv_buf = task_ctx->p; - uint8_t *data_buf = (uint8_t *)recv_buf->payload; - uint8_t *data_buf_to_free = NULL; - uint16_t length = recv_buf->len; - - if (recv_buf->next != NULL) { - data_buf = (uint8_t *)malloc(recv_buf->tot_len); - ESP_GOTO_ON_FALSE(data_buf, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate data buf when receiving Thread TREL message"); - length = recv_buf->tot_len; - data_buf_to_free = data_buf; - pbuf_copy_partial(recv_buf, data_buf, recv_buf->tot_len, 0); - } - otPlatTrelHandleReceived(esp_openthread_get_instance(), data_buf, length, task_ctx->source_addr); - -exit: - if (recv_buf) { - pbuf_free(recv_buf); - } - free(data_buf_to_free); - free(task_ctx->source_addr); - free(task_ctx); -} - -// TZ-1704 static void handle_trel_udp_recv(void *ctx, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, uint16_t port) { esp_err_t ret = ESP_OK; + otSockAddr *source_addr = NULL; + uint64_t event_trel_rx = 1; ESP_LOGD(OT_PLAT_LOG_TAG, "Receive from %s:%d", ip6addr_ntoa(&(addr->u_addr.ip6)), port); + ESP_GOTO_ON_FALSE(atomic_load(&s_recv_queue.used) < CONFIG_OPENTHREAD_TREL_BUFFER_SIZE, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "trel receive buffer full!"); + source_addr = (otSockAddr *)malloc(sizeof(otSockAddr)); + ESP_GOTO_ON_FALSE(source_addr, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL"); - ot_trel_recv_task_t *task_ctx = (ot_trel_recv_task_t *)malloc(sizeof(ot_trel_recv_task_t)); - ESP_GOTO_ON_FALSE(task_ctx, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL"); - task_ctx->p = p; - task_ctx->source_addr = (otSockAddr *)malloc(sizeof(otSockAddr)); - ESP_GOTO_ON_FALSE(task_ctx->source_addr, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL"); - memset(task_ctx->source_addr, 0, sizeof(otSockAddr)); - task_ctx->source_addr->mPort = port; - memcpy(&task_ctx->source_addr->mAddress.mFields.m32, addr->u_addr.ip6.addr, sizeof(addr->u_addr.ip6.addr)); - - ESP_GOTO_ON_ERROR(esp_openthread_task_queue_post(trel_recv_task, task_ctx), exit, OT_PLAT_LOG_TAG, "Failed to receive OpenThread TREL message"); + memset(source_addr, 0, sizeof(otSockAddr)); + source_addr->mPort = port; + memcpy(&source_addr->mAddress.mFields.m32, addr->u_addr.ip6.addr, sizeof(addr->u_addr.ip6.addr)); + s_trel_receive_buffer[s_recv_queue.tail].source_addr = source_addr; + s_trel_receive_buffer[s_recv_queue.tail].p = p; + s_recv_queue.tail = (s_recv_queue.tail + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE; + atomic_fetch_add(&s_recv_queue.used, 1); + assert(write(s_trel_event_fd, &event_trel_rx, sizeof(event_trel_rx)) == sizeof(event_trel_rx)); exit: if (ret != ESP_OK) { - free(task_ctx->source_addr); - free(task_ctx); if (p) { pbuf_free(p); } @@ -146,25 +124,76 @@ exit: static esp_err_t ot_new_trel(void *ctx) { - ot_trel_t *task = (ot_trel_t *)ctx; + s_ot_trel.trel_pcb = udp_new(); + ESP_RETURN_ON_FALSE(s_ot_trel.trel_pcb != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create a new UDP pcb"); + udp_bind(s_ot_trel.trel_pcb, IP6_ADDR_ANY, s_ot_trel.port); + udp_recv(s_ot_trel.trel_pcb, handle_trel_udp_recv, NULL); + return ESP_OK; +} + +void esp_openthread_trel_update(esp_openthread_mainloop_context_t *mainloop) +{ + FD_SET(s_trel_event_fd, &mainloop->read_fds); + if (s_trel_event_fd > mainloop->max_fd) { + mainloop->max_fd = s_trel_event_fd; + } +} + +esp_err_t esp_openthread_trel_process(otInstance *aInstance, const esp_openthread_mainloop_context_t *mainloop) +{ + uint64_t event_read = 0; + assert(read(s_trel_event_fd, &event_read, sizeof(event_read)) == sizeof(event_read)); + struct pbuf *recv_buf = NULL; + uint8_t *data_buf = NULL; + uint16_t length = 0; + otSockAddr *source_addr = NULL; + + while (atomic_load(&s_recv_queue.used)) { + if (s_trel_receive_buffer[s_recv_queue.head].p != NULL) { + + uint8_t *data_buf_to_free = NULL; + bool should_handle = true; + recv_buf = s_trel_receive_buffer[s_recv_queue.head].p; + data_buf = (uint8_t *)recv_buf->payload; + length = recv_buf->len; + source_addr = s_trel_receive_buffer[s_recv_queue.head].source_addr; + + if (recv_buf->next != NULL) { + data_buf = (uint8_t *)malloc(recv_buf->tot_len); + if (data_buf) { + pbuf_copy_partial(recv_buf, data_buf, recv_buf->tot_len, 0); + } else { + should_handle = false; + } + length = recv_buf->tot_len; + data_buf_to_free = data_buf; + } + if (should_handle) { + otPlatTrelHandleReceived(aInstance, data_buf, length, source_addr); + } + pbuf_free(recv_buf); + free(data_buf_to_free); + free(source_addr); + + s_recv_queue.head = (s_recv_queue.head + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE; + atomic_fetch_sub(&s_recv_queue.used, 1); + } + } - task->trel_pcb = udp_new(); - ESP_RETURN_ON_FALSE(task->trel_pcb != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create a new UDP pcb"); - udp_bind(task->trel_pcb, IP6_ADDR_ANY, task->port); - udp_recv(task->trel_pcb, handle_trel_udp_recv, NULL); return ESP_OK; } void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort) { + ESP_RETURN_ON_FALSE(s_trel_event_fd == -1, , OT_PLAT_LOG_TAG, "ot trel has been initialized."); + s_trel_event_fd = eventfd(0, 0); + assert(s_trel_event_fd >= 0); *aUdpPort = s_ot_trel.port; esp_openthread_task_switching_lock_release(); - esp_err_t err = esp_netif_tcpip_exec(ot_new_trel, &s_ot_trel); - if (err != ESP_OK) { - ESP_LOGE(OT_PLAT_LOG_TAG, "Fail to create trel udp"); - } + ESP_ERROR_CHECK(esp_netif_tcpip_exec(ot_new_trel, NULL)); mdns_browse_new(TREL_MDNS_TYPE, TREL_MDNS_PROTO, trel_browse_notifier); esp_openthread_task_switching_lock_acquire(portMAX_DELAY); + ESP_ERROR_CHECK(esp_openthread_platform_workflow_register(&esp_openthread_trel_update, &esp_openthread_trel_process, s_trel_workflow)); } static esp_err_t trel_send_task(void *ctx) @@ -263,23 +292,41 @@ void otPlatTrelResetCounters(otInstance *aInstance) memset(&s_trel_counters, 0, sizeof(otPlatTrelCounters)); } -static void trel_disable_task(void *ctx) +static esp_err_t trel_disable_task(void *ctx) { - struct udp_pcb *pcb = (struct udp_pcb *)ctx; - udp_remove(pcb); + if (ctx) { + struct udp_pcb *pcb = (struct udp_pcb *)ctx; + udp_remove(pcb); + } + return ESP_OK; +} + +static void free_all_buffer(void) +{ + while (atomic_load(&s_recv_queue.used)) { + if (s_trel_receive_buffer[s_recv_queue.head].p != NULL) { + pbuf_free(s_trel_receive_buffer[s_recv_queue.head].p); + free(s_trel_receive_buffer[s_recv_queue.head].source_addr); + s_recv_queue.head = (s_recv_queue.head + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE; + atomic_fetch_sub(&s_recv_queue.used, 1); + } + } } void otPlatTrelDisable(otInstance *aInstance) { + ESP_RETURN_ON_FALSE(s_trel_event_fd >= 0, , OT_PLAT_LOG_TAG, "ot trel is not initialized."); esp_openthread_task_switching_lock_release(); - if (s_ot_trel.trel_pcb) { - tcpip_callback(trel_disable_task, s_ot_trel.trel_pcb); - } + esp_netif_tcpip_exec(trel_disable_task, s_ot_trel.trel_pcb); + s_ot_trel.trel_pcb = NULL; mdns_service_remove(TREL_MDNS_TYPE, TREL_MDNS_PROTO); s_is_service_registered = false; mdns_browse_delete(TREL_MDNS_TYPE, TREL_MDNS_PROTO); esp_openthread_task_switching_lock_acquire(portMAX_DELAY); - s_ot_trel.trel_pcb = NULL; + esp_openthread_platform_workflow_unregister(s_trel_workflow); + free_all_buffer(); + close(s_trel_event_fd); + s_trel_event_fd = -1; } const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance) diff --git a/examples/openthread/ot_br/main/esp_ot_br.c b/examples/openthread/ot_br/main/esp_ot_br.c index bbd5ae3f0a..ffcfd39157 100644 --- a/examples/openthread/ot_br/main/esp_ot_br.c +++ b/examples/openthread/ot_br/main/esp_ot_br.c @@ -199,15 +199,20 @@ void app_main(void) // * netif // * task queue // * border router - esp_vfs_eventfd_config_t eventfd_config = { + size_t max_eventfd = 3; + #if CONFIG_OPENTHREAD_RADIO_NATIVE || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI - // * radio driver (A native radio device needs a eventfd for radio driver.) - // * SpiSpinelInterface (The Spi Spinel Interface needs a eventfd.) - // The above will not exist at the same time. - .max_fds = 4, -#else - .max_fds = 3, + // * radio driver (A native radio device needs a eventfd for radio driver.) + // * SpiSpinelInterface (The Spi Spinel Interface needs a eventfd.) + // The above will not exist at the same time. + max_eventfd++; #endif +#if CONFIG_OPENTHREAD_RADIO_TREL + // * TREL reception (The Thread Radio Encapsulation Link needs a eventfd for reception.) + max_eventfd++; +#endif + esp_vfs_eventfd_config_t eventfd_config = { + .max_fds = max_eventfd, }; ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); ESP_ERROR_CHECK(nvs_flash_init()); diff --git a/examples/openthread/ot_trel/main/esp_ot_trel.c b/examples/openthread/ot_trel/main/esp_ot_trel.c index 05b7251da4..a333eface5 100644 --- a/examples/openthread/ot_trel/main/esp_ot_trel.c +++ b/examples/openthread/ot_trel/main/esp_ot_trel.c @@ -125,8 +125,9 @@ void app_main(void) // Used eventfds: // * netif // * ot task queue + // * ot trel esp_vfs_eventfd_config_t eventfd_config = { - .max_fds = 2, + .max_fds = 3, }; ESP_ERROR_CHECK(nvs_flash_init()); From ec25e21ae88019dc409b06ed4a6542984b9ba944 Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Thu, 29 May 2025 16:21:36 +0800 Subject: [PATCH 2/4] feat(openthread): handle MeshCoP mDNS service in state change callback --- .../include/esp_openthread_border_router.h | 8 ++++ .../include/esp_openthread_meshcop_mdns.h | 41 +++++++++++++++++++ .../private_include/esp_openthread_state.h | 7 +++- .../src/port/esp_openthread_state.c | 21 ++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 components/openthread/include/esp_openthread_meshcop_mdns.h diff --git a/components/openthread/include/esp_openthread_border_router.h b/components/openthread/include/esp_openthread_border_router.h index 6fe915bc53..e78c28bddd 100644 --- a/components/openthread/include/esp_openthread_border_router.h +++ b/components/openthread/include/esp_openthread_border_router.h @@ -76,6 +76,14 @@ esp_netif_t *esp_openthread_get_backbone_netif(void); */ esp_err_t esp_openthread_set_meshcop_instance_name(const char *instance_name); +/** + * @brief Gets the meshcop(e) instance name. + * + * @return The instance name. + * + */ +const char* esp_openthread_get_meshcop_instance_name(void); + #ifdef __cplusplus } #endif diff --git a/components/openthread/include/esp_openthread_meshcop_mdns.h b/components/openthread/include/esp_openthread_meshcop_mdns.h new file mode 100644 index 0000000000..4e2c5926fa --- /dev/null +++ b/components/openthread/include/esp_openthread_meshcop_mdns.h @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_openthread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Publishes the OpenThread meshcop service in mDNS + * + * @param[in] instance_name Instance name of meshcop mDNS service + * + * @return + * - ESP_OK success + * - ESP_ERR_NO_MEM memory error + * - ESP_FAIL failed to add service + * + */ +esp_err_t esp_openthread_publish_meshcop_mdns(const char *instance_name); + +/** + * @brief Removes the OpenThread meshcop service in mDNS + * + * @return + * - ESP_OK success + * - ESP_ERR_NO_MEM memory error + * - ESP_FAIL failed to remove service + * + */ +esp_err_t esp_openthread_remove_meshcop_mdns(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_openthread_state.h b/components/openthread/private_include/esp_openthread_state.h index e2ee350805..91d69c4283 100644 --- a/components/openthread/private_include/esp_openthread_state.h +++ b/components/openthread/private_include/esp_openthread_state.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,11 @@ #include #include +#define ESP_OPENTHREAD_BORDER_ROUTER_FLAG_OF_INTEREST \ + (OT_CHANGED_THREAD_ROLE | OT_CHANGED_THREAD_EXT_PANID | OT_CHANGED_THREAD_NETWORK_NAME | \ + OT_CHANGED_ACTIVE_DATASET | OT_CHANGED_THREAD_PARTITION_ID | OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE | \ + OT_CHANGED_PSKC) + #ifdef __cplusplus extern "C" { #endif diff --git a/components/openthread/src/port/esp_openthread_state.c b/components/openthread/src/port/esp_openthread_state.c index 153d343ea1..bc2641d905 100644 --- a/components/openthread/src/port/esp_openthread_state.c +++ b/components/openthread/src/port/esp_openthread_state.c @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -19,6 +21,19 @@ #define TAG "OT_STATE" +#if CONFIG_OPENTHREAD_BORDER_ROUTER +static void handle_ot_border_router_state_changed(otInstance* instance) +{ + otDeviceRole role = otThreadGetDeviceRole(esp_openthread_get_instance()); + + if (role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER) { + esp_openthread_publish_meshcop_mdns(esp_openthread_get_meshcop_instance_name()); + } else { + esp_openthread_remove_meshcop_mdns(); + } +} +#endif + static void handle_ot_netif_state_change(otInstance* instance) { if (otIp6IsEnabled(instance)) { @@ -123,6 +138,12 @@ static void ot_state_change_callback(otChangedFlags changed_flags, void* ctx) return; } +#if CONFIG_OPENTHREAD_BORDER_ROUTER + if (changed_flags & ESP_OPENTHREAD_BORDER_ROUTER_FLAG_OF_INTEREST) { + handle_ot_border_router_state_changed(instance); + } +#endif + if (changed_flags & OT_CHANGED_THREAD_ROLE) { handle_ot_role_change(instance); } From e9829623b8f3b67a97ff375c9de7c3afa731454c Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Thu, 29 May 2025 16:50:41 +0800 Subject: [PATCH 3/4] feat(openthread): update openthread upstream --- components/openthread/openthread | 2 +- components/openthread/sbom_openthread.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/openthread/openthread b/components/openthread/openthread index ec2b0d4873..b945928d72 160000 --- a/components/openthread/openthread +++ b/components/openthread/openthread @@ -1 +1 @@ -Subproject commit ec2b0d487356d2955346457a6515201039140037 +Subproject commit b945928d722177cd9caeab2e1025499628c101ef diff --git a/components/openthread/sbom_openthread.yml b/components/openthread/sbom_openthread.yml index 789349f827..51447eac61 100644 --- a/components/openthread/sbom_openthread.yml +++ b/components/openthread/sbom_openthread.yml @@ -5,4 +5,4 @@ supplier: 'Organization: Espressif Systems (Shanghai) CO LTD' originator: 'Organization: Google LLC' description: OpenThread released by Google is an open-source implementation of the Thread networking url: https://github.com/espressif/openthread -hash: ec2b0d487356d2955346457a6515201039140037 +hash: b945928d722177cd9caeab2e1025499628c101ef From c976fd0f3a12a159e7c48e087a4fe74ae135d45a Mon Sep 17 00:00:00 2001 From: Xu Si Yu Date: Mon, 30 Jun 2025 09:20:29 +0000 Subject: [PATCH 4/4] feat(openthread): update border router lib * esp-openthread: thread_zigbee/esp-openthread@16bfed5ec * openthread: espressif/openthread@b945928d7 * esp-idf: espressif/esp-idf@e9829623b --- components/openthread/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/openthread/lib b/components/openthread/lib index 2d6905d9a0..25151450b5 160000 --- a/components/openthread/lib +++ b/components/openthread/lib @@ -1 +1 @@ -Subproject commit 2d6905d9a0a206275f47f4a7f8008caffcb432e9 +Subproject commit 25151450b5f5168ef86b982981a4b1d40fa8e6f5