Economiser de l’énergie sur l’ESP32 grâce au Deep Sleep

L’ESP32 possède un mode d’économie d’énergie appelé Deep Sleep qui permet « d’endormir l’ESP32 ». C’est un peu comme quand on met en veille un ordinateur pour économiser de l’énergie. Pendant ce mode, l’ESP32 peut effectuer des tâches simples et se faire réveiller pour effectuer des tâches plus complexes.

A quoi ça sert ?

Ce mode d’économie d’énergie est utile quand l’ESP32 est alimenté par batterie et que l’ESP32 « travaille » ponctuellement. (Par exemple, lire une valeur d’un capteur et l’envoyer par WiFi toutes les 10 minutes). Si l’ESP32 est allumé 24h/24, la batterie sera très vite déchargée. Avec le mode Deep Sleep, les batteries dureront beaucoup plus longtemps.

Le mode Deep Sleep plonge l’ESP32 dans un état primitif

En effet, en mode Deep Sleep, les 2 CPU de l’ESP32 ne fonctionnent plus et c’est le processeur ULP (Ultra Low Processor) qui prend le relais. C’est un processeur qui consomme très peu d’énergie et qui peut exécuter certaines actions. La Flash et la RAM ne sont plus alimentées non plus, seule la mémoire RTC est encore alimentée et peut être utilisée. Le Wifi et le Bluetooth sont aussi bien sûr désactivés. Grâce à cette mémoire RTC on peut toujours accéder aux périphériques, interruptions, timers et capteurs internes.

Sources de réveil

Après avoir mis l’ESP32 en mode Deep Sleep, il y a plusieurs moyens de le réveiller :
  • Utiliser un timer (chronomètre) interne pour réveiller l’ESP32 à un moment choisi (réveil interne)

  • Utiliser les capteurs capacitifs tactiles

  • Utiliser les pins RTC

Note

On peut combiner différentes sources de réveil.

Avertissement

Il est aussi possible d’activer le mode Deep Sleep sans avoir configuré de sources de réveil. Dans ce cas l’ESP32 sera indéfiniment en mode Deep Sleep jusqu’à qu’on fasse un reset manuel (ou en reflashant la carte). On ne peut donc pas « briquer » l’ESP32 avec le mode Deep Sleep.

Utilisation

Lorsqu’on veut utiliser le mode Deep Sleep, il faut penser à :
  • Configurer les sources de réveil de l’ESP32

  • Eventuellement choisir quels périphériques vous voulez éteindre ou garder pendant le mode Deep Sleep. Par défaut, l’ESP32 éteint tous les périphériques qui ne sont pas nécessaires pour détecter la demande de réveil.

  • Utiliser la fonction esp_deep_sleep_start() pour rentrer dans le mode Deep Sleep.

Utiliser un Timer comme source de réveil

Voici le code utilisé pour tester le timer comme source de réveil :

#define uS_TO_S_FACTOR 1000000
#define TIME_TO_SLEEP  5

RTC_DATA_ATTR int bootCount = 0;

void print_wakeup_reason(){
   esp_sleep_wakeup_cause_t source_reveil;

   source_reveil = esp_sleep_get_wakeup_cause();

   switch(source_reveil){
      case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Réveil causé par un signal externe avec RTC_IO"); break;
      case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Réveil causé par un signal externe avec RTC_CNTL"); break;
      case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Réveil causé par un timer"); break;
      case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Réveil causé par un touchpad"); break;
      default : Serial.printf("Réveil pas causé par le Deep Sleep: %d\n",source_reveil); break;
   }
}

void setup(){
   Serial.begin(115200);

   ++bootCount;
   Serial.println("----------------------");
   Serial.println(String(bootCount)+ "eme Boot ");

   //Affiche la raison du réveil
   print_wakeup_reason();

   //Configuration du timer
   esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
   Serial.println("ESP32 réveillé dans " + String(TIME_TO_SLEEP) + " seconds");

   //Rentre en mode Deep Sleep
   Serial.println("Rentre en mode Deep Sleep");
   Serial.println("----------------------");
   delay(100);
   esp_deep_sleep_start();
   Serial.println("Ceci ne sera jamais affiché");
}

void loop(){
}

La fonction print_wakeup_reason() affiche la source de réveil de l’ESP32. Lors du 1er boot de l’ESP32, le réveil n’a pas été causé par le Deep Sleep mais par le « Hard resetting via RTS pin… » sur l’Arduino IDE. Ensuite lors des boots suivants, l’ESP32 est réveillé du deep sleep grace à un timer toutes les 5 secondes.

On peut changer la durée du Deep Sleep en modifiant le timer avec la fonction esp_sleep_enable_timer_wakeup() .

ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
----------------------
1eme Boot
Réveil pas causé par le Deep Sleep: 0
ESP32 réveillé dans 5 seconds
Rentre en mode Deep Sleep
----------------------
ets Jun  8 2016 00:22:57

