ieee802154: support light sleep

This commit is contained in:
xiaqilin
2023-05-25 18:18:03 +08:00
committed by BOT
parent f30a420a74
commit dc4b3f24bc
22 changed files with 699 additions and 12 deletions

View File

@@ -41,6 +41,14 @@ examples/openthread/ot_cli:
reason: only test on esp32h2
<<: *openthread_dependencies
examples/openthread/ot_power_save:
enable:
- if: IDF_TARGET == "esp32c6"
disable_test:
- if: IDF_TARGET in ["esp32h2", "esp32c6"]
temporary: true
reason: No support # TO-DO: TZ-134
examples/openthread/ot_rcp:
enable:
- if: IDF_TARGET in ["esp32h2", "esp32c6"]

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_power_save](ot_power_save) is an OpenThread Power save example, it supports 802.15.4 radio light sleep. It runs on an 802.15.4 SoC like ESP32-C6.

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_power_save)

View File

@@ -0,0 +1,46 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |
# OpenThread Power Save Example
The example demonstrates the OpenThread Power Save based on light sleep, which reduces power consumption by turning off RF, PHY, BB, and IEEE802154 MAC during the sleep state.
## How to use example
### Hardware Required
* Prepare an ESP32-C6 development board as an OpenThread Sleepy End Device (SED).
* Connect the board using a USB cable for power supply and programming.
* Choose another ESP32-C6 as the OpenThread Leader.
## Configure the Openthread Dataset
* Run [ot_cli](../ot_cli/) on another ESP32-C6 device to create openthread dataset configuration and start an openthread network as the leader.
* Configure the Openthread dataset (`CONFIG_OPENTHREAD_NETWORK_DATASET`) in [ot_power_save](../ot_power_save/main/esp_ot_power_save_config.h), ensuring that the device's dataset matches the dataset of the leader.
## Erase the ot_storage
If desired, erase the ot_storage by running `idf.py -p <PORT> erase-flash` before flashing the board to remove previous examples or other project data.
### Build and Flash
Build the project and flash it to the board. Use the following command: `idf.py -p <PORT> build 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_power_save.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,206 @@
/*
* 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_power_save_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 "Power save is only supported for the SoCs which have IEEE 802.15.4 module"
#endif
#define TAG "ot_esp_power_save"
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)
{
int num_char = strlen(hex_string);
if (num_char % 2 == true) {
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 num_char/2;
}
static void create_config_network(otInstance *instance)
{
otLinkModeConfig linkMode;
uint16_t dataset_str_len = strlen(CONFIG_OPENTHREAD_NETWORK_DATASET);
otOperationalDatasetTlvs datasetTlvs;
memset(&linkMode, 0, sizeof(otLinkModeConfig));
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();
}
if (dataset_str_len > (OT_OPERATIONAL_DATASET_MAX_LENGTH * 2)) {
ESP_LOGE(TAG, "dataset length error.");
abort();
}
datasetTlvs.mLength = hex_string_to_binary(CONFIG_OPENTHREAD_NETWORK_DATASET, datasetTlvs.mTlvs);
if (!datasetTlvs.mLength) {
ESP_LOGE(TAG, "Failed convert configured dataset");
abort();
}
if (otDatasetSetActiveTlvs(instance, &datasetTlvs) != OT_ERROR_NONE) {
ESP_LOGE(TAG, "Failed to set OpenThread otDatasetSetActiveTlvs.");
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();
}
}
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,54 @@
/*
* 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_DATASET "0e080000000000010000000300000b35060004001fffe002084c14b4d26855fcd00708fdf7e918eb62e2a905107ca0e75a6ead4b960cfe073386943605030f4f70656e5468726561642d616631360102af1604102b9084b26c9a7d10a1a729bfc2e84ea00c0402a0f7f8"
# 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 = "ot_storage", \
.netif_queue_size = 10, \
.task_queue_size = 10, \
}

View File

@@ -0,0 +1,6 @@
# 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,
ot_storage, data, 0x3a, , 0x2000,
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,
6 ot_storage, data, 0x3a, , 0x2000,

View File

@@ -0,0 +1,53 @@
CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2048
#
# 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
#
# ESP32H2-TODO: JIRA TZ-127, enable HW acceleration.
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