ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
openthread_esp.cpp
Go to the documentation of this file.
2#if defined(USE_OPENTHREAD) && defined(USE_ESP_IDF)
3#include <openthread/logging.h>
4#include "openthread.h"
5
6#include "esp_log.h"
7#include "esp_openthread.h"
8#include "esp_openthread_lock.h"
9
10#include "esp_task_wdt.h"
12#include "esphome/core/log.h"
13
14#include "esp_err.h"
15#include "esp_event.h"
16#include "esp_netif.h"
17#include "esp_netif_types.h"
18#include "esp_openthread_cli.h"
19#include "esp_openthread_netif_glue.h"
20#include "esp_vfs_eventfd.h"
21#include "freertos/FreeRTOS.h"
22#include "freertos/task.h"
23#include "nvs_flash.h"
24
25static const char *const TAG = "openthread";
26
27namespace esphome {
28namespace openthread {
29
31 // Used eventfds:
32 // * netif
33 // * ot task queue
34 // * radio driver
35 esp_vfs_eventfd_config_t eventfd_config = {
36 .max_fds = 3,
37 };
38 ESP_ERROR_CHECK(nvs_flash_init());
39 ESP_ERROR_CHECK(esp_event_loop_create_default());
40 ESP_ERROR_CHECK(esp_netif_init());
41 ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
42
43 xTaskCreate(
44 [](void *arg) {
45 static_cast<OpenThreadComponent *>(arg)->ot_main();
46 vTaskDelete(nullptr);
47 },
48 "ot_main", 10240, this, 5, nullptr);
49}
50
51static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) {
52 esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
53 esp_netif_t *netif = esp_netif_new(&cfg);
54 assert(netif != nullptr);
55 ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config)));
56
57 return netif;
58}
59
61 esp_openthread_platform_config_t config = {
62 .radio_config =
63 {
64 .radio_mode = RADIO_MODE_NATIVE,
65 .radio_uart_config = {},
66 },
67 .host_config =
68 {
69 // There is a conflict between esphome's logger which also
70 // claims the usb serial jtag device.
71 // .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB,
72 // .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(),
73 },
74 .port_config =
75 {
76 .storage_partition_name = "nvs",
77 .netif_queue_size = 10,
78 .task_queue_size = 10,
79 },
80 };
81
82 // Initialize the OpenThread stack
83 // otLoggingSetLevel(OT_LOG_LEVEL_DEBG);
84 ESP_ERROR_CHECK(esp_openthread_init(&config));
85
86#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE
87 ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance()));
88#endif
89
90#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
91 // The OpenThread log level directly matches ESP log level
92 (void) otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
93#endif
94 // Initialize the OpenThread cli
95#if CONFIG_OPENTHREAD_CLI
96 esp_openthread_cli_init();
97#endif
98
99 esp_netif_t *openthread_netif;
100 // Initialize the esp_netif bindings
101 openthread_netif = init_openthread_netif(&config);
102 esp_netif_set_default_netif(openthread_netif);
103
104#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
105 esp_cli_custom_command_init();
106#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
107
108 // Run the main loop
109#if CONFIG_OPENTHREAD_CLI
110 esp_openthread_cli_create_task();
111#endif
112 ESP_LOGI(TAG, "Activating dataset...");
113 otOperationalDatasetTlvs dataset = {};
114
115#ifndef USE_OPENTHREAD_FORCE_DATASET
116 // Check if openthread has a valid dataset from a previous execution
117 otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset);
118 if (error != OT_ERROR_NONE) {
119 // Make sure the length is 0 so we fallback to the configuration
120 dataset.mLength = 0;
121 } else {
122 ESP_LOGI(TAG, "Found OpenThread-managed dataset, ignoring esphome configuration");
123 ESP_LOGI(TAG, "(set force_dataset: true to override)");
124 }
125#endif
126
127#ifdef USE_OPENTHREAD_TLVS
128 if (dataset.mLength == 0) {
129 // If we didn't have an active dataset, and we have tlvs, parse it and pass it to esp_openthread_auto_start
130 size_t len = (sizeof(USE_OPENTHREAD_TLVS) - 1) / 2;
131 if (len > sizeof(dataset.mTlvs)) {
132 ESP_LOGW(TAG, "TLV buffer too small, truncating");
133 len = sizeof(dataset.mTlvs);
134 }
135 parse_hex(USE_OPENTHREAD_TLVS, sizeof(USE_OPENTHREAD_TLVS) - 1, dataset.mTlvs, len);
136 dataset.mLength = len;
137 }
138#endif
139
140 // Pass the existing dataset, or NULL which will use the preprocessor definitions
141 ESP_ERROR_CHECK(esp_openthread_auto_start(dataset.mLength > 0 ? &dataset : nullptr));
142
143 esp_openthread_launch_mainloop();
144
145 // Clean up
146 esp_openthread_deinit();
147 esp_openthread_netif_glue_deinit();
148 esp_netif_destroy(openthread_netif);
149
150 esp_vfs_eventfd_unregister();
151 this->teardown_complete_ = true;
152 vTaskDelete(NULL);
153}
154
156 network::IPAddresses addresses;
157 struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
158 uint8_t count = 0;
159 esp_netif_t *netif = esp_netif_get_default_netif();
160 count = esp_netif_get_all_ip6(netif, if_ip6s);
161 assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
162 for (int i = 0; i < count; i++) {
163 addresses[i + 1] = network::IPAddress(&if_ip6s[i]);
164 }
165 return addresses;
166}
167
168std::optional<InstanceLock> InstanceLock::try_acquire(int delay) {
169 if (esp_openthread_lock_acquire(delay)) {
170 return InstanceLock();
171 }
172 return {};
173}
174
176 while (!esp_openthread_lock_acquire(100)) {
177 esp_task_wdt_reset();
178 }
179 return InstanceLock();
180}
181
182otInstance *InstanceLock::get_instance() { return esp_openthread_get_instance(); }
183
184InstanceLock::~InstanceLock() { esp_openthread_lock_release(); }
185
186} // namespace openthread
187} // namespace esphome
188#endif
static std::optional< InstanceLock > try_acquire(int delay)
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:144
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:279
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.
Definition helpers.cpp:226
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29