rst:0x5 (DEEPSLEEP_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
----------------------
2eme Boot
Réveil causé par un timer
ESP32 réveillé dans 5 seconds
Rentre en mode Deep Sleep
----------------------

Utiliser un Touchpad comme source de réveil

Voici le code utilisé pour réveiller l’ESP32 avec les touchpads :

#define seuil 30 //Seuil de détection pour le capteur capacitif

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;

void fonction_isr(){
}

void setup(){
   Serial.begin(115200);

   ++bootCount;
   Serial.println("----------------------");
   Serial.println(String(bootCount)+ "eme Boot ");

   //Affiche la source du reveil et le numéro du touchpad
   print_wakeup_reason();
   print_wakeup_touchpad();

   //Configuration d'une interruption pour le touchpad T0 (GPIO4)
   touchAttachInterrupt(T0, fonction_isr, seuil);

   //Active le réveil par les touchpads
   esp_sleep_enable_touchpad_wakeup();

   //Rentre en mode Deep Sleep
   Serial.println("Rentre en mode Deep Sleep");
   Serial.println("----------------------");
   esp_deep_sleep_start();
}

void loop(){
}


void print_wakeup_reason(){
   esp_sleep_wakeup_cause_t source_reveil;

   source_reveil = esp_sleep_get_wakeup_cause();

   switch(source_reveil){
      case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Réveil causé par un signal externe avec RTC_IO"); break;
      case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Réveil causé par un signal externe avec RTC_CNTL"); break;
      case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Réveil causé par un timer"); break;
      case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Réveil causé par un touchpad"); break;
      default : Serial.printf("Réveil pas causé par le Deep Sleep: %d\n",source_reveil); break;
   }
}


void print_wakeup_touchpad(){
   touch_pad_t pin;
   touchPin = esp_sleep_get_touchpad_wakeup_status();
   switch(touchPin){
      case 0  : Serial.println("Fil touché au GPIO 4"); break;
      case 1  : Serial.println("Fil touché au GPIO 0"); break;
      case 2  : Serial.println("Fil touché au GPIO 2"); break;
      case 3  : Serial.println("Fil touché au GPIO 15"); break;
      case 4  : Serial.println("Fil touché au GPIO 13"); break;
      case 5  : Serial.println("Fil touché au GPIO 12"); break;
      case 6  : Serial.println("Fil touché au GPIO 14"); break;
      case 7  : Serial.println("Fil touché au GPIO 27"); break;
      case 8  : Serial.println("Fil touché au GPIO 33"); break;
      case 9  : Serial.println("Fil touché au GPIO 32"); break;
      default : Serial.println("Réveil pas causé les touchpads"); break;
   }
}

La fonction print_wakeup_reason() affiche la source de réveil de l’ESP32 et la fonction print_wakeup_touchpad() affiche le numéro du touchpad utilisé. On ajoute une interruption sur le touchpad T0 (GPIO4) avec la fonction touchAttachInterrupt() Lors du 1er boot de l’ESP32, le réveil n’a pas été causé par le Deep Sleep mais par le « Hard resetting via RTS pin… » sur l’Arduino IDE.

ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
----------------------
1eme Boot
Réveil pas causé par le Deep Sleep: 0
Réveil pas causé les touchpads
Rentre en mode Deep Sleep
----------------------
ets Jun  8 2016 00:22:57

rst:0x5 (DEEPSLEEP_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
----------------------
2eme Boot
Réveil causé par un touchpad
Fil touché au GPIO 4
Rentre en mode Deep Sleep
-------------

Utiliser un pin GPIO comme source de réveil

On peut aussi utiliser les pins GPIO avec un bouton poussoir pour réveiller l’ESP32 du Deep Sleep.

Avertissement

On ne peut utiliser que les pins RTC_GPIO (encart en bleu clair)

../../_images/full_pinout_Wroom_rev2.png

Pinout de la carte uPesy ESP32 Wroom Devkit

Dans cet exemple, on utilise un bouton poussoir avec une résistance pulldown externe relié au pin 33 :

../../_images/DeepSleep0.png
RTC_DATA_ATTR int bootCount = 0;

void setup(){
   Serial.begin(115200);

   ++bootCount;
      Serial.println("----------------------");
      Serial.println(String(bootCount)+ "eme Boot ");

   //Affiche la source du reveil
      print_wakeup_reason();

   //Configure le GPIO33 comme source de réveil quand la tension vaut 3.3V
   esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,HIGH);

      //Rentre en mode Deep Sleep
      Serial.println("Rentre en mode Deep Sleep");
      Serial.println("----------------------");
      esp_deep_sleep_start();
}

void loop(){}

void print_wakeup_reason(){
   esp_sleep_wakeup_cause_t source_reveil;

   source_reveil = esp_sleep_get_wakeup_cause();

   switch(source_reveil){
      case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Réveil causé par un signal externe avec RTC_IO"); break;
      case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Réveil causé par un signal externe avec RTC_CNTL"); break;
      case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Réveil causé par un timer"); break;
      case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Réveil causé par un touchpad"); break;
      default : Serial.printf("Réveil pas causé par le Deep Sleep: %d\n",source_reveil); break;
   }
}

On obtient dans le moniteur série :

ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
----------------------
1eme Boot
Réveil pas causé par le Deep Sleep: 0
Rentre en mode Deep Sleep
----------------------
ets Jun  8 2016 00:22:57

rst:0x5 (DEEPSLEEP_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
----------------------
2eme Boot
Réveil causé par un signal externe avec RTC_IO
Rentre en mode Deep Sleep
-----------------

Effacement de la RAM

Quand le mode Deep Sleep est activé le CPU et la RAM n’est plus alimenté. Cela implique que lorsque l’ESP32 se réveille du Deep Sleep toutes les variables contenues dans la RAM sont effacées. Heureusement, il existe une mémoire RAM RTC de 8 Ko qui reste allumée pendant le Deep Sleep. Pour stocker des variables dans cette mémoire, il faut rajouter l’attribut RTC_DATA_ATTR .

Par exemple, la variable qui stocke le nombre de rédemarrage de l’ESP32 dans les exemples est stocké dans cette mémoire.

RTC_DATA_ATTR int bootCount = 0;