From a7bacfd671f404ca4beacf1ceab73092bae5d6b2 Mon Sep 17 00:00:00 2001 From: imi415 Date: Tue, 8 Jun 2021 00:54:03 +0800 Subject: [PATCH] Added automatic light sleep. --- main/CMakeLists.txt | 1 + main/Kconfig.projbuild | 85 ++++++++++++++++++++++++++++++++++++++++ main/app_lib_epd_impl.c | 30 ++++++++++++-- main/app_lib_pm_impl.c | 14 +++++++ main/app_lib_pm_impl.h | 6 +++ main/app_lib_wifi_impl.c | 25 +++++++++++- main/app_lib_wifi_impl.h | 2 + main/app_task.h | 20 ++++++++++ main/app_task_battery.c | 2 +- main/app_task_epd.c | 22 ++++++++++- main/app_task_led.c | 8 +++- main/main.c | 36 +++++++++++------ 12 files changed, 232 insertions(+), 19 deletions(-) create mode 100644 main/app_lib_pm_impl.c create mode 100644 main/app_lib_pm_impl.h create mode 100644 main/app_task.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index a95e223..9f8d03b 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -8,6 +8,7 @@ idf_component_register(SRCS "app_lib_epd_impl.c" "app_lib_wifi_impl.c" "app_lib_fs_impl.c" + "app_lib_pm_impl.c" "lib/gdew042t2_epd.c" "lib/ltc2941_battery.c" "lib/sht35_dht.c" diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 8b9e93e..2852a90 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -17,4 +17,89 @@ menu "Application Specific Configuration" default 5 help Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. + + config APP_WIFI_LISTEN_INTERVAL + int "WiFi listen interval" + default 3 + help + Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. + For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen + to beacon is 300 ms. + + choice APP_POWER_SAVE_MODE + prompt "power save mode" + default APP_POWER_SAVE_MIN_MODEM + help + Power save mode for the esp32 to use. Modem sleep mode includes minimum and maximum power save modes. + In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be + lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short + for DTIM is determined by AP. + In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data + may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power + is saved but broadcast data is more easy to lose. + + config APP_POWER_SAVE_NONE + bool "none" + config APP_POWER_SAVE_MIN_MODEM + bool "minimum modem" + config APP_POWER_SAVE_MAX_MODEM + bool "maximum modem" + endchoice + + + choice APP_MAX_CPU_FREQ + prompt "Maximum CPU frequency" + default APP_MAX_CPU_FREQ_80 + depends on PM_ENABLE + help + Maximum CPU frequency to use for dynamic frequency scaling. + + config APP_MAX_CPU_FREQ_80 + bool "80 MHz" + config APP_MAX_CPU_FREQ_160 + bool "160 MHz" + config APP_MAX_CPU_FREQ_240 + bool "240 MHz" + depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + endchoice + + config APP_MAX_CPU_FREQ_MHZ + int + default 80 if APP_MAX_CPU_FREQ_80 + default 160 if APP_MAX_CPU_FREQ_160 + default 240 if APP_MAX_CPU_FREQ_240 + + + choice APP_MIN_CPU_FREQ + prompt "Minimum CPU frequency" + default APP_MIN_CPU_FREQ_10M + depends on PM_ENABLE + help + Minimum CPU frequency to use for dynamic frequency scaling. + Should be set to XTAL frequency or XTAL frequency divided by integer. + + config APP_MIN_CPU_FREQ_40M + bool "40 MHz (use with 40MHz XTAL)" + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO + config APP_MIN_CPU_FREQ_20M + bool "20 MHz (use with 40MHz XTAL)" + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO + config APP_MIN_CPU_FREQ_10M + bool "10 MHz (use with 40MHz XTAL)" + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO + config APP_MIN_CPU_FREQ_26M + bool "26 MHz (use with 26MHz XTAL)" + depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO + config APP_MIN_CPU_FREQ_13M + bool "13 MHz (use with 26MHz XTAL)" + depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO + endchoice + + config APP_MIN_CPU_FREQ_MHZ + int + default 40 if APP_MIN_CPU_FREQ_40M + default 20 if APP_MIN_CPU_FREQ_20M + default 10 if APP_MIN_CPU_FREQ_10M + default 26 if APP_MIN_CPU_FREQ_26M + default 13 if APP_MIN_CPU_FREQ_13M endmenu diff --git a/main/app_lib_epd_impl.c b/main/app_lib_epd_impl.c index 65cb10a..faf45eb 100644 --- a/main/app_lib_epd_impl.c +++ b/main/app_lib_epd_impl.c @@ -1,3 +1,5 @@ +#include + #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -5,7 +7,7 @@ #include "app_lib_epd_impl.h" -#define MAXIMUM_XFER_SIZE 400 * 300 / 8 // FULL fb. +#define MAXIMUM_XFER_SIZE 4000 #define MAX_BUSY_POLL_COUNT 10 static void _spi_pre_transfere_callback(spi_transaction_t *txn) { @@ -46,6 +48,7 @@ void app_lib_epd_impl_init(app_lib_epd_impl_t *impl) { .spics_io_num = BOARD_EPD_SPI_CS_PIN, .queue_size = 7, .pre_cb = _spi_pre_transfere_callback, + .flags = SPI_DEVICE_HALFDUPLEX, }; ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO)); @@ -102,15 +105,36 @@ gd_epd_042_ret_t app_lib_epd_impl_write_cmd(app_lib_epd_impl_t *impl, uint8_t *c gd_epd_042_ret_t app_lib_epd_impl_write_data(app_lib_epd_impl_t *impl, uint8_t *data, uint32_t len) { esp_err_t ret; + + uint8_t *dma_buffer = heap_caps_malloc(MAXIMUM_XFER_SIZE, MALLOC_CAP_DMA); + if(dma_buffer == NULL) return EPD_ERR; + + uint8_t has_partial = (len % MAXIMUM_XFER_SIZE ? 1 : 0); + uint32_t txn_count = len / MAXIMUM_XFER_SIZE + has_partial; + spi_transaction_t txn = { .length = 8 * len, .tx_buffer = data, .user = (void *)1 }; - ret = spi_device_polling_transmit(impl->spi_handle, &txn); + for(uint32_t i = 0; i < txn_count; i++) { + uint16_t txn_bytes = MAXIMUM_XFER_SIZE; + if(i == (txn_count - 1) && has_partial) { + txn_bytes = len % MAXIMUM_XFER_SIZE; + } + + memcpy(dma_buffer, &data[i * MAXIMUM_XFER_SIZE], txn_bytes); + + txn.length = 8 * txn_bytes; + txn.tx_buffer = dma_buffer; + + ret = spi_device_polling_transmit(impl->spi_handle, &txn); + if(ret != ESP_OK) return EPD_ERR; + } + + free(dma_buffer); - if(ret != ESP_OK) return EPD_ERR; return EPD_OK; } diff --git a/main/app_lib_pm_impl.c b/main/app_lib_pm_impl.c new file mode 100644 index 0000000..0598334 --- /dev/null +++ b/main/app_lib_pm_impl.c @@ -0,0 +1,14 @@ +#include "esp_pm.h" + +#define DVFS_MAXIMUM_FREQ CONFIG_APP_MAX_CPU_FREQ_MHZ +#define DVFS_MINIMUM_FREQ CONFIG_APP_MIN_CPU_FREQ_MHZ + +void app_lib_pm_init(void) { + esp_pm_config_esp32s2_t pm_config = { + .max_freq_mhz = DVFS_MAXIMUM_FREQ, + .min_freq_mhz = DVFS_MINIMUM_FREQ, + .light_sleep_enable = true + }; + + ESP_ERROR_CHECK(esp_pm_configure(&pm_config)); +} \ No newline at end of file diff --git a/main/app_lib_pm_impl.h b/main/app_lib_pm_impl.h new file mode 100644 index 0000000..9122384 --- /dev/null +++ b/main/app_lib_pm_impl.h @@ -0,0 +1,6 @@ +#ifndef __APP_LIB_PM_IMPL_H +#define __APP_LIB_PM_IMPL_H + +void app_lib_pm_init(void); + +#endif \ No newline at end of file diff --git a/main/app_lib_wifi_impl.c b/main/app_lib_wifi_impl.c index 8769076..8b4ba00 100644 --- a/main/app_lib_wifi_impl.c +++ b/main/app_lib_wifi_impl.c @@ -17,6 +17,21 @@ static const char *TAG = "APP_WIFI"; #define APP_WIFI_SSID CONFIG_APP_WIFI_SSID #define APP_WIFI_PASS CONFIG_APP_WIFI_PASSWORD #define APP_WIFI_RETR CONFIG_APP_WIFI_MAXIMUM_RETRY +#define APP_WIFI_INTV CONFIG_APP_WIFI_LISTEN_INTERVAL + +#if CONFIG_APP_POWER_SAVE_MIN_MODEM +#define DEFAULT_PS_MODE WIFI_PS_MIN_MODEM + +#elif CONFIG_APP_POWER_SAVE_MAX_MODEM +#define DEFAULT_PS_MODE WIFI_PS_MAX_MODEM + +#elif CONFIG_APP_POWER_SAVE_NONE +#define DEFAULT_PS_MODE WIFI_PS_NONE + +#else +#define DEFAULT_PS_MODE WIFI_PS_NONE + +#endif 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_START) { @@ -38,7 +53,7 @@ static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_ } } -void app_lib_start_wifi(void) { +void app_lib_init_wifi(void) { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); @@ -65,6 +80,7 @@ void app_lib_start_wifi(void) { .ssid = APP_WIFI_SSID, .password = APP_WIFI_PASS, .threshold.authmode = WIFI_AUTH_WPA2_PSK, + .listen_interval = APP_WIFI_INTV, .pmf_cfg = { .capable = true, @@ -75,5 +91,12 @@ void app_lib_start_wifi(void) { ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); +} + +void app_lib_start_wifi(void) { ESP_ERROR_CHECK(esp_wifi_start()); +} + +void app_lib_stop_wifi(void) { + ESP_ERROR_CHECK(esp_wifi_stop()); } \ No newline at end of file diff --git a/main/app_lib_wifi_impl.h b/main/app_lib_wifi_impl.h index 6d072d3..b4a18a4 100644 --- a/main/app_lib_wifi_impl.h +++ b/main/app_lib_wifi_impl.h @@ -10,6 +10,8 @@ extern EventGroupHandle_t g_wifi_event_group; #define WIFI_CONNECTED_BIT BIT0 #define WIFI_ERROR_BIT BIT1 +void app_lib_init_wifi(void); void app_lib_start_wifi(void); +void app_lib_stop_wifi(void); #endif \ No newline at end of file diff --git a/main/app_task.h b/main/app_task.h new file mode 100644 index 0000000..a3467a2 --- /dev/null +++ b/main/app_task.h @@ -0,0 +1,20 @@ +#ifndef __APP_TASK_H +#define __APP_TASK_H + +#include "esp_sleep.h" + +typedef struct { + esp_sleep_source_t wakeup_source; + void *user_params; +} app_task_params_t; + +void vTaskLEDs(void *pvParameters); +extern TaskHandle_t xTaskLEDsHandle; + +void vTasBatteryExample(void *pvParameters); +extern TaskHandle_t xTaskBatteryExampleHandle; + +extern TaskHandle_t xTaskEPDExampleHandle; +void vTaskEPDExample(void *pvParameters); + +#endif \ No newline at end of file diff --git a/main/app_task_battery.c b/main/app_task_battery.c index 0887d05..93d0d8e 100644 --- a/main/app_task_battery.c +++ b/main/app_task_battery.c @@ -44,6 +44,6 @@ void vTasBatteryExample(void *pvParameters) { ESP_LOGI(TAG, "Capacity: %04.2fmAh, Charge: %03.2f%%", mah, percentage); - vTaskDelay(pdMS_TO_TICKS(5000)); + vTaskDelay(pdMS_TO_TICKS(30000)); } } \ No newline at end of file diff --git a/main/app_task_epd.c b/main/app_task_epd.c index b57ede7..527b711 100644 --- a/main/app_task_epd.c +++ b/main/app_task_epd.c @@ -4,9 +4,13 @@ #include "freertos/task.h" #include "freertos/queue.h" +#include "esp_log.h" + #include "app_lib_epd_impl.h" -static uint8_t frame_data[2][15000]; +static uint8_t *frame_data[2]; + +static const char *TAG = "TASK_EPD"; extern const unsigned char pixiv_82311703_p0_4indexed[]; extern const unsigned char pixiv_57852730_p0_4indexed[]; @@ -19,6 +23,16 @@ extern const unsigned char pixiv_57852730_p0_4indexed[]; TaskHandle_t xTaskEPDExampleHandle = NULL; void vTaskEPDExample(void *pvParameters) { + + ESP_LOGI(TAG, "EPD task started."); + + frame_data[0] = malloc(15000); + frame_data[1] = malloc(15000); + if(frame_data[0] == NULL || frame_data[1] == NULL) { + ESP_LOGE(TAG, "Failed to allocate memory on heap."); + vTaskSuspend(NULL); + } + app_lib_epd_impl_t epd_impl = {0}; gd_epd_042_t epd = { @@ -40,11 +54,15 @@ void vTaskEPDExample(void *pvParameters) { frame_data[1][i] = ARRAY_EVEN_BYTES(pixiv_57852730_p0_4indexed, i); } + ESP_LOGI(TAG, "EPD frame constructed."); + gd_epd_042_load(&epd, frame_data[0], frame_data[1]); gd_epd_042_update(&epd); gd_epd_042_deepsleep(&epd); + ESP_LOGI(TAG, "EPD task finished."); + for(;;) { - vTaskDelay(pdMS_TO_TICKS(10000)); + vTaskSuspend(NULL); } } \ No newline at end of file diff --git a/main/app_task_led.c b/main/app_task_led.c index a52b844..352eb12 100644 --- a/main/app_task_led.c +++ b/main/app_task_led.c @@ -105,7 +105,13 @@ void vTaskLEDs(void *pvParameters) { ledc_channels[i].channel, LEDC_FADE_NO_WAIT); } current_phase++; - if(current_phase == 8) current_phase = 0; + if(current_phase == 8) { + for(uint8_t i = 0; i < 4; i++) { + ledc_set_duty(ledc_channels[i].speed_mode, ledc_channels[i].channel, 0); + } + + vTaskSuspend(NULL); + } vTaskDelay(pdMS_TO_TICKS(BREATHE_LED_CYCLE)); } } \ No newline at end of file diff --git a/main/main.c b/main/main.c index 9ef6b15..0fd87fb 100644 --- a/main/main.c +++ b/main/main.c @@ -8,17 +8,11 @@ #include "app_lib_wifi_impl.h" #include "app_lib_fs_impl.h" +#include "app_lib_pm_impl.h" #include "user_board.h" -void vTaskLEDs(void *pvParameters); -extern TaskHandle_t xTaskLEDsHandle; - -void vTasBatteryExample(void *pvParameters); -extern TaskHandle_t xTaskBatteryExampleHandle; - -extern TaskHandle_t xTaskEPDExampleHandle; -void vTaskEPDExample(void *pvParameters); +#include "app_task.h" void app_lib_i2c_system_init(void); @@ -27,15 +21,35 @@ static const char *TAG = "APP_MAIN"; void app_main(void) { // Init system peripherals. app_lib_i2c_system_init(); + + //app_lib_pm_init(); + + app_lib_init_wifi(); app_lib_start_wifi(); + app_lib_fs_init(); ESP_LOGI(TAG, "System initialized, starting tasks."); // Init tasks - xTaskCreate(vTaskLEDs, "TASK_LEDs", 1024, NULL, 4, &xTaskLEDsHandle); - xTaskCreate(vTasBatteryExample, "TASK_BAT", 2048, NULL, 4, &xTaskBatteryExampleHandle); - xTaskCreate(vTaskEPDExample, "TASK_EPD", 2048, NULL, 4, &xTaskEPDExampleHandle); + if(xTaskCreate(vTaskLEDs, "TASK_LEDs", 1024, NULL, 4, &xTaskLEDsHandle) != pdPASS) { + ESP_LOGE(TAG, "Task LED creation failed."); + for(;;) { + vTaskSuspend(NULL); + } + } + if(xTaskCreate(vTasBatteryExample, "TASK_BAT", 2048, NULL, 4, &xTaskBatteryExampleHandle) != pdPASS) { + ESP_LOGE(TAG, "Task BAT creation failed."); + for(;;) { + vTaskSuspend(NULL); + } + } + if(xTaskCreate(vTaskEPDExample, "TASK_EPD", 2048, NULL, 4, &xTaskEPDExampleHandle) != pdPASS) { + ESP_LOGE(TAG, "Task EPD creation failed."); + for(;;) { + vTaskSuspend(NULL); + } + } ESP_LOGI(TAG, "Task created, suspending current task.");