mirror of
https://github.com/esphome/esphome.git
synced 2024-12-25 15:04:54 +01:00
openthread: move esp-idf specific logic into openthread_esp
This commit is contained in:
parent
1432164a42
commit
06b10bfd90
4 changed files with 355 additions and 328 deletions
|
@ -18,6 +18,9 @@
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#define TAG "openthread"
|
#define TAG "openthread"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
@ -25,298 +28,181 @@ namespace openthread {
|
||||||
|
|
||||||
OpenThreadComponent *global_openthread_component = nullptr;
|
OpenThreadComponent *global_openthread_component = nullptr;
|
||||||
|
|
||||||
OpenThreadComponent::OpenThreadComponent() {
|
OpenThreadComponent::OpenThreadComponent() { global_openthread_component = this; }
|
||||||
global_openthread_component = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenThreadComponent::~OpenThreadComponent() {
|
OpenThreadComponent::~OpenThreadComponent() {
|
||||||
auto lock = EspOpenThreadLockGuard::TryAcquire(100);
|
auto lock = OpenThreadLockGuard::TryAcquire(100);
|
||||||
if (!lock) {
|
if (!lock) {
|
||||||
ESP_LOGW(TAG, "Failed to acquire OpenThread lock in destructor, leaking memory");
|
ESP_LOGW(TAG, "Failed to acquire OpenThread lock in destructor, leaking memory");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
otInstance *instance = esp_openthread_get_instance();
|
otInstance *instance = lock->get_instance();
|
||||||
otSrpClientClearHostAndServices(instance);
|
otSrpClientClearHostAndServices(instance);
|
||||||
otSrpClientBuffersFreeAllServices(instance);
|
otSrpClientBuffersFreeAllServices(instance);
|
||||||
|
|
||||||
global_openthread_component = nullptr;
|
global_openthread_component = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void OpenThreadComponent::setup() {
|
|
||||||
ESP_LOGI("openthread", "Setting up OpenThread...");
|
|
||||||
// Used eventfds:
|
|
||||||
// * netif
|
|
||||||
// * ot task queue
|
|
||||||
// * radio driver
|
|
||||||
// TODO: Does anything else in esphome set this up?
|
|
||||||
esp_vfs_eventfd_config_t eventfd_config = {
|
|
||||||
.max_fds = 3,
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(nvs_flash_init());
|
|
||||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
||||||
ESP_ERROR_CHECK(esp_netif_init());
|
|
||||||
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
|
|
||||||
|
|
||||||
xTaskCreate([](void* arg) {
|
|
||||||
static_cast<OpenThreadComponent*>(arg)->ot_main();
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}, "ot_main", 10240, this, 5, nullptr);
|
|
||||||
|
|
||||||
xTaskCreate([](void* arg) {
|
|
||||||
static_cast<OpenThreadComponent*>(arg)->srp_setup();
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}, "ot_srp_setup", 10240, this, 5, nullptr);
|
|
||||||
ESP_LOGI("openthread", "OpenThread started");
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config)
|
|
||||||
{
|
|
||||||
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
|
|
||||||
esp_netif_t *netif = esp_netif_new(&cfg);
|
|
||||||
assert(netif != NULL);
|
|
||||||
ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config)));
|
|
||||||
|
|
||||||
return netif;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenThreadComponent::ot_main() {
|
|
||||||
|
|
||||||
esp_openthread_platform_config_t config = {
|
|
||||||
.radio_config = {
|
|
||||||
.radio_mode = RADIO_MODE_NATIVE,
|
|
||||||
.radio_uart_config = {},
|
|
||||||
},
|
|
||||||
.host_config = {
|
|
||||||
// There is a conflict between esphome's logger which also
|
|
||||||
// claims the usb serial jtag device.
|
|
||||||
// .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB,
|
|
||||||
// .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(),
|
|
||||||
},
|
|
||||||
.port_config = {
|
|
||||||
.storage_partition_name = "nvs",
|
|
||||||
.netif_queue_size = 10,
|
|
||||||
.task_queue_size = 10,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize the OpenThread stack
|
|
||||||
ESP_ERROR_CHECK(esp_openthread_init(&config));
|
|
||||||
|
|
||||||
#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE
|
|
||||||
ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
|
|
||||||
// The OpenThread log level directly matches ESP log level
|
|
||||||
(void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
|
|
||||||
#endif
|
|
||||||
// Initialize the OpenThread cli
|
|
||||||
#if CONFIG_OPENTHREAD_CLI
|
|
||||||
esp_openthread_cli_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
esp_netif_t *openthread_netif;
|
|
||||||
// Initialize the esp_netif bindings
|
|
||||||
openthread_netif = init_openthread_netif(&config);
|
|
||||||
esp_netif_set_default_netif(openthread_netif);
|
|
||||||
|
|
||||||
#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
|
|
||||||
esp_cli_custom_command_init();
|
|
||||||
#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
|
|
||||||
|
|
||||||
// Run the main loop
|
|
||||||
#if CONFIG_OPENTHREAD_CLI
|
|
||||||
esp_openthread_cli_create_task();
|
|
||||||
#endif
|
|
||||||
ESP_LOGI(TAG, "Activating dataset...");
|
|
||||||
otOperationalDatasetTlvs dataset;
|
|
||||||
otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset);
|
|
||||||
ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL));
|
|
||||||
|
|
||||||
esp_openthread_launch_mainloop();
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
esp_openthread_netif_glue_deinit();
|
|
||||||
esp_netif_destroy(openthread_netif);
|
|
||||||
|
|
||||||
esp_vfs_eventfd_unregister();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenThreadComponent::is_connected() {
|
bool OpenThreadComponent::is_connected() {
|
||||||
otInstance *instance = esp_openthread_get_instance();
|
auto lock = OpenThreadLockGuard::TryAcquire(100);
|
||||||
if (instance == nullptr) {
|
if (!lock) {
|
||||||
return false;
|
ESP_LOGW(TAG, "Failed to acquire OpenThread lock in is_connected");
|
||||||
}
|
return false;
|
||||||
|
|
||||||
otDeviceRole role = otThreadGetDeviceRole(instance);
|
|
||||||
|
|
||||||
// TODO: If we're a leader, check that there is at least 1 known peer
|
|
||||||
return role >= OT_DEVICE_ROLE_CHILD;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This gets used by mqtt in order to register the device's IP. Likely it doesn't
|
|
||||||
// make sense to return thread-local addresses, since they can't be reached from outside the thread network.
|
|
||||||
// It could make more sense to return the off-mesh-routable address instead.
|
|
||||||
network::IPAddresses OpenThreadComponent::get_ip_addresses() {
|
|
||||||
network::IPAddresses addresses;
|
|
||||||
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
|
|
||||||
uint8_t count = 0;
|
|
||||||
esp_netif_t *netif = esp_netif_get_default_netif();
|
|
||||||
count = esp_netif_get_all_ip6(netif, if_ip6s);
|
|
||||||
assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
addresses[i + 1] = network::IPAddress(&if_ip6s[i]);
|
|
||||||
}
|
}
|
||||||
return addresses;
|
|
||||||
|
otInstance *instance = lock->get_instance();
|
||||||
|
if (instance == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
otDeviceRole role = otThreadGetDeviceRole(instance);
|
||||||
|
|
||||||
|
// TODO: If we're a leader, check that there is at least 1 known peer
|
||||||
|
return role >= OT_DEVICE_ROLE_CHILD;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the off-mesh routable address
|
// Gets the off-mesh routable address
|
||||||
std::optional<otIp6Address> OpenThreadComponent::get_omr_address() {
|
std::optional<otIp6Address> OpenThreadComponent::get_omr_address() {
|
||||||
auto lock = EspOpenThreadLockGuard::Acquire();
|
auto lock = OpenThreadLockGuard::Acquire();
|
||||||
return this->get_omr_address(lock);
|
return this->get_omr_address(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<otIp6Address> OpenThreadComponent::get_omr_address(std::optional<EspOpenThreadLockGuard> &lock) {
|
std::optional<otIp6Address> OpenThreadComponent::get_omr_address(std::optional<OpenThreadLockGuard> &lock) {
|
||||||
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
|
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
|
||||||
otInstance *instance = nullptr;
|
otInstance *instance = nullptr;
|
||||||
|
|
||||||
instance = esp_openthread_get_instance();
|
instance = lock->get_instance();
|
||||||
|
|
||||||
otBorderRouterConfig aConfig;
|
otBorderRouterConfig aConfig;
|
||||||
while (otNetDataGetNextOnMeshPrefix(instance, &iterator, &aConfig) != OT_ERROR_NONE) {
|
while (otNetDataGetNextOnMeshPrefix(instance, &iterator, &aConfig) != OT_ERROR_NONE) {
|
||||||
lock.reset();
|
lock.reset();
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
lock = EspOpenThreadLockGuard::TryAcquire(portMAX_DELAY);
|
lock = OpenThreadLockGuard::TryAcquire(portMAX_DELAY);
|
||||||
if (!lock) {
|
if (!lock) {
|
||||||
ESP_LOGW("OT SRP", "Could not re-acquire lock");
|
ESP_LOGW("OT SRP", "Could not re-acquire lock");
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
};
|
|
||||||
const otIp6Prefix * omrPrefix = &aConfig.mPrefix;
|
|
||||||
|
|
||||||
char addressAsString[40];
|
|
||||||
otIp6PrefixToString(omrPrefix, addressAsString, 40);
|
|
||||||
ESP_LOGW("OT SRP", "USING omr prefix %s", addressAsString);
|
|
||||||
|
|
||||||
const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(instance);
|
|
||||||
for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext){
|
|
||||||
const otIp6Address *localIp = &addr->mAddress;
|
|
||||||
if (otIp6PrefixMatch(&omrPrefix->mPrefix, localIp)) {
|
|
||||||
otIp6AddressToString(localIp, addressAsString, 40);
|
|
||||||
ESP_LOGW("OT SRP", "USING %s for SRP address", addressAsString);
|
|
||||||
return *localIp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ESP_LOGW("OT SRP", "Could not find the OMR address");
|
};
|
||||||
return {};
|
const otIp6Prefix *omrPrefix = &aConfig.mPrefix;
|
||||||
|
|
||||||
|
char addressAsString[40];
|
||||||
|
otIp6PrefixToString(omrPrefix, addressAsString, 40);
|
||||||
|
ESP_LOGW("OT SRP", "USING omr prefix %s", addressAsString);
|
||||||
|
|
||||||
|
const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(instance);
|
||||||
|
for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext) {
|
||||||
|
const otIp6Address *localIp = &addr->mAddress;
|
||||||
|
if (otIp6PrefixMatch(&omrPrefix->mPrefix, localIp)) {
|
||||||
|
otIp6AddressToString(localIp, addressAsString, 40);
|
||||||
|
ESP_LOGW("OT SRP", "USING %s for SRP address", addressAsString);
|
||||||
|
return *localIp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGW("OT SRP", "Could not find the OMR address");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenThreadComponent::srp_setup(){
|
void OpenThreadComponent::srp_setup() {
|
||||||
otError error;
|
otError error;
|
||||||
otInstance *instance = nullptr;
|
auto lock = OpenThreadLockGuard::Acquire();
|
||||||
auto lock = EspOpenThreadLockGuard::Acquire();
|
otInstance *instance = lock->get_instance();
|
||||||
instance = esp_openthread_get_instance();
|
|
||||||
|
|
||||||
// set the host name
|
// set the host name
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
char *existing_host_name = otSrpClientBuffersGetHostNameString(instance, &size);
|
char *existing_host_name = otSrpClientBuffersGetHostNameString(instance, &size);
|
||||||
uint16_t len = host_name.size();
|
uint16_t len = host_name.size();
|
||||||
if (len > size) {
|
if (len > size) {
|
||||||
ESP_LOGW("OT SRP", "Hostname is too long, choose a shorter project name");
|
ESP_LOGW("OT SRP", "Hostname is too long, choose a shorter project name");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(existing_host_name, host_name.c_str(), len + 1);
|
memcpy(existing_host_name, host_name.c_str(), len + 1);
|
||||||
|
|
||||||
error = otSrpClientSetHostName(instance, existing_host_name);
|
error = otSrpClientSetHostName(instance, existing_host_name);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
ESP_LOGW("OT SRP", "Could not set host name with srp server");
|
ESP_LOGW("OT SRP", "Could not set host name with srp server");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t arrayLength;
|
||||||
|
otIp6Address *hostAddressArray = otSrpClientBuffersGetHostAddressesArray(instance, &arrayLength);
|
||||||
|
|
||||||
|
const std::optional<otIp6Address> localIp = this->get_omr_address(lock);
|
||||||
|
if (!localIp) {
|
||||||
|
ESP_LOGW("OT SRP", "Could not get local IP address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(hostAddressArray, &*localIp, sizeof(localIp));
|
||||||
|
|
||||||
|
error = otSrpClientSetHostAddresses(instance, hostAddressArray, 1);
|
||||||
|
if (error != 0) {
|
||||||
|
ESP_LOGW("OT SRP", "Could not set ip address with srp server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this
|
||||||
|
// component
|
||||||
|
this->mdns_services_ = this->mdns_->get_services();
|
||||||
|
for (const auto &service : this->mdns_services_) {
|
||||||
|
otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
|
||||||
|
if (!entry) {
|
||||||
|
ESP_LOGW("OT SRP", "Failed to allocate service entry");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t arrayLength;
|
// Set service name
|
||||||
otIp6Address *hostAddressArray = otSrpClientBuffersGetHostAddressesArray(instance, &arrayLength);
|
char *string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size);
|
||||||
|
std::string full_service = service.service_type + "." + service.proto;
|
||||||
const std::optional<otIp6Address> localIp = this->get_omr_address(lock);
|
if (full_service.size() > size) {
|
||||||
if (!localIp) {
|
ESP_LOGW("OT SRP", "Service name too long: %s", full_service.c_str());
|
||||||
ESP_LOGW("OT SRP", "Could not get local IP address");
|
continue;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
memcpy(hostAddressArray, &*localIp, sizeof(localIp));
|
memcpy(string, full_service.c_str(), full_service.size() + 1);
|
||||||
|
|
||||||
error = otSrpClientSetHostAddresses(instance, hostAddressArray, 1);
|
// Set instance name (using host_name)
|
||||||
if (error != 0){
|
string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size);
|
||||||
ESP_LOGW("OT SRP", "Could not set ip address with srp server");
|
if (this->host_name.size() > size) {
|
||||||
return;
|
ESP_LOGW("OT SRP", "Instance name too long: %s", this->host_name.c_str());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
memcpy(string, this->host_name.c_str(), this->host_name.size() + 1);
|
||||||
|
|
||||||
// Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this component
|
// Set port
|
||||||
this->mdns_services_ = this->mdns_->get_services();
|
entry->mService.mPort = service.port;
|
||||||
for (const auto& service : this->mdns_services_) {
|
|
||||||
otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
|
|
||||||
if (!entry) {
|
|
||||||
ESP_LOGW("OT SRP", "Failed to allocate service entry");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set service name
|
otDnsTxtEntry *mTxtEntries =
|
||||||
char *string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size);
|
reinterpret_cast<otDnsTxtEntry *>(this->pool_alloc(sizeof(otDnsTxtEntry) * service.txt_records.size()));
|
||||||
std::string full_service = service.service_type + "." + service.proto;
|
// Set TXT records
|
||||||
if (full_service.size() > size) {
|
entry->mService.mNumTxtEntries = service.txt_records.size();
|
||||||
ESP_LOGW("OT SRP", "Service name too long: %s", full_service.c_str());
|
for (size_t i = 0; i < service.txt_records.size(); i++) {
|
||||||
continue;
|
const auto &txt = service.txt_records[i];
|
||||||
}
|
mTxtEntries[i].mKey = txt.key.c_str();
|
||||||
memcpy(string, full_service.c_str(), full_service.size() + 1);
|
mTxtEntries[i].mValue = reinterpret_cast<const uint8_t *>(txt.value.c_str());
|
||||||
|
mTxtEntries[i].mValueLength = txt.value.size();
|
||||||
// Set instance name (using host_name)
|
|
||||||
string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size);
|
|
||||||
if (this->host_name.size() > size) {
|
|
||||||
ESP_LOGW("OT SRP", "Instance name too long: %s", this->host_name.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
memcpy(string, this->host_name.c_str(), this->host_name.size() + 1);
|
|
||||||
|
|
||||||
// Set port
|
|
||||||
entry->mService.mPort = service.port;
|
|
||||||
|
|
||||||
otDnsTxtEntry *mTxtEntries = reinterpret_cast<otDnsTxtEntry*>(this->pool_alloc(sizeof(otDnsTxtEntry) * service.txt_records.size()));
|
|
||||||
// Set TXT records
|
|
||||||
entry->mService.mNumTxtEntries = service.txt_records.size();
|
|
||||||
for (size_t i = 0; i < service.txt_records.size(); i++) {
|
|
||||||
const auto& txt = service.txt_records[i];
|
|
||||||
mTxtEntries[i].mKey = txt.key.c_str();
|
|
||||||
mTxtEntries[i].mValue = reinterpret_cast<const uint8_t*>(txt.value.c_str());
|
|
||||||
mTxtEntries[i].mValueLength = txt.value.size();
|
|
||||||
}
|
|
||||||
entry->mService.mTxtEntries = mTxtEntries;
|
|
||||||
entry->mService.mNumTxtEntries = service.txt_records.size();
|
|
||||||
|
|
||||||
// Add service
|
|
||||||
error = otSrpClientAddService(instance, &entry->mService);
|
|
||||||
if (error != OT_ERROR_NONE) {
|
|
||||||
ESP_LOGW("OT SRP", "Failed to add service: %s", otThreadErrorToString(error));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
entry->mService.mTxtEntries = mTxtEntries;
|
||||||
|
entry->mService.mNumTxtEntries = service.txt_records.size();
|
||||||
|
|
||||||
otSrpClientEnableAutoStartMode(instance, nullptr, nullptr);
|
// Add service
|
||||||
|
error = otSrpClientAddService(instance, &entry->mService);
|
||||||
|
if (error != OT_ERROR_NONE) {
|
||||||
|
ESP_LOGW("OT SRP", "Failed to add service: %s", otThreadErrorToString(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
otSrpClientEnableAutoStartMode(instance, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *OpenThreadComponent::pool_alloc(size_t size) {
|
void *OpenThreadComponent::pool_alloc(size_t size) {
|
||||||
uint8_t* ptr = new uint8_t[size];
|
uint8_t *ptr = new uint8_t[size];
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
this->_memory_pool.emplace_back(std::unique_ptr<uint8_t[]>(ptr));
|
this->_memory_pool.emplace_back(std::unique_ptr<uint8_t[]>(ptr));
|
||||||
}
|
}
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
|
||||||
|
|
||||||
void OpenThreadComponent::set_host_name(std::string host_name){
|
|
||||||
this->host_name = host_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenThreadComponent::set_mdns(esphome::mdns::MDNSComponent *mdns) {
|
|
||||||
this->mdns_ = mdns;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenThreadComponent::set_host_name(std::string host_name) { this->host_name = host_name; }
|
||||||
|
|
||||||
|
void OpenThreadComponent::set_mdns(esphome::mdns::MDNSComponent *mdns) { this->mdns_ = mdns; }
|
||||||
|
|
||||||
} // namespace openthread
|
} // namespace openthread
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -6,44 +6,58 @@
|
||||||
|
|
||||||
#include <openthread/thread.h>
|
#include <openthread/thread.h>
|
||||||
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
#include "openthread_esp.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace openthread {
|
namespace openthread {
|
||||||
|
|
||||||
|
class OpenThreadLockGuard;
|
||||||
|
|
||||||
class OpenThreadComponent : public Component {
|
class OpenThreadComponent : public Component {
|
||||||
public:
|
public:
|
||||||
OpenThreadComponent();
|
OpenThreadComponent();
|
||||||
~OpenThreadComponent();
|
~OpenThreadComponent();
|
||||||
void setup() override;
|
void setup() override;
|
||||||
float get_setup_priority() const override {
|
float get_setup_priority() const override { return setup_priority::WIFI; }
|
||||||
return setup_priority::WIFI;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_host_name(std::string host_name);
|
void set_host_name(std::string host_name);
|
||||||
void set_mdns(esphome::mdns::MDNSComponent *mdns);
|
void set_mdns(esphome::mdns::MDNSComponent *mdns);
|
||||||
bool is_connected();
|
bool is_connected();
|
||||||
network::IPAddresses get_ip_addresses();
|
network::IPAddresses get_ip_addresses();
|
||||||
std::optional<otIp6Address> get_omr_address();
|
std::optional<otIp6Address> get_omr_address();
|
||||||
void ot_main();
|
void ot_main();
|
||||||
protected:
|
|
||||||
void srp_setup();
|
|
||||||
std::optional<otIp6Address> get_omr_address(std::optional<EspOpenThreadLockGuard> &lock);
|
|
||||||
std::string host_name;
|
|
||||||
void *pool_alloc(size_t size);
|
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
esphome::mdns::MDNSComponent *mdns_{nullptr};
|
void srp_setup();
|
||||||
std::vector<esphome::mdns::MDNSService> mdns_services_;
|
std::optional<otIp6Address> get_omr_address(std::optional<OpenThreadLockGuard> &lock);
|
||||||
std::vector<std::unique_ptr<uint8_t[]>> _memory_pool;
|
std::string host_name;
|
||||||
|
void *pool_alloc(size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// void platform_init();
|
||||||
|
|
||||||
|
esphome::mdns::MDNSComponent *mdns_{nullptr};
|
||||||
|
std::vector<esphome::mdns::MDNSService> mdns_services_;
|
||||||
|
std::vector<std::unique_ptr<uint8_t[]>> _memory_pool;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern OpenThreadComponent *global_openthread_component;
|
extern OpenThreadComponent *global_openthread_component;
|
||||||
|
|
||||||
|
class OpenThreadLockGuard {
|
||||||
|
public:
|
||||||
|
static std::optional<OpenThreadLockGuard> TryAcquire(int delay);
|
||||||
|
static std::optional<OpenThreadLockGuard> Acquire();
|
||||||
|
~OpenThreadLockGuard();
|
||||||
|
|
||||||
|
// Returns the global openthread instance guarded by this lock
|
||||||
|
otInstance *get_instance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Use a private constructor in order to force thehandling
|
||||||
|
// of acquisition failure
|
||||||
|
OpenThreadLockGuard() {}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace openthread
|
} // namespace openthread
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
// #ifdef USE_ESP_IDF
|
||||||
|
#include "openthread_esp.h"
|
||||||
|
#include "openthread.h"
|
||||||
|
#include <openthread/logging.h>
|
||||||
|
|
||||||
|
#include "esp_openthread.h"
|
||||||
|
#include "esp_openthread_lock.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esp_task_wdt.h"
|
||||||
|
|
||||||
|
#include "esp_openthread_cli.h"
|
||||||
|
#include "esp_openthread_netif_glue.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "esp_vfs_eventfd.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "esp_netif_types.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#define TAG "openthread"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace openthread {
|
||||||
|
|
||||||
|
void OpenThreadComponent::setup() {
|
||||||
|
ESP_LOGI("openthread", "Setting up OpenThread...");
|
||||||
|
// Used eventfds:
|
||||||
|
// * netif
|
||||||
|
// * ot task queue
|
||||||
|
// * radio driver
|
||||||
|
// TODO: Does anything else in esphome set this up?
|
||||||
|
esp_vfs_eventfd_config_t eventfd_config = {
|
||||||
|
.max_fds = 3,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
|
||||||
|
|
||||||
|
xTaskCreate(
|
||||||
|
[](void *arg) {
|
||||||
|
static_cast<OpenThreadComponent *>(arg)->ot_main();
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
},
|
||||||
|
"ot_main", 10240, this, 5, nullptr);
|
||||||
|
|
||||||
|
xTaskCreate(
|
||||||
|
[](void *arg) {
|
||||||
|
static_cast<OpenThreadComponent *>(arg)->srp_setup();
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
},
|
||||||
|
"ot_srp_setup", 10240, this, 5, nullptr);
|
||||||
|
|
||||||
|
ESP_LOGI("openthread", "OpenThread started");
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) {
|
||||||
|
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
|
||||||
|
esp_netif_t *netif = esp_netif_new(&cfg);
|
||||||
|
assert(netif != NULL);
|
||||||
|
ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config)));
|
||||||
|
|
||||||
|
return netif;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenThreadComponent::ot_main() {
|
||||||
|
esp_openthread_platform_config_t config = {
|
||||||
|
.radio_config =
|
||||||
|
{
|
||||||
|
.radio_mode = RADIO_MODE_NATIVE,
|
||||||
|
.radio_uart_config = {},
|
||||||
|
},
|
||||||
|
.host_config =
|
||||||
|
{
|
||||||
|
// There is a conflict between esphome's logger which also
|
||||||
|
// claims the usb serial jtag device.
|
||||||
|
// .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB,
|
||||||
|
// .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(),
|
||||||
|
},
|
||||||
|
.port_config =
|
||||||
|
{
|
||||||
|
.storage_partition_name = "nvs",
|
||||||
|
.netif_queue_size = 10,
|
||||||
|
.task_queue_size = 10,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize the OpenThread stack
|
||||||
|
ESP_ERROR_CHECK(esp_openthread_init(&config));
|
||||||
|
|
||||||
|
#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE
|
||||||
|
ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
|
||||||
|
// The OpenThread log level directly matches ESP log level
|
||||||
|
(void) otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
|
||||||
|
#endif
|
||||||
|
// Initialize the OpenThread cli
|
||||||
|
#if CONFIG_OPENTHREAD_CLI
|
||||||
|
esp_openthread_cli_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_netif_t *openthread_netif;
|
||||||
|
// Initialize the esp_netif bindings
|
||||||
|
openthread_netif = init_openthread_netif(&config);
|
||||||
|
esp_netif_set_default_netif(openthread_netif);
|
||||||
|
|
||||||
|
#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
|
||||||
|
esp_cli_custom_command_init();
|
||||||
|
#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
|
||||||
|
|
||||||
|
// Run the main loop
|
||||||
|
#if CONFIG_OPENTHREAD_CLI
|
||||||
|
esp_openthread_cli_create_task();
|
||||||
|
#endif
|
||||||
|
ESP_LOGI(TAG, "Activating dataset...");
|
||||||
|
otOperationalDatasetTlvs dataset;
|
||||||
|
otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset);
|
||||||
|
ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL));
|
||||||
|
|
||||||
|
esp_openthread_launch_mainloop();
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
esp_openthread_netif_glue_deinit();
|
||||||
|
esp_netif_destroy(openthread_netif);
|
||||||
|
|
||||||
|
esp_vfs_eventfd_unregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This gets used by mqtt in order to register the device's IP. Likely it doesn't
|
||||||
|
// make sense to return thread-local addresses, since they can't be reached from outside the thread network.
|
||||||
|
// It could make more sense to return the off-mesh-routable address instead.
|
||||||
|
network::IPAddresses OpenThreadComponent::get_ip_addresses() {
|
||||||
|
network::IPAddresses addresses;
|
||||||
|
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
|
||||||
|
uint8_t count = 0;
|
||||||
|
esp_netif_t *netif = esp_netif_get_default_netif();
|
||||||
|
count = esp_netif_get_all_ip6(netif, if_ip6s);
|
||||||
|
assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
addresses[i + 1] = network::IPAddress(&if_ip6s[i]);
|
||||||
|
}
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<OpenThreadLockGuard> OpenThreadLockGuard::TryAcquire(int delay) {
|
||||||
|
if (esp_openthread_lock_acquire(delay)) {
|
||||||
|
return OpenThreadLockGuard();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<OpenThreadLockGuard> OpenThreadLockGuard::Acquire() {
|
||||||
|
while (!esp_openthread_lock_acquire(100)) {
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
}
|
||||||
|
return OpenThreadLockGuard();
|
||||||
|
}
|
||||||
|
|
||||||
|
otInstance *OpenThreadLockGuard::get_instance() { return esp_openthread_get_instance(); }
|
||||||
|
|
||||||
|
OpenThreadLockGuard::~OpenThreadLockGuard() { esp_openthread_lock_release(); }
|
||||||
|
|
||||||
|
} // namespace openthread
|
||||||
|
} // namespace esphome
|
||||||
|
// #endif
|
|
@ -1,46 +1 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include "esp_openthread.h"
|
|
||||||
#include "esp_openthread_lock.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esp_task_wdt.h"
|
|
||||||
|
|
||||||
#include "esp_openthread_cli.h"
|
|
||||||
#include "esp_openthread_netif_glue.h"
|
|
||||||
#include "esp_event.h"
|
|
||||||
#include "nvs_flash.h"
|
|
||||||
#include "esp_vfs_eventfd.h"
|
|
||||||
#include "esp_netif.h"
|
|
||||||
#include "esp_netif_types.h"
|
|
||||||
#include "esp_err.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
namespace esphome {
|
|
||||||
namespace openthread {
|
|
||||||
|
|
||||||
class EspOpenThreadLockGuard {
|
|
||||||
public:
|
|
||||||
static std::optional<EspOpenThreadLockGuard> TryAcquire(TickType_t delay) {
|
|
||||||
if (esp_openthread_lock_acquire(delay)) {
|
|
||||||
return EspOpenThreadLockGuard();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
static std::optional<EspOpenThreadLockGuard> Acquire() {
|
|
||||||
while (!esp_openthread_lock_acquire(100)) {
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
}
|
|
||||||
return EspOpenThreadLockGuard();
|
|
||||||
}
|
|
||||||
~EspOpenThreadLockGuard() { esp_openthread_lock_release(); }
|
|
||||||
private:
|
|
||||||
// Use a private constructor in order to force thehandling
|
|
||||||
// of acquisition failure
|
|
||||||
EspOpenThreadLockGuard() {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue