RTC und NTP in MicroPython mit ESP8266 und ESP32 - Teil 2 - Dornröschenschlaf - AZ-Delivery

Heute geht es ab ins Schlaflabor. Sollten Sie Teil 1 der Blog-Reihe verpasst haben, finden sie diesen hier. Wir legen den ESP32/ESP8266 zur Ruhe, das hilft Strom sparen im Batteriebetrieb. Nebenbei bieten das RTC-Modul des MicroPython-Kernels und der DS1302 aber auch noch die Möglichkeit, Daten abzulegen, die einen Neustart überleben. Normalerweise sind ja alle Variablen und Objekte futsch, wenn der Controller neu startet. Wie sie konserviert werden können und welche Schlafmodi und Erweckungsmöglichkeiten ESP8266 und ESP32 bieten, das erfahren Sie in

MicroPython auf dem ESP32 und ESP8266

heute

Dornröschenschlaf

100 Jahre werden wir unsere ESPs nicht gerade in den Winterschlaf schicken und wachküssen werden wir sie auch nicht. Das überlassen wir lieber der RTC. Beim ESP32 können wir auch manche Beinchen krabbeln (lassen) und wenn es dabei einen Pegelwechsel gibt, dann kann das auch dazu führen, dass der Controller aufwacht. In jedem Fall macht er dann einen Neustart und alles, was sich im RAM befunden hatte, ist ins Nirwana ausgewandert. Alles, bis auf die Daten im RTC-RAM und die RTC selbst. Was brauchen wir für die Experimente?

Hardware

1

ESP32 Dev Kit C unverlötet oder

ESP32 Dev Kit C V4 unverlötet oder

ESP32 NodeMCU Module WLAN WiFi Development Board mit CP2102 oder

NodeMCU-ESP-32S-Kit

1

NodeMCU Lua Amica Modul V2 ESP8266 ESP-12F WIFI oder

D1 Mini NodeMcu mit ESP8266-12F WLAN Modul oder

NodeMCU Lua Lolin V3 Module ESP8266 ESP-12F WIFI

1

0,91 Zoll OLED I2C Display 128 x 32 Pixel

1

KY-004 Taster Modul

1

Breadboard Kit - 3x Jumper Wire m2m/f2m/f2f + 3er Set MB102 Breadbord kompatibel mit Arduino und Raspberry Pi - 1x Set

1

DS1302 Serial Real Time Clock RTC Echtzeituhr Clock Modul

Die Software

Fürs Flashen und die Programmierung des ESP32:

Thonny oder

µPyCraft

Verwendete Firmware für den ESP8266:

v1.19.1 (2022-06-18) .bin

Verwendete Firmware für den ESP32:

v1.19.1 (2022-06-18) .bin

Die MicroPython-Programme zum Projekt:

ds1302.py Hardwaretreiber für die DS1302

ssd1306.py Hardwaretreiber für das OLED-Display

oled.py API für das OLED-Display

DS1302-set+get.py Programm für den Uhrenvergleich NTP-RTC-DS1302

rtc-set+get.py Programm für den Uhrenvergleich NTP-RTC

wake_on_gpio.py Tiefschlaf und erwachen durch Pegelwechsel

tiefschlaf_demo.py Tiefschlaf und Erwachen durch Timer

schlafarten.py Möglichkeiten zum Stromsparen

schlafen+wachen.py Testprogramm für Stromsparmodi auf ESP8266 und ESP32

MicroPython - Sprache - Module und Programme

Zur Installation von Thonny finden Sie hier eine ausführliche Anleitung (english version). Darin gibt es auch eine Beschreibung, wie die Micropython-Firmware (Stand 18.06.2022) auf den ESP-Chip gebrannt wird.

MicroPython ist eine Interpretersprache. Der Hauptunterschied zur Arduino-IDE, wo Sie stets und ausschließlich ganze Programme flashen, ist der, dass Sie die MicroPython-Firmware nur einmal zu Beginn auf den ESP32 flashen müssen, damit der Controller MicroPython-Anweisungen versteht. Sie können dazu Thonny, µPyCraft oder esptool.py benutzen. Für Thonny habe ich den Vorgang hier beschrieben.

Sobald die Firmware geflasht ist, können Sie sich zwanglos mit Ihrem Controller im Zwiegespräch unterhalten, einzelne Befehle testen und sofort die Antwort sehen, ohne vorher ein ganzes Programm kompilieren und übertragen zu müssen. Genau das stört mich nämlich an der Arduino-IDE. Man spart einfach enorm Zeit, wenn man einfache Tests der Syntax und der Hardware bis hin zum Ausprobieren und Verfeinern von Funktionen und ganzen Programmteilen über die Kommandozeile vorab prüfen kann, bevor man ein Programm daraus strickt. Zu diesem Zweck erstelle ich auch gerne immer wieder kleine Testprogramme. Als eine Art Makro fassen sie wiederkehrende Befehle zusammen. Aus solchen Programmfragmenten entwickeln sich dann mitunter ganze Anwendungen.

Autostart

Soll das Programm autonom mit dem Einschalten des Controllers starten, kopieren Sie den Programmtext in eine neu angelegte Blankodatei. Speichern Sie diese Datei unter boot.py im Workspace ab und laden Sie sie zum ESP-Chip hoch. Beim nächsten Reset oder Einschalten startet das Programm automatisch.

Programme testen

Manuell werden Programme aus dem aktuellen Editorfenster in der Thonny-IDE über die Taste F5 gestartet. Das geht schneller als der Mausklick auf den Startbutton, oder über das Menü Run. Lediglich die im Programm verwendeten Module müssen sich im Flash des ESP32 befinden.

Zwischendurch doch mal wieder Arduino-IDE?

Sollten Sie den Controller später wieder zusammen mit der Arduino-IDE verwenden wollen, flashen Sie das Programm einfach in gewohnter Weise. Allerdings hat der ESP32/ESP8266 dann vergessen, dass er jemals MicroPython gesprochen hat. Umgekehrt kann jeder Espressif-Chip, der ein kompiliertes Programm aus der Arduino-IDE oder die AT-Firmware oder LUA oder … enthält, problemlos mit der MicroPython-Firmware versehen werden. Der Vorgang ist immer so, wie hier beschrieben.

Die Schaltungen

Abbildung 1: ESP8266 - Schaltung

Abbildung 1: ESP8266 - Schaltung

Damit der ESP8266 aufwachen kann, muss man den Pin GPIO16 = D0 mit dem RST-Anschluss verbinden. Mit dem Tastermodul schenke ich dem ESP8266 eine Flashtaste, weil der arme Kerl selbst keine an Bord hat.

All das braucht ein ESP32 nicht. Trotzdem kriegt auch er eine Taste von mir, damit er nicht weint und vor allem damit ich ihn wachkitzeln kann.

Abbildung 2: ESP32 - Schaltung

Abbildung 2: ESP32 - Schaltung

Der Controller geht schlafen

Na dann gute Nacht!

Das kann man sagen, wenn man sich keine Möglichkeit offengehalten hat, das Kerlchen auch wieder aufzuwecken, um zum Beispiel Änderungen am Programm hochzuladen. Denn dazu muss der ESP32/ESP8266 natürlich hellwach sein. Welche Schlafmodi zur Verfügung stehen und vor allem welche Erweckungsmethoden es gibt, das schauen wir uns jetzt an.

Als Erstes fällt auf, dass die Kernel für ESP8266 und ESP32 unterschiedliche Features aufweisen. Der Object Inspector von Thonny klärt auf. Sie können ihn über das Menü View – Object inspector starten.

Abbildung 3: Das ESP8266 machine-Modul

Abbildung 3: Das ESP8266 machine-Modul

Abbildung 4: Das ESP32 machine-Modul

Abbildung 4: Das ESP32 machine-Modul

ESP8266

Tiefschlaf und Reset

Tasten wir uns langsam an die Materie heran. Im interaktiven Modus, also über REPL, setze ich einige Kommandos ab. Ich importiere einige Klassen und Funktionen, deklariere das Tuple rtcTag und instanziiere das Objekt rtc.

>>> from machine import RTC, deepsleep, reset_cause, reset
>>> rtcTag=(2022,11,27,8,10,20,0,0)
>>> rtc=RTC()
>>> rtcTag
(2022, 11, 27, 8, 10, 20, 0, 0)

Die Funktion reset_cause() liefert als Grund für einen Reset einen Zahlencode gemäß der folgenden Tabelle 1.

ESP8266

 

 

PWRON_RESET

0

nach dem Einschalten

WDT_RESET

1

nach dem Ansprechen des Cerberus (=Watchdog aka Wachhund)

SOFT_RESET

4

nach dem Aufruf der Funktion machine.reset()

DEEPSLEEP_RESET

5

nach dem Erwachen aus dem Tiefschlaf

HARD_RESET

6

nach dem Drücken der RST-Taste

Tabelle 1: Reset-Codes

Unmittelbar nach dem Einschalten bekommen wir beim ESP8266 eine 0. Nach einem Soft-Reset eine 4.

>>> from machine import RTC, deepsleep, reset_cause, reset
>>> reset()
.
>>> reset_cause()
4
>>> rtcTag
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'rtcTag' isn't defined

