Using RTC with Backup Battery on ESP32-P4

Sudheesh
Posts: 4
Joined: Tue Aug 26, 2025 1:01 pm

Using RTC with Backup Battery on ESP32-P4

Postby Sudheesh » Thu Sep 04, 2025 5:17 am

Hi everyone,

I’m new to ESP and currently working with the ESP32-P4 on a custom board. I want to use the RTC with a backup battery so that it keeps running when the main power is removed.

I have connected VBAT to a CR1220 backup battery, following the ESP32-P4 datasheet.

I have a few questions:
1.How can I enable and use the RTC with battery backup on the ESP32-P4?
2.Which configurations need to be changed in menuconfig to support this?
3.Are there example codes available for reading/writing the RTC time and verifying that it retains the correct time after a power loss?
4.Do I need to configure any special registers or settings for RTC backup mode?

Any guidance, example code, or reference documentation would be very helpful.

Thank you!

ahsrabrifat
Posts: 201
Joined: Sat Jan 18, 2025 2:31 pm

Re: Using RTC with Backup Battery on ESP32-P4

Postby ahsrabrifat » Thu Sep 04, 2025 5:46 pm

You’ve correctly wired the VBAT pin to your CR1220 battery—but to make it active, you need to configure ESP-IDF to support VBAT.

Sudheesh
Posts: 4
Joined: Tue Aug 26, 2025 1:01 pm

Re: Using RTC with Backup Battery on ESP32-P4

Postby Sudheesh » Fri Sep 05, 2025 6:20 pm

Thank you,ahsrabrifat.
I am not familiar with the configuration. Could you please share any documentation on that? I am using ESP-IDF v5.5.


_Dejan_
Posts: 1
Joined: Sat Nov 15, 2025 2:41 pm

Re: Using RTC with Backup Battery on ESP32-P4

Postby _Dejan_ » Sat Nov 15, 2025 2:47 pm

Hi,
@Sudheesh Did you make it work?
First link "ESP32-P4 Battery Backup Solution" is for "ESP-IoT-Solution" and not for "ESP-Idf" ... If I look into file which I need modify there is no "pmu_sleep_start" function...
Any solution for ESP-IDF? I need to keep RTC if power is loss... NTP is not option because there is no internet or loacal connection(No LAN or WiFi)...

LeverNuts
Posts: 1
Joined: Mon Dec 29, 2025 8:28 pm

Re: Using RTC with Backup Battery on ESP32-P4

Postby LeverNuts » Mon Dec 29, 2025 8:36 pm

@Sudheesh and @_Dejan_

Has anyone had luck with this? Using VSCode extension I can find VBAT settings that seem relevant. I am specifically trying to keep the RTC through power loss. Following the above links gave me a variety of results. I was able to get RTC to persist through power loss, but booting became very inconsistent. It would be nice if Espressif could provide an example or improved documentation.

username
Posts: 593
Joined: Thu May 03, 2018 1:18 pm

Re: Using RTC with Backup Battery on ESP32-P4

Postby username » Tue Dec 30, 2025 1:12 am

You have to go into menuconfig to enable VBAT.
Screenshot 2025-12-29 191118.png
Screenshot 2025-12-29 191118.png (58.19 KiB) Viewed 2639 times

LLM_coder
Posts: 1
Joined: Fri Apr 03, 2026 5:34 pm

Re: Using RTC with Backup Battery on ESP32-P4

Postby LLM_coder » Fri Apr 03, 2026 5:47 pm

Could NOT get this to work. Ultimately, I had to add a DS3231 to the I2C bus. I'm currently progging a Waveshare ESP32-P4 dev board (with a 7" screen). We tried the following things to try and get them to work:

ESP32-P4 RTC Time Persistence Across Power Cycles — Approaches Tried

Hardware
  • Board: Waveshare ESP32-P4-WIFI6-Touch-LCD-7B
  • SoC: ESP32-P4 rev 1.3
  • ESP-IDF: v5.4
  • VBAT: CR1220 coin cell, measured 3.27V, seated in onboard battery holder
  • VBAT circuit: CR1220 connected to ESP_VBAT pin via onboard holder (confirmed in schematic — VBAT feeds the LP domain power input)
  • Peripherals using SDIO: SD card on SDMMC Slot 0 (4-bit, 40MHz), WiFi via ESP32-C6 coprocessor on SDIO Slot 1 (esp_hosted, 4-bit, 40MHz)
