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.
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:
- With 16 KB (16384 bytes), more than 1600 records can be stored.
- With 8 KB (8192 bytes), more than 800 records can be stored.
Finally, it’s already huge!
Compatible standby modes
RTC memory can be used in all power modes except Hibernation mode.
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.
recordCounter++;
Operation is much simpler than EEPROM
Limitations
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() { Serial.begin(115200); 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 recordCounter++; delay(LED_DELAY); 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 Serial.println(String(i)+","+String(Records[i].Temp)+","+String(Records[i].Pressure)); } 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); esp_deep_sleep_start(); } void loop() { }
Platformio.ini configuration file for a LoLin D32 development board.
[env:lolin_d32] platform = espressif32 board = lolin_d32 framework = arduino monitor_speed = 115200 lib_deps = 525
Circuit
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
recordCounter++;
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 0,24.10,982.82 1,24.10,982.79 2,24.10,982.87 3,24.10,982.81 4,24.10,982.84
Updates
11/09/2020 Publication de