Merge branch 'backport/openthread_light_sleep' into 'release/v5.1'

Backportv5.1/openthread light sleep

See merge request espressif/esp-idf!24736
This commit is contained in:
Jiang Jiang Jian 2023-07-13 10:20:38 +08:00
commit a300e79589
27 changed files with 536 additions and 22 deletions

View File

@ -42,6 +42,7 @@ extern "C" {
#define REGDMA_SYSTIMER_LINK(_pri) ((0x14 << 8) | _pri)
#define REGDMA_BLE_MAC_LINK(_pri) ((0x15 << 8) | _pri)
#define REGDMA_MODEM_BT_BB_LINK(_pri) ((0x16 << 8) | _pri)
#define REGDMA_MODEM_IEEE802154_LINK(_pri) ((0x17 << 8) | _pri)
#define REGDMA_MODEM_FE_LINK(_pri) ((0xFF << 8) | _pri)
typedef enum {

View File

@ -32,9 +32,8 @@ typedef enum sleep_retention_module_bitmap {
SLEEP_RETENTION_MODULE_WIFI_MAC = BIT(10),
SLEEP_RETENTION_MODULE_WIFI_BB = BIT(11),
SLEEP_RETENTION_MODULE_BLE_MAC = BIT(12),
SLEEP_RETENTION_MODULE_BLE_BB = BIT(13),
SLEEP_RETENTION_MODULE_BT_BB = BIT(13),
SLEEP_RETENTION_MODULE_802154_MAC = BIT(14),
SLEEP_RETENTION_MODULE_802154_BB = BIT(15),
/* digital peripheral module, which includes Interrupt Matrix, HP_SYSTEM,
* TEE, APM, UART, Timer Group, IOMUX, SPIMEM, SysTimer, etc.. */

View File

@ -81,21 +81,21 @@ bool IRAM_ATTR clock_domain_pd_allowed(void)
const uint32_t modules = sleep_retention_get_modules();
const uint32_t mask = (const uint32_t) (
SLEEP_RETENTION_MODULE_CLOCK_SYSTEM
#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE
#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
| SLEEP_RETENTION_MODULE_CLOCK_MODEM
#endif
);
return ((modules & mask) == mask);
}
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP || CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP || CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
ESP_SYSTEM_INIT_FN(sleep_clock_startup_init, BIT(0), 106)
{
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
sleep_clock_system_retention_init();
#endif
#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE
#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
sleep_clock_modem_retention_init();
#endif
return ESP_OK;

View File

@ -279,9 +279,9 @@ bool IRAM_ATTR modem_domain_pd_allowed(void)
const uint32_t mask_wifi = (const uint32_t) (SLEEP_RETENTION_MODULE_WIFI_MAC |
SLEEP_RETENTION_MODULE_WIFI_BB);
const uint32_t mask_ble = (const uint32_t) (SLEEP_RETENTION_MODULE_BLE_MAC |
SLEEP_RETENTION_MODULE_BLE_BB);
SLEEP_RETENTION_MODULE_BT_BB);
const uint32_t mask_154 = (const uint32_t) (SLEEP_RETENTION_MODULE_802154_MAC |
SLEEP_RETENTION_MODULE_802154_BB);
SLEEP_RETENTION_MODULE_BT_BB);
return (((modules & mask_wifi) == mask_wifi) ||
((modules & mask_ble) == mask_ble) ||
((modules & mask_154) == mask_154));

View File

@ -21,7 +21,6 @@
#include "sdkconfig.h"
#include "esp_pmu.h"
static __attribute__((unused)) const char *TAG = "sleep";
/**

View File

@ -35,7 +35,7 @@ static esp_err_t btbb_sleep_retention_init(void)
[1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_BT_BB_LINK(0x01), BB_PART_1_ADDR, BB_PART_1_ADDR, BB_PART_1_SIZE, 0, 0), .owner = BTBB_LINK_OWNER },
[2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_BT_BB_LINK(0x02), BB_PART_2_ADDR, BB_PART_2_ADDR, BB_PART_2_SIZE, 0, 0), .owner = BTBB_LINK_OWNER }
};
esp_err_t err = sleep_retention_entries_create(btbb_regs_retention, ARRAY_SIZE(btbb_regs_retention), REGDMA_LINK_PRI_5, SLEEP_RETENTION_MODULE_BLE_BB);
esp_err_t err = sleep_retention_entries_create(btbb_regs_retention, ARRAY_SIZE(btbb_regs_retention), REGDMA_LINK_PRI_5, SLEEP_RETENTION_MODULE_BT_BB);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for btbb retention");
ESP_LOGI(TAG, "btbb sleep retention initialization");
return ESP_OK;
@ -43,7 +43,7 @@ static esp_err_t btbb_sleep_retention_init(void)
static void btbb_sleep_retention_deinit(void)
{
sleep_retention_entries_destroy(SLEEP_RETENTION_MODULE_BLE_BB);
sleep_retention_entries_destroy(SLEEP_RETENTION_MODULE_BT_BB);
}
#endif // SOC_PM_MODEM_RETENTION_BY_REGDMA && CONFIG_FREERTOS_USE_TICKLESS_IDLE

View File

@ -74,4 +74,12 @@ menu "IEEE 802.15.4"
Enabling this option increases throughput by ~5% at the expense of ~2.1k
IRAM code size increase.
config IEEE802154_SLEEP_ENABLE
# Todo: Remove when support safe power-down of the power domain (IDF-7317)
bool "Enable IEEE802154 light sleep"
depends on PM_ENABLE && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
default n
help
Enabling this option allows the IEEE802.15.4 module to be powered down during automatic light sleep,
which reduces current consumption.
endmenu # IEEE 802.15.4

View File

@ -24,6 +24,13 @@
#include "esp_ieee802154_timer.h"
#include "hal/ieee802154_ll.h"
#include "esp_attr.h"
#include "esp_phy_init.h"
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
#include "esp_pm.h"
#include "esp_private/esp_clk.h"
#include "esp_private/sleep_retention.h"
#endif
#define CCA_DETECTION_TIME 8
@ -38,6 +45,8 @@ static uint8_t s_enh_ack_frame[128];
static uint8_t s_recent_rx_frame_info_index;
static portMUX_TYPE s_ieee802154_spinlock = portMUX_INITIALIZER_UNLOCKED;
static esp_err_t ieee802154_sleep_init(void);
static IRAM_ATTR void event_end_process(void)
{
ieee802154_etm_channel_clear(IEEE802154_ETM_CHANNEL0);
@ -193,7 +202,11 @@ IEEE802154_STATIC bool stop_current_operation(void)
break;
case IEEE802154_STATE_IDLE:
// do nothing
ieee802154_ll_set_cmd(IEEE802154_CMD_STOP);
break;
case IEEE802154_STATE_SLEEP:
// Do nothing
break;
case IEEE802154_STATE_RX:
@ -580,10 +593,12 @@ IEEE802154_STATIC IRAM_ATTR void ieee802154_exit_critical(void)
void ieee802154_enable(void)
{
modem_clock_module_enable(ieee802154_periph.module);
s_ieee802154_state = IEEE802154_STATE_IDLE;
}
void ieee802154_disable(void)
{
modem_clock_module_disable(ieee802154_periph.module);
s_ieee802154_state = IEEE802154_STATE_DISABLE;
}
@ -616,12 +631,13 @@ esp_err_t ieee802154_mac_init(void)
#endif
memset(s_rx_frame, 0, sizeof(s_rx_frame));
s_ieee802154_state = IEEE802154_STATE_IDLE;
// TODO: Add flags for IEEE802154 ISR allocating. TZ-102
ret = esp_intr_alloc(ieee802154_periph.irq_id, 0, ieee802154_isr, NULL, NULL);
ESP_RETURN_ON_FALSE(ret == ESP_OK, ESP_FAIL, IEEE802154_TAG, "IEEE802154 MAC init failed");
ESP_RETURN_ON_FALSE(ieee802154_sleep_init() == ESP_OK, ESP_FAIL, IEEE802154_TAG, "IEEE802154 MAC sleep init failed");
return ret;
}
@ -743,12 +759,49 @@ esp_err_t ieee802154_receive_at(uint32_t time)
return ESP_OK;
}
static esp_err_t ieee802154_sleep_init(void)
{
esp_err_t err = ESP_OK;
#if SOC_PM_MODEM_RETENTION_BY_REGDMA && CONFIG_FREERTOS_USE_TICKLESS_IDLE
#define N_REGS_IEEE802154() (((IEEE802154_MAC_DATE_REG - IEEE802154_REG_BASE) / 4) + 1)
const static sleep_retention_entries_config_t ieee802154_mac_regs_retention[] = {
[0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_IEEE802154_LINK(0x00), IEEE802154_REG_BASE, IEEE802154_REG_BASE, N_REGS_IEEE802154(), 0, 0), .owner = ENTRY(3) },
};
err = sleep_retention_entries_create(ieee802154_mac_regs_retention, ARRAY_SIZE(ieee802154_mac_regs_retention), REGDMA_LINK_PRI_7, SLEEP_RETENTION_MODULE_802154_MAC);
ESP_RETURN_ON_ERROR(err, IEEE802154_TAG, "failed to allocate memory for ieee802154 mac retention");
ESP_LOGI(IEEE802154_TAG, "ieee802154 mac sleep retention initialization");
#endif
return err;
}
IRAM_ATTR void ieee802154_enter_sleep(void)
{
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
esp_phy_disable();
#if SOC_PM_RETENTION_HAS_CLOCK_BUG
sleep_retention_do_extra_retention(true);// backup
#endif
ieee802154_disable(); // IEEE802154 CLOCK Disable
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
}
IRAM_ATTR void ieee802154_wakeup(void)
{
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
ieee802154_enable(); // IEEE802154 CLOCK Enable
#if SOC_PM_RETENTION_HAS_CLOCK_BUG
sleep_retention_do_extra_retention(false);// restore
#endif
esp_phy_enable();
#endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE
}
esp_err_t ieee802154_sleep(void)
{
ieee802154_enter_critical();
stop_current_operation();
s_ieee802154_state = IEEE802154_STATE_IDLE;
s_ieee802154_state = IEEE802154_STATE_SLEEP;
ieee802154_exit_critical();
return ESP_OK;

View File

@ -280,6 +280,9 @@ esp_ieee802154_state_t esp_ieee802154_get_state(void)
return ESP_IEEE802154_RADIO_DISABLE;
case IEEE802154_STATE_IDLE:
return ESP_IEEE802154_RADIO_IDLE;
case IEEE802154_STATE_SLEEP:
return ESP_IEEE802154_RADIO_SLEEP;
case IEEE802154_STATE_RX:
@ -331,6 +334,16 @@ uint8_t esp_ieee802154_get_recent_lqi(void)
return ieee802154_get_recent_lqi();
}
void esp_ieee802154_enter_sleep(void)
{
ieee802154_enter_sleep();
}
void esp_ieee802154_wakeup(void)
{
ieee802154_wakeup();
}
__attribute__((weak)) void esp_ieee802154_receive_done(uint8_t *data, esp_ieee802154_frame_info_t *frame_info)
{

View File

@ -111,6 +111,16 @@ esp_ieee802154_state_t esp_ieee802154_get_state(void);
*/
esp_err_t esp_ieee802154_sleep(void);
/**
* @brief The IEEE 802.15.4 enter sleep.
*/
void esp_ieee802154_enter_sleep(void);
/**
* @brief The IEEE 802.15.4 wakeup.
*/
void esp_ieee802154_wakeup(void);
/**
* @brief Set the IEEE 802.15.4 Radio to receive state.
*

View File

@ -18,6 +18,7 @@ extern "C" {
*/
typedef enum {
ESP_IEEE802154_RADIO_DISABLE, /*!< Radio not up */
ESP_IEEE802154_RADIO_IDLE, /*!< Radio in the idle state */
ESP_IEEE802154_RADIO_SLEEP, /*!< Radio in the sleep state */
ESP_IEEE802154_RADIO_RECEIVE, /*!< Radio in the receive state */
ESP_IEEE802154_RADIO_TRANSMIT, /*!< Radio in the transmit state */

View File

@ -31,6 +31,7 @@ extern "C" {
typedef enum {
IEEE802154_STATE_DISABLE, /*!< IEEE802154 radio state disable */
IEEE802154_STATE_IDLE, /*!< IEEE802154 radio state idle */
IEEE802154_STATE_SLEEP, /*!< IEEE802154 radio state sleep */
IEEE802154_STATE_RX, /*!< IEEE802154 radio state rx */
IEEE802154_STATE_TX_ACK, /*!< IEEE802154 radio state tx ack */
IEEE802154_STATE_TX_ENH_ACK, /*!< IEEE802154 radio state tx enh-ack */
@ -177,6 +178,18 @@ uint8_t ieee802154_get_recent_lqi(void);
*/
ieee802154_state_t ieee802154_get_state(void);
/**
* @brief The IEEE 802.15.4 enter sleep.
*
*/
void ieee802154_enter_sleep(void);
/**
* @brief The IEEE 802.15.4 wakeup.
*
*/
void ieee802154_wakeup(void);
/** The following three functions are only used for internal test. **/
/**
* @brief The clear channel assessment done.

View File

@ -129,17 +129,17 @@ def test_based_txrx(dut: Tuple[IdfDut, IdfDut]) -> None:
receive.expect('RX Start', timeout=10)
transmit.expect('ieee802154>', timeout=10)
transmit.write('tx -l 10')
transmit.expect('tx sfd done, Radio state: 3', timeout=10)
transmit.expect('tx sfd done, Radio state: 4', timeout=10)
transmit.expect('Tx Done 10 bytes', timeout=10)
transmit.expect('00 01 02 03 04 05 06 07', timeout=10)
transmit.expect('08 09 00 00 00 00 00 00', timeout=10)
receive.expect('rx sfd done, Radio state: 2', timeout=10)
receive.expect('rx sfd done, Radio state: 3', timeout=10)
receive.expect('Rx Done 10 bytes', timeout=10)
receive.expect('00 01 02 03 04 05 06 07', timeout=10)
receive.write('rx -r 0')
receive.expect('radio exit receive mode', timeout=10)
transmit.write('tx -l 10')
transmit.expect('tx sfd done, Radio state: 3', timeout=10)
transmit.expect('tx sfd done, Radio state: 4', timeout=10)
transmit.expect('Tx Done 10 bytes', timeout=10)
transmit.expect('00 01 02 03 04 05 06 07', timeout=10)
transmit.expect('08 09 00 00 00 00 00 00', timeout=10)
@ -344,13 +344,13 @@ def test_based_autoack(dut: Tuple[IdfDut, IdfDut]) -> None:
receive.expect('RX Start', timeout=10)
transmit.write('tx 0x20 0x88 0x00 0x0A 0x28 0xDB 0x6F 0xBC 0x94 0x5A 0x43 0x68 0x02 0xaa 0x15 0x30 0x01 0x02')
transmit.expect('tx sfd done, Radio state: 3', timeout=10)
transmit.expect('rx sfd done, Radio state: 3', timeout=10)
transmit.expect('tx sfd done, Radio state: 4', timeout=10)
transmit.expect('rx sfd done, Radio state: 4', timeout=10)
transmit.expect('Tx Done 18 bytes', timeout=10)
transmit.expect('20 88 00 0a 28 db 6f bc', timeout=10)
transmit.expect('94 5a 43 68 02 aa 15 30', timeout=10)
receive.expect('rx sfd done, Radio state: 2', timeout=10)
receive.expect('rx sfd done, Radio state: 3', timeout=10)
receive.expect('Rx Done 18 bytes', timeout=10)
receive.expect('20 88 00 0a 28 db 6f bc', timeout=10)
receive.expect('94 5a 43 68 02 aa 15 30', timeout=10)
@ -573,7 +573,7 @@ def test_based_transmit_failed(dut: IdfDut) -> None:
transmit.write('tx -l 10 -C')
transmit.expect('the Frame Transmission failed, Failure reason: 1', timeout=10)
transmit.write('tx -l 10')
transmit.expect('tx sfd done, Radio state: 3', timeout=10)
transmit.expect('tx sfd done, Radio state: 4', timeout=10)
transmit.expect('Tx Done 10 bytes', timeout=10)
transmit.expect('00 01 02 03 04 05 06 07', timeout=10)
transmit.expect('08 09 00 00 00 00 00 00', timeout=10)

View File

@ -140,6 +140,11 @@ if(CONFIG_OPENTHREAD_ENABLED)
"src/esp_openthread_dns64.c")
endif()
if(NOT CONFIG_FREERTOS_USE_TICKLESS_IDLE)
list(APPEND exclude_srcs
"src/port/esp_openthread_sleep.c")
endif()
if(CONFIG_OPENTHREAD_FTD)
set(device_type "OPENTHREAD_FTD=1")
elseif(CONFIG_OPENTHREAD_MTD)

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef CONFIG_FREERTOS_USE_TICKLESS_IDLE
/**
* @brief This function initializes the OpenThread sleep.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failure
*
*/
esp_err_t esp_openthread_sleep_init(void);
/**
* @brief This function performs the OpenThread sleep process.
*
*/
void esp_openthread_sleep_process(void);
/**
* @brief This function performs the OpenThread wakeup process.
*
*/
void esp_openthread_wakeup_process(void);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,6 +12,7 @@
#include "esp_openthread_lock.h"
#include "esp_openthread_platform.h"
#include "esp_openthread_state.h"
#include "esp_openthread_sleep.h"
#include "esp_openthread_task_queue.h"
#include "esp_openthread_types.h"
#include "freertos/FreeRTOS.h"
@ -57,6 +58,10 @@ static size_t hex_string_to_binary(const char *hex_string, uint8_t *buf, size_t
esp_err_t esp_openthread_init(const esp_openthread_platform_config_t *config)
{
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
ESP_RETURN_ON_ERROR(esp_openthread_sleep_init(), OT_PLAT_LOG_TAG,
"Failed to initialize OpenThread esp pm_lock");
#endif
ESP_RETURN_ON_ERROR(esp_openthread_platform_init(config), OT_PLAT_LOG_TAG,
"Failed to initialize OpenThread platform driver");
esp_openthread_lock_acquire(portMAX_DELAY);
@ -159,11 +164,17 @@ esp_err_t esp_openthread_launch_mainloop(void)
mainloop.timeout.tv_sec = 0;
mainloop.timeout.tv_usec = 0;
}
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
esp_openthread_sleep_process();
#endif
esp_openthread_lock_release();
if (select(mainloop.max_fd + 1, &mainloop.read_fds, &mainloop.write_fds, &mainloop.error_fds,
&mainloop.timeout) >= 0) {
esp_openthread_lock_acquire(portMAX_DELAY);
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
esp_openthread_wakeup_process();
#endif
error = esp_openthread_platform_process(instance, &mainloop);
while (otTaskletsArePending(instance)) {
otTaskletsProcess(instance);

View File

@ -273,7 +273,7 @@ otError otPlatRadioEnable(otInstance *aInstance)
otError otPlatRadioDisable(otInstance *aInstance)
{
esp_ieee802154_disable();
// radio will be disabled in esp_openthread_radio_deinit()
return OT_ERROR_NONE;
}

View File

@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
#include "esp_log.h"
#include "esp_check.h"
#include "esp_ieee802154.h"
#include "esp_pm.h"
static esp_pm_lock_handle_t s_pm_lock = NULL;
static const char* TAG = "esp openthread sleep";
static bool s_ot_sleep = false;
esp_err_t esp_openthread_sleep_init(void)
{
esp_err_t err = ESP_OK;
err = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "ieee802154", &s_pm_lock);
if (err == ESP_OK) {
esp_pm_lock_acquire(s_pm_lock);
ESP_LOGI(TAG, "Enable ieee802154 light sleep, the wake up source is ESP timer");
} else {
if (s_pm_lock != NULL) {
esp_pm_lock_delete(s_pm_lock);
s_pm_lock = NULL;
}
}
return err;
}
void esp_openthread_sleep_process(void)
{
if (esp_ieee802154_get_state() == ESP_IEEE802154_RADIO_SLEEP) {
esp_ieee802154_enter_sleep();
esp_pm_lock_release(s_pm_lock);
s_ot_sleep = true;
}
}
void esp_openthread_wakeup_process(void)
{
if (s_ot_sleep) {
esp_pm_lock_acquire(s_pm_lock);
esp_ieee802154_wakeup();
s_ot_sleep = false;
}
}
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE

View File

@ -25,3 +25,11 @@ examples/openthread/ot_rcp:
- if: IDF_TARGET == "esp32h2"
temporary: true
reason: only test on esp32c6
examples/openthread/ot_sleepy_device:
enable:
- if: IDF_TARGET == "esp32c6"
disable_test:
- if: IDF_TARGET in ["esp32h2", "esp32c6"]
temporary: true
reason: No support # TO-DO: TZ-134

View File

@ -11,3 +11,5 @@ In this folder, it contains following OpenThread examples:
* [ot_rcp](ot_rcp) is an [OpenThread RCP](https://openthread.io/platforms/co-processor) example. It runs on an 802.15.4 SoC like ESP32-H2, to extend 802.15.4 radio.
* [ot_br](ot_br) is an [OpenThread Border Router](https://openthread.io/guides/border-router) example. It runs on a Wi-Fi SoC such as ESP32, ESP32-C3 and ESP32-S3. It needs an 802.15.4 SoC like ESP32-H2 running [ot_rcp](ot_rcp) example to provide 802.15.4 radio.
* [ot_sleepy_device](ot_sleepy_device) is an OpenThread sleepy device example, it supports 802.15.4 radio light sleep. It runs on an 802.15.4 SoC.

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ot_sleepy_device)

View File

@ -0,0 +1,40 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |
# OpenThread Sleepy Device Example
The example demonstrates the Thread Sleepy End Device (SED), the device will enter [Light Sleep mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/system/sleep_modes.html#sleep-modes) during idle state.
## How to use example
### Hardware Required
* 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 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.
### 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) 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
```

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "esp_ot_sleepy_device.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,140 @@
/*
* 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_openthread.h"
#include "esp_openthread_netif_glue.h"
#include "esp_ot_sleepy_device_config.h"
#include "esp_vfs_eventfd.h"
#include "driver/uart.h"
#include "nvs_flash.h"
#include "openthread/logging.h"
#include "openthread/thread.h"
#ifdef CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
#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 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_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));
#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);
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);
}
static esp_err_t ot_power_save_init(void)
{
esp_err_t rc = ESP_OK;
#ifdef CONFIG_PM_ENABLE
int cur_cpu_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
esp_pm_config_t pm_config = {
.max_freq_mhz = cur_cpu_freq_mhz,
.min_freq_mhz = cur_cpu_freq_mhz,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
.light_sleep_enable = true
#endif
};
rc = esp_pm_configure(&pm_config);
#endif
return rc;
}
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));
ESP_ERROR_CHECK(ot_power_save_init());
xTaskCreate(ot_task_worker, "ot_power_save_main", 4096, NULL, 5, NULL);
}

View File

@ -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 3000
#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, \
}

View File

@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x120000,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 0x120000,

View File

@ -0,0 +1,51 @@
#
# Partition Table
#
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
# end of Partition Table
#
# mbedTLS
#
# TODO: Re-enable HW acceleration when HW AES support pm_lock (IDF-7704)
CONFIG_MBEDTLS_HARDWARE_AES=n
CONFIG_MBEDTLS_HARDWARE_MPI=n
CONFIG_MBEDTLS_HARDWARE_SHA=n
CONFIG_MBEDTLS_CMAC_C=y
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y
CONFIG_MBEDTLS_ECJPAKE_C=y
# end of mbedTLS
#
# OpenThread
#
CONFIG_OPENTHREAD_ENABLED=y
CONFIG_OPENTHREAD_BORDER_ROUTER=n
CONFIG_OPENTHREAD_DNS64_CLIENT=y
# end of OpenThread
#
# lwIP
#
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
CONFIG_LWIP_IPV6_NUM_ADDRESSES=8
CONFIG_LWIP_MULTICAST_PING=y
# end of lwIP
#
# IEEE 802.15.4
#
CONFIG_IEEE802154_ENABLED=y
# end of IEEE 802.15.4
#
# light sleep
#
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
CONFIG_IEEE802154_SLEEP_ENABLE=y
# end of light sleep