Bei einem Hard-Reset wie bei einem Soft-Reset sind die Daten aus dem RAM verschwunden, abgewandert ins Nirwana. Deshalb musste ich erneut importieren.

Stecken Sie jetzt auf dem Breadboard einen Jumperdraht von D0 = GPIO16 zum Pin RST, wie oben in Abbildung 1 gezeigt.

Nun schicken wir den ESP8266 für 5 Sekunden schlafen. Wird keine Zeitdauer übergeben, schläft der Controller bis zum St. Nimmerleinstag und kann nur noch durch die RST-Taste wachgekitzelt werden.

>>> deepsleep(5000)
…….
MicroPython v1.19.1 on 2022-06-18; ESP module with ESP8266
Type "help()" for more information.

>>> reset_cause()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'reset_cause' isn't defined

Der Controller hat also auch nach dem Erwachen aus dem Tiefschlaf vergessen, was wir an Werkzeugen importiert hatten. Also wieder von vorne. Dieses Mal stellen wir noch die RTC und überzeugen uns ob sie auch tickt.

>>> from machine import RTC, deepsleep, reset_cause, reset
>>> rtcTag=(2022,11,27,0,8,10,20,0)
>>> rtc=RTC()
>>> rtc.datetime(rtcTag)
>>> rtc.datetime()
(2022, 11, 27, 6, 8, 10, 31, 367)

>>> deepsleep()
…….
MicroPython v1.19.1 on 2022-06-18; ESP module with ESP8266
Type "help()" for more information.

>>> from machine import RTC, deepsleep, reset_cause, reset
>>> reset_cause()
5
>>> rtc=RTC()
>>> rtc.datetime()
(2022, 11, 27, 6, 8, 12, 15, 959)

Aha! Nach dem Erwachen aus dem Tiefschlaf kann sich der Controller also an die Uhrzeit erinnern, er hat die Zählung der Sekunden sogar fortgeführt. Ähnlich verhält es sich mit einem Bytesobjekt, das man in das RTC-Memory schreiben kann. Beim ESP8266 dürfen das um die 500 Bytes sein, beim ESP32 2000.

>>> rtc.memory(b"12345 Das ist ein Test!")
>>> rtc.memory()
b'12345 Das ist ein Test!'

>>> deepsleep()
…….
MicroPython v1.19.1 on 2022-06-18; ESP module with ESP8266
Type "help()" for more information.

>>> from machine import RTC, deepsleep, reset_cause, reset
>>> rtc=RTC()
>>> rtc.memory()
b'12345 Das ist ein Test!'

Das funktioniert auch nach einem Soft-Reset.

>>> reset()
…….
MicroPython v1.19.1 on 2022-06-18; ESP module with ESP8266
Type "help()" for more information.

>>> from machine import RTC, deepsleep, reset_cause, reset
>>> rtc=RTC()
>>> rtc.memory()
b'12345 Das ist ein Test!'
>>> reset_cause()
4

Und sogar nach einem Hard-Reset (RST-Taste) sind die Daten noch da.

Und sogar nach einem Hard-Reset (RST-Taste) sind die Daten noch da.
>>> from machine import RTC, deepsleep, reset_cause, reset
>>> rtc=RTC()
>>> rtc.memory()
b'12345 Das ist ein Test!'
>>> reset_cause()
6
>>> rtc.datetime()
(2022, 11, 27, 6, 8, 20, 9, 603)

Allerdings wird nach einem Stromausfall auch das RTC-Modul gelöscht, es gibt ja keinen Puffer-Akku. Will man also Daten im ausgeschalteten Zustand sichern, dann gibt es nur zwei Wege: entweder in einer Datei im Flashspeicher des Controllers, oder im gepufferten RAM eines DS1302, bzw. eines entsprechenden anderen Bausteins.

Leichter Schlaf

Nach dem leichten Schlaf erwacht, kann sich der ESP8266 noch gut an die zuvor erfolgten Wertzuweisungen an Variablen erinnern, natürlich auch an den Inhalt des RTC-RAM-Speichers.

>>> from machine import RTC, reset_cause, lightsleep
>>> rtc=RTC()
>>> sleepTime=5000
>>> rtc.memory(b"1234 Das ist ein Test")
>>> lightsleep(3000)
>>> rtc.memory()
b'1234 Das ist ein Test'
>>> sleepTime
5000

Wenn wir diese Kommandos in ein Programm übersetzen, dann stellen wir fest, dass dieses in der Zeile fortgesetzt wird, welche auf den Aufruf von lightsleep() folgt.

Während nun im Normalmodus ein Strom von 78mA fließt, wird dieser im lightsleep-Modus auf Werte zwischen 25 mA und 35 mA reduziert. Drastisch geht die Stromstärke im deepsleep-Modus zurück auf 3,3 mA.