Problem Statement

Code: Select all

gettimeofday()
returns epoch 0 (1970-01-01) on every boot — including soft reboots from serial DTR/RTS reset — even though the ESP32-P4 datasheet (Section 4.1.4.10) documents a 48-bit RTC Timer that provides "uninterrupted operation during any reset or sleep mode, except for power-on reset of LP system."

The goal was to maintain wall-clock time across power outages using the CR1220 battery, without relying on NTP or deep sleep.

Relevant sdkconfig

Code: Select all

CONFIG_RTC_CLK_SRC_INT_RC=y
CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y
CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y
CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y
CONFIG_SOC_RTC_MEM_SUPPORTED=y
How ESP-IDF System Time Works (background)

Code: Select all

gettimeofday()
computes:

Code: Select all

boot_time + time_since_boot
  • Code: Select all

    boot_time
    is stored in LP_STORE2/3 registers (

    Code: Select all

    LP_SYSTEM_REG_LP_STORE2_REG
    /

    Code: Select all

    LP_SYSTEM_REG_LP_STORE3_REG
    ) via

    Code: Select all

    esp_time_impl_set_boot_time()
    in

    Code: Select all

    components/newlib/port/esp_time_impl.c
  • Code: Select all

    time_since_boot
    uses

    Code: Select all

    s_microseconds_offset + esp_system_get_time()
    where

    Code: Select all

    s_microseconds_offset
    bridges the ESP timer to the RTC counter
  • Code: Select all

    settimeofday()
    writes:

    Code: Select all

    boot_time = now - time_since_boot
    into LP_STORE2/3
  • The 48-bit RTC counter (

    Code: Select all

    esp_rtc_get_time_us()
    ) provides the monotonic reference
If LP_STORE2/3 and the RTC counter both survive a reset, time should persist.

[hr]

Approach 1: Baseline — System Calls Only (the P4 "onboard RTC" shim)

Implementation: Replaced the external DS3231 I2C driver with a shim that wraps

Code: Select all

gettimeofday()
/

Code: Select all

settimeofday()
, treating the ESP32-P4's internal RTC as the time source.

Result:

Code: Select all

gettimeofday()
always returns epoch ~3 seconds (just boot time) after any reset. Diagnostic logging confirmed:

Code: Select all

[DIAG] Reset: POWERON, RTC counter: 3298603 us (0.0 hrs), epoch: 3
Even a serial DTR/RTS reset (not actual power loss) reports

Code: Select all

ESP_RST_POWERON
and shows the RTC counter at ~3.3 seconds (just the time since boot started). The LP_STORE2/3 registers are zeroed.

Root cause: Every reset — including serial-triggered resets — appears as

Code: Select all

RESET_REASON_CHIP_POWER_ON
to the ROM, which zeros LP_STORE registers and resets the 48-bit RTC counter. The VBAT switching that would keep the LP domain alive during main power loss is not enabled by default.

[hr]

Approach 2: NVS Bootstrap

Implementation: On every boot, before the RTC driver reads

Code: Select all

gettimeofday()
, check if NVS contains

Code: Select all

last_sync_ts
(minutes since epoch, saved on every NTP sync). If

Code: Select all

gettimeofday()
shows year < 2025, call

Code: Select all

settimeofday()
with the NVS timestamp to prime the clock.

Also saved

Code: Select all

rtc_ref_us
(raw

Code: Select all

esp_rtc_get_time_us()
value at last NTP sync) to NVS, so that if the RTC counter survived a power cycle, we could compute:

Code: Select all

current_time = nvs_epoch + (rtc_counter_now - rtc_counter_saved) / 1e6
for second-accurate recovery.

Result: Clock was primed to approximately correct time (minute resolution from NVS). Components started immediately without the 27-second NTP wait. However, the RTC counter delta was always zero because the counter resets on every boot — so the "second-accurate VBAT refinement" path never activated. The time was stale by the duration of the power outage.

[hr]

Approach 3: PMU VBAT Auto-Switch via Direct Register Write

