ESP32 Deep Sleep: Aufwachquellen, Stromverbrauch und Akkulaufzeit
ESP32 Deep Sleep verständlich erklärt: Strom in µA je Schlafmodus, Aufwecken per Timer, Taster, PIR und Touch (ext0/ext1), RTC-Speicher und 18650-Laufzeit.
ESP32 Deep Sleep (Tiefschlaf) ist der Energiesparmodus, in dem der ESP32 seine Prozessorkerne, das WLAN- und Bluetooth-Funkmodul und den Hauptspeicher abschaltet und nur den kleinen RTC-Bereich weiterlaufen lässt. In diesem Modus sinkt der Verbrauch des Chips von 240 mA beim Senden über WLAN auf etwa 10 µA, also auf ein Vierundzwanzigtausendstel. Das Board wacht auf, wenn die eingestellte Zeit abgelaufen ist, ein Taster gedrückt wird, ein PIR-Sensor eine Bewegung erkennt oder ein Pin berührt wird, erledigt seine Aufgabe in wenigen Sekunden und schläft wieder ein. So laufen batteriebetriebene Wetterstationen, Gartensensoren, Türalarme und Messgeräte mit einem 18650-Akku monatelang statt nur ein paar Tage.
In dieser Anleitung lernst du die Schlafmodi des ESP32 mit ihren echten Stromwerten kennen und erfährst, wie du das Board per Timer, Taster, PIR-Sensor (ext0 und ext1) oder Berührung weckst, wie du Zähler und Messwerte im RTC-Speicher ablegst, der den Schlaf übersteht, warum Entwicklungsboards selbst im Schlaf Milliampere verbrauchen und wie du Schritt für Schritt ausrechnest, wie viele Monate ein einzelner 18650-Akku hält. Die Arduino-Sketche sind für Version 3.1 oder neuer des ESP32-Arduino-Cores geschrieben; am Ende siehst du, wie das Gleiche mit MicroPython funktioniert.

