Una delle tematiche principali per dispositivi embedded è il consumo energetico. Se infatti il dispositivo che si sta realizzando dovrà essere alimentato a batteria, è necessario ridurre al minimo il consumo di corrente in modo da massimizzare l’autonomia (= il tempo di funzionamento prima che sia necessario sostituire o ricaricare la batteria).
Il chip esp32 offre 5 diverse modalità di consumo energetico (power modes). La modalità di funzionamento “nomale” prende il nome di active mode; in tale modalità tutte le funzionalità del chip sono disponibili. Iniziando a ridurre la velocità di funzionamento e disattivando periferiche e cores, si passa a diverse modalità di risparmio energetico, riassunte nel seguente schema:
In questo primo articolo relativo al risparmio energetico del chip esp32, vi parlerò della modalità deep sleep.
Deep sleep
Il framework esp-idf supporta attualmente due modalità di risparmio ergetico: light sleep e deep sleep. Tra le due, la modalità deep sleep è quella che offre un risparmio maggiore di energia. In tale modalità vengono spente:
[badlist]
- entrambe le CPU
- la maggior parte della memoria RAM
- tutte le periferiche
[/badlist]
sono invece di default mantenute in esecuzione:
[checklist]
- il controller RTC
- le periferiche RTC, incluso il coprocessore ULP
- Le memorie RTC lenta e veloce (slow e fast)
[/checklist]
L’attivazione della modalità deep sleep avviene con il comando esp_deep_sleep_start(), mentre è possibile “risvegliarsi” (wakeup) da tale modalità in base a diversi eventi:
Al risveglio dalla modalità deep sleep, avviene un nuovo boot. E’ molto importante comprendere quindi che il programma non riprende dal punto in cui è stato chiamato il metodo esp_deep_sleep_start().
Vediamo nel dettaglio la configurazione e l’utilizzo di due eventi: di touch pad e ULP vi parlerò in un prossimo articolo.
Timer
L’evento di wakeup più semplice è quello che utilizza un timer del controller RTC. Utilizzando il metodo:
esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us) |
è possibile risvegliare il chip esp32 dopo il numero di microsecondi specificati come parametro. Il metodo deve essere chiamato prima di attivare la modalità deep sleep:
// wakeup after 10 seconds esp_sleep_enable_timer_wakeup(10000000); esp_deep_sleep_start(); |
I/O triggers
In un precedente articolo vi ho parlato della possibilità di avere interrupts al cambio di stato di uno dei pin digitali del chip esp32. Possiamo sfruttare una funzionalità simile per risvegliare il chip esp32 dallo sleep.
Con il metodo
esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) |
è possibile attivare il wakeup se avviene un cambio di stato (level) del pin specificato (gpio_num).
I pin utilizzabili sono soltanto quelli con funzionalità RTC (0, 2, 4, 12-15, 25-27, 32-39) e i livelli sono 0 (= basso) o 1 (alto). Se ad esempio vogliamo configurare il risveglio da deep sleep quando il pin 4 ha uno stato logico basso, scriveremo:
esp_sleep_enable_ext0_wakeup(4, 0); |
E’ disponibile anche un metodo per monitorare diversi pins:
esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode) |
i pin (anche per tale metodo è possibile specificare solo quelli sopra elencati) vanno indicati come bitmask, mentre le modalità possibili sono:
- ESP_EXT1_WAKEUP_ALL_LOW = wakeup quanto tutti i pins sono a livello logico basso
- ESP_EXT1_WAKEUP_ANY_HIGH = wakeup quando almeno un pin è a livello logico alto
Dopo il risveglio…
Se sono stati attivati più eventi di wakeup, è possibile conoscere quale evento ha causato il risveglio con:
esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause() |
Le costanti possibili sono:
Per l’evento ext1_wakeup, è possibile conoscere la bitmask dei pin che hanno causato il risveglio con:
uint64_t esp_sleep_get_ext1_wakeup_status() |
Memoria
Come spiegato sopra, in deep sleep viene preservato il contenuto delle memorie RTC fast e RTC slow. E’ quindi possibile utilizzare tali aree di memoria per memorizzare dati da conservare durante lo sleep.
Per indicare al compilatore che una variabile deve essere memorizzata nella RTC slow memory è sufficiente usare l’attributo RTC_DATA_ATTR, oppure RTC_RODATA_ATTR se tale variabile è in sola lettura (read only):
RTC_DATA_ATTR static time_t last; |
Demo
Ho preparato un programma (il codice sorgente è disponibile nel mio repository Github) che mostra il funzionamento della modalità deep sleep e due diversi eventi di wake up (sottitoli in italiano disponibili):
i have trouble with the deepsleep of the esp32 (huzzah32)
I hope any one of you could help me.
i am using this:
switch (esp_deep_sleep_get_wakeup_cause()) {
case ESP_DEEP_SLEEP_WAKEUP_EXT1: {
uint64_t wakeup_pin_mask = esp_deep_sleep_get_ext1_wakeup_status();
if (wakeup_pin_mask != 0) {
int pin = __builtin_ffsll(wakeup_pin_mask) – 1;
printf(“Wake up from GPIO %d\n”, pin);
} else {
printf(“Wake up from GPIO\n”);
}
break;
}
and get the following error:
“error: esp_deep_sleep_get_ext1_wakeup_status() not declared in this scope”
I include deepsleep:
#include
Did anyone had an hint for me?
Thanks a lot!!
Regards,
Stivi
Hi, it seems they changed the method to esp_sleep_get_ext1_wakeup_status()
Thanks a lot Luca! You made my day!
I haven´t seen it!
It´s working now!
Ciao Luca,
Grazie del tuo video, mi sarà molto di aiuto nel progettino che sto realizzando. Vorrei costruire un tastierino wireless bluetooth usando un comune matrix keypad a membrana e ESP32. Il mio problema è che vorrei sfruttare il deep sleep e risvegliare il dispositivo solo quando l’utente digita qualche tasto. Mi accontenterei anche di perdere la prima cifra digitata eventualmente. Tu che consigli mi daresti per realizzare questa cosa? Grazie mille!
Ciao Alessandro, quei tastierini normalmente sono gestiti come “matrice”, quindi penso si possa usare il wake sui pin di I/O a cui hai collegato il tastierino! buon diverimento!
Si certo, però il problema è che dato che sono gestiti a matrice, per poter capire quale tasto è stato premuto, si cicla sulle righe (collegate ai pin di output) e si da livello logico alto, andando poi a leggere le colonne per capire quale tasto è stato premuto. Per questo motivo, diversamente dal pushbutton che è sempre “alimentato”, gli switch della matrice sono alimentati a rotazione ma solo mentre il codice è in esecuzione. Forse ho capito male io, me lo confermi? Grazie ancora!
Any compatibility with Time.h Library?
Hi, if you’re referring to Arduino Time library, sorry but my tutorials are not for Arduino
Yes, this library is for Arduino, but it can use with ESP32.
In general words, ¿can we store real time variables in deep-sleep mode?
Ciao,
avrei bisogno di un consiglio.
Sto cercando di realizzare la classica stazione meteo con una scheda Lolin32 Lite, collegata ad un oled 0,91, un sensore bme280 e un sensore mq-2.
A livello di codice il tutto funziona ma il problema nasce quando mando in deep-sleep la scheda.
Vorrei spegnere anche le periferiche, soprattutto i due led sul sensore MQ-2.
Non ho trovato un modo per disabilitare i 3,3v sulla scheda; allora pensavo di utilizzare il comando digitalWrite(pin, state) passando da High a Low.
Però dopo aver attivato digitalWrite(32, HIGH) quando vado a leggere la tensione sul pin mi da solo 1,55v e lo stesso se scelgo altri pin come il 15 o il 19.
Pensavo ad un difetto della scheda quindi ho provato con una seconda board nuova ma ottengo lo stesso valore di 1,55v
Puoi aiutarmi?
Grazie
ciao Massimo! Iniziamo dai sensori, normalmente questi offrono un comando per entrare in deep sleep… quello che faccio io (ad esempio con un display ST7735) è mandare il comando di sleep prima di mettere in sleep il microprocessore. In alternativa puoi usare dei pin per comandare direttamente l’alimentazione di sensori e LED. Devi solo stare attento che quando metti l’esp32 in deep sleep lo stato dei pin è normalmente perso. Per mantenerlo devi richiederlo con il metodo gpio_hold_en()
C’è uno sviluppo nel mio problema.
Quando collego il sensore MQ-2 ad un pin che non sia il Vcc, non so perchè ma al sensore, da quel pin in HIGH, non arrivano 3,3v ma solo 1,55v e quindi non si accende.
Penso che sia un problema di assorbimento elettrico eccessivo, perchè in realtà sul pin in HIGH escono regolarmente i 3,3v ma solo se non c’è il sensore MQ collegato a quel pin.
Ho fatto alcuni esperimenti ed ho scoperto che se voglio alimentare un sensore MQ non dal pin Vcc, devo prelevare corrente contemporaneamente da 2 pin impostati come Output in High.
Comunque, alla fine, ripensandoci, addormentare un sensore del gas non ha nessun senso, viene a mancare la funzione di monitoraggio continuo e poi manca il preriscaldamento del sensore, perciò lo elimino dal progetto.
Riporto questa mia breve esperienza con Lolin32 Lite perchè possa essere utile anche ad altri.
Ciao 🙂
massimo confermo che la corrente disponibile sui PIN (max 40mA da datasheet) non è sicuramente sufficiente per alimentare sensori etc… si usa normalmente per pilotare un transistor che – come se fosse un interruttore – alimenta o meno il sensore direttamente da Vcc.