33#ifdef USE_OPENTHREAD_RCP_UART
34OpenThreadComponent::OpenThreadComponent(
uint32_t rcp_baud_rate, uint8_t rcp_rx_pin, uint8_t rcp_tx_pin,
36 : rcp_baud_rate_(rcp_baud_rate), rcp_rx_pin_(rcp_rx_pin), rcp_tx_pin_(rcp_tx_pin), rcp_reset_pin_(rcp_reset_pin) {
41void OpenThreadComponent::dump_config() {
42 ESP_LOGCONFIG(TAG,
"Open Thread:");
43#if CONFIG_OPENTHREAD_FTD
44 ESP_LOGCONFIG(TAG,
" Device Type: FTD");
45#elif CONFIG_OPENTHREAD_MTD
46 ESP_LOGCONFIG(TAG,
" Device Type: MTD");
48 if (this->poll_period_ > 0) {
49 ESP_LOGCONFIG(TAG,
" Device is configured as Sleepy End Device (SED)");
51 ESP_LOGCONFIG(TAG,
" Poll Period: %" PRIu32
"s",
duration);
53 ESP_LOGCONFIG(TAG,
" Device is configured as Minimal End Device (MED)");
56 if (this->output_power_.has_value()) {
57 ESP_LOGCONFIG(TAG,
" Output power: %" PRId8
"dBm", *this->output_power_);
59#ifdef USE_OPENTHREAD_RCP_UART
61 " Radio: External RCP\n"
62 " Transport: UART1 at %" PRIu32
" baud\n"
65 this->rcp_baud_rate_, this->rcp_rx_pin_, this->rcp_tx_pin_);
66 if (this->rcp_reset_pin_ !=
nullptr) {
67 LOG_PIN(
" RCP Reset Pin: ", this->rcp_reset_pin_);
70 ESP_LOGCONFIG(TAG,
" Radio: Native 802.15.4");
72#ifdef USE_OPENTHREAD_BORDER_ROUTER
73 ESP_LOGCONFIG(TAG,
" Border Router: ENABLED\n"
74 " Backbone: Wi-Fi STA\n"
75 " Experimental: YES");
79void OpenThreadComponent::on_state_changed(otChangedFlags
flags,
void *context) {
80 if (
flags & OT_CHANGED_THREAD_ROLE) {
81 auto *self =
static_cast<OpenThreadComponent *
>(context);
84 otInstance *instance = self->get_openthread_instance_();
85 otDeviceRole role = otThreadGetDeviceRole(instance);
86 self->connected_ = role >= OT_DEVICE_ROLE_CHILD;
91std::optional<otIp6Address> OpenThreadComponent::get_omr_address() {
93 if (this->is_failed())
95 InstanceLock
lock = InstanceLock::acquire();
96 return this->get_omr_address_(lock);
99std::optional<otIp6Address> OpenThreadComponent::get_omr_address_(InstanceLock &lock) {
100 otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
101 otInstance *instance =
nullptr;
103 instance =
lock.get_instance();
105 otBorderRouterConfig config;
106 if (otNetDataGetNextOnMeshPrefix(instance, &iterator, &config) != OT_ERROR_NONE) {
110 const otIp6Prefix *omr_prefix = &config.mPrefix;
111 const otNetifAddress *unicast_addresses = otIp6GetUnicastAddresses(instance);
112 for (
const otNetifAddress *addr = unicast_addresses; addr; addr = addr->mNext) {
113 const otIp6Address *local_ip = &addr->mAddress;
114 if (otIp6PrefixMatch(&omr_prefix->mPrefix, local_ip)) {
121#ifdef USE_OPENTHREAD_SRP
122void OpenThreadComponent::defer_factory_reset_external_callback() {
123 ESP_LOGD(TAG,
"Defer factory_reset_external_callback_");
124 this->defer([
this]() { this->factory_reset_external_callback_(); });
127void OpenThreadSrpComponent::srp_callback(otError err,
const otSrpClientHostInfo *host_info,
128 const otSrpClientService *services,
129 const otSrpClientService *removed_services,
void *context) {
131 ESP_LOGW(TAG,
"SRP client reported an error: %s", otThreadErrorToString(err));
132 for (
const otSrpClientHostInfo *host = host_info; host; host =
nullptr) {
133 ESP_LOGW(TAG,
" Host: %s", host->mName);
135 for (
const otSrpClientService *service = services; service; service = service->mNext) {
136 ESP_LOGW(TAG,
" Service: %s", service->mName);
141void OpenThreadSrpComponent::srp_start_callback(
const otSockAddr *server_socket_address,
void *context) {
142 ESP_LOGI(TAG,
"SRP client has started");
145void OpenThreadSrpComponent::srp_factory_reset_callback(otError err,
const otSrpClientHostInfo *host_info,
146 const otSrpClientService *services,
147 const otSrpClientService *removed_services,
void *context) {
148 OpenThreadComponent *obj = (OpenThreadComponent *) context;
149 if (err == OT_ERROR_NONE && removed_services != NULL && host_info != NULL &&
150 host_info->mState == OT_SRP_CLIENT_ITEM_STATE_REMOVED) {
151 ESP_LOGD(TAG,
"Successful Removal SRP Host and Services");
152 }
else if (err != OT_ERROR_NONE) {
154 ESP_LOGW(TAG,
"SRP client event/error: %s", otThreadErrorToString(err));
156 obj->defer_factory_reset_external_callback();
159void OpenThreadSrpComponent::setup() {
171 InstanceLock
lock = InstanceLock::acquire();
172 otInstance *instance =
lock.get_instance();
174 otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_callback,
nullptr);
178 char *existing_host_name = otSrpClientBuffersGetHostNameString(instance, &
size);
180 uint16_t host_name_len = host_name.
size();
181 if (host_name_len >=
size) {
182 ESP_LOGW(TAG,
"Hostname is too long, choose a shorter project name");
185 memset(existing_host_name, 0,
size);
186 memcpy(existing_host_name, host_name.c_str(), host_name_len);
188 error = otSrpClientSetHostName(instance, existing_host_name);
190 ESP_LOGW(TAG,
"Could not set host name");
194 error = otSrpClientEnableAutoHostAddress(instance);
196 ESP_LOGW(TAG,
"Could not enable auto host address");
201 const auto &mdns_services = this->mdns_->get_services();
202 ESP_LOGD(TAG,
"Setting up SRP services. count = %d\n", mdns_services.size());
203 for (
const auto &service : mdns_services) {
204 otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
206 ESP_LOGW(TAG,
"Failed to allocate service entry");
211 char *
string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &
size);
212 std::string full_service = std::string(MDNS_STR_ARG(service.service_type)) +
"." + MDNS_STR_ARG(service.proto);
213 if (full_service.size() >=
size) {
214 ESP_LOGW(TAG,
"Service name too long: %s", full_service.c_str());
217 memcpy(
string, full_service.c_str(), full_service.size() + 1);
220 string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &
size);
221 if (host_name_len >=
size) {
222 ESP_LOGW(TAG,
"Instance name too long: %s", host_name.c_str());
225 memset(
string, 0,
size);
226 memcpy(
string, host_name.c_str(), host_name_len);
229 entry->mService.mPort = service.port.value();
231 otDnsTxtEntry *txt_entries =
232 reinterpret_cast<otDnsTxtEntry *
>(this->pool_alloc_(
sizeof(otDnsTxtEntry) * service.txt_records.size()));
234 entry->mService.mNumTxtEntries = service.txt_records.size();
235 for (
size_t i = 0; i < service.txt_records.size(); i++) {
236 const auto &txt = service.txt_records[i];
239 const char *value_str = MDNS_STR_ARG(txt.value);
240 txt_entries[i].mKey = MDNS_STR_ARG(txt.key);
242 txt_entries[i].mValue =
reinterpret_cast<const uint8_t *
>(strdup(value_str));
243 txt_entries[i].mValueLength = strlen(value_str);
247 size_t value_len = strlen(value_str);
248 char *value_copy =
reinterpret_cast<char *
>(this->pool_alloc_(value_len + 1));
249 memcpy(value_copy, value_str, value_len + 1);
250 txt_entries[i].mValue =
reinterpret_cast<const uint8_t *
>(value_copy);
251 txt_entries[i].mValueLength = value_len;
254 entry->mService.mTxtEntries = txt_entries;
255 entry->mService.mNumTxtEntries = service.txt_records.size();
258 error = otSrpClientAddService(instance, &entry->mService);
259 if (error != OT_ERROR_NONE) {
260 ESP_LOGW(TAG,
"Failed to add service: %s", otThreadErrorToString(error));
262 ESP_LOGD(TAG,
"Added service: %s", full_service.c_str());
265 otSrpClientEnableAutoStartMode(instance, OpenThreadSrpComponent::srp_start_callback,
nullptr);
266 ESP_LOGD(TAG,
"Finished SRP setup");
269void *OpenThreadSrpComponent::pool_alloc_(
size_t size) {
270 uint8_t *
ptr =
new uint8_t[
size];
271 this->memory_pool_.emplace_back(std::unique_ptr<uint8_t[]>(ptr));
277bool OpenThreadComponent::teardown() {
280 switch (this->teardown_stage_) {
281 case TeardownStage::TEARDOWN_STAGE_NOT_STARTED: {
283 auto lock = InstanceLock::try_acquire(100);
287 ESP_LOGV(TAG,
"Failed to acquire OpenThread lock during teardown");
291 TeardownStage expected = TeardownStage::TEARDOWN_STAGE_NOT_STARTED;
292 if (!this->teardown_stage_.compare_exchange_strong(expected, TeardownStage::TEARDOWN_STAGE_STOP_IN_PROCESS)) {
295 otInstance *instance =
lock.get_instance();
299 otIp6SetAddressCallback(instance,
nullptr,
nullptr);
300#ifdef USE_OPENTHREAD_BORDER_ROUTER
301 if (this->border_router_started_) {
302 if (
const esp_err_t err = esp_openthread_border_router_deinit(); err != ESP_OK) {
303 ESP_LOGW(TAG,
"Failed to deinitialize OpenThread Border Router: %s",
esp_err_to_name(err));
305 this->border_router_started_ =
false;
308#ifdef USE_OPENTHREAD_SRP
309 ESP_LOGV(TAG,
"Clear SRP");
310 otSrpClientClearHostAndServices(instance);
311 otSrpClientBuffersFreeAllServices(instance);
313 if (otThreadSetEnabled(instance,
false) != OT_ERROR_NONE) {
314 ESP_LOGW(TAG,
"Failed to disable Thread during teardown");
316 if (otIp6SetEnabled(instance,
false) != OT_ERROR_NONE) {
317 ESP_LOGW(TAG,
"Failed to disable IPv6 during teardown");
322 ESP_LOGV(TAG,
"Stop OpenThread");
323 if (
int error = this->openthread_stop_(); error != 0) {
325 ESP_LOGW(TAG,
"Failed attempt to stop OpenThread %d", error);
326 this->teardown_stage_ = TeardownStage::TEARDOWN_STAGE_COMPLETED;
329 case TeardownStage::TEARDOWN_STAGE_STOP_IN_PROCESS:
333 case TeardownStage::TEARDOWN_STAGE_COMPLETED:
334 ESP_LOGV(TAG,
"OpenThreadComponent Teardown Complete");
337 return this->teardown_stage_ == TeardownStage::TEARDOWN_STAGE_COMPLETED;
340void OpenThreadComponent::on_factory_reset(
const std::function<
void()> &callback) {
341#ifndef USE_OPENTHREAD_SRP
345 if (this->is_failed()) {
350 this->factory_reset_external_callback_ = callback;
351 ESP_LOGD(TAG,
"Start Removal SRP Host and Services");
353 InstanceLock
lock = InstanceLock::acquire();
354 otInstance *instance =
lock.get_instance();
355 otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_factory_reset_callback,
this);
356 error = otSrpClientRemoveHostAndServices(instance,
true,
true);
357 if (error != OT_ERROR_NONE) {
358 ESP_LOGW(TAG,
"Failed to Remove SRP Host and Services");
361 ESP_LOGD(TAG,
"Waiting on Confirmation Removal SRP Host and Services");
365void OpenThreadComponent::apply_poll_period(
uint32_t poll_period) {
366#if CONFIG_OPENTHREAD_MTD
367 this->set_poll_period(poll_period);
368 if (!this->is_lock_initialized()) {
370 ESP_LOGD(TAG,
"Not (yet) ready to apply");
373 auto lock = InstanceLock::try_acquire(100);
375 ESP_LOGW(TAG,
"Failed to acquire lock in action");
378 this->apply_linkmode_(
lock.get_instance());
380 ESP_LOGW(TAG,
"OpenThread action has no effect on FTD devices (MTD only)");
384void OpenThreadComponent::apply_linkmode_(otInstance *instance) {
385 otLinkModeConfig link_mode_config{};
386#if CONFIG_OPENTHREAD_FTD
387 link_mode_config.mRxOnWhenIdle =
true;
388 link_mode_config.mDeviceType =
true;
389 link_mode_config.mNetworkData =
true;
390#elif CONFIG_OPENTHREAD_MTD
391 if (this->poll_period_ > 0) {
392 if (otLinkSetPollPeriod(instance, this->poll_period_) != OT_ERROR_NONE) {
393 ESP_LOGE(TAG,
"Failed to set pollperiod");
395 ESP_LOGD(TAG,
"Link Polling Period: %" PRIu32, otLinkGetPollPeriod(instance));
397 link_mode_config.mRxOnWhenIdle = this->poll_period_ == 0;
398 link_mode_config.mDeviceType =
false;
399 link_mode_config.mNetworkData =
false;
402 if (otThreadSetLinkMode(instance, link_mode_config) != OT_ERROR_NONE) {
403 ESP_LOGE(TAG,
"Failed to set linkmode");
405#ifdef ESPHOME_LOG_HAS_DEBUG
406 link_mode_config = otThreadGetLinkMode(instance);
407 ESP_LOGD(TAG,
"Link Mode Device Type: %s, Network Data: %s, RX On When Idle: %s",
408 TRUEFALSE(link_mode_config.mDeviceType), TRUEFALSE(link_mode_config.mNetworkData),
409 TRUEFALSE(link_mode_config.mRxOnWhenIdle));