Der RTC-Bereich ist also stets gegen Datenverlust abgesichert, sofern der ESP8266 mit ausreichend Spannung versorgt wird. Die Objekte im normalen RAM bleiben nur im lightsleep-Modus erhalten. Bei ausgeschaltetem Board bleiben die Daten nur in einem externen batteriegepufferten RAM, wie im DS1302, erhalten, oder in einer Datei im Flash des Controllerblocks.

Soll das Programm an der Stelle nach dem Schlafbefehl fortgeführt werden, kommt nur der lightsleep-Modus in Frage. Im deepsleep-Modus erfolgt praktisch ein Kaltstart, bei dem das Hauptprogramm von Anfang an gestartet wird.

Mit schlafen+wachen.py habe ich mir ein Programm gebastelt, das auf dem ESP8266 eingesetzt werden kann, das aber auch auf dem ESP32 läuft und hilft, die verschiedenen Features beider Systeme zu untersuchen.

# schlafen+wachen.py
#
from machine import reset_cause,\
    deepsleep, lightsleep, \
    sleep, SoftI2C, Pin
import sys
from oled import OLED

port=sys.platform
lifeTime=5000
sleepTime=5000
taste=Pin(0,Pin.IN,Pin.PULL_UP)
wakePin=Pin(14)

if port == "esp8266":
   i2c=SoftI2C(scl=Pin(5),sda=Pin(4))
   grund={
       0:"PowerOn Reset",
       1:"Watchdog Reset",
       2:"???",
       4:"Soft Reset",
       5:"Deepsleep Reset",
       6:"Hard Reset",
      }
   rc=reset_cause()
elif port == "esp32":
   i2c=SoftI2C(scl=Pin(22),sda=Pin(21))
   from machine import wake_reason
   import esp32
   esp32.wake_on_ext0(wakePin,esp32.WAKEUP_ALL_LOW)
   grund={
       1:"PowerOn Reset",
       2:"EXT0 Wake",
       3:"EXT1 Wake",
       4:"Deepsleep Reset",
       5:"TOUCH Wake",
       6:"ULP Wake",
      }
   rc=wake_reason()
else:
   raise RuntimeError("Unknown Port")

print("Neustart ausgeloest durch:",rc,grund[rc])
d=OLED(i2c,heightw=64) # 128x32-Pixel-Display
d.clearAll()
d.writeAt("NEUSTART DURCH",0,0)
d.writeAt("{}".format(rc),0,1)
d.writeAt(grund[rc],0,2)
sleep(3000)

print(port, "geht in",lifeTime,"ms schlafen")
d.clearAll()
d.writeAt("ICH BIN WACH",0,0)
d.writeAt("fuer".format(rc),0,1)
d.writeAt("{} ms".format(lifeTime),0,2)
sleep(lifeTime)
if taste.value() == 0:
   d.clearAll()
   d.writeAt("PROGRAMM",0,0)
   d.writeAt("BEENDET",0,1)
   sys.exit()

print(port, "schlaeft jetzt",sleepTime,"ms")
d.clearAll()
d.writeAt("ICH SCHLAFE",0,0)
d.writeAt("jetzt fuer".format(rc),0,1)
d.writeAt("{} ms".format(sleepTime),0,2)

# lightsleep(sleepTime)
deepsleep(sleepTime)

if port == "esp8266":
   rc=reset_cause()
else:
   rc=wake_reason()
print(port, "Bin aufgewacht nach ",sleepTime,"ms")
d.clearAll()
d.writeAt("AUFGEWACHT",0,0)
d.writeAt("DURCH {}".format(rc),0,1)
d.writeAt(grund[rc],0,2)

Damit Strommessungen durchgeführt werden können, muss das ESP8266-Board vom USB-Bus getrennt werden und aus einer eigenen Spannungsquelle versorgt werden. Um dennoch Rückmeldungen zu bekommen, habe ich ein OLED-Display eingesetzt.

Nach dem Import einiger Klassen und Funktionen deklariere ich ein paar Variablen und Objekte. Der Backslash erlaubt mir, die Liste über mehrere Zeilen auszudehnen.

from machine import reset_cause,\
    deepsleep, lightsleep, \
    sleep, SoftI2C, Pin
import sys
from oled import OLED

port=sys.platform

Die Stringkonstante sys.platform sagt mit, welchen Controller ich habe.

lifeTime=5000
sleepTime=5000

Zwei Variablen für die Wach- und Schlaf-Phase

taste=Pin(0,Pin.IN,Pin.PULL_UP)

Mit der Flashtaste an GPIO0 kann ich das Programm abbrechen, damit ich nicht in die Schlafphase komme, wenn ich Programmänderungen hochladen möchte. Mit der Kombination Strg+C ist das nicht immer so einfach.

