espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.28k stars 7.2k forks source link

ESP32-S3 Console Component not working in automatic sleep mode (IDFGH-13200) #14137

Closed gadget-man closed 3 weeks ago

gadget-man commented 2 months ago

Answers checklist.

IDF version.

5.2.2

Espressif SoC revision.

ESP32-S3

Operating System used.

macOS

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

ES32-S3-DevKitC-1U

Power Supply used.

USB

What is the expected behavior?

Using the console component https://github.com/espressif/esp-idf/tree/e7070e777a079695f69720ffb3c631c5fe620cc6/examples/system/console should work when automatic light sleep is enabled

What is the actual behavior?

As soon as the configuration is updated to enable auto-light-sleep, the console stops responding.

Steps to reproduce.

  1. Download example console component
  2. Add the following code and update sdkconfig to allow PM features // Enable automatic light sleep
    esp_pm_config_t pm_config = {
        .max_freq_mhz = 160,
        .min_freq_mhz = 10, // Minimum frequency for light sleep
        .light_sleep_enable = true};
    ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
  3. Re-flash and run code. ...

Debug Logs.

I (202) cpu_start: Multicore app
I (212) cpu_start: Pro cpu start user code
I (212) cpu_start: cpu freq: 160000000 Hz
I (212) cpu_start: Application information:
I (215) cpu_start: Project name:     console
I (220) cpu_start: App version:      5f28a76-dirty
I (225) cpu_start: Compile time:     Jul  4 2024 15:11:13
I (232) cpu_start: ELF file SHA256:  8e0575a33...
I (237) cpu_start: ESP-IDF:          v5.2.2-dirty
I (242) cpu_start: Min chip rev:     v0.0
I (247) cpu_start: Max chip rev:     v0.99 
I (252) cpu_start: Chip rev:         v0.1
I (257) heap_init: Initializing. RAM available for dynamic allocation:
I (264) heap_init: At 3FCA4448 len 000452C8 (276 KiB): RAM
I (270) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (276) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (282) heap_init: At 600FE05C len 00001F8C (7 KiB): RTCRAM
I (289) spi_flash: detected chip: generic
I (293) spi_flash: flash io: dio
W (297) spi_flash: Detected size(8192k) larger than the size in the binary image header(4096k). Using the size in the binary image header.
I (310) sleep: Configure to isolate all GPIO pins in sleep state
I (317) sleep: Enable automatic switching of GPIO sleep configuration
I (336) main_task: Started on CPU0
I (346) main_task: Calling app_main()
I (376) example: Command history enabled

This is an example of ESP-IDF console component.
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
Press Enter or Ctrl+C will terminate the console environment.
I (466) pm: Frequency switching config: CPU_MAX: 160, APB_MAX: 80, APB_MIN: 10, Light sleep: ENABLED
I (466) sleep: Code start at 0x42000020, total 616399, data start at 0x3c0a0020, total 148612 Bytes
0x42000020: _stext at ??:?

More Information.

I've also tried adding the below as options after configuring PM, however it makes no difference:

' // Set UART wake-up source ESP_ERROR_CHECK(uart_set_wakeup_threshold(UART_NUM_0, 3)); // Set threshold to wake up on receiving 3 bytes ESP_ERROR_CHECK(esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM)); '

igrr commented 2 months ago

Hi @gadget-man, These two features (console over UART and automatic light sleep), could be made to work together, but probably not in a way you would like. In the console component, we could take a power management lock, preventing the system from entering light sleep mode, and keeping UART working. However, the result would be the same as if you used console without automatic light sleep.

On ESP32-S3, it is not possible to enter light sleep mode and also keep receiving bytes on UART. UART peripheral can be used to wake up from light sleep, but it doesn't put the byte which caused the wakeup into the FIFO.

One possible compromise would be along these lines:

This would require the host to first send a byte to trigger wakeup (e.g. CR), then the actual command.

Let me know if such a solution would be useful for your use case.

gadget-man commented 2 months ago

Hi @igrr

My use case is to use the console for device-specific configuration post-flashing, and also for some support requirements (e.g. to retrieve the contents of a file saved to a LFS partition), so I think what you've described could work very well. I will be using a custom python script to create and send these commands, so that script could very easily include a UART wake up command before sending the main command payload. As soon either the command is received (or the the response has been sent), the lock could be released.