Implementation: Based on Espressif's ESP32-P4 VBAT documentation, set the PMU to auto-switch the LP domain to VBAT when main power drops:

Code: Select all

// In DS3231::init(), before any SDIO initialization:
REG_SET_FIELD(PMU_HP_SLEEP_LP_DIG_POWER_REG, PMU_HP_SLEEP_VDDBAT_MODE, 2);  // AUTO mode
REG_SET_BIT(PMU_VDDBAT_CFG_REG, PMU_VDDBAT_SW_UPDATE);

// Poll for activation:
while (2 != REG_GET_FIELD(PMU_VDDBAT_CFG_REG, PMU_ANA_VDDBAT_MODE)) { ... }
Result — Partial success then crash:

The VBAT switching did work for time persistence. After power cycling (unplug, wait, replug), the boot log showed:

Code: Select all

MAIN: RTC Time: 2026-04-03 12:16:33
This was the correct time, proving LP_STORE2/3 and the RTC counter survived the power cycle via VBAT.

However, both SDIO peripherals crashed on the VBAT-wake boot:
  • SD card (SDMMC Slot 0):

    Code: Select all

    sdmmc_init_sd_ssr: sdmmc_send_cmd returned 0x107

    Code: Select all

    ESP_ERR_TIMEOUT
  • WiFi SDIO (Slot 1, esp_hosted):

    Code: Select all

    assert failed: xQueueSemaphoreTake queue.c:1709 (( pxQueue ))
Root cause: When VBAT keeps the LP domain powered, the reset reason is no longer

Code: Select all

RESET_REASON_CHIP_POWER_ON
. In

Code: Select all

components/esp_system/port/soc/esp32p4/clk.c
lines 375-399, LP peripheral cleanup — including SDIO PLL clock gate clearing — only runs on

Code: Select all

CHIP_POWER_ON
:

Code: Select all

if (rst_reason == RESET_REASON_CHIP_POWER_ON) {
    // These only run on cold boot:
    REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL2_CLK_EN);
    REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL1_CLK_EN);
    REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL0_CLK_EN);
    // ... more LP clock cleanup ...
}
When VBAT keeps the LP domain alive, stale SDIO PLL clock states, LP UART clocks, and LP pad configurations survive into the next boot, causing the SDIO host controller to initialize against corrupted state.

Additional finding:

Code: Select all

PMU_ANA_VDDBAT_MODE
(bits [1:0] of

Code: Select all

PMU_VDDBAT_CFG_REG
) is a read-only status register reflecting the current power mode. It only updates during actual PMU state transitions (sleep entry/exit), not from runtime register writes. The poll loop always timed out with "VBAT auto-switch failed to activate" even though the configuration register was written correctly — the mode only takes effect on the next power transition.

[hr]

Approach 4: LP Peripheral Force-Reset + SDIO Clock Cleanup + VBAT Enable

Implementation: Before enabling VBAT, force-reset LP peripherals and replay the

Code: Select all

CHIP_POWER_ON
cleanup:

Code: Select all

// Step 1: Force-reset LP peripherals
PMU.power.lp_peri.force_reset = 1;
esp_rom_delay_us(100);
PMU.power.lp_peri.force_reset = 0;

// Step 2: Clear stale SDIO/LP clocks (from clk.c:375-399)
REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL2_CLK_EN);
REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL1_CLK_EN);
REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL0_CLK_EN);
// ... all other LP clock gates from clk.c ...

// Step 3: Enable VBAT
REG_SET_FIELD(PMU_HP_SLEEP_LP_DIG_POWER_REG, PMU_HP_SLEEP_VDDBAT_MODE, 2);
REG_SET_BIT(PMU_VDDBAT_CFG_REG, PMU_VDDBAT_SW_UPDATE);
Result — Worse crash:

The

Code: Select all

lp_peri
force-reset was too destructive. It wiped LP_STORE registers (including

Code: Select all

LP_STORE4
= XTAL frequency calibration) and corrupted LP ROM execution state:

Code: Select all

W (4868) rtc_clk: invalid RTC_XTAL_FREQ_REG value, assume 40MHz   (×6 times)
After the first boot completed, subsequent boots crashed immediately in LP ROM code:

Code: Select all