wakePin=Pin(14)

Für den ESP32 definiere ich noch ein Pin-Objekt, mit dem ich den Controller aufwecken möchte. Beim ESP8266 gibt es dieses Feature nicht. Da könnte man nur die RST-Taste verwenden.

Abhängig vom Port führe ich dann weitere Aktionen durch.

if port == "esp8266":
   i2c=SoftI2C(scl=Pin(5),sda=Pin(4))
   grund={
       0:"PowerOn Reset",
       1:"Watchdog Reset",
       2:"???",
       4:"Soft Reset",
       5:"Deepsleep Reset",
       6:"Hard Reset",
      }
   rc=reset_cause()

Beim ESP8266 gibt es nicht viel zu tun. Die richtigen I2C-Pins müssen definiert werden und das Dictionary für den Klartext des Reset-Grundes ist zu deklarieren. Die Funktion reset_cause() verrät den Grund für den letzten Reset. In Tabelle 1 finden Sie eine Übersicht.

elif port == "esp32":
   i2c=SoftI2C(scl=Pin(22),sda=Pin(21))
   from machine import wake_reason
   import esp32
   esp32.wake_on_ext0(wakePin,esp32.WAKEUP_ALL_LOW)
   grund={
       1:"PowerOn Reset",
       2:"EXT0 Wake",
       3:"EXT1 Wake",
       4:"Deepsleep Reset",
       5:"TOUCH Wake",
       6:"ULP Wake",
      }
   rc=wake_reason()
else:
   raise RuntimeError("Unknown Port")

Beim ESP32 dito, zusätzlich zwei weitere Importe, sowie der Aufruf von wake_on_ext0(), um den Schlafmodus durch das Betätigen einer Taste an GPIO14 zu beenden. Wie Sie sehen, gibt es eine andere Zuordnung der Wake- oder Reset-Ereignisse, als beim ESP8266. Für die Abfrage des Grundes ist beim ESP32 die Funktion wake_reason() zuständig. Ist es kein ESP32- oder ESP8266-Board (PY-Board etc.), dann wirft das Programm eine Exception.

print("Neustart ausgeloest durch:",rc,grund[rc])
d=OLED(i2c,heightw=64) # 128x32-Pixel-Display
d.clearAll()
d.writeAt("NEUSTART DURCH",0,0)
d.writeAt("{}".format(rc),0,1)
d.writeAt(grund[rc],0,2)
sleep(3000)

Der jetzt folgende Block informiert über den Neustart und dessen Hintergrund im Terminalfenster und auf dem OLED-Display. Bemerkenswert ist die Funktion sleep(). Sie wohnt nämlich nicht im Modul time, sondern in machine. Die Verzögerung wird auch nicht in Sekunden, sondern in Millisekunden angegeben. Vergleichen Sie dazu auch die Abbildungen 3 und 4.

print(port, "geht in",lifeTime,"ms schlafen")
d.clearAll()
d.writeAt("ICH BIN WACH",0,0)
d.writeAt("fuer".format(rc),0,1)
d.writeAt("{} ms".format(lifeTime),0,2)
sleep(lifeTime)
if taste.value() == 0:
   d.clearAll()
   d.writeAt("PROGRAMM",0,0)
   d.writeAt("BEENDET",0,1)
   sys.exit()

Dann sagt mir der Controller, dass er jetzt noch für 5 Sekunden wach ist. Am Ende der Wachphase habe ich Gelegenheit das Programm sauber abzubrechen, wenn ich während dieser Zeit die Flashtaste drücke und halte. Bei einem ESP8266 Node MCU oder einem Amica befindet sich diese auf dem Board, dem ESP8266 D1 mini (pro) muss ich ein extra Tastenmodul sponsern.

print(port, "schlaeft jetzt",sleepTime,"ms")
d.clearAll()
d.writeAt("ICH SCHLAFE",0,0)
d.writeAt("jetzt fuer".format(rc),0,1)
d.writeAt("{} ms".format(sleepTime),0,2)

# lightsleep(sleepTime)
deepsleep(sleepTime)

Kurz bevor der Controller schlafen geht, informiert er mich darüber. Je nachdem welche der beiden untersten Zeilen aktiv ist, fällt der ESP8266/ESP32 jetzt in leichten oder, wie hier, in Tiefschlaf.

if port == "esp8266":
   rc=reset_cause()
else:
   rc=wake_reason()
print(port, "Bin aufgewacht nach ",sleepTime,"ms")
d.clearAll()
d.writeAt("AUFGEWACHT",0,0)
d.writeAt("DURCH {}".format(rc),0,1)
d.writeAt(grund[rc],0,2)