I also considered using a GPIO (e.g. DTR) to firstly suspend power management and then start the console, but it would be much more convenient if this could all be managed via UART.

gadget-man commented 2 months ago

I see from the logs that UART wake-up is possible with light sleep mode, however I'm struggling to get it to work with automatic light sleep. Is there a guide somewhere for how I could detect wake from automatic light sleep due to UART, so that I can acquire a PM lock NO_LIGHT_SLEEP until I'm done?

gadget-man commented 2 months ago

I've tried a very simple example script to test UART (not yet with console), as follows:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_pm.h"
#include "esp_sleep.h"
#include "esp_log.h"

#define UART_NUM (UART_NUM_0)
#define UART_TXD (UART_PIN_NO_CHANGE) //(GPIO_NUM_1)
#define UART_RXD (UART_PIN_NO_CHANGE) //(GPIO_NUM_3)

#define BUF_SIZE (1024)

static QueueHandle_t uart_queue;

char *TAG = "uart_sleep_test";

static void uart_task(void *arg)
{
    esp_pm_lock_handle_t pm_lock;
    esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 1, "NO_LIGHT_SLEEP", &pm_lock);

    uint8_t *data = malloc(BUF_SIZE);
    uart_event_t event;

    while (1)
    {
        if (xQueueReceive(uart_queue, &event, portMAX_DELAY))
        {
            esp_pm_lock_acquire(pm_lock);
            switch (event.type)
            {
            case UART_DATA:
            {
                int len = uart_read_bytes(UART_NUM, data, event.size, portMAX_DELAY);
                ESP_LOGI(TAG, "Received %d bytes", len);
                uart_write_bytes(UART_NUM, (const char *)data, len);
            }
            break;
            default:
                break;
            }
            esp_pm_lock_release(pm_lock);
        }
    }
}

void app_main(void)
{
    uart_config_t 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,
        .source_clk = UART_SCLK_XTAL,
    };
    uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 32, &uart_queue, 0);
    uart_param_config(UART_NUM, &uart_config);
    uart_set_pin(UART_NUM, UART_TXD, UART_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    xTaskCreate(uart_task, "uart_event_task", 2048, NULL, 12, NULL);

    vTaskDelay(1000 / portTICK_PERIOD_MS);

    esp_pm_config_t pm_config = {
        .max_freq_mhz = 240,
        .min_freq_mhz = 10,
        .light_sleep_enable = true,
    };
    // gpio_wakeup_enable(UART_RXD, GPIO_INTR_LOW_LEVEL);
    // esp_sleep_enable_gpio_wakeup();
    uart_set_wakeup_threshold(UART_NUM, 3);
    esp_sleep_enable_uart_wakeup(UART_NUM);
    esp_pm_configure(&pm_config);
}

However even this doesn't work on my ESP-32 DevKit - it's fine if I comment out .light_sleep_enable = true, but as soon as I enable it I get the below output but posting anything to serial gets no response:

