From 57189ec1a7d90edc9d7f927e8142ced64f18ae60 Mon Sep 17 00:00:00 2001 From: xiaqilin Date: Mon, 4 Mar 2024 14:07:25 +0800 Subject: [PATCH] feat(openthread): add cli in openthread sleep example --- .../ot_sleepy_device/light_sleep/README.md | 102 ++++++++++++++---- .../light_sleep/main/Kconfig.projbuild | 9 ++ .../light_sleep/main/esp_ot_sleepy_device.c | 61 ++++++++++- .../light_sleep/sdkconfig.defaults | 1 + examples/openthread/pytest_otbr.py | 17 ++- 5 files changed, 157 insertions(+), 33 deletions(-) create mode 100644 examples/openthread/ot_sleepy_device/light_sleep/main/Kconfig.projbuild diff --git a/examples/openthread/ot_sleepy_device/light_sleep/README.md b/examples/openthread/ot_sleepy_device/light_sleep/README.md index d49a314b5e..01e8259d2b 100644 --- a/examples/openthread/ot_sleepy_device/light_sleep/README.md +++ b/examples/openthread/ot_sleepy_device/light_sleep/README.md @@ -6,34 +6,100 @@ The example demonstrates the Thread Sleepy End Device (SED), the device will ent ## How to use example ### Hardware Required +* One 802.15.4 SoC (for example ESP32-H2) runs [ot_cli](../../ot_cli/) example, and forms a Thread network. +* A second 802.15.4 SoC runs this example. -* Prepare an 802.15.4 SoC development board as an OpenThread Sleepy End Device (SED). -* Connect the board using a USB cable for power supply and programming. -* Choose another 802.15.4 SoC as the OpenThread Leader. +### Configure the project -## Configure the Openthread Dataset +Set the chip target: `idf.py set-target `, then configure the project via `idf.py menuconfig`. -* Run [ot_cli](../../ot_cli/) on another 802.15.4 SoC device to create openthread dataset configuration and start an openthread network as the leader. -* Configure the Openthread dataset using `idf.py menuconfig` in `Component config ---> Openthread ---> Thread Operation Dataset`, ensuring that the openthread sleepy device's dataset matches the dataset of the leader. +There are two options to configure Openthread Dataset: + +* Auto start mode: Enable `OPENTHREAD_AUTO_START` under `OpenThread Sleepy Example---> Enable the automatic start mode`, and configure the dataset under `Component config ---> Openthread ---> Thread Operation Dataset`. +* Manual mode: Disable `OPENTHREAD_AUTO_START`, use the CLI command to configure the dataset and start network. ### Build and Flash Build the project and flash it to the board. Use the following command: `idf.py -p erase-flash flash monitor`. +### Configure the Openthread sleepy device +``` +> mode - +> pollperiod 3000 +> dataset set active +> ifconfig up +> thread start +``` + ### Example Output As the example runs, you will see the log output indicating the initialization and operation of OpenThread, including the device joining the OpenThread network as a Sleepy End Device (SED) and periodic polling of the leader. ``` -I (769) btbb_init: btbb sleep retention initialization -I (769) ieee802154: ieee802154 mac sleep retention initialization -I(769) OPENTHREAD:[I] ChildSupervsn-: Timeout: 0 -> 190 -I (699) main_task: Returned from app_main() -I (799) OPENTHREAD: OpenThread attached to netif -I(799) OPENTHREAD:[N] Mle-----------: Mode 0x0f -> 0x04 [rx-on:no ftd:no full-net:no] -I(809) OPENTHREAD:[N] Mle-----------: Role disabled -> detached -I (819) OPENTHREAD: netif up -I(1519) OPENTHREAD:[N] Mle-----------: Attach attempt 1, AnyPartition reattaching with Active Dataset -I(2479) OPENTHREAD:[N] Mle-----------: RLOC16 fffe -> 5023 -I(2529) OPENTHREAD:[N] Mle-----------: Role detached -> child -``` \ No newline at end of file +I (486) app_init: ESP-IDF: v5.3-dev-2053-g4d7e86eeb6-dirty +I (493) app_init: Min chip rev: v0.0 +I (497) app_init: Max chip rev: v0.99 +I (502) app_init: Chip rev: v0.1 +I (507) sleep: Enable automatic switching of GPIO sleep configuration +I (514) sleep_clock: System Power, Clock and Reset sleep retention initialization +I (522) sleep_clock: Modem Power, Clock and Reset sleep retention initialization +I (530) sleep_sys_periph: Interrupt Matrix sleep retention initialization +I (538) sleep_sys_periph: HP System sleep retention initialization +I (545) sleep_sys_periph: TEE/APM sleep retention initialization +I (551) sleep_sys_periph: UART sleep retention initialization +I (558) sleep_sys_periph: Timer Group sleep retention initialization +I (565) sleep_sys_periph: IO Matrix sleep retention initialization +I (572) sleep_sys_periph: SPI Mem sleep retention initialization +I (579) sleep_sys_periph: SysTimer sleep retention initialization +I (597) main_task: Started on CPU0 +I (597) main_task: Calling app_main() +I (608) pm: Frequency switching config: CPU_MAX: 96, APB_MAX: 96, APB_MIN: 96, Light sleep: ENABLED +I (609) ot_esp_power_save: Create ot cI (631) phy: phy_version: 230,2, 9aae6ea, Jan 15 2024, 11:17:12 +I (633) phy: libbtbb version: 944f18e, Jan 15 2024, 11:17:25 +I (634) btbb_init: btbb sleep retention initialization +I (646) ieee802154: ieee802154 mac sleep retention initialization +I (652) gdma: GDMA pair (0, 0) retention initialization +I(660) OPENTHREAD:[I] ChildSupervsn-: Timeout: 0 -> 190 +> I (664) OPENTHREAD: OpenThread attached to netif +I (635) main_task: Returned from app_main() +> mode - + +I(2250683) OPENTHREAD:[N] Mle-----------: Mode 0x0f -> 0x04 [rx-on:no ftd:no full-net:no] +Done +> pollperiod 3000 + +Done + +> dataset set active 0e080000000000010000000300001a35060004001fffe00208dead00beef00cafe0708fd000db800a00000051000112233445566778899aabbccdd0000030e4f70656e5468726561642d455350010212340410104810e2315100afd6bc9215a6bfac530c0402a0f7f8 + +Done + +> ifconfig up + +Done +I (2274801) OT_STATE: netif up +> thread start + +I(2279917) OPENTHREAD:[N] Mle-----------: Role disabled -> detached +Done +> I(2280368) OPENTHREAD:[N] Mle-----------: Attach attempt 1, AnyPartition reattaching with Active Dataset +I(2281262) OPENTHREAD:[N] Mle-----------: RLOC16 fffe -> 5019 +I(2281264) OPENTHREAD:[N] Mle-----------: Role detached -> child + +``` + +When the device is running in auto start mode, the running log is as follows: + +``` +I(662) OPENTHREAD:[I] ChildSupervsn-: Timeout: 0 -> 190 +> I (666) OPENTHREAD: OpenThread attached to netif +I(668) OPENTHREAD:[N] Mle-----------: Mode 0x0f -> 0x04 [rx-on:no ftd:no full-net:no] +I (637) main_task: Returned from app_main() +I(693) OPENTHREAD:[N] Mle-----------: Role disabled -> detached +I (705) OT_STATE: netif up +I(867) OPENTHREAD:[N] Mle-----------: Attach attempt 1, AnyPartition reattaching with Active Dataset +I(1819) OPENTHREAD:[N] Mle-----------: RLOC16 fffe -> 500b +I(1821) OPENTHREAD:[N] Mle-----------: Role detached -> child +``` +### Note +Currently, UART wakeup is not enabled. Once the device joins the network as a child and enters sleep mode, the OT CLI will become inaccessible. diff --git a/examples/openthread/ot_sleepy_device/light_sleep/main/Kconfig.projbuild b/examples/openthread/ot_sleepy_device/light_sleep/main/Kconfig.projbuild new file mode 100644 index 0000000000..7ea03a4120 --- /dev/null +++ b/examples/openthread/ot_sleepy_device/light_sleep/main/Kconfig.projbuild @@ -0,0 +1,9 @@ +menu "OpenThread Sleepy Example" + + config OPENTHREAD_AUTO_START + bool 'Enable the automatic start mode.' + default False + help + If enabled, the Openthread Device will create or connect to thread network with pre-configured + network parameters automatically. Otherwise, user need to configure Thread via CLI command manually. +endmenu diff --git a/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c b/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c index 95b5bc2e1d..ea45794a26 100644 --- a/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c +++ b/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c @@ -20,6 +20,8 @@ #include "esp_event.h" #include "esp_log.h" #include "esp_openthread.h" +#include "esp_openthread_cli.h" +#include "esp_openthread_lock.h" #include "esp_openthread_netif_glue.h" #include "esp_ot_sleepy_device_config.h" #include "esp_vfs_eventfd.h" @@ -27,7 +29,6 @@ #include "nvs_flash.h" #include "openthread/logging.h" #include "openthread/thread.h" - #if CONFIG_ESP_SLEEP_DEBUG #include "esp_timer.h" #include "esp_sleep.h" @@ -45,6 +46,9 @@ #define TAG "ot_esp_power_save" +static esp_pm_lock_handle_t s_cli_pm_lock = NULL; + +#if CONFIG_OPENTHREAD_AUTO_START static void create_config_network(otInstance *instance) { otLinkModeConfig linkMode = { 0 }; @@ -62,7 +66,41 @@ static void create_config_network(otInstance *instance) ESP_LOGE(TAG, "Failed to set OpenThread linkmode."); abort(); } - ESP_ERROR_CHECK(esp_openthread_auto_start(NULL)); + + otOperationalDatasetTlvs dataset; + otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); + ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); +} +#endif // CONFIG_OPENTHREAD_AUTO_START + +static esp_err_t esp_openthread_sleep_device_init(void) +{ + esp_err_t ret = ESP_OK; + + ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "otcli", &s_cli_pm_lock); + if (ret == ESP_OK) { + esp_pm_lock_acquire(s_cli_pm_lock); + ESP_LOGI(TAG, "Successfully created CLI pm lock"); + } else { + if (s_cli_pm_lock != NULL) { + esp_pm_lock_delete(s_cli_pm_lock); + s_cli_pm_lock = NULL; + } + ESP_LOGI(TAG, " Failed to create CLI pm lock"); + } + return ret; +} + +static void process_state_change(otChangedFlags flags, void* context) +{ + otDeviceRole ot_device_role = otThreadGetDeviceRole(esp_openthread_get_instance()); + if(ot_device_role == OT_DEVICE_ROLE_CHILD) { + if (s_cli_pm_lock != NULL) { + esp_pm_lock_release(s_cli_pm_lock); + esp_pm_lock_delete(s_cli_pm_lock); + s_cli_pm_lock = NULL; + } + } } static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) @@ -88,6 +126,7 @@ static void print_sleep_flag(void *arg) static void ot_task_worker(void *aContext) { + otError ret; esp_openthread_platform_config_t config = { .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), @@ -97,17 +136,31 @@ static void ot_task_worker(void *aContext) // Initialize the OpenThread stack ESP_ERROR_CHECK(esp_openthread_init(&config)); + esp_openthread_lock_acquire(portMAX_DELAY); + ret = otSetStateChangedCallback(esp_openthread_get_instance(), process_state_change, esp_openthread_get_instance()); + esp_openthread_lock_release(); + if(ret != OT_ERROR_NONE) { + ESP_LOGE(TAG, "Failed to set state changed callback"); + } #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_AUTO_START create_config_network(esp_openthread_get_instance()); +#endif // CONFIG_OPENTHREAD_AUTO_START +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_create_task(); +#endif #if CONFIG_ESP_SLEEP_DEBUG esp_sleep_set_sleep_context(&s_sleep_ctx); esp_log_level_set(TAG, ESP_LOG_DEBUG); @@ -170,6 +223,6 @@ void app_main(void) ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); ESP_ERROR_CHECK(ot_power_save_init()); - + ESP_ERROR_CHECK(esp_openthread_sleep_device_init()); xTaskCreate(ot_task_worker, "ot_power_save_main", 4096, NULL, 5, NULL); } diff --git a/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults b/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults index 577b06837d..94a0b3e11a 100644 --- a/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults +++ b/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults @@ -21,6 +21,7 @@ CONFIG_MBEDTLS_ECJPAKE_C=y CONFIG_OPENTHREAD_ENABLED=y CONFIG_OPENTHREAD_BORDER_ROUTER=n CONFIG_OPENTHREAD_DNS64_CLIENT=y +CONFIG_OPENTHREAD_CLI=y # end of OpenThread # diff --git a/examples/openthread/pytest_otbr.py b/examples/openthread/pytest_otbr.py index 69d80fa10b..c307adc1c7 100644 --- a/examples/openthread/pytest_otbr.py +++ b/examples/openthread/pytest_otbr.py @@ -1,8 +1,6 @@ # SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Unlicense OR CC0-1.0 # !/usr/bin/env python3 - - import copy import os.path import re @@ -581,17 +579,14 @@ def test_ot_sleepy_device(dut: Tuple[IdfDut, IdfDut]) -> None: ocf.init_thread(leader) time.sleep(3) leader_para = ocf.thread_parameter('leader', '', '12', '7766554433221100', False) - leader_para.setnetworkname('OpenThread-ESP') - leader_para.setpanid('0x1234') - leader_para.setextpanid('dead00beef00cafe') - leader_para.setnetworkkey('aabbccddeeff00112233445566778899') - leader_para.setpskc('104810e2315100afd6bc9215a6bfac53') ocf.joinThreadNetwork(leader, leader_para) ocf.wait(leader, 5) - output = sleepy_device.expect(pexpect.TIMEOUT, timeout=5) - assert not bool(fail_info.search(str(output))) - ocf.clean_buffer(sleepy_device) - sleepy_device.serial.hard_reset() + dataset = ocf.getDataset(leader) + ocf.execute_command(sleepy_device, 'mode -') + ocf.execute_command(sleepy_device, 'pollperiod 3000') + ocf.execute_command(sleepy_device, 'dataset set active ' + dataset) + ocf.execute_command(sleepy_device, 'ifconfig up') + ocf.execute_command(sleepy_device, 'thread start') info = sleepy_device.expect(r'(.+)detached -> child', timeout=20)[1].decode(errors='replace') assert not bool(fail_info.search(str(info))) info = sleepy_device.expect(r'(.+)PMU_SLEEP_PD_TOP: True', timeout=10)[1].decode(errors='replace')