ESP32. Stocker des données temporaires dans la mémoire RTC durant la mise en veille • Domotique et objets connectés à faire soi-même

L’ESP32 dispose de 16 Ko de SRAM appelée mémoire rapide RTC qui permet de stocker des données même lorsque le processeur est mis en veille. Les données enregistrées dans la mémoire RTC ne sont pas effacées pendant le sommeil profond (deep-sleep). Elles persistent jusqu’à ce que l’on appuie sur le bouton de réinitialisation (ou l’entrée EN de la carte ESP32).

C’est quoi la mémoire RTC ?

La mémoire RTC (Real Time Clock) est une zone de la SRAM du processeur qui reste alimentée et accessible aux fonctions RTC du micro-contrôleur ESP32 et du coprocesseur ULP même lorsque la veille est activée.

En effet, même en veille (sauf en hibernation), certaines fonctionnalités du processeur continuent à être exécutées à intervalle régulier et permettent de déclencher des événements ou des traitements.

v6iatstuhv9j8ge8sksk-7788085

La taille et la répartition de la mémoire RTC varie en fonction de la version du processeur ESP32.

A partir de la version 4 du kit de développement ESP32-DevKitC, on dispose de 16 Ko de mémoire RTC. Pour les versions antérieures, la mémoire RTC était partagée ainsi : 8 Ko pour le processeur (et donc par le code Arduino) et 8 Ko utilisable par le coprocesseur ULP.

Voici un tableau comparatif établi à partir des documentations officielles d’Espressif.

ESP32-DevKitC v4 ESP32-DevKitC, toutes versions antérieures
16 Ko SRAM réservée
  • 8 Ko de SRAM RTC, appelé mémoire RTC FAST peut être utilisé pour le stockage de données. Elle est accessible par le processeur principal pendant le démarrage RTC en sortant du mode Deep-Sleep.
  • 8 Ko de SRAM RTC, appelée mémoire RTC SLOW et accessible par le coprocesseur ULP lorsque le processeur est en sommeil profond (Deep-Sleep).
Architectures :

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

Architecture :

ESP32-D0WDQ6

Documentation technique Documentation technique

La question que l’on peut se poser est “quelle est le nombre de mesures que l’on peut stocker avec si peu d’espace”.

Si on prend ce tableau, un entier long nécessite un espace mémoire de 8 octets que l’on va arrondir à 10 octets pour avoir un ordre de grandeur réaliste, ce qui donne :

  • Avec 16 Ko (16384 octets), on pourra stocker plus de 1600 enregistrements.
  • Avec 8 Ko (8192 octets), on pourra stocker plus de 800 enregistrements.

Finalement, c’est déjà énorme !

Les modes de veille compatibles

La mémoire RTC est utilisable dans tous les modes d’alimentation à l’exception du mode Hibernation.

Active Modem-Sleep Light-Sleep Deep-Sleep Hibernation

Que peut-on stocker dans la mémoire RTC ?

Tous les types de variables C++ peuvent être stockés dans la mémoire RTC, y compris les structures comme nous allons le voir dans l’exemple.

Comment utiliser la mémoire RTC ?

Il n’y a quasiment rien à faire au niveau du programme. La seule chose est d’ajouter RTC_DATA_ATTRdevant la déclaration la variable que l’on souhaite placer dans la mémoire RTC.

Par exemple ici, on enregistrera le nombre de démarrage (réveil) de l’ESP32.

RTC_DATA_ATTR int recordCounter = 0;

Comme la variable est directement stockée dans la mémoire RTC, la valeur est automatiquement actualisée sans qu’il y ait besoin d’appeler une méthode particulière. Ici par exemple, on incrémente la valeur du compteur.

recordCounter++;

Le fonctionnement est beaucoup plus simple que l’EEPROM

Limitations

Il y a malheureusement une limitation à connaître avant de se lancer, la mémoire RTC est volatile, c’est à dire que les données sont effacée en cas de coupure d’alimentation ou après un Reset de la carte.

Il faudra donc utiliser cette approche pour un stockage temporaire de données non essentielles.

Un exemple de code Arduino à téléverser

Créer un nouveau croquis et coller le code ci-dessous. Si vous utilisez l’IDE Arduino, retirer la première ligne #include nécessaire pour compiler avec PlatformIO.

#include 
#include 
#include 

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() {
}

Fichier de configuration platformio.ini pour une carte de développement LoLin D32.

[env:lolin_d32]
platform = espressif32
board = lolin_d32
framework = arduino
monitor_speed = 115200
lib_deps = 
   525

Circuit

Nous allons utiliser un BMP180 (ou un BME280 pour avoir le taux d’humidité relative) pour collecter des mesures (températures, pression atmosphérique…) via le bus I2C. Ici, la broche SDA du BMP180 est connecté sur la broche 22, la broche SCL sur l’entrée 19. Une LED connectée sur la broche 34 sera allumée durant 500ms chaque fois que l’ESP32 se réveille et enregistre une nouvelle mesure.

yjvogjlwaujexnmjyxut-2917953

Comment fonctionne le code du projet ?

On va tout d’abord créer une structure destinée à recevoir les mesures du BMP180

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

Puis on réserve un espace mémoire sous la forme d’un tableau dont la taille est définie par la variable maxRecords.

En faisant précédé la déclaration du tableau par RTC_DATA_ATTR, on indique au compilateur que celui-ci sera stocké dans la mémoire RTC.

RTC_DATA_ATTR bmp180Records Records[maxRecords];

Les broches I2C (SDA et SCL) peuvent ne pas être exposées (disponibles) sur le connecteur GPIO. Heureusement, l’adaptation de la librairie Wire.h pour ESP32 permet d’attribuer manuellement les broches du bus I2C.

Wire.begin(I2C_SDA, I2C_SCL);

A chaque fois que le processeur se réveille, on enregistre les nouvelles mesures dans le tableau à la position du pointeur (recordCounter).

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

Que l’on incrémente pour le prochain réveil

recordCounter++;

Comme la taille du tableau de données est fixée par la variable maxRecords, il convient de réinitialiser le compteur pour se positionner au début du tableau. On vient en quelques lignes de code créer un buffer tournant.

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

Et après 5 enregistrements, on exporte au format CSV les données vers le port série.

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

Mises à jour

11/09/2020 Publication de l’article

Avez-vous aimé cet article ?