War es ein Tiefschlaf, dann sehe ich diese letzte Ausgabe nie, denn in diesem Fall startet das Programm wieder ganz von vorne. War es lightsleep, wird das Programm mit dem if-Konstrukt fortgesetzt.

ESP32

Das Modul machine des ESP32 kennt auch die beiden Schlafmodi: lightsleep und deepsleep. Beim ESP32 konnte ich keine großartigen Unterschiede in der Stromaufnahme feststellen. Im wachen Zustand zieht die Schaltung von Abbildung 2, wenn kein Programm läuft, 38mA. Während der Befehl sleep läuft, fließen 13mA, im Tiefschlaf 12mA. Das OLED-Display hat dabei einen Anteil von 3mA. Beim Neustart steigt die Stromstärke kurz auf über 50 mA. Aber zwischen lightsleep und deepsleep gibt es keinen signifikanten Unterschied in der Stromstärke. Werden während der Wartephasen Programmbefehle ausgeführt, liegt die Stromstärke ebenfalls bei knapp 50 mA.

Erwecken kann man den ESP32 auf mehrere Arten, timergesteuert durch betätigen einer Taste, die mit einem der RTC-GPIOs verbunden sein muss, oder über ein Touchpad an einem der entsprechenden Touch-GPIOs. Ich stelle jeweils ein Beispielprogramm dazu vor. Grundlage ist das Programm schlafen+wachen.py, das an entsprechenden Stellen Änderungen erfährt. Die aufzurufenden Funktionen sind im Modul esp32 zu Hause, das dem ESP8266 logischerweise nicht zur Verfügung steht.

Das timergesteuerte Erwachen geschieht beim ESP32 genauso wie beim ESP8266 und bedarf daher keiner weiteren Erläuterung. Neu dagegen ist das Erwecken über eine oder mehrere Tasten oder über ein Touchpad. Mit der Funktion wake_reason() aus dem Modul machine, die es nur beim ESP32 gibt, lässt sich der Grund für das Erwachen abfragen.

ESP32

 

 

PWRON_RESET

1

nach dem Einschalten

EXT0_WAKE

2

nach dem Drücken einer Taste

EXT1_WAKE

3

nach dem Drücken einer von mehreren Tasten

DEEPSLEEP_RESET

4

nach dem Erwachen aus dem Tiefschlaf

TOUCHPAD_WAKE /

SOFT_RESET

5

Nach Berührung eines Touchpads

nach dem Aufruf der Funktion machine.reset()

ULP_WAKE

6

Ultra Low Power Prozessor

Tabelle 2

Wake_on_ext0

Mit dieser Funktion aus dem Modul esp32 eröffne ich mir die Möglichkeit, die Schlafphase, egal welche, über einen Tastendruck zu beenden.

wakePin=Pin(14)
import esp32
esp32.wake_on_ext0(wakePin,esp32.WAKEUP_ALL_LOW)

Mit wakePin übergebe ich ein Pin-Objekt. Als Anschluss muss einer der RTC-GPIOs gewählt werden, denn nur diese sind während der Ruhezeit noch aktiv. Dennoch wird bei der Instanziierung des Pin-Objekts die ganz normale GPIO-Nummer angegeben. Die Tabelle 3 zeigt auf, welche Anschlüsse in Frage kommen.

RTC_GPIO0

 GPIO36

RTC_GPIO3

 GPIO39

RTC_GPIO9

 GPIO32

RTC_GPIO8

 GPIO33

RTC_GPIO6

 GPIO25

RTC_GPIO7

 GPIO26

RTC_GPIO17

 GPIO27

RTC_GPIO16

 GPIO14

RTC_GPIO15

 GPIO12

RTC_GPIO14

 GPIO13

RTC_GPIO11

 GPIO0

RTC_GPIO13

 GPIO15

RTC_GPIO12

 GPIO2

RTC_GPIO10

 GPIO4

Tabelle 3

Im Programm schlafen+wachen.py ist das Aufwecken durch eine Taste bereits eingebaut. Die Konstante esp32.WAKEUP_ALL_LOW hat den Wert False (siehe Abbildung 5) und sorgt dafür, dass der ESP32 erwacht, wenn an GPIO14 ein Low-Pegel anliegt. Das entspricht der Schaltung in Abbildung 2. Alternativ wird mit WAKUP_ANY_HIGH der ESP32 aufgeweckt, wenn der Pegel auf 3,3V geht.

Abbildung 5: Attribute des Moduls esp32

Abbildung 5: Attribute des Moduls esp32

wake_on_ext1

Mit wake_on_etx1() können mehrere Tasten den ESP32 aufwecken. Die Pin-Objekte werden dann als Tupel oder als Liste übergeben. Die entsprechenden Zeilen könnten etwa so aussehen.

