Dauerbetrieb: Autostart (main.py) und Watchdog

MicroPython fuehrt main.py nach jedem Reset selbst aus -- nach Stromausfall oder
Watchdog-Neustart laeuft die Anzeige damit ohne Notebook wieder an. Bisher gab es
die Datei bewusst nicht, weil am Aufbau gearbeitet wurde.

**Das Abbruchfenster ist kein Komfort, sondern die Notbremse.** Ohne es waere ein
Geraet, dessen Programm beim Start haengt, nur noch durch Neuflashen erreichbar:
Die Endlosschleife belegt die REPL sofort nach dem Reset. main.py wartet deshalb
erst AUTOSTART_DELAY (3 s), in denen Ctrl-C zurueck an die REPL fuehrt -- dann
laeuft auch kein Watchdog.

Watchdog (WDT_TIMEOUT = 120 s): startet das Geraet neu, wenn die Poll-Schleife
stehenbleibt. Gefuettert wird zu Beginn *jedes* Durchlaufs, auch nach einem
Fehler -- "KEIN LINK" ist kein Stillstand, die Schleife laeuft ja weiter. Der
Wert muss ueber allem liegen, was regulaer blockieren darf (WLAN 20 s,
Montierung 3 s, dazu ein MQTT-Versuch); knapper waere er eine Neustart-Schleife.

Ein gestarteter Watchdog laesst sich nicht mehr abschalten. Deshalb legt ihn nur
main.py an -- im Handbetrieb ueber die REPL startete das Geraet sonst neu,
waehrend man nachdenkt.

Weiteres:
- Nach einem Watchdog-Neustart meldet main.py das beim Start
  (machine.reset_cause()), sonst saehe er aus wie ein Stromausfall.
- Eine Ausnahme faengt main.py ab und startet nach RESTART_DELAY (10 s) neu,
  statt in die REPL zu fallen und die Anzeige einzufrieren. Die Pause verhindert
  eine Neustart-Schleife im Sekundentakt.
- Autostart faehrt mit Helligkeitsregelung (with_ldr=True, Nutzerentscheidung).

Am Geraet bestaetigt: Autostart nach Reset laeuft durch (Watchdog aktiv, WLAN,
MQTT), ein absichtlich herbeigefuehrter Stillstand loest den Watchdog aus und das
Geraet kommt von allein wieder hoch -- mit der Meldung "Vorheriger Lauf wurde vom
Watchdog beendet". Ctrl-C im Startfenster gibt die REPL frei.

Beim Geraetetest aufgefallen: make_watchdog wurde ohne log aufgerufen, im
Startprotokoll stand also nichts vom Watchdog. Behoben und durch einen Test
festgehalten. 178 Tests gruen (neu: test_main.py).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-27 18:41:33 +02:00
parent 433c53aabd
commit 142a621066
8 changed files with 489 additions and 7 deletions
+10 -1
View File
@@ -48,7 +48,7 @@ def format_coordinates(ra_raw, dec_raw):
def poll_loop(display, client, interval=None, once=False,
read_ldr=None, dimmer=None, ensure_link=None, log=None,
bridge=None):
bridge=None, feed=None):
"""Zyklisch die Montierung abfragen und die Anzeige nachfuehren.
display offenes Display-Objekt
@@ -65,6 +65,11 @@ def poll_loop(display, client, interval=None, once=False,
bridge optional: mqtt.MqttBridge -- empfaengt Einstellungen und
veroeffentlicht den Zustand. Faellt der Broker aus, laeuft die
Anzeige unveraendert weiter (mqtt.py faengt seine Fehler selbst).
feed optional: Funktion ohne Argumente, die den Watchdog fuettert.
Wird zu Beginn **jedes** Durchlaufs gerufen -- auch nach einem
Fehler, denn eine gemeldete Stoerung ist kein Stillstand: Die
Schleife laeuft ja weiter und zeigt "KEIN LINK". Der Watchdog
soll nur greifen, wenn gar nichts mehr geht.
Laeuft bis KeyboardInterrupt oder -- bei once=True -- genau einmal. Fehler
von Anzeige und Montierung werden abgefangen: die Schleife meldet sie und
@@ -77,6 +82,10 @@ def poll_loop(display, client, interval=None, once=False,
last_shown = None
while True:
# Ganz am Anfang und ausserhalb des try: Der Watchdog misst, ob die
# Schleife noch laeuft -- nicht, ob sie fehlerfrei laeuft.
if feed is not None:
feed()
try:
# Zuerst MQTT abholen: So wirkt eine gerade gesetzte Helligkeit noch
# in diesem Durchlauf, statt eine Runde hinterherzuhinken.