-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'backport/add_ot_radio_stats_enable_config_5_2' into 're…
…lease/v5.2' Backport/backport some openthread features(backportV5.2) See merge request espressif/esp-idf!26886
- Loading branch information
Showing
20 changed files
with
442 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
| Supported Targets | ESP32-H2 | | ||
| ----------------- | -------- | | ||
|
||
# OpenThread Sleepy Device Example | ||
|
||
The example demonstrates the Thread Sleepy End Device (SED), the device will enter [Deep Sleep mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/system/sleep_modes.html#sleep-modes) during idle state. | ||
|
||
This example is designed to address a specific deep sleep application scenario. First, it connects to the Thread network, and after 5 seconds when the state changes to CHILD, it enters deep sleep mode. There are two ways to wake up in this example: one is by using a 20-second periodic RTC timer, and the other is through GPIO input. Deep sleep is part of the upper-layer logic, and it's the user's responsibility to manage it in their own applications. If you need more wake-up methods, you can refer to the [Exapmle deep sleep](../../../system/deep_sleep/). Additionally, Espressif provides a stub for handling wake-ups, which allows for a quick check, and the user can decide whether to wake up or continue deep sleep in this stub, as explained in the [Example deep sleep stub](../../../system/deep_sleep_wake_stub). | ||
|
||
Note: Implementing a standard Thread Sleepy Device is recommended using the [Light Sleep example](../light_sleep). Deep sleep triggers a reboot, and the device needs to undergo a re-attach process to rejoin the network. This means additional packet interactions are necessary after each wake-up from deep sleep. It can be advantageous in reducing power consumption, especially when the device remains in a sleep state for extended periods, such as more than 30 minutes. | ||
## How to use example | ||
|
||
### Hardware Required | ||
|
||
* Prepare two 802.15.4 SoC development boards, one for an OpenThread Leader and the other one for an OpenThread Sleepy End Device (SED). | ||
* Connect the board using a USB cable for power supply and programming. | ||
|
||
## Configure the Openthread Dataset | ||
|
||
* 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 device. | ||
|
||
### Build and Flash | ||
|
||
Build the project and flash it to the board. Use the following command: `idf.py -p <PORT> erase-flash flash monitor`. | ||
|
||
### 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). | ||
|
||
``` | ||
I(281) OPENTHREAD:[I] Settings------: Read NetworkInfo {rloc:0x4001, extaddr:623954c9725869e6, role:child, mode:0x04, version:4, keyseq:0x0, ... | ||
I(291) OPENTHREAD:[I] Settings------: ... pid:0x3b33d767, mlecntr:0x3ba17, maccntr:0x3baa8, mliid:868f19ce8c3f6207} | ||
I(301) OPENTHREAD:[I] Settings------: Read ParentInfo {extaddr:3afe8db4802dc1aa, version:4} | ||
I (311) ot_esp_power_save: Wake up from timer. Time spent in deep sleep and boot: 20321ms | ||
I (331) ot_esp_power_save: Enabling timer wakeup, 20s | ||
I (331) OPENTHREAD: OpenThread attached to netif | ||
I(341) OPENTHREAD:[N] Mle-----------: Role disabled -> detached | ||
I (291) main_task: Returned from app_main() | ||
I (371) OT_STATE: netif up | ||
I(511) OPENTHREAD:[N] Mle-----------: Role detached -> child | ||
I (531) ot_esp_power_save: Start one-shot timer for 5s to enter the deep sleep | ||
I (5531) ot_esp_power_save: Enter deep sleep | ||
``` | ||
|
||
When the device enter deep sleep, GPIO9 also can wake up the device, you can push down the BOOT button then you can see the device wakes up: | ||
|
||
``` | ||
I(281) OPENTHREAD:[I] Settings------: Read NetworkInfo {rloc:0x4001, extaddr:623954c9725869e6, role:child, mode:0x04, version:4, keyseq:0x0, ... | ||
I(291) OPENTHREAD:[I] Settings------: ... pid:0x3b33d767, mlecntr:0x3d576, maccntr:0x3d609, mliid:868f19ce8c3f6207} | ||
I(301) OPENTHREAD:[I] Settings------: Read ParentInfo {extaddr:3afe8db4802dc1aa, version:4} | ||
I (321) ot_esp_power_save: Wake up from GPIO. Time spent in deep sleep and boot: 8470ms | ||
I (331) ot_esp_power_save: Enabling timer wakeup, 20s | ||
I (331) OPENTHREAD: OpenThread attached to netif | ||
I(341) OPENTHREAD:[N] Mle-----------: Role disabled -> detached | ||
I (291) main_task: Returned from app_main() | ||
I (371) OT_STATE: netif up | ||
I(511) OPENTHREAD:[N] Mle-----------: Role detached -> child | ||
I (531) ot_esp_power_save: Start one-shot timer for 5s to enter the deep sleep | ||
I (5531) ot_esp_power_save: Enter deep sleep | ||
``` | ||
|
||
During the deep sleep, a typical power consumption is shown below: | ||
![H2-deep-sleep-power-consumption](image/H2-deep-sleep-power-consumption.png) |
Binary file added
BIN
+90.4 KB
...penthread/ot_sleepy_device/deep_sleep/image/H2-deep-sleep-power-consumption.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
220 changes: 220 additions & 0 deletions
220
examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2023 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 <stdint.h> | ||
#include <stdio.h> | ||
#include <unistd.h> | ||
#include <string.h> | ||
#include "esp_err.h" | ||
#include "esp_event.h" | ||
#include "esp_log.h" | ||
#include "esp_sleep.h" | ||
#include "esp_timer.h" | ||
#include "esp_openthread.h" | ||
#include "esp_openthread_netif_glue.h" | ||
#include "esp_ot_sleepy_device_config.h" | ||
#include "esp_vfs_eventfd.h" | ||
#include "nvs_flash.h" | ||
#include "driver/rtc_io.h" | ||
#include "driver/uart.h" | ||
#include "openthread/logging.h" | ||
#include "openthread/thread.h" | ||
|
||
#if !SOC_IEEE802154_SUPPORTED | ||
#error "Openthread sleepy device is only supported for the SoCs which have IEEE 802.15.4 module" | ||
#endif | ||
|
||
#define TAG "ot_esp_power_save" | ||
|
||
static RTC_DATA_ATTR struct timeval s_sleep_enter_time; | ||
static esp_timer_handle_t s_oneshot_timer; | ||
|
||
static void create_config_network(otInstance *instance) | ||
{ | ||
otLinkModeConfig linkMode = { 0 }; | ||
|
||
linkMode.mRxOnWhenIdle = false; | ||
linkMode.mDeviceType = false; | ||
linkMode.mNetworkData = false; | ||
|
||
if (otLinkSetPollPeriod(instance, CONFIG_OPENTHREAD_NETWORK_POLLPERIOD_TIME) != OT_ERROR_NONE) { | ||
ESP_LOGE(TAG, "Failed to set OpenThread pollperiod."); | ||
abort(); | ||
} | ||
|
||
if (otThreadSetLinkMode(instance, linkMode) != OT_ERROR_NONE) { | ||
ESP_LOGE(TAG, "Failed to set OpenThread linkmode."); | ||
abort(); | ||
} | ||
ESP_ERROR_CHECK(esp_openthread_auto_start(NULL)); | ||
} | ||
|
||
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; | ||
} | ||
|
||
static void ot_state_change_callback(otChangedFlags changed_flags, void* ctx) | ||
{ | ||
OT_UNUSED_VARIABLE(ctx); | ||
static otDeviceRole s_previous_role = OT_DEVICE_ROLE_DISABLED; | ||
otInstance* instance = esp_openthread_get_instance(); | ||
if (!instance) { | ||
return; | ||
} | ||
otDeviceRole role = otThreadGetDeviceRole(instance); | ||
if (role == OT_DEVICE_ROLE_CHILD && s_previous_role != OT_DEVICE_ROLE_CHILD) { | ||
// Start the one-shot timer | ||
const int before_deep_sleep_time_sec = 5; | ||
ESP_LOGI(TAG, "Start one-shot timer for %ds to enter the deep sleep", before_deep_sleep_time_sec); | ||
ESP_ERROR_CHECK(esp_timer_start_once(s_oneshot_timer, before_deep_sleep_time_sec * 1000000)); | ||
} | ||
s_previous_role = role; | ||
} | ||
|
||
static void s_oneshot_timer_callback(void* arg) | ||
{ | ||
// Enter deep sleep | ||
ESP_LOGI(TAG, "Enter deep sleep"); | ||
gettimeofday(&s_sleep_enter_time, NULL); | ||
esp_deep_sleep_start(); | ||
} | ||
|
||
static void ot_deep_sleep_init(void) | ||
{ | ||
// Within this function, we print the reason for the wake-up and configure the method of waking up from deep sleep. | ||
// This example provides support for two wake-up sources from deep sleep: RTC timer and GPIO. | ||
|
||
// The one-shot timer will start when the device transitions to the CHILD state for the first time. | ||
// After a 5-second delay, the device will enter deep sleep. | ||
|
||
const esp_timer_create_args_t s_oneshot_timer_args = { | ||
.callback = &s_oneshot_timer_callback, | ||
.name = "one-shot" | ||
}; | ||
|
||
ESP_ERROR_CHECK(esp_timer_create(&s_oneshot_timer_args, &s_oneshot_timer)); | ||
|
||
// Print the wake-up reason: | ||
struct timeval now; | ||
gettimeofday(&now, NULL); | ||
int sleep_time_ms = (now.tv_sec - s_sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - s_sleep_enter_time.tv_usec) / 1000; | ||
esp_sleep_wakeup_cause_t wake_up_cause = esp_sleep_get_wakeup_cause(); | ||
switch (wake_up_cause) { | ||
case ESP_SLEEP_WAKEUP_TIMER: { | ||
ESP_LOGI(TAG, "Wake up from timer. Time spent in deep sleep and boot: %dms", sleep_time_ms); | ||
break; | ||
} | ||
case ESP_SLEEP_WAKEUP_EXT1: { | ||
ESP_LOGI(TAG, "Wake up from GPIO. Time spent in deep sleep and boot: %dms", sleep_time_ms); | ||
break; | ||
} | ||
case ESP_SLEEP_WAKEUP_UNDEFINED: | ||
default: | ||
ESP_LOGI(TAG, "Not a deep sleep reset"); | ||
break; | ||
} | ||
|
||
// Set the methods of how to wake up: | ||
// 1. RTC timer waking-up | ||
const int wakeup_time_sec = 20; | ||
ESP_LOGI(TAG, "Enabling timer wakeup, %ds\n", wakeup_time_sec); | ||
ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000)); | ||
|
||
// 2. GPIO waking-up | ||
#if CONFIG_IDF_TARGET_ESP32C6 | ||
// For ESP32C6 boards, RTCIO only supports GPIO0~GPIO7 | ||
// GPIO7 pull down to wake up | ||
const int gpio_wakeup_pin = 7; | ||
#elif CONFIG_IDF_TARGET_ESP32H2 | ||
// You can wake up by pulling down GPIO9. On ESP32H2 development boards, the BOOT button is connected to GPIO9. | ||
// You can use the BOOT button to wake up the boards directly. | ||
const int gpio_wakeup_pin = 9; | ||
#endif | ||
const uint64_t gpio_wakeup_pin_mask = 1ULL << gpio_wakeup_pin; | ||
// The configuration mode depends on your hardware design. | ||
// Since the BOOT button is connected to a pull-up resistor, the wake-up mode is configured as LOW. | ||
const uint64_t ext_wakeup_mode = 0 << gpio_wakeup_pin; | ||
ESP_ERROR_CHECK(esp_sleep_enable_ext1_wakeup_with_level_mask(gpio_wakeup_pin_mask, ext_wakeup_mode)); | ||
|
||
// Also these two GPIO configurations are also depended on the hardware design. | ||
// The BOOT button is connected to the pull-up resistor, so enable the pull-up mode and disable the pull-down mode. | ||
|
||
// Notice: if these GPIO configurations do not match the hardware design, the deep sleep module will enable the GPIO hold | ||
// feature to hold the GPIO voltage when enter the sleep, which will ensure the board be waked up by GPIO. But it will cause | ||
// 3~4 times power consumption increasing during sleep. | ||
ESP_ERROR_CHECK(gpio_pullup_en(gpio_wakeup_pin)); | ||
ESP_ERROR_CHECK(gpio_pulldown_dis(gpio_wakeup_pin)); | ||
} | ||
|
||
|
||
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(), | ||
}; | ||
|
||
// Initialize the OpenThread stack | ||
ESP_ERROR_CHECK(esp_openthread_init(&config)); | ||
|
||
ot_deep_sleep_init(); | ||
|
||
#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC | ||
// The OpenThread log level directly matches ESP log level | ||
(void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); | ||
#endif | ||
esp_netif_t *openthread_netif; | ||
// Initialize the esp_netif bindings | ||
openthread_netif = init_openthread_netif(&config); | ||
esp_netif_set_default_netif(openthread_netif); | ||
otSetStateChangedCallback(esp_openthread_get_instance(), ot_state_change_callback, NULL); | ||
|
||
create_config_network(esp_openthread_get_instance()); | ||
|
||
// 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 | ||
// * ot task queue | ||
// * radio driver | ||
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(ot_task_worker, "ot_power_save_main", 4096, NULL, 5, NULL); | ||
} |
53 changes: 53 additions & 0 deletions
53
examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device_config.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2023 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 | ||
|
||
#include "esp_openthread_types.h" | ||
|
||
# define CONFIG_OPENTHREAD_NETWORK_POLLPERIOD_TIME 30000 | ||
|
||
#if SOC_IEEE802154_SUPPORTED | ||
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ | ||
{ \ | ||
.radio_mode = RADIO_MODE_NATIVE, \ | ||
} | ||
#endif | ||
|
||
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ | ||
{ \ | ||
.host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ | ||
.host_uart_config = { \ | ||
.port = 0, \ | ||
.uart_config = \ | ||
{ \ | ||
.baud_rate = 115200, \ | ||
.data_bits = UART_DATA_8_BITS, \ | ||
.parity = UART_PARITY_DISABLE, \ | ||
.stop_bits = UART_STOP_BITS_1, \ | ||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ | ||
.rx_flow_ctrl_thresh = 0, \ | ||
.source_clk = UART_SCLK_DEFAULT, \ | ||
}, \ | ||
.rx_pin = UART_PIN_NO_CHANGE, \ | ||
.tx_pin = UART_PIN_NO_CHANGE, \ | ||
}, \ | ||
} | ||
|
||
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ | ||
{ \ | ||
.storage_partition_name = "nvs", \ | ||
.netif_queue_size = 10, \ | ||
.task_queue_size = 10, \ | ||
} |
File renamed without changes.
Oops, something went wrong.