Mode d’endormissement de l’ESP32

Introduction

De nos jours, de nombreux systèmes sont bloqués dans des lieux peu accessibles par l’humain voire inaccessibles. Ces modules sont obligés de vivre sur batterie très longtemps. La consommation d’énergie est donc une problématique très importante pour les nouveaux systèmes. Pour réduire la consommation de vos systèmes, je vais vous présenter les modes d’endormissement sur ESP32. Pour comprendre cet article, il vous faut des bases en développement Arduino, des bases en développement sur microcontrôleur et des bases sur le fonctionnement d’un linker script. Si vous êtes en train de développer une nouvelle application basse consommation sur ESP32, cet article est pour vous.

L’ESP32 peut se diviser en 7 sous-modules :

  • Périphériques
  • Wifi
  • Bluetooth
  • Radio
  • Noyau et mémoire
  • Ultra Low Power coprocesseur (ULP)
  • RTC

Pour gagner de l’énergie certains de ces modules sont mis hors tension. Vous devez donc bien savoir ce que vous utilisez dans votre application pour choisir le bon mode d’endormissement.

Mode actif

Le mode actif est le mode normal de l’ESP32. L’ensemble des modules listés dans l’introduction sont actifs.

L’ESP32 consomme alors beaucoup d’énergie. Utiliser ce mode est donc déconseillé pour des systèmes sous batterie. Pour réduire la consommation de votre système dans ce mode, la seule solution qui s’offre à vous est de contrôler la fréquence de l’horloge du CPU.

Pour un fonctionnement normal, la consommation est approximativement comprise entre 160 et 240 mA.

Mode modem

Modules actifs

Fonctionnement du mode

Dans le mode Modem, les modules de Bluetooth et Wifi sont éteint, mais ils sont remis sous tension à intervalle régulier pour garder la connexion.

L’ESP32 peut être dans cet état que lorsqu’il est connecté à un routeur en mode station utilisant le mécanisme DTIM (Delivery Traffic Indication Message). La balise envois un message DTIM tous les n secondes et réveille l’ESP32. Il est impossible pour l’ESP32 d’envoyer des messages au routeur en dehors des périodes où il reçoit le DTIM. Le temps d’endormissement de l’ESP32 dépend de la configuration du routeur.

Pour ce mode, la consommation est approximativement comprise entre 20 et 25 mA.

Mode de sommeil léger

Modules actifs

Fonctionnement du mode

Le comportement de ce mode ressemble grandement au fonctionnement du mode précèdent. Pour réduire encore plus la consommation, le principe de “Clock Gating” est utilisé. Le Clock Gating consiste à ignorer ou à retirer le signal d’horloge quand le circuit n’est pas utilisé. Le CPU est donc en “pause” pendant ces périodes. Dans un CPU, le changement d’état des bascules flip-flop consomme de l’énergie. Le CLock Gating permet d’éviter le changement d’état de ces bascules et donc de rendre la consommation du CPU nulle. Par contre l’ULP et le module RTC continue a fonctionner normalement.

Lors de l’endormissement, l’état d’exécution est chargé entièrement en RAM. Quand l’ESP se réveille, l’exécution redémarre là où elle s’est stoppée.

Pour ce mode, la consommation est approximativement de 0.8 mA.

Exemple de code et explication

1
2
3
4
5
6
7
8
9
void setup() {  
 Serial.begin(115200);  
}    

void loop() {
 esp_sleep_enable_timer_wakeup(2000000); // config timer for wake up after 2 sec  
 int ret = esp_light_sleep_start(); // start timer and put ESP32 to sleep
 Serial.println("ESP awoken");
}

Un timer est configuré lors de l’appel de la fonction esp_sleep_enable_timer_wakeup. L’ESP32 s’endort et le timer est lancé avec l’appel de fonction esp_light_sleep_start. Vous devriez recevoir le print après 2 secondes quand l’ESP se réveille.

Mode de sommeil profond

Modules actifs

Fonctionnement du mode

Vous pouvez remarquer que lorsque ce mode est actif, la mémoire n’est plus sous tension. L’ensemble des données sont alors perdues. En effet lors de l’endormissement, la seule mémoire utilisable est la mémoire RTC.

Pour pouvoir utiliser la mémoire RTC, l’attribut RTC_DATA_ATTR ou RTC_RODATA_ATTR doit être précisé lors de la déclaration de variable.

Exemple :

RTC_DATA_ATTR int c = 0;

RTC_RODATA_ATTR int c = 0; //read only variable

Voici la définition de ces deux attributs :

#define RTC_DATA_ATTR _SECTION_ATTR_IMPL(".rtc.data", __COUNTER__)

#define RTC_RODATA_ATTR _SECTION_ATTR_IMPL(".rtc.rodata", __COUNTER__)

Nous pouvons voir que grâce à ces attributs, vous indiquez à votre linker de placer vos variables dans les zones .rtc.data et .rtc.rodata de la mémoire. Si nous regardons le code du linker script, ces zones de mémoires sont comprise dans la mémoire IRAM du RTC.

Pour les fonctions, l’attribut RTC_IRAM_ATTR doit être utilisé. Exemple :

void RTC_IRAM_ATTR function_in_rtc(void);

Voici la définition de cet attribut :

#define RTC_IRAM_ATTR _SECTION_ATTR_IMPL(".rtc.text", __COUNTER__)

Cet attribut indique la zone .text de la mémoire RTC. Cette partie de la mémoire est exécutable contrairement aux deux segments de mémoires précédents. Vous pouvez donc y mettre du code. Cette mémoire persiste dans un cycle d’endormissement profond et de réveil.

