Severe byte loss after waking up from Light Sleep using UART

chariri
Posts: 3
Joined: Sun Mar 23, 2025 1:11 am

Severe byte loss after waking up from Light Sleep using UART

Postby chariri » Thu Jul 03, 2025 6:03 am

MCU: ESP32-C3
ESP-IDF 5.4.1
UART config: 115200, 8N1 w/o hw flow control, source_clk = UART_SCLK_XTAL, allow_pd = false

According to the documentation, the byte triggering wakeup and probably a few bytes following it will be lost. However in my case where a frame of 100 bytes is transmitted over the UART line, only 70~96 bytes are received:

1. The first 4 bytes are almost guaranteed to be lost;
2. In the frame, long runs of 0x00 and 0xFF are more prone to loss - the byte loss happens in the middle of the frame!
3. Most frames received only contains 70~ valid bytes;

The UART and light sleep is enabled as follow:

Code: Select all

    ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "uart", &_pm_lock));
    _recv_buffer.resize(uart_buffer_size);
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
    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,
        .rx_flow_ctrl_thresh = 122,
        .source_clk = UART_SCLK_XTAL,
        .flags = {
            .allow_pd = false,
            .backup_before_sleep = false,
        }
    };
#pragma GCC diagnostic pop
    ESP_ERROR_CHECK(uart_driver_install(addr::uart_num, uart_buffer_size, uart_buffer_size, 16, &_uart_queue, 0));
    ESP_ERROR_CHECK(uart_param_config(addr::uart_num, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(addr::uart_num, pin::uart1_txd, pin::uart1_rxd, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)
    );
    ESP_ERROR_CHECK(uart_set_wakeup_threshold(addr::uart_num, 3));
    ESP_LOGI(TAG, "UART driver installed");

//... somewhere else in main()
    esp_pm_config_t esp_pm = {
        .max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
        .min_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
        .light_sleep_enable = true,
    };
    ESP_ERROR_CHECK(esp_pm_configure(&esp_pm));
    ESP_ERROR_CHECK(esp_sleep_enable_gpio_wakeup());
    gpio_sleep_set_direction(pin::uart1_rxd, GPIO_MODE_INPUT);
    gpio_sleep_set_pull_mode(pin::uart1_rxd, GPIO_FLOATING);
    ESP_ERROR_CHECK(esp_sleep_enable_uart_wakeup(addr::uart_num));  // uart 1
The code for receiving UART data:

Code: Select all

void uart_bus::tick_recv()  // this is run in a infinite loop in a separate FreeRTOS task.
{
    uart_event_t event;
    if (xQueueReceive(_uart_queue, &event, pdMS_TO_TICKS(max_wait_time_ms)) == pdPASS)  // 200 ms
    {
        if (event.type != UART_DATA)
        {
            ESP_LOGW(TAG, "event %d", (int) event.type);
            return;
        }
        size_t remaining = 0;
        ESP_ERROR_CHECK_WITHOUT_ABORT(uart_get_buffered_data_len(addr::uart_num, &remaining));
        while (remaining > 0)
        {
            auto len = uart_read_bytes(
                addr::uart_num, _recv_buffer.data(),
                std::max(static_cast<size_t>(50), std::min(_recv_buffer.size(), remaining)),
                pdMS_TO_TICKS(max_wait_time_ms)
            );
            if (len < 0)
            {
                ESP_LOGE(TAG, "Error reading from UART");
                return;
            }
            if (len == 0)
                return;
            if (auto h = _handler.load(std::memory_order_relaxed))
                h->on_recv(std::span(_recv_buffer.data(), len));
            ESP_ERROR_CHECK_WITHOUT_ABORT(uart_get_buffered_data_len(addr::uart_num, &remaining));
        }
        char ub[16] = {0};
        uart_write_bytes(addr::uart_num, ub, 100);
    }
}
Data examples (correct vs garbled):

Code: Select all

b56201075c0000000000e5070307000000f0ffffffff000000000000240000000000000000000000000098bdffffffffffff007684df0000000000000000000000000000000000000000583e0f0080a812010f2700005c40562f0000000000000000c92d
        5c0064cdd215e50703          0b0529ff00f14a2800002400000000000000            98bdffffff                          0300000000000000000000000000583e0f0080a812010f2700005c40562f0000000000000000d30b
Since the device is battery powered and the UART data comes only once per 1-4s, it is essential that auto light sleep works to help reduce the overall power usage. Due to limitations of the GNSS module used, not much other options for waking up are reliable - the only last resort may be have the GNSS module send a "victim message" before the actual message. The feasibility of such a solution really depends on the implementation of the GNSS firmware and is not preferred.

Any help is much appreciated!

chariri
Posts: 3
Joined: Sun Mar 23, 2025 1:11 am

Re: Severe byte loss after waking up from Light Sleep using UART

Postby chariri » Sat Jul 05, 2025 4:21 pm

Update: I tried holding the power management lock to prevent entering the light sleep again when reading from the UART after being woken up by UART. This prevents byte loss in between a transfer, but more than expected byte loss still happens at the beginning of a transfer.

Who is online

Users browsing this forum: ChatGPT-User, PerplexityBot, PetalBot and 2 guests