I (27) boot: ESP-IDF v5.2.2-dirty 2nd stage bootloader
I (27) boot: compile time Jul  6 2024 17:30:26
I (27) boot: Multicore bootloader
I (30) boot: chip revision: v0.1
I (34) boot.esp32s3: Boot SPI Speed : 80MHz
I (39) boot.esp32s3: SPI Mode       : DIO
I (44) boot.esp32s3: SPI Flash Size : 2MB
I (48) boot: Enabling RNG early entropy source...
I (54) boot: Partition Table:
I (57) boot: ## Label            Usage          Type ST Offset   Length
I (65) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (72) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (80) boot:  2 factory          factory app      00 00 00010000 00100000
I (87) boot: End of partition table
I (91) esp_image: segment 0: paddr=00010020 vaddr=3c030020 size=0ca48h ( 51784) map
I (109) esp_image: segment 1: paddr=0001ca70 vaddr=3fc95b00 size=02a5ch ( 10844) load
I (112) esp_image: segment 2: paddr=0001f4d4 vaddr=40374000 size=00b44h (  2884) load
I (117) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=20ac8h (133832) map
I (149) esp_image: segment 4: paddr=00040af0 vaddr=40374b44 size=10ec4h ( 69316) load
I (165) esp_image: segment 5: paddr=000519bc vaddr=600fe000 size=0005ch (    92) load
I (172) boot: Loaded app from partition at offset 0x10000
I (172) boot: Disabling RNG early entropy source...
I (185) cpu_start: Multicore app
I (194) cpu_start: Pro cpu start user code
I (194) cpu_start: cpu freq: 160000000 Hz
I (194) cpu_start: Application information:
I (197) cpu_start: Project name:     uart_sleep
I (203) cpu_start: App version:      1
I (207) cpu_start: Compile time:     Jul  6 2024 17:30:06
I (213) cpu_start: ELF file SHA256:  7ca9354be...
I (218) cpu_start: ESP-IDF:          v5.2.2-dirty
I (224) cpu_start: Min chip rev:     v0.0
I (229) cpu_start: Max chip rev:     v0.99 
I (233) cpu_start: Chip rev:         v0.1
I (238) heap_init: Initializing. RAM available for dynamic allocation:
I (245) heap_init: At 3FC98F30 len 000507E0 (321 KiB): RAM
I (251) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (258) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (264) heap_init: At 600FE05C len 00001F8C (7 KiB): RTCRAM
I (271) spi_flash: detected chip: generic
I (275) spi_flash: flash io: dio
W (279) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (292) sleep: Configure to isolate all GPIO pins in sleep state
I (299) sleep: Enable automatic switching of GPIO sleep configuration
I (317) main_task: Started on CPU0
I I (1327) pm: Frequency switching config: CPU_MAX: 240, APB_MAX: 80, APB_MIN: 10, Light sleep: ENABLED
I (1327) sleep: Code start at 0x42000020, total 133831, data start at 0x3c0x42000020: _stext at ??:?
esp-wzh commented 2 months ago

I see from the logs that UART wake-up is possible with light sleep mode, however I'm struggling to get it to work with automatic light sleep. Is there a guide somewhere for how I could detect wake from automatic light sleep due to UART, so that I can acquire a PM lock NO_LIGHT_SLEEP until I'm done?

Hi, @gadget-man You can refer to this PR, which supports controlling the power state of the chip through an external wakeup source (GPIO/UART)

gadget-man commented 2 months ago

Many thanks for this. In the meantime, I'd developed something similar using a GPIO interrupt handler on UART_Rx to wake the device from automatic light sleep. This PR looks a more eloquent solution so I'll look to update using that.

One thing I noticed when testing - when configuring the UART, you have to manually specify the TX and RX pins, otherwise you get a constant bit on the Rx pin as soon as the device sleeps. I.e. the below fails: uart_set_pin(UART_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); however this works: (UART_TXD and UART_RXD defined as GPIO_NUM_43 and GPIO_NUM_44 respectively) uart_set_pin(UART_NUM, UART_TXD, UART_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); Is this a bug?

esp-wzh commented 2 months ago

If PM_SLP_DISABLE_GPIO or ESP_SLEEP_GPIO_RESET_WORKAROUND is enabled in menuconfig, when entering sleep mode, all GPIOs will switch to another configuration(isolation stae to save power), including UART RX/TX. Pulses may be generated during the switching process. You can disable the switching for UART pin with the following code:

    // Disable SLP_SEL to change UART TX/RX status automantically in lightsleep.
    gpio_sleep_sel_dis(U0TXD_GPIO_NUM);
    gpio_sleep_sel_dis(U0RXD_GPIO_NUM);
gadget-man commented 2 months ago

Got it thanks, that makes sense and I did have them enabled to reduce power when in deep sleep. for my purposes, creating a bespoke UART command handler is the way forwards, however I propose to leave this issue open as others may wish to use console component with light sleep enabled.

I suggest a combination of optionally enabling a start bit with timeout would then require the user to send commands including the start bit. Console could set a PM lock and return a 'retry' response if the start bit wasn't received due to wake-up delays, and the user could then manually resubmit the same command, with a timeout to automatically clear the lock if no repeat command was provided within x seconds.

Sherry616 commented 3 weeks ago

Thanks for reporting, we will close this issue. Feel free to reopen if have more updates. Thanks for using our Espressif product!