ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
openthread_zephyr.cpp
Go to the documentation of this file.
2#if defined(USE_OPENTHREAD) && defined(USE_NRF52)
3#include <openthread/dataset.h>
4#include <openthread/thread.h>
5#include <openthread/logging.h>
6#include "openthread.h"
8#include <zephyr/net/openthread.h>
9
10ESPHOME_LOG_TAG(TAG, "openthread");
11
12namespace esphome::openthread {
13
14static void on_thread_state_changed(otChangedFlags flags, struct openthread_context *ot_context, void *user_data) {
15 // Delegate connection status tracking to common callback
16 if (global_openthread_component != nullptr) {
17 OpenThreadComponent::on_state_changed(flags, global_openthread_component);
18 }
19 if (flags & OT_CHANGED_THREAD_ROLE) {
20 otDeviceRole role = otThreadGetDeviceRole(ot_context->instance);
21 ESP_LOGI(TAG, "Thread role changed to %s", otThreadDeviceRoleToString(role));
22 }
23 if (flags & OT_CHANGED_THREAD_NETDATA) {
24 ESP_LOGI(TAG, "Thread network data updated");
25 }
26 if (flags & (OT_CHANGED_THREAD_ROLE | OT_CHANGED_THREAD_NETDATA)) {
27 char buf[NET_IPV6_ADDR_LEN];
28 for (const otNetifAddress *addr = otIp6GetUnicastAddresses(ot_context->instance); addr != nullptr;
29 addr = addr->mNext) {
30 ESP_LOGI(TAG, " Address: %s", net_addr_ntop(AF_INET6, &addr->mAddress, buf, sizeof(buf)));
31 }
32 }
33}
34
35static struct openthread_state_changed_cb ot_state_changed_cb = {.state_changed_cb = on_thread_state_changed};
36
37void OpenThreadComponent::setup() {
38 struct openthread_context *context = openthread_get_default_context();
39 this->lock_initialized_ = true;
40 otOperationalDatasetTlvs dataset = {};
41
42#ifndef USE_OPENTHREAD_FORCE_DATASET
43 otError error = otDatasetGetActiveTlvs(context->instance, &dataset);
44 if (error != OT_ERROR_NONE) {
45 dataset.mLength = 0;
46 } else {
47 ESP_LOGI(TAG, "Found existing dataset, ignoring config (force_dataset: true to override)");
48 }
49#endif
50
51#ifdef USE_OPENTHREAD_TLVS
52 if (dataset.mLength == 0) {
53 const size_t tlv_chars = sizeof(USE_OPENTHREAD_TLVS) - 1;
54 if ((tlv_chars % 2) != 0) {
55 ESP_LOGE(TAG, "Invalid OpenThread TLV hex string length (must be even, got %zu)", tlv_chars);
56 this->mark_failed();
57 return;
58 }
59
60 size_t len = tlv_chars / 2;
61 if (len > sizeof(dataset.mTlvs)) {
62 ESP_LOGE(TAG, "OpenThread TLV too long (max %zu bytes, got %zu bytes)", sizeof(dataset.mTlvs), len);
63 this->mark_failed();
64 return;
65 }
66
67 size_t parsed = parse_hex(USE_OPENTHREAD_TLVS, tlv_chars, dataset.mTlvs, len);
68 if (parsed != tlv_chars) {
69 ESP_LOGE(TAG, "Invalid OpenThread TLV hex string (expected %zu hex chars, got %zu)", tlv_chars, parsed);
70 this->mark_failed();
71 return;
72 }
73 dataset.mLength = len;
74 }
75#endif
76 if (dataset.mLength > 0) {
77 otError error = otDatasetSetActiveTlvs(context->instance, &dataset);
78 if (error != OT_ERROR_NONE) {
79 ESP_LOGE(TAG, "Failed to set active dataset: %s", otThreadErrorToString(error));
80 this->mark_failed();
81 return;
82 }
83 }
84 openthread_state_changed_cb_register(context, &ot_state_changed_cb);
85 openthread_start(context);
86
87 InstanceLock lock = InstanceLock::acquire();
88 this->apply_linkmode_(lock.get_instance());
89}
90
91otInstance *OpenThreadComponent::get_openthread_instance_() { return openthread_get_default_instance(); }
92
93int OpenThreadComponent::openthread_stop_() {
94 // Zephyr has no stack-stop API, so stop is synchronous here; mark complete immediately.
95 this->teardown_stage_ = TeardownStage::TEARDOWN_STAGE_COMPLETED;
96 return 0;
97}
98
99network::IPAddresses OpenThreadComponent::get_ip_addresses() {
100 network::IPAddresses addresses;
101 auto lock = InstanceLock::acquire();
102 size_t addr_count = 0;
103 for (const otNetifAddress *addr = otIp6GetUnicastAddresses(openthread_get_default_instance());
104 addr != nullptr && addr_count + 1 < addresses.size(); addr = addr->mNext) {
105 struct in6_addr ip6;
106 memcpy(&ip6, addr->mAddress.mFields.m8, sizeof(ip6));
107 addresses[addr_count + 1] = network::IPAddress(&ip6);
108 addr_count++;
109 }
110 return addresses;
111}
112
113InstanceLock InstanceLock::try_acquire(int delay) {
114 if (global_openthread_component == nullptr || !global_openthread_component->is_lock_initialized()) {
115 return InstanceLock(false);
116 }
117 struct openthread_context *ot_context = openthread_get_default_context();
118 if (k_mutex_lock(&ot_context->api_lock, K_MSEC(delay)) == 0) {
119 return InstanceLock(true);
120 }
121 return InstanceLock(false);
122}
123
124InstanceLock InstanceLock::acquire() {
125 struct openthread_context *ot_context = openthread_get_default_context();
126 k_mutex_lock(&ot_context->api_lock, K_FOREVER);
127 return InstanceLock(true);
128}
129
130otInstance *InstanceLock::get_instance() { return openthread_get_default_instance(); }
131
132InstanceLock::~InstanceLock() {
133 if (this->owns_) {
134 struct openthread_context *ot_context = openthread_get_default_context();
135 k_mutex_unlock(&ot_context->api_lock);
136 }
137}
138
139} // namespace esphome::openthread
140#endif
uint16_t flags
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:292
OpenThreadComponent * global_openthread_component
const void size_t len
Definition hal.h:68
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:370
void HOT delay(uint32_t ms)
Definition hal.cpp:85
ESPHOME_LOG_TAG(TAG, "openthread")
SemaphoreHandle_t lock