wakePin = Pin(14)
taste = Pin(0,Pin.IN,Pin.PULL_UP)
tasten = (wakePin, taste)
import esp32
esp32.wake_on_ext1(tsten,esp32.WAKEUP_ALL_LOW)

Jetzt kann der ESP32 auch über die Flashtaste aufgeweckt werden.

wake_on_touch

Auch über eines der Touchpins kann man den ESP32 aufwecken. Der Anschluss mehrerer Touchpads ist möglich. Es kann aber nicht gleichzeitig über Tasten gearbeitet werden, der Interpreter meldet da einen Fehler.

Traceback (most recent call last):
 File "<stdin>", line 37, in <module>
ValueError: no resources

Der elif-Teil könnte jetzt also etwa so aussehen. Die wake_on_ext0-Zeile ist auskommentiert.

elif port == "esp32":
   i2c=SoftI2C(scl=Pin(22),sda=Pin(21))
   from machine import wake_reason, TouchPad
   import esp32
   wake1 = Pin(15, mode = Pin.IN)
   wake2= Pin(2, mode = Pin.IN)
   touch1 = TouchPad(wake1)
   touch2= TouchPad(wake2)
   touch1.config(300)
   touch2.config(300)
   esp32.wake_on_touch(True)
   #esp32.wake_on_ext0(wakePin,esp32.WAKEUP_ALL_LOW)
   grund={
       1:"PowerOn Reset",
       2:"EXT0 Wake",
       3:"EXT1 Wake",
       4:"Deepsleep Reset",
       5:"TOUCH Wake",
       6:"ULP Wake",
      }
   rc=wake_reason()

Ich habe zwei Platinenstücke an die Pins GPIO15 und GPIO2 angeschlossen. Die beiden erzeugten Touchpad-Objekte werden so konfiguriert, dass der mit touchX.read() ausgelesene Wert ohne Berührung (z. B. 419) deutlich über dem an die Methode config() übergebenen Argument liegt und der mit Berührung (z.B. 65) ermittelte Wert deutlich darunter.

Wird das Programm ein zweites Mal gestartet und der ESP32 mit einem Touch aufgeweckt, dann bekommen wir den Grund der vorangegangenen Erweckung korrekt mitgeteilt.

Neustart ausgeloest durch: 5 TOUCH Wake
this is the constructor of OLED class
Size:128x32
esp32 geht in 5000 ms schlafen
esp32 schlaeft jetzt 5000 ms
lightsleep(sleepTime)
# deepsleep(sleepTime)
Wird lightsleep statt deepsleep aktiviert, dann wird der Programmteil nach dem Aufruf ausgeführt, ganz egal, ob der ESP32 durch den Timer oder durch Touch oder Tasten ausgeweckt wird. Danach wird das Programm beendet. Soll dieser Teil mehrfach durchlaufen werden, müsste man ihn in einer while-Schleife verpacken, etwa so:
# schlafen+wachen.py
#
from machine import reset_cause,\
    deepsleep, lightsleep, \
    sleep, SoftI2C, Pin
import sys
from oled import OLED

port=sys.platform
lifeTime=5000
sleepTime=5000
taste=Pin(0,Pin.IN,Pin.PULL_UP)
wakePin=Pin(14)

if port == "esp8266":
   i2c=SoftI2C(scl=Pin(5),sda=Pin(4))
   grund={
       0:"PowerOn Reset",
       1:"Watchdog Reset",
       2:"???",
       4:"Soft Reset",
       5:"Deepsleep Reset",
       6:"Hard Reset",
      }
   rc=reset_cause()
elif port == "esp32":
   i2c=SoftI2C(scl=Pin(22),sda=Pin(21))
   from machine import wake_reason, TouchPad
   import esp32
   wake1 = Pin(15, mode = Pin.IN)
   wake2= Pin(2, mode = Pin.IN)
   touch1 = TouchPad(wake1)
   touch2= TouchPad(wake2)
   touch1.config(300)
   touch2.config(300)
   esp32.wake_on_touch(True)
   #esp32.wake_on_ext0(wakePin,esp32.WAKEUP_ALL_LOW)
   grund={
       1:"PowerOn Reset",
       2:"EXT0 Wake",
       3:"EXT1 Wake",
       4:"Deepsleep Reset",
       5:"TOUCH Wake",
       6:"ULP Wake",
      }
   rc=wake_reason()
else:
   raise RuntimeError("Unknown Port")
