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 |
1 |
NodeMCU Lua Amica Modul V2 ESP8266 ESP-12F WIFI oder |
1 |
|
1 |
|
1 |
|
1 |
Die Software
Fürs Flashen und die Programmierung des ESP32:
Thonny oder
Verwendete Firmware für einen ESP32:
Verwendete Firmware für einen ESP8266:
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
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
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 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
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
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.
7 Kommentare
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
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
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
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
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
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
Hallo, folgende py codes lassen sich nicht downloaden:
wake_on_gpio.py
schlafarten.py