Was ist ESP32 Deep Sleep und wie funktioniert er?
Im ESP32 gibt es zwei getrennte Welten. Die erste ist der Hauptbereich mit den Prozessorkernen, 520 KB RAM und dem WLAN- und Bluetooth-Funk. Die zweite ist der RTC-Bereich mit einem eigenen kleinen Timer, 8 KB RTC-FAST- und 8 KB RTC-SLOW-Speicher, den RTC-GPIO-Pins und einem extrem sparsamen Koprozessor namens ULP. Im Tiefschlaf wird der Hauptbereich komplett abgeschaltet, nur der RTC-Bereich läuft weiter. Wenn es Zeit zum Aufwachen ist, startet der Chip eigentlich neu: Der Bootloader läuft, das Programm wird erneut aus dem Flash geladen und setup() beginnt von vorn. Deshalb erledigt ein Deep-Sleep-Programm seine ganze Arbeit in setup(), loop() läuft nie. Normale Variablen werden bei jedem Aufwachen zurückgesetzt; nur Variablen im RTC-Speicher bleiben erhalten.
ESP32-Schlafmodi und Stromverbrauch (µA)
| Modus | Was läuft weiter? | Strom (ESP32-Chip) |
|---|---|---|
| Aktiv: WLAN senden (802.11b, 19,5 dBm) | CPU und Funk | 240 mA |
| Aktiv: WLAN empfangen und lauschen | CPU und Funk | 95–100 mA |
| Modem-Sleep, 240 MHz | CPU läuft, Funk aus | 30–68 mA |
| Modem-Sleep, 80 MHz | CPU läuft langsamer, Funk aus | 20–31 mA |
| Light Sleep (leichter Schlaf) | CPU angehalten, RAM bleibt erhalten | 0,8 mA |
| Deep Sleep mit ULP | RTC-Bereich und ULP-Koprozessor | 150 µA |
| Deep Sleep (Tiefschlaf) | RTC-Timer und RTC-Speicher | 10 µA |
| Hibernation | Nur RTC-Timer und einige RTC-Pins | 5 µA |
| Aus (EN-Pin LOW) | Nichts | 1 µA |
Die Werte stammen aus dem ESP32-Datenblatt von Espressif und gelten nur für den Chip selbst. Zwei Punkte in der Tabelle sind wichtig. Erstens macht das Programm im Light Sleep an der Stelle weiter, an der es angehalten hat, und alles im RAM bleibt erhalten, der Verbrauch ist aber achtzigmal höher als im Tiefschlaf; das passt für Pausen von wenigen Sekunden. Im Deep Sleep startet das Programm neu, dafür sinkt der Verbrauch auf Mikroampere, die richtige Wahl für Wartezeiten von Minuten oder Stunden. Zweitens bleibt in keinem der beiden Schlafmodi die WLAN- oder Bluetooth-Verbindung bestehen; nach jedem Aufwachen musst du dich neu verbinden. Neuere Chips liegen etwas anders: Der ESP32-C3 braucht im Tiefschlaf etwa 5 µA, der ESP32-S3 etwa 7–8 µA.
Wenn du noch weniger Verbrauch willst, kannst du auch die RTC-Peripherie und den RTC-Speicher abschalten. Mit esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF) und derselben Zeile für ESP_PD_DOMAIN_RTC_SLOW_MEM und ESP_PD_DOMAIN_RTC_FAST_MEM kommt der Chip dem Hibernation-Modus nahe und braucht etwa 5 µA. Dann gehen die Variablen im RTC-Speicher verloren, und nur der Timer oder ext1 kann das Board wecken. In den meisten Projekten ist der Unterschied zwischen 10 µA und 5 µA neben dem Rest des Boards bedeutungslos; löse zuerst die Probleme aus dem nächsten Abschnitt.
Warum verbrauchen Entwicklungsboards im Tiefschlaf Milliampere?
Wenn du deinen Sketch hochlädst und ein Multimeter anschließt, siehst du oft 5–15 mA statt 10 µA. Schuld ist nicht der Chip, sondern die Zusatzbauteile auf dem Board. Die meisten günstigen DevKit- und NodeMCU-32S-Boards nutzen einen AMS1117-Spannungsregler, der 5 V auf 3,3 V herunterregelt und schon ohne Last typischerweise 5 mA Ruhestrom zieht. Dazu kommen eine ständig leuchtende Power-LED und der USB-Seriell-Chip (CP2102 oder CH340), der mit dem Computer kommuniziert. Zusammen sind das meist rund 8 mA, die dem Akku selbst bei schlafendem ESP32 etwa 190 mAh pro Tag entziehen; ein 18650-Akku mit 3000 mAh hält mit so einem Board kaum zwei Wochen. Zum Messen schaltest du das Multimeter in Reihe zwischen Akku und Schaltung und wechselst im Schlaf in den µA-Bereich; Details findest du in der Anleitung Multimeter richtig benutzen.
| Verbraucher | Strom im Schlaf | Lösung |
|---|---|---|
| ESP32-Modul (Tiefschlaf) | ≈10 µA | Schon niedrig, nichts zu tun |
| AMS1117-Spannungsregler | Typisch 5 mA (Ruhestrom) | MCP1700 (1,6 µA Ruhestrom) oder HT7333 (rund 4 µA) verwenden |
| Power-LED | 1–3 mA | LED oder ihren Vorwiderstand auslöten |
| USB-Seriell-Chip (CP2102, CH340) | Je nach Board, kann den mA-Bereich erreichen | Board ohne USB-Chip oder nacktes Modul verwenden |
| Spannungsteiler 100 kΩ + 100 kΩ am Akku | 21 µA (bei 4,2 V) | 1 MΩ + 1 MΩ verwenden oder den Teiler per MOSFET nur zum Messen einschalten |
| Dauerhaft versorgte Sensoren | Je nach Sensor µA bis mA | Sensor über einen GPIO oder MOSFET nur während der Messung versorgen |
Dein erster Deep-Sleep-Sketch: Aufwachen per Timer
- Installiere in der Arduino IDE über den Boardverwalter das Paket esp32 by Espressif Systems (Version 3.1 oder neuer) und wähle ESP32 Dev Module als Board.
- Lade den folgenden Sketch hoch und öffne den seriellen Monitor mit 115200 Baud.
- Das Board wacht alle 10 Sekunden auf; beobachte, wie der Boot-Zähler steigt und als Weckgrund Timer ausgegeben wird.
- Drücke die EN-Taste: Der Zähler springt auf 1 zurück, weil das ein normaler Reset und kein Aufwachen aus dem Tiefschlaf ist.
- Setze TIME_TO_SLEEP in deinem echten Projekt auf 600 (10 Minuten) oder 3600 (1 Stunde).
1// ESP32 deep sleep: zamanlayıcıyla uyanma ve RTC sayaç / timer wake-up and RTC counter
2#define uS_TO_S_FACTOR 1000000ULL // 1 saniye = 1.000.000 µs / 1 second = 1,000,000 µs
3#define TIME_TO_SLEEP 10 // uyku süresi, saniye / sleep time in seconds
4
5RTC_DATA_ATTR int bootCount = 0; // RTC belleğinde, derin uykuda silinmez / in RTC memory, kept in deep sleep
6
7void printWakeupReason() {
8 esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
9 switch (cause) {
10 case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Zamanlayici / Timer"); break;
11 case ESP_SLEEP_WAKEUP_EXT0: Serial.println("ext0 (tek pin / single pin)"); break;
12 case ESP_SLEEP_WAKEUP_EXT1: Serial.println("ext1 (coklu pin / multiple pins)"); break;
13 case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Dokunmatik / Touch"); break;
14 default: Serial.printf("Ilk acilis ya da reset / Power-on or reset (%d)\n", (int)cause); break;
15 }
16}
17
18void setup() {
19 Serial.begin(115200);
20 delay(500); // Seri Monitör'e zaman tanı / give the Serial Monitor time
21 bootCount++;
22 Serial.printf("Uyanis sayisi / Boot count: %d\n", bootCount);
23 printWakeupReason();
24
25 // Burada sensörü oku, veriyi gönder / read the sensor and send the data here
26
27 esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); // 10 s sonra uyan / wake after 10 s
28 Serial.println("Derin uyku basliyor / Entering deep sleep");
29 Serial.flush(); // yazı bitmeden uyuma / finish printing before sleeping
30 esp_deep_sleep_start(); // buradan sonrası çalışmaz / nothing below runs
31}
32
33void loop() {
34 // Derin uykuda loop() hiç çalışmaz / loop() never runs with deep sleep
35}Das Herzstück des Sketches sind zwei Zeilen: esp_sleep_enable_timer_wakeup() legt die Weckzeit in Mikrosekunden fest, esp_deep_sleep_start() schickt den Chip sofort schlafen; alles, was danach steht, wird nie ausgeführt. Vergiss bei der Zeitberechnung das Suffix ULL (64 Bit) nicht: 3600 * 1000000 sprengt den 32-Bit-Bereich von int, der Compiler gibt nur eine Warnung aus, und das Board wacht zur falschen Zeit oder gar nicht mehr auf. Serial.flush() wartet, bis der Sendepuffer leer ist; ohne diese Zeile werden die letzten Zeilen abgeschnitten. esp_sleep_get_wakeup_cause() verrät, warum das Board aufgewacht ist, und liefert nach dem ersten Einschalten oder einem Reset keine Schlafquelle. Der RTC-Timer, der die Schlafzeit zählt, läuft standardmäßig mit einem internen RC-Oszillator von etwa 150 kHz. Er ist nicht so stabil wie ein Quarz und kann mit der Temperatur um einige Prozent abweichen; wenn die Uhrzeit wichtig ist, solltest du sie beim Aufwachen per NTP synchronisieren.
Zähler und Messwerte im RTC-Speicher behalten
Wenn du dir zwischen zwei Weckvorgängen etwas merken musst, schreibst du RTC_DATA_ATTR vor die Variable: Der Compiler legt sie dann nicht im Haupt-RAM ab, sondern im 8 KB großen RTC-SLOW-Speicher, der im Tiefschlaf versorgt bleibt. Das ist ideal für kleine Daten wie Zähler, die letzten Messwerte, WLAN-Kanalinformationen oder die zuletzt gesendete Temperatur. Dauerhaft ist dieser Speicher aber nicht: Laut Espressif-Dokumentation werden die Daten im RTC-Speicher bei jedem Neustart außer dem Aufwachen aus dem Tiefschlaf neu initialisiert. Akkuwechsel, ein Druck auf EN, neuer Code oder ESP.restart() setzen den Zähler zurück. Einstellungen, die einen Stromausfall überstehen müssen, schreibst du mit der Bibliothek Preferences in den Flash, aber nicht bei jedem Aufwachen, denn der Flash verträgt nur begrenzt viele Schreibzyklen.
| Ereignis | Normale Variable | RTC_DATA_ATTR | Preferences (Flash) |
|---|---|---|---|
| Aufwachen aus dem Tiefschlaf | Zurückgesetzt | Bleibt erhalten | Bleibt erhalten |
| EN-Taste oder ESP.restart() | Zurückgesetzt | Zurück auf den Startwert | Bleibt erhalten |
| Akku entfernt, Stromausfall | Zurückgesetzt | Gelöscht | Bleibt erhalten |
| Neuer Code hochgeladen | Zurückgesetzt | Gelöscht | Bleibt erhalten (wenn der Flash nicht gelöscht wird) |
Geschickt eingesetzt, vervielfacht dieser Speicher die Akkulaufzeit. Die WLAN-Verbindung ist bei jedem Aufwachen der teuerste Teil. Deshalb misst der folgende Sketch alle 10 Minuten die Akkuspannung, schaltet WLAN aber nur einmal pro Stunde ein: Sechs Messwerte sammeln sich in einem Array im RTC-Speicher, beim sechsten werden alle auf einmal gesendet. Außerdem speichert er Kanal und MAC-Adresse (BSSID) des Routers im RTC-Speicher; beim nächsten Mal verbindet sich der ESP32 direkt mit diesem Router, ohne alle Kanäle abzusuchen, und die Verbindung steht deutlich schneller.
1// RTC belleğinde ölçüm biriktirip saatte bir gönderme / buffer readings in RTC memory, send hourly
2#include <WiFi.h>
3
4const char* SSID = "AgAdi"; // Wi-Fi adı / Wi-Fi name
5const char* PASS = "Sifre123"; // Wi-Fi şifresi / Wi-Fi password
6const int BAT_PIN = 35; // 1M + 1M bölücünün orta noktası / divider midpoint
7const uint64_t SLEEP_US = 600ULL * 1000000ULL; // 10 dakika / 10 minutes
8const int BATCH = 6; // 6 ölçümde bir gönder / send every 6 readings
9
10RTC_DATA_ATTR float readings[BATCH]; // uykuda korunan ölçümler / readings kept in sleep
11RTC_DATA_ATTR int count = 0; // biriken ölçüm sayısı / number of stored readings
12RTC_DATA_ATTR int32_t savedChannel = 0; // son Wi-Fi kanalı / last Wi-Fi channel
13RTC_DATA_ATTR uint8_t savedBssid[6]; // modemin MAC adresi / access point MAC address
14
15bool connectFast() {
16 WiFi.mode(WIFI_STA);
17 if (savedChannel > 0) {
18 WiFi.begin(SSID, PASS, savedChannel, savedBssid); // taramadan bağlan / connect without scanning
19 } else {
20 WiFi.begin(SSID, PASS); // ilk sefer: normal bağlantı / first time: normal connect
21 }
22 unsigned long start = millis();
23 while (WiFi.status() != WL_CONNECTED) {
24 if (millis() - start > 10000) { // 10 s içinde bağlanamazsa vazgeç / give up after 10 s
25 savedChannel = 0; // bir dahaki sefere tara / scan next time
26 return false;
27 }
28 delay(50);
29 }
30 savedChannel = WiFi.channel();
31 memcpy(savedBssid, WiFi.BSSID(), 6);
32 return true;
33}
34
35void setup() {
36 Serial.begin(115200);
37 // Pili Wi-Fi açılmadan önce ölç / measure the battery before Wi-Fi starts
38 float v = analogReadMilliVolts(BAT_PIN) * 2 / 1000.0; // bölücü gerilimi yarıya indirir / divider halves it
39 readings[count++] = v;
40 Serial.printf("Olcum / Reading %d: %.2f V\n", count, v);
41
42 if (count >= BATCH) {
43 if (connectFast()) {
44 for (int i = 0; i < BATCH; i++) {
45 Serial.printf("Gonder / Send: %.2f V\n", readings[i]); // burada HTTP ya da MQTT / HTTP or MQTT here
46 }
47 count = 0;
48 } else {
49 for (int i = 1; i < BATCH; i++) readings[i - 1] = readings[i]; // en eskiyi at / drop the oldest
50 count = BATCH - 1;
51 }
52 WiFi.disconnect(true);
53 WiFi.mode(WIFI_OFF); // radyoyu kapat / turn the radio off
54 }
55
56 Serial.flush();
57 esp_sleep_enable_timer_wakeup(SLEEP_US);
58 esp_deep_sleep_start();
59}
60
61void loop() {
62}Die Akkuspannung wird von einem Spannungsteiler aus 1 MΩ + 1 MΩ halbiert und an GPIO 35 gelesen, einem ADC1-Kanal. Ein 100-nF-Kondensator zwischen Teilermitte und GND hält die Messung selbst bei einem so hochohmigen Teiler stabil. Wichtig ist auch, vor dem Einschalten des WLAN zu messen, denn der Strom beim Funken lässt die Akkuspannung kurz einbrechen. Klappt die WLAN-Verbindung nicht, wird der älteste Wert verworfen und der Rest für den nächsten Versuch aufgehoben. Das Funkmodul vor dem Schlafen mit WiFi.disconnect(true) und WiFi.mode(WIFI_OFF) sauber abzuschalten, empfiehlt auch Espressif. Noch schneller wird die Verbindung, wenn du mit WiFi.config() eine feste IP vergibst und so die DHCP-Wartezeit sparst. Die Berechnung des Teilers findest du in der Anleitung Spannungsteiler, die Ladeschaltung in TP4056-Lademodul.
Aufwachen per Taster oder PIR-Sensor: ext0 und ext1
Neben dem Timer kann auch ein Signal von außen den ESP32 wecken. Dafür gibt es zwei Wege, und beide funktionieren nur an RTC-GPIO-Pins: 0, 2, 4, 12–15, 25–27 und 32–39. ext0 überwacht einen einzelnen Pin und weckt das Board, sobald er den gewählten Pegel (HIGH oder LOW) erreicht; weil dabei die RTC-Peripherie versorgt bleibt, funktionieren die internen Pull-up- und Pull-down-Widerstände auch im Schlaf. ext1 überwacht über eine Bitmaske mehrere Pins gleichzeitig. Beim klassischen ESP32 hat ext1 zwei Modi: ESP_EXT1_WAKEUP_ANY_HIGH weckt, sobald einer der gewählten Pins HIGH wird, ESP_EXT1_WAKEUP_ALL_LOW erst, wenn alle gleichzeitig LOW sind. Den Modus ANY_LOW, der weckt, sobald irgendein Pin LOW wird, gibt es nur beim ESP32-S2, S3, C6 und H2. Wenn du einen klassischen ESP32 also mit mehreren Tastern wecken willst, schließe die Taster an 3V3 an und gib jedem Pin einen 10-kΩ-Pull-down-Widerstand. Du kannst auch mehrere Quellen gleichzeitig aktivieren; sind etwa PIR und ein stündlicher Timer aktiv, wacht das Board bei dem Ereignis auf, das zuerst eintritt.
| Weckquelle | Funktion | Pins | Hinweis |
|---|---|---|---|
| Timer | esp_sleep_enable_timer_wakeup() | Kein Pin nötig | Zeit in Mikrosekunden, 64 Bit |
| ext0 | esp_sleep_enable_ext0_wakeup() | Ein RTC-GPIO | Weckt bei HIGH oder LOW; nicht mit Touch oder ULP kombinierbar |
| ext1 | esp_sleep_enable_ext1_wakeup_io() | Mehrere RTC-GPIOs | ANY_HIGH oder ALL_LOW; externe Widerstände empfohlen |
| Touch | touchSleepWakeUpEnable() | T0–T9 (10 Pins) | Weckt, wenn der Wert unter die Schwelle fällt; nicht mit ext0 kombinierbar |
| ULP-Koprozessor | esp_sleep_enable_ulp_wakeup() | Je nach Programm | Kann im Schlaf Sensoren lesen; für Fortgeschrittene |
Verdrahtung der Beispielschaltung
| Bauteil | Pin am Bauteil | ESP32-Pin | Hinweis |
|---|---|---|---|
| PIR-Sensor (HC-SR501 oder AM312) | OUT | GPIO 33 | Weckt über ext0 |
| PIR-Sensor | VCC / GND | 5V (HC-SR501) oder 3V3 (AM312) / GND | Der HC-SR501 braucht mindestens 4,5 V |
| Taster A | Ein Bein / anderes Bein | 3V3 / GPIO 25 | 10 kΩ Pull-down zwischen GPIO 25 und GND |
| Taster B | Ein Bein / anderes Bein | 3V3 / GPIO 26 | 10 kΩ Pull-down zwischen GPIO 26 und GND |
| Touch-Fläche oder Draht | – | GPIO 27 (T7) | Kurz gehalten arbeitet sie zuverlässiger |
| LED + 220 Ω | Anode | GPIO 2 | Bei den meisten DevKits sitzt auch die blaue Onboard-LED an diesem Pin |
| Akku-Spannungsteiler (1 MΩ + 1 MΩ) | Mittelpunkt | GPIO 35 | 100 nF zwischen Mittelpunkt und GND |
| 18650 + TP4056 + 3,3-V-LDO | LDO-Ausgang / GND | 3V3 / GND | 470-µF-Kondensator am LDO-Ausgang |
Sketch zum Aufwachen per PIR-Sensor (ext0)
1// PIR ile uyanma (ext0) / wake on PIR motion (ext0)
2#include "driver/rtc_io.h"
3
4const gpio_num_t PIR_PIN = GPIO_NUM_33; // RTC GPIO olmalı / must be an RTC GPIO
5const int LED_PIN = 2; // uyarı LED'i / alert LED
6
7RTC_DATA_ATTR int motionCount = 0; // toplam hareket sayısı / total motion count
8
9void setup() {
10 Serial.begin(115200);
11 rtc_gpio_deinit(PIR_PIN); // pini yeniden normal GPIO yap / turn the pad back into a normal GPIO
12 pinMode(PIR_PIN, INPUT);
13 pinMode(LED_PIN, OUTPUT);
14
15 if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
16 motionCount++;
17 Serial.printf("Hareket! / Motion! Toplam / Total: %d\n", motionCount);
18 digitalWrite(LED_PIN, HIGH); // burada bildirim gönderebilirsin / send a notification here
19 delay(1000);
20 digitalWrite(LED_PIN, LOW);
21 }
22
23 // PIR çıkışı LOW olana kadar bekle, yoksa hemen yeniden uyanır / wait for LOW or it wakes again at once
24 while (digitalRead(PIR_PIN) == HIGH) {
25 delay(100);
26 }
27
28 esp_sleep_enable_ext0_wakeup(PIR_PIN, 1); // pin HIGH olunca uyan / wake when the pin goes HIGH
29 rtc_gpio_pullup_dis(PIR_PIN);
30 rtc_gpio_pulldown_en(PIR_PIN); // boştayken LOW tut / keep it LOW while idle
31 Serial.println("Uyku / Sleep");
32 Serial.flush();
33 esp_deep_sleep_start();
34}
35
36void loop() {
37}Nach dem Aufwachen macht der Sketch den Pin mit rtc_gpio_deinit() zuerst wieder zu einem normalen GPIO. Nach einem ext0-Weckvorgang bleibt der Pin im RTC-Modus, und ohne diese Zeile funktioniert digitalRead() womöglich nicht richtig. Der Sketch zählt die Bewegung, lässt die LED aufleuchten und wartet dann, bis der PIR-Ausgang wieder LOW ist. Diese Wartezeit ist entscheidend: Schläft das Board ein, während der Ausgang noch HIGH ist, wacht es sofort wieder auf und landet in einer Schleife, die sich mehrmals pro Sekunde ein- und ausschaltet. Der HC-SR501 hält seinen Ausgang für die eingestellte Zeit auf HIGH; dreh das Zeit-Potentiometer auf das Minimum, um die Wartezeit zu verkürzen. Da der HC-SR501 mindestens 4,5 V braucht, nimm für Projekte mit einem einzelnen 18650 lieber das kleine AM312-PIR-Modul, das auch mit 3,3 V läuft. Einstellungen des Sensors findest du in der Anleitung PIR-Bewegungsmelder.
Sketch zum Aufwachen mit zwei Tastern (ext1)
1// İki butonla uyanma (ext1) / wake with two buttons (ext1)
2#include "driver/rtc_io.h"
3
4const gpio_num_t BTN_A = GPIO_NUM_25; // buton A: 3V3'e, 10k pull-down / button A: to 3V3, 10k pull-down
5const gpio_num_t BTN_B = GPIO_NUM_26; // buton B: 3V3'e, 10k pull-down / button B: to 3V3, 10k pull-down
6const uint64_t WAKE_MASK = (1ULL << BTN_A) | (1ULL << BTN_B);
7
8void setup() {
9 Serial.begin(115200);
10 rtc_gpio_deinit(BTN_A); // pinleri normal GPIO'ya döndür / back to normal GPIO
11 rtc_gpio_deinit(BTN_B);
12 pinMode(BTN_A, INPUT);
13 pinMode(BTN_B, INPUT);
14
15 if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1) {
16 uint64_t pins = esp_sleep_get_ext1_wakeup_status(); // hangi pin uyandırdı? / which pin woke us?
17 if (pins & (1ULL << BTN_A)) Serial.println("Buton A: isigi ac / Button A: lights on");
18 if (pins & (1ULL << BTN_B)) Serial.println("Buton B: isigi kapat / Button B: lights off");
19 } else {
20 Serial.println("Ilk acilis / First boot");
21 }
22
23 while (digitalRead(BTN_A) == HIGH || digitalRead(BTN_B) == HIGH) {
24 delay(10); // butonlar bırakılana kadar bekle / wait until released
25 }
26
27 esp_sleep_enable_ext1_wakeup_io(WAKE_MASK, ESP_EXT1_WAKEUP_ANY_HIGH); // biri HIGH olunca / any goes HIGH
28 Serial.flush();
29 esp_deep_sleep_start();
30}
31
32void loop() {
33}Nach einem ext1-Weckvorgang liefert esp_sleep_get_ext1_wakeup_status() eine Bitmaske, die zeigt, welcher Pin geweckt hat. So kannst du beiden Tastern verschiedene Aufgaben geben: Einer schaltet das Licht ein, der andere aus, oder einer sendet einen Messwert und der andere öffnet ein Menü. Da die Taster an 3V3 hängen und die Pins mit 10 kΩ nach GND gezogen werden, lesen sie losgelassen LOW und gedrückt HIGH; genau das erwartet der Modus ANY_HIGH. Der Sketch wartet vor dem Einschlafen, bis die Taster losgelassen sind, sonst würde ein noch gedrückter Taster das Board sofort wieder wecken. Nutzt du noch den ESP32-Arduino-Core 2.x oder 3.0.x, dem die _io-Funktion fehlt, schreibst du statt esp_sleep_enable_ext1_wakeup_io mit denselben Parametern esp_sleep_enable_ext1_wakeup. Warum der Pull-down-Widerstand nötig ist, erklärt die Anleitung Taster am Arduino anschließen ausführlich.
Aufwachen per Berührung (Touch-Wakeup)
Zehn Pins des ESP32 (T0–T9) arbeiten als kapazitive Touch-Sensoren und erkennen deinen Finger sogar im Tiefschlaf. Ein Draht, ein Streifen Kupferband oder eine Leiterplattenfläche am Pin verhält sich wie ein Taster und kann sogar hinter einer dünnen Kunststoff- oder Holzabdeckung sitzen. Beim klassischen ESP32 sinkt der Wert von touchRead() bei Berührung: Mit einem kurzen Draht liest du unberührt meist 60–80 und berührt 10–20. Die Weckschwelle legst du zwischen diese beiden Werte; die 40 im Beispiel ist ein guter Startwert. Beim klassischen ESP32 kannst du mehrere Touch-Pins gleichzeitig als Weckquelle nutzen. Beim ESP32-S2 und S3 kann dagegen nur ein Pin wecken, der Wert steigt bei Berührung, und die Schwelle hat eine ganz andere Größenordnung (5000 im Beispiel von Espressif). Touch-Wakeup lässt sich nicht mit ext0 kombinieren; brauchst du zusätzlich einen Taster, nimm ext1. Die Pinliste und die Kalibrierung findest du in der Anleitung ESP32-Touch-Pins.
1// Dokunarak uyandırma: T7 = GPIO 27 / touch wake-up: T7 = GPIO 27
2#define THRESHOLD 40 // değer bunun altına inince uyanır / wakes when the value drops below this
3
4RTC_DATA_ATTR int touchWakes = 0;
5
6void setup() {
7 Serial.begin(115200);
8 delay(500);
9 if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TOUCHPAD) {
10 touchWakes++;
11 Serial.printf("Dokunusla uyanma / Touch wake #%d\n", touchWakes);
12 }
13 // Eşiği ayarlamak için dokunmadan ve dokunarak değeri izle / watch the value untouched and touched
14 Serial.printf("T7 degeri / value: %d\n", (int)touchRead(T7));
15
16 touchSleepWakeUpEnable(T7, THRESHOLD); // dokunmatikle uyanmayı aç / enable touch wake-up
17 Serial.flush();
18 esp_deep_sleep_start();
19}
20
21void loop() {
22}ESP32 Deep Sleep mit MicroPython
Wenn du den ESP32 mit MicroPython programmierst, geht das Gleiche mit kürzerem Code. machine.deepsleep(ms) schickt das Board für die angegebenen Millisekunden schlafen, esp32.wake_on_ext0() macht einen Pin zur Weckquelle, und machine.RTC().memory() stellt einen Bytepuffer bereit, der den Schlaf übersteht; beim ESP32 fasst er standardmäßig 2048 Bytes. Nach dem Aufwachen läuft main.py von vorn; mit machine.reset_cause() erkennst du, ob das Board aus dem Tiefschlaf kommt, und mit machine.wake_reason(), was es geweckt hat. RTC-Pins mit eingestelltem Pull-up oder Pull-down behalten diese Einstellung auch im Schlaf. Wenn MicroPython neu für dich ist, beginne mit der Anleitung MicroPython-Grundlagen.
1import machine, esp32, time
2from machine import Pin
3
4rtc = machine.RTC()
5led = Pin(2, Pin.OUT)
6
7mem = rtc.memory() # RTC belleğini oku / read RTC memory
8count = int.from_bytes(mem, "little") if mem else 0
9
10if machine.reset_cause() == machine.DEEPSLEEP_RESET:
11 count += 1
12 if machine.wake_reason() == machine.EXT0_WAKE:
13 print("Butonla uyandi / Woken by button")
14 else:
15 print("Zamanlayiciyla uyandi / Woken by timer")
16else:
17 count = 0
18 print("Ilk acilis / First boot")
19
20rtc.memory(count.to_bytes(4, "little")) # sayacı sakla / store the counter
21print("Uyanis / Wake:", count)
22
23led.on() # kısa iş: ölç ve gönder / short task: measure and send
24time.sleep_ms(200)
25led.off()
26
27button = Pin(33, Pin.IN, Pin.PULL_DOWN) # buton GPIO 33 ile 3V3 arasında / button between GPIO 33 and 3V3
28esp32.wake_on_ext0(button, esp32.WAKEUP_ANY_HIGH) # basınca uyan / wake on press
29machine.deepsleep(60000) # 60 s uyu / sleep for 60 sDer Raspberry Pi Pico hat dagegen keinen Tiefschlaf im Mikroamperebereich wie der ESP32. In MicroPython hält machine.lightsleep(ms) die meisten Takte an, pausiert die CPU und behält den RAM, während machine.deepsleep(ms) das Board nach derselben Art Schlaf zurücksetzt. Selbst in diesen Modi bleibt der Verbrauch des Boards im Milliamperebereich. Willst du mit dem Pico ein Gerät bauen, das monatelang mit Batterie läuft, erzielst du bessere Ergebnisse, wenn du die Versorgung zwischen den Messungen mit einem Power-Timer wie dem TPL5110 komplett abschaltest. Die Grundlagen findest du in der Anleitung Was ist der Raspberry Pi Pico?.
Ein Sensor, der monatelang mit einem 18650 läuft: Akkulaufzeit berechnen
Um die Akkulaufzeit eines Geräts zu bestimmen, das schläft und aufwacht, berechnest du zuerst den mittleren Strom: Mittlerer Strom = (Wachstrom × Wachzeit + Schlafstrom × Schlafzeit) / Gesamtzeit. Dann teilst du die nutzbare Akkukapazität durch diesen Mittelwert. Von einem 18650 mit 3000 mAh auf dem Etikett kannst du nicht alles nutzen; realistisch ist es, wegen Unterspannungsabschaltung, Reglerverlusten und Alterung etwa 85 %, also 2550 mAh, anzusetzen. Die folgende Tabelle gilt für einen Sensor, der alle 10 Minuten aufwacht, Daten per WLAN sendet und während der Verbindung im Mittel 120 mA zieht.
| Szenario | Schlafstrom | Wachphase | Mittlerer Strom | Geschätzte Laufzeit |
|---|---|---|---|---|
| DevKit-Board | 8 mA | 3 s, 120 mA | 8,56 mA | ≈12 Tage |
| DevKit-Board, schnelle Verbindung | 8 mA | 1 s, 120 mA | 8,19 mA | ≈13 Tage |
| Nacktes Modul + MCP1700 + 1-MΩ-Teiler | 15 µA | 3 s, 120 mA | 0,61 mA | ≈173 Tage (5,7 Monate) |
| Nacktes Modul, schnelle Verbindung | 15 µA | 1 s, 120 mA | 0,215 mA | ≈494 Tage (16 Monate) |
| Nacktes Modul, stündlicher Sammelversand | 15 µA | 1 s WLAN pro Stunde (120 mA) + 5 × 0,3 s Messung (40 mA) | 0,065 mA | Theoretisch ≈4,5 Jahre |
Das Ergebnis ist eindeutig: Auf einem Entwicklungsboard bringt eine kürzere Wachphase fast nichts, weil vor allem die 8 mA im Schlaf den Akku leeren. Sinkt der Schlafstrom auf Mikroampere, kehrt sich das Verhältnis um, und jede wache Sekunde zählt: Die WLAN-Verbindung von 3 auf 1 Sekunde zu verkürzen, verdreifacht die Laufzeit beinahe. Fällt der mittlere Strom unter 0,1 mA, spielt die Selbstentladung des Akkus eine Rolle. Eine Li-Ion-Zelle verliert etwa 2–3 % Kapazität pro Monat, deshalb schrumpfen die theoretischen 4,5 Jahre in der Praxis auf rund 2 Jahre. Mit eigenen Werten rechnest du am besten mit dem Akkulaufzeit-Rechner; mehr über mAh und Wh erfährst du in der Anleitung Was bedeutet mAh?.
- Das richtige Board wählen: Boards ohne USB-Chip und Power-LED oder ein nacktes ESP32-Modul brauchen im Schlaf nur Mikroampere.
- Den Regler tauschen: Nimm statt des AMS1117 einen LDO mit wenigen Mikroampere Ruhestrom.
- Den Sensor nur zum Messen versorgen: Sensoren mit wenigen mA kannst du direkt aus einem GPIO speisen, größere Lasten über einen P-Kanal-MOSFET; ein Pull-up-Widerstand am Gate hält den MOSFET im Schlaf aus. Details in der Anleitung MOSFET am Arduino.
- WLAN selten einschalten: Sammle Messwerte im RTC-Speicher und sende sie gebündelt; schickst du Daten an einen anderen ESP32 in der Nähe, überträgt ESP-NOW sie viel schneller, weil keine Anmeldung am Router nötig ist.
- Die Verbindung beschleunigen: Mit fester IP sowie gespeichertem Kanal und BSSID dauert die WLAN-Verbindung oft weniger als eine Sekunde.
- Unnötige Wartezeiten streichen: Entferne im fertigen Projekt die Ausgaben für den seriellen Monitor und lange delay()-Zeilen; jede wache Millisekunde kostet Akku.
- Teiler und LEDs nicht vergessen: Ein dauerhaft angeschlossener Teiler aus 100 kΩ + 100 kΩ zieht doppelt so viel Strom wie der schlafende ESP32.
Projektideen mit ESP32 Deep Sleep
- Akku-Wetterstation: Miss mit einem BME280 alle 10 Minuten Temperatur, Luftfeuchte und Luftdruck und sende die Werte stündlich per MQTT an Home Assistant.
- Bodenfeuchtesensor für den Garten: Wache stündlich auf, miss die Bodenfeuchte und schick eine Nachricht aufs Handy, wenn die Erde trocken wird.
- Tür- und Fensteralarm: Öffnet sich die Tür, weckt ein Reedkontakt den ESP32 über ext0, und ein Telegram-Bot schickt dir eine Nachricht.
- Batteriebetriebener Sicherheitssensor: Baue ein Gerät, das bei einer Bewegung am PIR aufwacht und eine Meldung sendet und den Rest der Zeit monatelang schläft.
- Touch-Türklingel: Klebe Kupferband hinter eine Holzplatte; bei Berührung wacht der ESP32 auf und gibt der Klingel im Haus per ESP-NOW ein Signal.
- Akku-Temperaturlogger: Miss mit einem wasserdichten DS18B20 die Temperatur von Kühlschrank oder Aquarium, sammle die Werte im RTC-Speicher und sende sie ein paarmal am Tag.
Häufige Fehler und ihre Lösungen
| Symptom | Ursache | Lösung |
|---|---|---|
| Das Board wacht direkt nach dem Einschlafen wieder auf | Der Weckpin ist noch aktiv (PIR HIGH, Taster gedrückt) oder hängt in der Luft | Vor dem Schlafen warten, bis der Pin im Ruhepegel ist, und einen Pull-down- oder Pull-up-Widerstand ergänzen |
| Das Board wacht nicht zur erwarteten Zeit auf | 32-Bit-Überlauf bei der Zeitberechnung (3600 * 1000000) | ULL an die Konstanten hängen: 3600ULL * 1000000ULL |
| Der Zähler beginnt immer bei 1 | Die Variable ist nicht RTC_DATA_ATTR oder das Board wurde zurückgesetzt | RTC_DATA_ATTR ergänzen; für dauerhafte Daten Preferences verwenden |
| Im Schlaf werden 5–15 mA gemessen | Regler, Power-LED, USB-Chip, Spannungsteiler oder Sensoren | Stromsparendes Board oder nacktes Modul, LDO mit niedrigem Ruhestrom |
| Die letzten Zeilen im seriellen Monitor fehlen | Das Board schlief ein, bevor der Puffer leer war | Vor esp_deep_sleep_start() Serial.flush() aufrufen |
| digitalRead() liefert nach dem Aufwachen falsche Werte | Der Weckpin ist noch im RTC-Modus | Vor der Nutzung rtc_gpio_deinit() aufrufen |
| Ein Taster an GPIO 34–39 weckt nicht | Diese Pins haben keine internen Pull-Widerstände, der Pin hängt in der Luft | Externen 10-kΩ-Widerstand ergänzen |
| Im Schlaf lässt sich kein Code hochladen | Das Board hat keine Auto-Reset-Schaltung; beim S3 und C3 verschwindet der native USB-Port im Schlaf | BOOT gedrückt halten, EN (RESET) kurz drücken, dann den Upload starten |
| Ein Relais oder eine LED ändert beim Einschlafen den Zustand | Digitale Pins sind im Tiefschlaf hochohmig | Pull-Widerstand verwenden oder den Pegel mit rtc_gpio_hold_en() halten |
ESP32 Deep Sleep ist der Schlüssel, um denselben Code mit Akku monatelang statt tagelang laufen zu lassen: Halte die Arbeit kurz, wähle die passende Weckquelle, speichere die kleinen Daten, die du zwischen den Weckvorgängen brauchst, im RTC-Speicher und vergiss nie den Eigenverbrauch des Boards. Wenn du die ESP32-Grundlagen auffrischen willst, ist die Anleitung Was ist der ESP32? ein guter Einstieg. In der BlueGrays-App kannst du im Projekt ESP32 Deep Sleep — Akku sparen (OLED + RTC-RAM-Zähler) die Timer- und RTC-Zähler-Logik aus diesem Artikel an einer Schaltung mit OLED-Display ausprobieren, mit Schritt-für-Schritt-Schaltplan und fertigem Code.
Häufige Fragen
Wie viel Strom braucht der ESP32 im Deep Sleep?
Laut Espressif-Datenblatt braucht der ESP32-Chip im Tiefschlaf mit RTC-Timer und RTC-Speicher etwa 10 µA, im Hibernation-Modus 5 µA. Auf Entwicklungsboards wie dem DevKit liegt der Schlafstrom wegen Spannungsregler, Power-LED und USB-Seriell-Chip aber meist bei 5–15 mA.
Wie weckt man den ESP32 aus dem Deep Sleep?
Es gibt fünf Wege: den RTC-Timer, einen einzelnen RTC-Pin (ext0), mehrere RTC-Pins (ext1), die Touch-Pins und den ULP-Koprozessor. Die Weckquelle stellst du vor dem Aufruf von esp_deep_sleep_start() ein, und du kannst mehrere gleichzeitig aktivieren; nur ext0 und Touch-Wakeup lassen sich nicht kombinieren.
Gehen Variablen im ESP32 Deep Sleep verloren?
Ja, normale Variablen gehen verloren, denn das Aufwachen aus dem Tiefschlaf ist ein Neustart und setup() läuft von vorn. Variablen mit RTC_DATA_ATTR liegen im RTC-Speicher und bleiben zwischen den Weckvorgängen erhalten, kehren aber nach einem Stromausfall, einem Druck auf EN oder neuem Code auf ihren Startwert zurück.
Wie lange läuft ein ESP32 mit einem 18650-Akku?
Je nach Board und Nutzung zwischen einem Tag und über einem Jahr. Ein DevKit, das dauerhaft mit dem WLAN verbunden ist, hält mit einem 18650 mit 3000 mAh nur 1–2 Tage, ein DevKit, das alle 10 Minuten aufwacht, etwa 12 Tage. Eine stromsparende Schaltung mit 15 µA Schlafstrom schafft bei derselben Aufgabe je nach Verbindungsdauer 5 bis 16 Monate.
Was ist der Unterschied zwischen Light Sleep und Deep Sleep beim ESP32?
Im Light Sleep wird die CPU angehalten, der RAM bleibt aber erhalten; nach dem Aufwachen macht das Programm an derselben Stelle weiter, und der Chip braucht etwa 0,8 mA. Im Deep Sleep wird auch der RAM abgeschaltet, der Verbrauch sinkt auf 10 µA, dafür startet das Programm bei jedem Aufwachen neu. Light Sleep passt für Pausen von Sekunden, Deep Sleep für Wartezeiten von Minuten oder Stunden.
Wie lädt man Code auf einen ESP32 im Deep Sleep hoch?
Bei Boards mit Auto-Reset-Schaltung startet der Upload meist problemlos, weil das Upload-Tool den Chip über den EN-Pin zurücksetzt. Klappt das nicht, halte die BOOT-Taste gedrückt, drücke kurz EN (RESET) und starte dann den Upload. Bei Boards mit nativem USB wie dem ESP32-S3 und C3 verschwindet der Port im Schlaf, deshalb ist diese Methode dort oft nötig.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
LED Blinken mit ESP32
Lehrt die grundlegende Verwendung von GPIO-Ausgängen mit ESP32. Wir erreichen einen Blinkeffekt, indem wir eine LED über einen digitalen Pin ein- und ausschalten. ESP32-Version des klassischen Blink-Projekts auf Arduino UNO; die grundlegenden Blink-Befehle in der Arduino-Umgebung bleiben gleich, aber die Pinbelegung und die GPIO-Pegel von 3,3 V des ESP32 unterscheiden sich von denen des Arduino UNO.

ESP32 LED-Steuerung mit Drucktaste (Push)
Die LED leuchtet auf, wenn die Taste gedrückt wird und geht aus, wenn sie losgelassen wird. Zeigt, wie der digitale Eingangspin des ESP32 und der interne Pull-up-Widerstand verwendet werden. Dank des INPUT_PULLUP-Modus ist kein externer Widerstand erforderlich.

ESP32 Deep Sleep – Batteriesparmodus (OLED + RTC-RAM-Zähler)
Der Deep-Sleep-Modus des ESP32 senkt den Stromverbrauch auf etwa 10 µA, indem er die CPU und die meisten Peripheriegeräte abschaltet, und ist für batteriebetriebene IoT-Geräte von entscheidender Bedeutung. In diesem Projekt verwenden wir drei verschiedene Weckquellen: den Timer (15 s), einen externen Pin (EXT0 – Taste GPIO 33) und die Berührungserkennung (T0/GPIO 4). Mit RTC_DATA_ATTR markierte Variablen bleiben während des Deep-Sleep-Modus erhalten – der Boot-Zähler, der Timer-Wake-Zähler und

ESP32 Bewegungsalarmsystem (PIR + Buzzer + LED + Telegram)
Ein intelligentes Sicherheitsalarmsystem, das Buzzer und LED aktiviert, sobald der PIR-Sensor Bewegung erkennt, und gleichzeitig eine sofortige Benachrichtigung per Telegram an den Besitzer sendet. Ein 30-sekündiger Cooldown begrenzt die Häufigkeit wiederholter Alarme; dieser Mechanismus überprüft weder die Gültigkeit der PIR-Erkennungen noch verhindert er Fehlauslösungen. Geeignet für Heimsicherheit, Garagenüberwachung, Lagerräume oder Arbeitszimmer. Mit ESP32-CAM sind Foto-Benachrichtigungen m