while 1:
   print("Neustart ausgeloest durch:",rc,grund[rc])
   d=OLED(i2c,heightw=32) # 128x32-Pixel-Display
   d.clearAll()
   d.writeAt("NEUSTART DURCH",0,0)
   d.writeAt("{}".format(rc),0,1)
   d.writeAt(grund[rc],0,2)
   sleep(3000)

   print(port, "geht in",lifeTime,"ms schlafen")
   d.clearAll()
   d.writeAt("ICH BIN WACH",0,0)
   d.writeAt("fuer".format(rc),0,1)
   d.writeAt("{} ms".format(lifeTime),0,2)
   sleep(lifeTime)
   if taste.value() == 0:
       d.clearAll()
       d.writeAt("PROGRAMM",0,0)
       d.writeAt("BEENDET",0,1)
       sys.exit()

   print(port, "schlaeft jetzt",sleepTime,"ms")
   d.clearAll()
   d.writeAt("ICH SCHLAFE",0,0)
   d.writeAt("jetzt fuer".format(rc),0,1)
   d.writeAt("{} ms".format(sleepTime),0,2)

   lightsleep(sleepTime)
   # deepsleep(sleepTime)

   if port == "esp8266":
       rc=reset_cause()
   else:
       rc=wake_reason()
   print(port, "Bin aufgewacht nach ",sleepTime,"ms")
   d.clearAll()
   d.writeAt("AUFGEWACHT",0,0)
   d.writeAt("DURCH {}".format(rc),0,1)
   d.writeAt(grund[rc],0,2)
   sleep(3000)

Abbildung 6: ESP8266-NodeMCU mit DS1302-RTC

Abbildung 6: ESP8266-NodeMCU mit DS1302-RTC

Mit all den neuen Erkenntnissen sind Sie jetzt in der Lage, batterieschonendere Schaltungen zu entwickeln und Daten über die Schlafphasen hinaus zu konservieren. Dabei hat sich aber herausgestellt, dass der Effekt beim ESP8266 deutlicher zu Tage tritt, als bei seinem großen Bruder. Stromstärken von wenigen µA in der Tiefschlafphase konnte ich bei keinem der untersuchten Boards feststellen. Solche Angaben beziehen sich wohl nur auf den Chip und nicht auf die ganze Platine. Entscheidend ist aber die Stromaufnahme von letzterer.

Esp-32Esp-8266Projekte für anfängerSmart homeStromversorgung

7 Kommentare

Werner

Werner

bei den Dateien wake_on_gpio.py und schlafarten.py kommt die Fehlermeldung:
“Not Found

The requested URL was not found on this server."

MisterD

MisterD

Spannend wäre den testaufbau gleich noch für mehrere verschiedene Boards zu nutzen,vor allem würden mich der pico und pico w interessieren.

Thomas

Thomas

Alter Kaffee. Solche Untersuchungen, allerdings in C++, gibt es schon seit Jahren. Einen nackten ESP (ohne USB, ohne ASM1117 und ohne LED) kann man auf ein paar µA im Schlaf trimmen. Wer wirklich mit Akku oder Batterie sinnvoll im Langzeitmodus expeirmentieren will, sollte daher keinen NodeMCU, Wemos oder ähnliches verwenden, sondern immer auf den nackten ESP zurückgreifen und ein bisschen löten. Es sei denn, er überlässt die Tiefschlafsteuerung einem Attiny oder einem Echtzeit-RTC, um den EN-PIN mittels geeigneter Schaltung auf VCC zu setzen bzw. zu trennen. Man muss dan naber auch damit leben, dass der ESP einen Neustart macht und nur noch weiß, was er im RTC-Memory oder im Flash als Datei gespeichert hat.

Reiner Knapp

Reiner Knapp

Nur so als Hinweis zum Stromverbrauch mein Vortrag auf der Piandmore: https://piandmore.de/de/conference/pam12/call/public-media/2289

Man sollte sich nicht mit 12mA Stromverbrauch im standby zufrieden geben!

Harald

Harald

Das Unternehmen ist recht sinnlos. Zum Stromsparen muß man die reinen ESPs verwenden. Durch die Breakout-Boards wird extrem viel Strom verbaraucht. Deshalb ist auch zweischen light- und deep-sleep nur ein geringer Unterschied meßbar.

Veit

Veit

Hallo
Tolles Projekt. Leider nur in Micro Python. Ich bin fast 70 Jahre und habe mich in die Arduino IDE eingearbeitet. Mühselig und Langsamm !!
Eine neue Sprache muss und will ich nicht noch anfangen.
Viele Grüße an das tolle Team und weiter so.

Herbert Dietl

Herbert Dietl

Hallo, folgende py codes lassen sich nicht downloaden:
wake_on_gpio.py
schlafarten.py

Kommentar hinterlassen

Alle Kommentare werden von einem Moderator vor der Veröffentlichung überprüft

Empfohlene Blogbeiträge

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery