DIY Projects

ESP32. Store temporary data in RTC memory during deep-sleep

esp32 rtc memory deep sleep store reload

The ESP32 has 16KB of SRAM called RTC fast memory which allows data to be stored even when the processor is put to sleep. Data stored in RTC memory is not erased during deep sleep. They persist until the reset button (or the EN input of the ESP32 board) is pressed.


What is RTC memory?

The RTC (Real Time Clock) memory is an area of the processor SRAM which remains powered and accessible to the RTC functions of the ESP32 microcontroller and the ULP coprocessor even when standby is activated.

Indeed, even in standby (except in hibernation), certain processor functionalities continue to be executed at regular intervals and make it possible to trigger events or processes.

The size and distribution of RTC memory varies depending on the version of the ESP32 processor.

From version 4 of the ESP32-DevKitC development kit, 16 KB of RTC memory is available. For previous versions, the RTC memory was shared as follows: 8 KB for the processor (and therefore by the Arduino code) and 8 KB usable by the ULP coprocessor.

Here is a comparative table drawn up from the official documents of Espressif.

ESP32-DevKitC v4 ESP32-DevKitC, all previous versions
16 Ko of SRAM reserved
  • 8 KB of RTC SRAM, called RTC FAST memory, can be used for data storage. It is accessible by the main processor during RTC boot when exiting Deep-Sleep mode.
    8 KB of RTC SRAM, called RTC SLOW memory and accessible by the ULP coprocessor when the processor is in Deep-Sleep.

ESP32-D0WD-V3 • ESP32-D0WDQ6-V3 • ESP32-D0WD • ESP32-D0WDQ6 • ESP32-D2WD • ESP32-S0WD • ESP32-U4WDH



Read the datasheet Read the Datasheet

The question that can be asked is “how many measurements can be stored with so little space”.

If we take this table, a long integer requires a memory space of 8 bytes which we will round up to 10 bytes to have a realistic order of magnitude, which gives:

Finally, it’s already huge!

Compatible standby modes

RTC memory can be used in all power modes except Hibernation mode.

Active Modem-Sleep Light-Sleep Deep-Sleep Hibernation

What can be stored in RTC memory?

All types of C++ variables can be stored in RTC memory, including structures as we will see in the example.

How to use RTC memory?

There is hardly anything to do with the program. The only thing is to add RTC_DATA_ATTR before the declaration the variable that we want to place in the RTC memory.

For example here, we will record the number of start-ups (wakeup) of the ESP32.

RTC_DATA_ATTR int recordCounter = 0;

As the variable is directly stored in the RTC memory, the value is automatically updated without the need to call a particular method. Here, for example, the value of the counter is incremented.


Operation is much simpler than EEPROM


There is unfortunately a limitation to know before launching, the RTC memory is volatile, that is to say that the data is erased in the event of a power cut or after a reset of the board.

This approach should therefore be used for temporary storage of non-essential data.

An example of Arduino code to upload

Create a new sketch and paste the code below. If you are using the Arduino IDE, remove the first #include <Arduino.h> line needed to compile with PlatformIO.

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>

RTC_DATA_ATTR int recordCounter = 0;

typedef struct {
  float Temp;
  float Pressure;
} bmp180Records;

#define maxRecords 5 // Nombre max enregistrements 
#define sleepTime  10 // Every 10-mins of sleep 10 x 60-secs

RTC_DATA_ATTR bmp180Records Records[maxRecords];

Adafruit_BMP085 bmp;

#define LED_PIN   32
#define LED_DELAY 500  // Allume la LED 500ms pour informer que ESP32 vient de faire un enregistrement
#define I2C_SDA   19   // Broche I2C SDA
#define I2C_SCL   22   // Broche I2C SCL

void setup() {
  pinMode(LED_PIN, OUTPUT);

  Wire.begin(I2C_SDA, I2C_SCL);

  bool status = bmp.begin();

  if ( status ) {
    Serial.println("Record new values");
    digitalWrite (LED_PIN, HIGH);
    Records[recordCounter].Temp     = bmp.readTemperature();       // Units °C
    Records[recordCounter].Pressure = bmp.readPressure() / 100.0F; // Units hPa
    if ( recordCounter == 1 ) {
      Serial.println(String(recordCounter) + " record is stored in RTC memory");
    } else {
      Serial.println(String(recordCounter) + " records are stored in RTC memory");

    if (recordCounter >= maxRecords) {
      for (int i = 0; i < maxRecords; i++){
        // Display records in CSV format to the serial port
      recordCounter = 0;
  } else {
    // Verifier la connexion I2C du BMP190 / BME280
    Serial.println("Sorry but BMP180 did not respond !");
  // Reveil regulierement le processeur de l ESP32 pour faire une nouvelle mesure
  esp_sleep_enable_timer_wakeup(sleepTime * 1000000);

void loop() {

Platformio.ini configuration file for a LoLin D32 development board.

platform = espressif32
board = lolin_d32
framework = arduino
monitor_speed = 115200
lib_deps = 


We are going to use a BMP180 (or a BME280 to have the relative humidity rate) to collect measurements (temperatures, atmospheric pressure …) via the I2C bus. Here, the SDA pin of the BMP180 is connected to pin 22, the SCL pin to input 19. An LED connected to pin 34 will be on for 500ms each time the ESP32 wakes up and records a new measurement.

How does the project code work?

We will first create a structure intended to receive the measurements of the BMP180

typedef struct {
  float Temp;
  float Pressure;
} bmp180Records;

Then we reserve a memory space in the form of an array whose size is defined by the maxRecords variable.

By preceding the declaration of the table by RTC_DATA_ATTR, we indicate to the compiler that it will be stored in the RTC memory.

RTC_DATA_ATTR bmp180Records Records[maxRecords];

The I2C pins (SDA and SCL) may not be exposed (available) on the GPIO connector. Fortunately, the adaptation of the Wire.h library for ESP32 makes it possible to manually assign the pins of the I2C bus.

Wire.begin(I2C_SDA, I2C_SCL);

Each time the processor wakes up, the new measurements are recorded in the table at the pointer position (recordCounter).

Records[recordCounter].Temp = bmp.readTemperature(); // Units °C
Records[recordCounter].Pressure = bmp.readPressure() / 100.0F; // Units hPa

That we increment for the next awakening


As the size of the data table is set by the maxRecords variable, the counter should be reset to position itself at the start of the table. In a few lines of code, we create a rotating buffer.

if (recordCounter >= maxRecords) {
   recordCounter = 0;

And after 5 records, the data is exported in CSV format to the serial port.

5 records are stored in RTC memory


11/09/2020 Publication de l’article

Click to rate this post!
[Total: 0 Average: 0]
Exit mobile version