diff --git a/components/openthread/CMakeLists.txt b/components/openthread/CMakeLists.txt index 4f8b52bc25..8b41f30532 100644 --- a/components/openthread/CMakeLists.txt +++ b/components/openthread/CMakeLists.txt @@ -135,7 +135,7 @@ idf_component_register(SRC_DIRS "${src_dirs}" EXCLUDE_SRCS "${exclude_srcs}" INCLUDE_DIRS "${public_include_dirs}" PRIV_INCLUDE_DIRS "${private_include_dirs}" - REQUIRES mbedtls ieee802154 console) + REQUIRES mbedtls ieee802154 console lwip) if(CONFIG_OPENTHREAD_ENABLED) if(CONFIG_OPENTHREAD_RADIO) @@ -156,11 +156,18 @@ if(CONFIG_OPENTHREAD_ENABLED) if(CONFIG_OPENTHREAD_ESP_LIB_FROM_INTERNAL_SRC) idf_component_get_property(openthread_port_lib openthread_port COMPONENT_LIB) + idf_component_get_property(lwip_lib lwip COMPONENT_LIB) + idf_component_get_property(esp_netif_lib esp_netif COMPONENT_LIB) idf_component_get_property(esp_system_lib esp_system COMPONENT_LIB) target_link_libraries(${COMPONENT_LIB} PUBLIC $ $) - + target_link_libraries(${COMPONENT_LIB} PUBLIC + $ + $ + $ + $ + $) if(CONFIG_OPENTHREAD_BORDER_ROUTER) idf_component_get_property(openthread_br_lib openthread_br COMPONENT_LIB) target_link_libraries(${COMPONENT_LIB} PUBLIC $) diff --git a/examples/openthread/ot_br/README.md b/examples/openthread/ot_br/README.md index 29acd1eed9..2a0f68abbe 100644 --- a/examples/openthread/ot_br/README.md +++ b/examples/openthread/ot_br/README.md @@ -27,8 +27,14 @@ ESP32 pin | ESP32-H2 pin ``` idf.py menuconfig ``` +Two ways are provided to setup the Thread Border Router in this example: -You need to configure the `CONFIG_EXAMPLE_WIFI_SSID` and `CONFIG_EXAMPLE_WIFI_PASSWORD` with your access point's ssid and psk. +- Auto Start +Enable `OPENTHREAD_BR_AUTO_START`, configure the `CONFIG_EXAMPLE_WIFI_SSID` and `CONFIG_EXAMPLE_WIFI_PASSWORD` with your access point's ssid and psk. +The device will connect to Wi-Fi and form a Thread network automatically after bootup. + +- Manual mode +Disable `OPENTHREAD_BR_AUTO_START`, and use the CLI command to configure the Wi-Fi and form Thread network manually. ### Build, Flash, and Run @@ -37,6 +43,57 @@ Build the project and flash it to the board, then run monitor tool to view seria ``` idf.py -p PORT build flash monitor ``` +If the OPENTHREAD_BR_AUTO_START option is enabled, The device will be connected to the configured Wi-Fi and Thread network automatically then act as the border router. + +Otherwise, you need to manually configure the Wi-Fi and Thread network with CLI command. + +Command `sta` is used for connecting WiFi. + +```bash +> sta +---wifi sta parameter--- +-s : wifi ssid +-p : wifi psk +---example--- +join a wifi, +ssid: threadcertAP +psk: threadcertAP : sta -s threadcertAP -p threadcertAP +Done +> sta -s threadcertAP -p threadcertAP +ssid: threadcertAP +psk: threadcertAP +I (47043) wifi:wifi driver task: 3ffd05ac, prio:23, stack:6656, core=0 + + +...... + + +I (49263) wifi:AP's beacon interval = 102400 us, DTIM period = 1 +I (50233) esp_netif_handlers: sta ip: 192.168.3.10, mask: 255.255.255.0, gw: 192.168.3.1 +wifi sta is connected successfully +Done +> +``` + +Command `wifiinfo` is used for checking the state of Wi-Fi connection and printing IP addresses. + +```bash +> wifiinfo +---get WiFi informations--- +-i : get sta addr +-s : get wifi state, disconnect or connect +Done +> wifiinfo -s +connected +Done +> wifiinfo -i +inet 192.168.3.10 +inet6 FE80::7AE3:6DFF:FECD:125C +Done +> +``` + +For forming Thread network, you can refer to [ot_cli_README](../ot_cli/README.md) ## Example Output @@ -56,9 +113,6 @@ I(8139) OPENTHREAD:[NOTE]-MLE-----: Allocate router id 50 I(8139) OPENTHREAD:[NOTE]-MLE-----: RLOC16 fffe -> c800 I(8159) OPENTHREAD:[NOTE]-MLE-----: Role Detached -> Leader ``` - -The device will automatically connect to the configured Wi-Fi and Thread network and act as the border router. - ## Using the border agent feature You need to ot-commissioner on the host machine and another Thread end device running OpenThread cli. diff --git a/examples/openthread/ot_br/main/CMakeLists.txt b/examples/openthread/ot_br/main/CMakeLists.txt index 83b4e7608e..768ef643df 100644 --- a/examples/openthread/ot_br/main/CMakeLists.txt +++ b/examples/openthread/ot_br/main/CMakeLists.txt @@ -1,2 +1,9 @@ -idf_component_register(SRCS "esp_ot_br.c" - INCLUDE_DIRS ".") +if(CONFIG_OPENTHREAD_BR_AUTO_START) + set(srcs "esp_ot_br.c") +else() + set(srcs "esp_ot_br.c" + "esp_ot_cli_extension.c" + "esp_br_wifi_cmd.c") +endif() + idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS ".") diff --git a/examples/openthread/ot_br/main/Kconfig.projbuild b/examples/openthread/ot_br/main/Kconfig.projbuild index 4d9e83a82f..fa8a651201 100644 --- a/examples/openthread/ot_br/main/Kconfig.projbuild +++ b/examples/openthread/ot_br/main/Kconfig.projbuild @@ -38,4 +38,12 @@ menu "OpenThread Border Router Example" help The OpenThread pre-shared commissioner key in hex string format + config OPENTHREAD_BR_AUTO_START + bool 'Enable the automatic start mode in Thread Border Router.' + default False + help + If enabled, The Thread Border Router will connect to Wi-Fi with pre-configured + SSID and PSK, and then form a Thread network automatically. Otherwise, user need + to configure Wi-Fi and Thread manually. + endmenu diff --git a/examples/openthread/ot_br/main/esp_br_wifi_cmd.c b/examples/openthread/ot_br/main/esp_br_wifi_cmd.c new file mode 100644 index 0000000000..d9b760f58d --- /dev/null +++ b/examples/openthread/ot_br/main/esp_br_wifi_cmd.c @@ -0,0 +1,173 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Border Router Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include "esp_netif_types.h" +#include "esp_openthread_border_router.h" +#include "esp_check.h" +#include "esp_event.h" +#include "esp_openthread_lock.h" +#include "esp_netif.h" +#include "esp_netif_ip_addr.h" +#include "esp_netif_net_stack.h" +#include "esp_wifi.h" +#include "freertos/event_groups.h" +#include "freertos/FreeRTOS.h" +#include "freertos/portmacro.h" +#include "freertos/task.h" +#include "openthread/cli.h" + +static EventGroupHandle_t wifi_event_group; +const int CONNECTED_IP4_BIT = BIT0; +const int CONNECTED_IP6_BIT = BIT1; +static bool s_wifi_connected = false; + +void esp_ot_wifi_netif_init(void) +{ + esp_netif_t *esp_netif = esp_netif_create_default_wifi_sta(); + assert(esp_netif); +} + +static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + xEventGroupClearBits(wifi_event_group, CONNECTED_IP4_BIT); + xEventGroupClearBits(wifi_event_group, CONNECTED_IP6_BIT); + esp_wifi_connect(); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + xEventGroupSetBits(wifi_event_group, CONNECTED_IP4_BIT); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { + esp_netif_create_ip6_linklocal(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF")); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) { + xEventGroupSetBits(wifi_event_group, CONNECTED_IP6_BIT); + } +} + +static void wifi_join(const char *ssid, const char *psk) +{ + static bool s_initialized = false; + if (!s_initialized) { + wifi_event_group = xEventGroupCreate(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_wifi_init(&cfg); + ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler, NULL) ); + ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); + ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &event_handler, NULL) ); + ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &event_handler, NULL) ); + esp_wifi_set_storage(WIFI_STORAGE_RAM); + esp_wifi_set_mode(WIFI_MODE_NULL); + s_initialized = true; + } + esp_wifi_start(); + wifi_config_t wifi_config = { 0 }; + strncpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + if (psk) { + strncpy((char *) wifi_config.sta.password, psk, sizeof(wifi_config.sta.password)); + } + + ESP_ERROR_CHECK( esp_wifi_set_ps(WIFI_PS_NONE) ); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + esp_wifi_connect(); + + int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_IP4_BIT | CONNECTED_IP6_BIT, pdFALSE, pdTRUE, 10000 / portTICK_PERIOD_MS); + + if (((bits & CONNECTED_IP4_BIT) != 0) && ((bits & CONNECTED_IP6_BIT) != 0)) { + s_wifi_connected = true; + xEventGroupClearBits(wifi_event_group, CONNECTED_IP4_BIT); + xEventGroupClearBits(wifi_event_group, CONNECTED_IP6_BIT); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &event_handler); + esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler); + esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &event_handler); + vEventGroupDelete(wifi_event_group); + s_initialized = false; + esp_openthread_lock_acquire(portMAX_DELAY); + esp_openthread_border_router_init(); + esp_openthread_lock_release(); + otCliOutputFormat("wifi sta is connected successfully\n"); + } else { + esp_wifi_disconnect(); + esp_wifi_stop(); + ESP_LOGW("","Connection time out, please check your ssid & password, then retry."); + otCliOutputFormat("wifi sta connection is failed\n"); + } +} + +void esp_ot_process_wifi_sta(void *aContext, uint8_t aArgsLength, char *aArgs[]) +{ + char ssid[100] = ""; + char psk[100] = ""; + (void)(aContext); + if (aArgsLength == 0) { + otCliOutputFormat("---wifi sta parameter---\n"); + otCliOutputFormat("-s : wifi ssid\n"); + otCliOutputFormat("-p : wifi psk\n"); + otCliOutputFormat("---example---\n"); + otCliOutputFormat("join a wifi:\nssid: threadcertAP \npsk: threadcertAP : sta -s threadcertAP -p threadcertAP\n"); + } else { + for (int i = 0; i < aArgsLength; i++) { + if (strcmp(aArgs[i], "-s") == 0) { + i++; + strcpy(ssid, aArgs[i]); + otCliOutputFormat("ssid: %s\n", ssid); + } else if (strcmp(aArgs[i], "-p") == 0) { + i++; + strcpy(psk, aArgs[i]); + otCliOutputFormat("psk: %s\n", psk); + } + } + if (!s_wifi_connected) { + wifi_join(ssid, psk); + } else { + otCliOutputFormat("wifi has already connected\n"); + } + } +} + +void esp_ot_process_get_wifi_info(void *aContext, uint8_t aArgsLength, char *aArgs[]) +{ + (void)(aContext); + if (aArgsLength == 0) { + otCliOutputFormat("---get wifi informations---\n"); + otCliOutputFormat("-i : get sta addr\n"); + otCliOutputFormat("-s : get wifi state, disconnect or connect\n"); + } else { + for (int i = 0; i < aArgsLength; i++) { + if (strcmp(aArgs[i], "-i") == 0) { + if (s_wifi_connected) { + esp_netif_ip_info_t local_ip; + char addr_str[128]; + esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &local_ip); + ip4addr_ntoa_r((ip4_addr_t *)(&local_ip.ip), addr_str, sizeof(addr_str) - 1); + otCliOutputFormat("inet %s\n"); + esp_ip6_addr_t if_ip6; + esp_netif_get_ip6_linklocal(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &if_ip6); + ip6addr_ntoa_r((ip6_addr_t *)(&if_ip6), addr_str, sizeof(addr_str) - 1); + otCliOutputFormat("inet6 %s\n", addr_str); + } else { + otCliOutputFormat("wifi is disconnected\n"); + } + } else if (strcmp(aArgs[i], "-s") == 0) { + if (s_wifi_connected) { + otCliOutputFormat("connected\n"); + } else { + otCliOutputFormat("disconnected\n"); + } + } + } + } +} diff --git a/examples/openthread/ot_br/main/esp_br_wifi_cmd.h b/examples/openthread/ot_br/main/esp_br_wifi_cmd.h new file mode 100644 index 0000000000..7d64315dd7 --- /dev/null +++ b/examples/openthread/ot_br/main/esp_br_wifi_cmd.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Border Router Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include "esp_netif.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief User command "sta" process. + * + */ +void esp_ot_process_wifi_sta(void *aContext, uint8_t aArgsLength, char *aArgs[]); + +/** + * @brief User command "wifiinfo" process. + * + */ +void esp_ot_process_get_wifi_info(void *aContext, uint8_t aArgsLength, char *aArgs[]); + +/** + * @brief Wifi netif init. + * + */ +void esp_ot_wifi_netif_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/openthread/ot_br/main/esp_ot_br.c b/examples/openthread/ot_br/main/esp_ot_br.c index 32a5535658..6b579c6f7f 100644 --- a/examples/openthread/ot_br/main/esp_ot_br.c +++ b/examples/openthread/ot_br/main/esp_ot_br.c @@ -1,7 +1,7 @@ /* * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD * - * SPDX-License-Identifier: CC0 + * SPDX-License-Identifier: CC0-1.0 * * OpenThread Border Router Example * @@ -52,9 +52,12 @@ #include "openthread/tasklet.h" #include "openthread/thread.h" #include "openthread/thread_ftd.h" +#include "esp_br_wifi_cmd.h" +#include "esp_ot_cli_extension.h" #define TAG "esp_ot_br" +#if CONFIG_OPENTHREAD_BR_AUTO_START static int hex_digit_to_int(char hex) { if ('A' <= hex && hex <= 'F') { @@ -152,6 +155,7 @@ static void launch_openthread_network(otInstance *instance) abort(); } } +#endif // CONFIG_OPENTHREAD_BR_AUTO_START static void ot_task_worker(void *aContext) { @@ -164,20 +168,23 @@ static void ot_task_worker(void *aContext) 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_openthread_set_backbone_netif(get_example_netif()); + ESP_ERROR_CHECK(esp_openthread_init(&config)); // Initialize border routing features esp_openthread_lock_acquire(portMAX_DELAY); ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&config))); - ESP_ERROR_CHECK(esp_openthread_border_router_init()); (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); esp_openthread_cli_init(); +#if CONFIG_OPENTHREAD_BR_AUTO_START + ESP_ERROR_CHECK(esp_openthread_border_router_init()); create_config_network(esp_openthread_get_instance()); launch_openthread_network(esp_openthread_get_instance()); +#else + esp_cli_custom_command_init(); +#endif // CONFIG_OPENTHREAD_BR_AUTO_START esp_openthread_lock_release(); // Run the main loop @@ -205,8 +212,14 @@ void app_main(void) ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); +#if CONFIG_OPENTHREAD_BR_AUTO_START ESP_ERROR_CHECK(example_connect()); ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); + esp_openthread_set_backbone_netif(get_example_netif()); +#else + esp_ot_wifi_netif_init(); + esp_openthread_set_backbone_netif(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF")); +#endif // CONFIG_OPENTHREAD_BR_AUTO_START 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); diff --git a/examples/openthread/ot_br/main/esp_ot_cli_extension.c b/examples/openthread/ot_br/main/esp_ot_cli_extension.c new file mode 100644 index 0000000000..1ac2b50d48 --- /dev/null +++ b/examples/openthread/ot_br/main/esp_ot_cli_extension.c @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "esp_openthread.h" +#include "openthread/cli.h" +#include "esp_ot_cli_extension.h" +#include "esp_br_wifi_cmd.h" + +static const otCliCommand kCommands[] = { + {"sta", esp_ot_process_wifi_sta}, + {"wifiinfo", esp_ot_process_get_wifi_info} +}; + +void esp_cli_custom_command_init() +{ + otInstance *instance = esp_openthread_get_instance(); + otCliSetUserCommands(kCommands, (sizeof(kCommands) / sizeof(kCommands[0])), instance); +} diff --git a/examples/openthread/ot_br/main/esp_ot_cli_extension.h b/examples/openthread/ot_br/main/esp_ot_cli_extension.h new file mode 100644 index 0000000000..1cb1040ee1 --- /dev/null +++ b/examples/openthread/ot_br/main/esp_ot_cli_extension.h @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @brief Init the custom command. +* +*/ +void esp_cli_custom_command_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/openthread/ot_br/main/esp_ot_config.h b/examples/openthread/ot_br/main/esp_ot_config.h index 8dc1fa2a9d..e237a8a14b 100644 --- a/examples/openthread/ot_br/main/esp_ot_config.h +++ b/examples/openthread/ot_br/main/esp_ot_config.h @@ -1,7 +1,7 @@ /* * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD * - * SPDX-License-Identifier: CC0 + * SPDX-License-Identifier: CC0-1.0 * * OpenThread Border Router Example *