rst:0x0 (N/A),boot:0xf (SPI_FAST_FLASH_BOOT)
invalid reset
Guru Meditation Error: Core 0 panic'ed (Illegal instruction)
PC: 0x4fc02ba6   ← LP ROM address space (0x4fc0xxxx)
Root cause: The

Code: Select all

lp_peri
power domain encompasses more than just SDIO-related state. The force-reset pulse zeroed critical LP system registers (XTAL calibration in LP_STORE4, RTC slow clock calibration in LP_STORE1, boot_time in LP_STORE2/3) and corrupted LP ROM state, making the LP core execute garbage instructions on subsequent boots.

[hr]

Approach 5: RTC_NOINIT_ATTR (investigated, not attempted)

Finding: We investigated using

Code: Select all

RTC_NOINIT_ATTR
to store a

Code: Select all

{magic, epoch, rtc_counter}
struct in LP RAM that survives resets. However:
  • Code: Select all

    RTC_NOINIT_ATTR
    persistence tests are explicitly disabled for ESP32-P4 in ESP-IDF 5.4 due to issue IDF-9564:

    Code: Select all

    // components/esp_system/test_apps/.../test_reset_reason.c:27-29
    #if (CONFIG_SOC_RTC_FAST_MEM_SUPPORTED || CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED) && !CONFIG_IDF_TARGET_ESP32P4
    #define CHECK_RTC_MEM 1
    #endif
    
  • The LP RAM linker section exists (

    Code: Select all

    lp_ram_seg
    at

    Code: Select all

    0x50108000
    , 8KB) and the macro is defined (not empty), but behavior is unvalidated on P4.
  • Even if it worked, without the RTC counter surviving, we'd only have the epoch snapshot, not elapsed time.
[hr]

Summary of Findings

Code: Select all

Approach                  | RTC Counter? | LP_STORE?  | SDIO?      | Usable?
--------------------------|-------------|-----------|-----------|---------------------------
Baseline (no VBAT)        | No (zero)   | No (zero) | Yes       | No time persistence
NVS bootstrap             | N/A (zero)  | N/A       | Yes       | Minute-res only, stale
VBAT auto-switch          | YES         | YES       | NO crash  | Time works, SDIO broken
LP force-reset + VBAT     | Destroyed   | Destroyed | Crashes   | Unusable
RTC_NOINIT_ATTR           | N/A (IDF-9564)                      | Untested/disabled on P4
[hr]

Questions for Espressif

1. Is there a supported way to enable VBAT power switching for the LP domain without going through the deep sleep infrastructure? Our device runs in full-power mode and experiences unexpected power loss. We need the PMU to be pre-configured so that when VDDA drops, it automatically switches to VBAT — without having called

Code: Select all

esp_deep_sleep_start()
first.

2. Can the SDIO PLL clock cleanup in

Code: Select all

clk.c:375-399
be extended to also run on VBAT wake resets? The current code only clears stale LP clocks on

Code: Select all

RESET_REASON_CHIP_POWER_ON
. When VBAT keeps the LP domain alive, the reset reason changes and these cleanup steps are skipped, leaving SDIO in a broken state.

3. Is there a way to selectively reset LP peripherals (SDIO clocks, GPIO pads) without resetting the LP_STORE registers and RTC counter?

Code: Select all

PMU.power.lp_peri.force_reset
is too broad — it wipes everything including XTAL calibration and boot_time.

4. What is the status of IDF-9564 (

Code: Select all

RTC_NOINIT_ATTR
disabled on ESP32-P4)? If LP RAM persistence across resets were reliable, it could provide an alternative path for time persistence without VBAT.

5. The

Code: Select all

esp_vbat
component referenced in the ESP-IoT-Solution VBAT documentation — is there a timeline for it being integrated into ESP-IDF as a standard component? A proper API for VBAT management that handles the LP domain cleanup on wake would solve this cleanly.

[hr]

Resolution

We ultimately attached an external DS3231 I2C RTC module (SDA=GPIO7, SCL=GPIO8) with its own battery backup. This provides sub-second time accuracy across power cycles without any dependency on the ESP32-P4's LP domain persistence.

Who is online

Users browsing this forum: Google [Bot], GPTBot and 5 guests