Le CPU est aussi hors tension, vous pouvez utiliser l’ULP pour continuer à lire des données de capteurs ou autres.

Quand l’ESP32 se réveil, comme le CPU et sa mémoire étaient éteint, votre application est relancé de 0. C’est-à-dire que le bootloader est lancé et pour du code Arduino, la fonction setup est appelé.

Avant le démarrage de l’esp, le “Deep Sleep Wake Stubs” peut se lancer. Ceci est un bout de code que vous pouvez personnaliser, pour plus d’info regardé dans la documentation. Ce bout de code est exécuté avant le bootloader.

Pour ce mode, la consommation est approximativement de 10 uA.

Exemple de code et explication

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
RTC_DATA_ATTR int n = 0;

void setup() {  
    Serial.begin(115200);  
    if (n == 0)
    {
        Serial.println("First Setup");
        n += 1;
        esp_sleep_enable_timer_wakeup(2000000);
        esp_deep_sleep_start();
    }
    else
    {
        Serial.println("ESP wake from Deep Sleep");
    }
}

void loop() {
}

Lors du lancement de votre code, la fonction setup est appelée une première fois. Lors de ce premier passage, n = 0 donc l’ESP passe en mode deep sleep. Quand le microcontrôleur se réveille, la fonction set up est appelé une deuxième fois. Cette fois n == 1 car n est présent dans la mémoire RTC qui n’est pas réinitialisé.

Mode hibernation

Dans ce mode, seulement un RTC timer et des gpios RTC sont actifs.

Seuls ceux-ci peuvent réveiller l’ESP32. Par contre la consommation de votre ESP32 s’élève à 5 uA d’après la datasheet².

Réveiller votre ESP32

Pour réveiller l’esp32, différentes méthodes sont possible. Je vais ici vous parler des timers et du réveil par signal externe.

Timer

Nous avons déjà vu dans les exemples précèdent la fonction esp_sleep_enable_timer_wakeup(temps en us)

Celle-ci permet de démarrer un timer qui réveillera l’esp32 après n micro-secondes. De plus, cette fonction est utilisable pour tous les modes de sommeil, car certains timers sont toujours disponible quel que soit le mode d’endormissement.

Signal externe

2 possibilités s’offrent à vous :

  • EXT0 pour utiliser qu’un seul gpio.
  • EXT1 pour plusieurs gpios.

Pour EXT0, la fonction esp_sleep_enable_ext0_wakeup(gpio_pin, gpio_state) permet de réveiller votre MCU quand le pin gpio_pin (numéro du pin) passe à l’état gpio_state (1 ou 0).

Exemple :

1
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1);

Votre ESP se réveillera quand le pin 33 passe à l’état haut.

Pour EXT1, il faut utiliser la fonction esp_sleep_enable_ext0_wakeup(pin_mask, level_mode) doit être utilisé. Pin_mask est un entier non signé de 64 bits. Les 40 premiers bits de poids faibles représentent chacun 1 pin (de 0 à 39). Level_mode peut prendre les valeurs :

  • ESP_EXT1_WAKEUP_ALL_LOW : réveil quand tous les pins sont à l’état bas.
  • ESP_EXT1_WAKEUP_ANY_LOW : réveil quand un des pins est à l’état bas. Cette option est dépréciée après l’ESP32.
  • ESP_EXT1_WAKEUP_ANY_HIGH : réveil quand un des pins est à l’état haut.

Exemple :

1
esp_sleep_enable_ext1_wakeup(0x300000000,ESP_EXT1_WAKEUP_ANY_HIGH);

Ce code permet de réveiller votre ESP quand le pin 33 ou le pin 32 passe à l’état haut.

Warning

Quand votre ESP est en sommeil profond, vous êtes obligé d’utiliser les pins RTC qui sont les seuls à être connecté au module RTC.

Cause du réveil

Lors du réveil du MCU, vous pouvez récupérer la raison qui a provoqué l’événement. Pour faire ceci, il faut utiliser la fonction esp_sleep_get_wakeup_cause() comme dans l’exemple ci-dessous :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
esp_sleep_wakeup_cause_t wakeup_reason;

wakeup_reason = esp_sleep_get_wakeup_cause();

switch(wakeup_reason)
{
    case ESP_SLEEP_WAKEUP_EXT0 : 
        Serial.println("Wakeup caused by external signal using RTC_IO"); 
        break;
    case ESP_SLEEP_WAKEUP_EXT1 : 
        Serial.println("Wakeup caused by external signal using RTC_CNTL"); 
        break;
    case ESP_SLEEP_WAKEUP_TIMER :
        Serial.println("Wakeup caused by timer");
        break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD :
        Serial.println("Wakeup caused by touchpad");
        break;
    case ESP_SLEEP_WAKEUP_ULP :
        Serial.println("Wakeup caused by ULP program");
        break;
    default :
        Serial.printf("Wakeup was caused by other reason: %d\n", wakeup_reason);
        break;
}

Pour voir l’ensemble des causes veuillez regarder ici.

Conclusion

Ce tutoriel est fini, vous êtes maintenant capable de comprendre la différence entre les modes d’endormissement de l’ESP32 et de créer un système qui utilise ces modes. Comme nous l’avons vu, il est simple de grandement réduire la consommation d’un système sur ESP32 en l’endormant. Par exemple, le mode actif est 32 000 fois plus gourmand que le mode hibernation. Mais il faut rester conscient des besoins de votre application lors de l’optimisation de votre consommation. Certains modes réduisent la réactivité de votre système et désactivent certains périphériques vitaux pour votre application.

Sources