Automatische Beleuchtung mit dem ESP32-PIR-Bewegungssensor
In diesem Projekt werden wir mithilfe des PIR-Bewegungssensors HC-SR501 Personen erkennen, die den Raum betreten, und die LED automatisch einschalten. Wir überwachen die Leuchtdauer der LED mit millis(); bei jedem HIGH-Messwert des Sensors wird der Zeitpunkt der letzten Bewegung aktualisiert, und die LED erlischt frühestens 10 Sekunden nach dem letzten HIGH-Messwert. Der aktuelle Code wartet außerdem in jedem Schleifendurchlauf mit delay(50) für 50 ms, sodass die Anwendungsschleife nicht vol

ESP32 DHT22 Temperatur und Luftfeuchtigkeit (mit LCD-Display)
Bei diesem Projekt werden die Umgebungstemperatur und die Luftfeuchtigkeit mit einem DHT22-Sensor gemessen und in Echtzeit auf einem 16x2 I2C-LCD angezeigt. Mithilfe der Adafruit-DHT-Bibliothek werden die Daten über das One-Wire-Protokoll vom Sensor ausgelesen. Dieses Projekt bildet die Grundlage für IoT-Wetterstationen, Smart-Home-Automatisierung oder Umgebungsüberwachungssysteme in Gewächshäusern. Die Kommunikation mit dem DHT22 erfolgt über den GPIO-4-Pin des ESP32.

ESP32 Temperatur-Datenlogger (DHT22 + DS1307 RTC + microSD + OLED)
Baue einen zeitgestempelten Temperaturlogger mit ESP32! Lese Temperatur/Feuchtigkeit vom DHT22 jede Minute, hole die aktuelle Zeit vom DS1307 RTC und protokolliere auf microSD-Karte als CSV. Das OLED zeigt live Temperatur (große Schrift), Luftfeuchtigkeit, Zeitstempel und Logzähler. Die /temp_log.csv-Datei auf der SD-Karte öffnet sich in Excel, Google Sheets oder Python pandas zur Analyse — perfekt für Gewächshausüberwachung, Raumtemperaturverfolgung, Laboraufzeichnung, Lebensmittellageru
Weitere Artikel
BlueGrays-App
Diese Schaltung Schritt für Schritt auf dem Handy nachbauen
Schaltplan, Code und Teileliste auf einem Bildschirm. Über 200 Arduino-, ESP32- und Pico-Projekte zum Nachbauen.
Mit dem Handy scannen
Richte die Kamera auf den Code, dann öffnet sich der App-Store deines Handys.
oder im Store öffnen

