// Copyright 2021 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "esp_check.h" #include "esp_err.h" #include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_netif_ip_addr.h" #include "esp_netif_net_stack.h" #include "esp_openthread.h" #include "esp_openthread_border_router.h" #include "esp_openthread_lock.h" #include "esp_openthread_netif_glue.h" #include "esp_openthread_types.h" #include "esp_ot_config.h" #include "esp_vfs_eventfd.h" #include "esp_wifi.h" #include "mdns.h" #include "nvs_flash.h" #include "protocol_examples_common.h" #include "sdkconfig.h" #include "driver/uart.h" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" #include "freertos/task.h" #include "hal/uart_types.h" #include "openthread/border_router.h" #include "openthread/cli.h" #include "openthread/dataset.h" #include "openthread/dataset_ftd.h" #include "openthread/dataset_updater.h" #include "openthread/error.h" #include "openthread/instance.h" #include "openthread/ip6.h" #include "openthread/tasklet.h" #include "openthread/thread.h" #include "openthread/thread_ftd.h" #define TAG "esp_ot_br" static int hex_digit_to_int(char hex) { if ('A' <= hex && hex <= 'F') { return 10 + hex - 'A'; } if ('a' <= hex && hex <= 'f') { return 10 + hex - 'a'; } if ('0' <= hex && hex <= '9') { return hex - '0'; } return -1; } static size_t hex_string_to_binary(const char *hex_string, uint8_t *buf, size_t buf_size) { int num_char = strlen(hex_string); if (num_char != buf_size * 2) { return 0; } for (size_t i = 0; i < num_char; i += 2) { int digit0 = hex_digit_to_int(hex_string[i]); int digit1 = hex_digit_to_int(hex_string[i + 1]); if (digit0 < 0 || digit1 < 0) { return 0; } buf[i / 2] = (digit0 << 4) + digit1; } return buf_size; } static void create_config_network(otInstance *instance) { otOperationalDataset dataset; uint16_t network_name_len = strnlen(CONFIG_OPENTHREAD_NETWORK_NAME, OT_NETWORK_NAME_MAX_SIZE + 1); assert(network_name_len <= OT_NETWORK_NAME_MAX_SIZE); if (otDatasetCreateNewNetwork(instance, &dataset) != OT_ERROR_NONE) { ESP_LOGE(TAG, "Failed to create OpenThread network dataset."); abort(); } dataset.mChannel = CONFIG_OPENTHREAD_NETWORK_CHANNEL; dataset.mComponents.mIsChannelPresent = true; dataset.mPanId = CONFIG_OPENTHREAD_NETWORK_PANID; dataset.mComponents.mIsPanIdPresent = true; memcpy(dataset.mNetworkName.m8, CONFIG_OPENTHREAD_NETWORK_NAME, network_name_len); dataset.mComponents.mIsNetworkNamePresent = true; if (hex_string_to_binary(CONFIG_OPENTHREAD_NETWORK_EXTPANID, dataset.mExtendedPanId.m8, sizeof(dataset.mExtendedPanId.m8)) != sizeof(dataset.mExtendedPanId.m8)) { ESP_LOGE(TAG, "Cannot convert OpenThread extended pan id. Please double-check your config."); abort(); } dataset.mComponents.mIsExtendedPanIdPresent = true; if (hex_string_to_binary(CONFIG_OPENTHREAD_NETWORK_MASTERKEY, dataset.mNetworkKey.m8, sizeof(dataset.mNetworkKey.m8)) != sizeof(dataset.mNetworkKey.m8)) { ESP_LOGE(TAG, "Cannot convert OpenThread master key. Please double-check your config."); abort(); } dataset.mComponents.mIsNetworkKeyPresent = true; if (hex_string_to_binary(CONFIG_OPENTHREAD_NETWORK_PSKC, dataset.mPskc.m8, sizeof(dataset.mPskc.m8)) != sizeof(dataset.mPskc.m8)) { ESP_LOGE(TAG, "Cannot convert OpenThread pre-shared commissioner key. Please double-check your config."); abort(); } dataset.mComponents.mIsPskcPresent = true; dataset.mComponents.mIsMeshLocalPrefixPresent = false; if (otDatasetSetActive(instance, &dataset) != OT_ERROR_NONE) { ESP_LOGE(TAG, "Failed to set OpenThread active dataset."); abort(); } if (otBorderRouterRegister(instance) != OT_ERROR_NONE) { ESP_LOGE(TAG, "Failed to register border router."); abort(); } if (otIp6SetEnabled(instance, true) != OT_ERROR_NONE) { ESP_LOGE(TAG, "Failed to enable OpenThread IP6 link"); abort(); } if (otThreadSetEnabled(instance, true) != OT_ERROR_NONE) { ESP_LOGE(TAG, "Failed to enable OpenThread"); abort(); } return; } static void ot_task_worker(void *aContext) { esp_openthread_platform_config_t config = { .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), }; esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); esp_netif_t *openthread_netif = esp_netif_new(&cfg); assert(openthread_netif != NULL); // Initialize the OpenThread stack ESP_ERROR_CHECK(esp_openthread_init(&config)); // Initialize border routing features ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&config))); ESP_ERROR_CHECK(esp_openthread_border_router_init(get_example_netif())); esp_openthread_lock_acquire(portMAX_DELAY); create_config_network(esp_openthread_get_instance()); esp_openthread_lock_release(); // Run the main loop esp_openthread_launch_mainloop(); // Clean up esp_netif_destroy(openthread_netif); esp_openthread_netif_glue_deinit(); esp_vfs_eventfd_unregister(); vTaskDelete(NULL); } void app_main(void) { // Used eventfds: // * netif // * task queue esp_vfs_eventfd_config_t eventfd_config = { .max_fds = 2, }; ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(example_connect()); ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); ESP_ERROR_CHECK(mdns_init()); ESP_ERROR_CHECK(mdns_hostname_set("esp-ot-br")); xTaskCreate(ot_task_worker, "ot_br_main", 20480, xTaskGetCurrentTaskHandle(), 5, NULL); }