Den ESP32 schlafen legen, um Strom zu sparen

Mein letztes Projekt, das ich hier heranziehen will, um die Schlafmodi des ESP32 an einem praktischen Beispiel zu erklären, ging darum, eine Mikrowelle an einer Funksteckdose mit einem CYD ferngesteuert an- und auszuschalten (siehe Screenshot rechts).
Bei meiner mittlerweile betagten Mikrowelle lässt sich keine genaue Zeit mehr einstellen. Aber das könnt ihr in dem Artikel dazu nachlesen. Nur noch so viel: CYD steht für "Cheap Yellow Display" und ist ein ESP32 mit Touchscreen und ein bisschen mehr auf einer Platine.
Deep oder Light Sleep?
Mein eigentlicher Plan war eigentlich, das CYD an eine Powerbank anzuschließen. Dabei sollte es natürlich nicht immer laufen, sondern ich wollte in den 23 Minuten und 40 Minuten pro Tag, wo es nicht gebraucht wird, den Bildschirm ausschalten und den ESP32 in einen Schlafmodus versetzen, in dem er so gut wie keinen Strom verbrauchen würde, den sogenannten Deep Sleep. In diesem Schlafmodus soll der ESP32 nur so 10 bis 150 µA ziehen. Laut Datenblatt. Warum das am Ende dann doch nicht so richtig funktioniert, erkläre ich euch später.Ich wollte meinen CYD On/Off-Timer nach 30 Sekunden Nichtstun in den Schlafmodus schicken und dann mit einem Tap auf den Bildschirm wieder aufwecken. Aber ich war da ein bisschen naiv und ging zu optimistisch an die Sache.
Schnell stellte sich heraus, dass der extrem stromsparende Deep Sleep immer so eine Art Reset macht, wenn er wieder aufwacht. Was natürlich unschön ist. Dann kann ich im Prinzip auch einen Schalter zwischen Netzteil und CYD löten. Dann habe ich Null Stromverbrauch und auch jedesmal einen Reset. Der Deep Sleep war also schnell vom Tisch.
Es gibt aber noch einen Light Sleep Modus. Mit dem kann man im Prinzip da weitermachen, wo man aufgehört hat. Denn der Speicherinhalt bleibt erhalten. Das kostet natürlich ein wenig Strom und so läuft der ESP32 im Light Sleep noch so mit 1 bis 5 mA - auf dem Datenblatt. Später sollte sich herausstellen: Das gilt nicht für das CYD.
Kurzer Einschub für Interessierte: Deep Sleep mit Restaurierung nach Reset
Ein kurzer Einschub für Interessierte, der ein wenig tiefer geht und den man nicht unbedingt gelesen haben muss:Man kann im Prinzip schon aus dem Deep Sleep so erwachen, dass es da weitergeht, wo man aufgehört hat. Dazu muss man sich den Zustand mit allen veränderbaren Variablen in den sogenannten RTC-Speicher kopieren. Der heißt so, weil da auch die Real Time Clock dran angeschlossen ist. Dieser Speicherbereich geht auch im Deep Sleep nicht verloren. Beim ESP32-WROOM32 (also "classic") hat man da 8 kBytes von, was genügen sollte.
Bei der Startroutine (setup() im Arduino Framework) muss man dann prüfen, ob man neu startet oder aufwacht, weil man beim Schlafen gehen zum Beispiel eine bestimmte Variable, etwa DeepSleepActive, gesetzt hat. Und wenn das der Fall ist, die normalen Variablen wieder aus denen im RTC-Speicher hinterlegten wiederherstellen und dann an die Stelle im Programm springen, wo es vor dem Deep Sleep verlassen worden ist.
Das ist eine Heidenarbeit für ein paar Milliampere gegenüber den Light Sleep und nur selten den Aufwand wert. Zudem bietet das viel Potential, um Fehler zu machen und ist nicht leicht zu testen. Aber wenn man wirklich wenig super stromsparend sein muss, führt wohl kein Weg um den Deep Sleep und man muss in den sauren Apfel beißen. Außer beim CYD. Da läuft das anders, wie ihr später seht.
Light Sleep, Deep Sleep oder doch nur "Power safe"?
Von der Programmierung her unterscheidet sich Deep Sleep und Light Sleep gar nicht so sehr von einander: Man definiert vorher einen Aufweckmechanismus. Dann startet man den Sleep und wenn das Aufwachereignis eintritt, wird bei Light an dieser Stelle im Code weitergemacht, bzw. bei Deep Sleep ein Reset durchgeführt.Aufweckmechanismus
Als Aufweckmechanismus kommen drei Arten in Frage:1. Ein Timer:
esp_sleep_enable_timer_wakeup(uint64_t time_in_us) Der Vorteil dabei ist, dass der ESP32 auf jeden Fall aufwacht und dann voll da ist. Man kann damit also alle Konditionen überprüfen. Das ist bei einer externen Aufweckquelle nicht der Fall.
2. Flankenwechsel an einem Pin:
esp_sleep_enable_ext0_wakeup(gpio_num_t gpio, int level)Man kann auch
esp_sleep_enable_gpio_wakeup()Kleiner Einschub für das CYD: Das CYD kann einen Interrupt auf Pin 36 (XPT2046_IRQ) senden, wenn der Touchscreen gedrückt wird. Das kann auch ein Hardware-Interrupt sein, wenn man die entsprechende Library benutzt. Ich benutze aber die Bitbang-Library für das Touchdisplay, damit das CYD überhaupt gescheit funktioniert und sich nichts überschneidet - es ist kompliziert, lest meine Artikel für mehr Infos. Die aktualisiert den Pin 36 nur softwaremäßig, also nicht, wenn der ESP32 im Sleep-Modus ist, denn dann läuft das Programm für den Pin36-Update ja nicht.
esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode)esp_sleep_enable_touchpad_wakeup() und
esp_sleep_enable_uart_wakeup()
3. Über den Ultra Low Power (ULP) Coprozessor:
esp_sleep_enable_ulp_wakeup()Abschalten, was geht
Bevor wir den ESP32 wirklich ins Bett schicken, machen wir aus, was geht. Sinnbildlich gesprochen machen wir den Fernseher und überall das Licht aus, eben alles, was Strom braucht. Hier ein Beispiel für mein CYD-Projekt:- Die Hintergrundbeleuchtung braucht am meisten Strom. Schalten wir sie aus.
- Wir können auch das LVGL-Dialogsystem ausschalten. Ich habe aber festgestellt, dass es nicht wieder richtig startet. Das ist also nur etwas für den Deep Sleep Modus, wenn eh ein Reset durchgeführt wird.
- Wir können auch den TFT-Screen und dessen Controller in den Sleep-Modus schicken (nur deep)
- Wir schalten WiFi und Bluetooth aus, die brauchen auch viel Energie
- Wir können die serielle Schnittstelle ausschalten
- Man könnte sogar den Analog Digital Wandler abschalten. Das funktionierte bei mir aber nicht stabil. Außerdem braucht der nicht viel Strom.
ESP32 schlafen schicken
Nachdem sozusagen der Wecker gestellt ist, schicken wir den ESP32 jetzt ins Bett, entweder in einen leichten Schlaf mitesp_light_sleep_start()esp_deep_sleep_start()Schlaf gut und träum süß

Nachdem alles mögliche ausgeschaltet ist, sollte ein ESP32 nur noch wenig Milliampere oder sogar beim Deep Sleep nur ein Bruchteil eines Milliamperes Strom ziehen.
Man muss allerdings beim Light Sleep aufpassen, was man alles vorher abschaltet, damit es weiter gehen kann. Beim Deep Sleep ist das egal, der macht eh einen Reset. Aber beim Light Sleep muss man daran denken, alles wieder korrekt herzustellen: WiFi und Bluetooth wieder zu initialisieren und die Verbindungen wieder herzustellen, das LVGL-Subsystem wieder auf die Beine zu stellen (hat bei mir nicht funktioniert), den Bildschirm-Controller und Bildschirm selbst wieder anzuschalten, die Hintergrundbeleuchtung wieder anzuschalten etc. pp. Dabei sollte man sich immer fragen, wieviel Strom man spart und ob es die Stromersparnis den Aufwand aufwiegt.
Ich habe mit dem CYD ein bisschen experimentiert und mir eine Routine geschrieben, in dem ich wählen kann, wie tief ich meinen CYD schlafen schicke. Dabei gibt es drei Abstufungen:
- deep = 0: kein Hardware-Sleep, kein HW-IRQ nötig, wählen wenn an Netzgerät
- deep = 1; light sleep, Hardware-Interrupt nötig, wählen wenn an Powerbank
- deep = 2; deep sleep (mit Reset), wäre nicht ein mechanischer Schalter sinnvoller?
void enter_powersave() {
int deep = 0;
debug_print ("Entering Sleep / Power Save Mode "); debug_println(deep);
// CPU Frequenz reduzieren vor Sleep
// bringt nichts, macht er wohl schon von selbst setCpuFrequencyMhz(80);
// 1. LVGL stoppen
if (deep > 1) lv_deinit();
// 2. Display ausschalten
if (deep > 1) { tft.fillScreen(TFT_BLACK); delay(10); } // bringt nichts und zeigt nach light aufwachen nichts mehr an
setTftBrightness(0); delay(10);
if (deep > 1) { tft.writecommand(0x28); delay(10); } // Display off // bringt nichts und zeigt nach light aufwachen nichts mehr an
tft.writecommand(0x10); delay(10); // Display-Controller Sleep mode
// 3. ggf. Hardware-IRQ-Aufweck-Ereignis setzen
if (deep > 0 && Pin_WakeUpIRQ != 0) {
pinMode(Pin_WakeUpIRQ, INPUT_PULLUP); // alternativer Pin, der auf GND gezogen werden muss zum Aufwachen
esp_err_t ret = esp_sleep_enable_ext0_wakeup((gpio_num_t) Pin_WakeUpIRQ, 0); // LOW triggert Wakeup
if (ret != ESP_OK) { debug_println ("Konnte Aufwach-IRQ nicht setzen!"); debug_println("Error: " + String(ret));}
}
// 4. Andere Peripherals ausschalten
// eigentlich schon generell aus
WiFi.mode(WIFI_OFF);
btStop();
Serial.end();
//stürzt ab? adc_power_off();
// deep sleep macht nach Aufwachen einen Reset!
if (deep == 2) {
esp_deep_sleep_start(); // wenn wieder aufgeweckt: RESET
} else if (deep == 1) {
esp_light_sleep_start(); // bringt nochmal was, von 0.5W (50% TftBrightness) auf 0.1W
} else if (deep == 0) {
delay (1000); //erstmal Touchscreen wieder loslassen
while (!xptTouched()) { // warten bis zum nächsten Touch
delay(50);
}
}
//restlichen Dinge wieder an
Serial.begin(115200);
//stürzt ab adc_power_on();
// Ab hier nur noch für LIGHT, deep macht immer Reset ///////////////////////
debug_print ("Exiting Sleep / Power Save Mode "); debug_println(deep);
// Display wieder aktivieren
setTftBrightness(TftBrightness);
tft.writecommand(0x11); // Wake Display from sleep
delay(120); // Warten bis Display bereit
refreshLVGL();
reset_sleep_timer();
}- 490 mW - im Normalbetrief bei 50% Helligkeit
- 280 mW - deep = 0
- 70 mW - deep = 1
- 68 mW - deep = 2
Beim Design des CYD scheint einfach nicht an einen Deep Sleep gedacht worden zu sein. Ich habe da ein paar Verdachtsmomente, die zu überprüfen mir allerdings nicht sonderlich sinnvoll erscheint, weil ich eh nicht viel dran ändern kann:
- mit ist aufgefallen, das die kleine rote LED in der RGB-LED immer schwach leuchtet. Das kostet natürlich ein bisschen Strom, wenn die ständig leuchtet. Okay, die könnte man auslöten, aber viel bringen würde es nicht.
- Die Spannungswandler, die die 5V vom USB-Port zu 3.3 Volt umwandeln, sind wahrscheinlich immer an und sind halt billig (heißt ja auch Cheap Yellow Display). Das ginge besser und hier geht bestimmt einiges an Strom flöten.
- Was ist mit dem kleinen Audio-Verstärker oben auf dem Board? Könnte es sein, dass der auch im Standby, wenn er gar nicht richtig benutzt wird, Strom zieht?
Mein Fazit
Die ESP32 Sleep Modes sind toll, um einen ESP32 lange an einem Akku oder einer Powerbank zu betreiben, wenn er nur hin und wieder benutzt wird. Dazu muss das Board aber auch entsprechend designt sein.Für das CYD ist das allerdings nichts, denn hier ist das Board einfach nicht auf Energie sparen ausgelegt.
Darum betreibe ich mein Mikrowellen-Steuer-CYD auch an einem USB-Ladegerät. Zusammen mit der Funksteckdose sind das trotzdem unter 1 Watt im Powersave-Modus. Das läppert sich zwar zu unter 3 Euro Stromkosten im Jahr, aber das ist mir der extra Komfort wert.
Demo und Video
Hier ein kleines Demonstrations-Video für die On/Off-Timer-App für das CYD gemacht, dass die Dialoge und Timer in Aktion zeigt:Quellen, Literaturverweise und weiterführende Links
- ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Vorstellung Hardware und Pinout Yellow Display) - Einbinden der LVGL Library
- ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Dialoge entwerfen mit der LVGL Library
- ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display) - Utility V5 WebRadio und MP3-Player
- LVGL-Dokumentation für Version 9.1

- ESP32-CYD (Cheap Yellow Display) Projekt mit LVGL: 433 MHz On/Off-Timer Funk-Steckdosen-Steuerung
- Espressif ESP32-Doku: Sleep Modes

- Espressif ESP32-Doku: Ultra Low Power (ULP) coprocessor

- Espressif ESP32-Doku: ULP FSM Coprocessor Programming
