14.3.2. Der Watchdog¶
Der Hardware-Watchdog ist das Fundament, auf dem jede andere Härtungsentscheidung steht. Er ist ein winziger unabhängiger Timer, der den Prozessor zurücksetzt, wenn ihm zu lange nichts anderes mitgeteilt wurde. Ein Skript, das sich an einem unzuverlässigen Sensor verklemmt, ein Netzwerkaufruf, der über sein Timeout hinaus blockiert, ein Speicherzuordner, der in einer Ecke des Heaps feststeckt, eine Ausnahme, die der Schleife entkommen ist – nichts davon hält den Watchdog auf. Der Timer zählt ungeachtet dessen herunter, und die Kamera startet neu.
Für ein ausgeliefertes Produkt ist ein Watchdog nicht optional. Ohne ihn lässt jeder der oben genannten Fehlerzustände die Kamera tot zurück, bis jemand es bemerkt und sie aus- und wieder einschaltet. Mit ihm kommt die Kamera von selbst wieder hoch, und der einzige Beleg für den Fehler ist eine Zeile im Protokoll.
Siehe auch
Die Seite Watchdog-Timer im Hardware-Kapitel behandelt, was ein Watchdog auf Hardware-Ebene ist, sowie die Grundlagen der machine.WDT-API. Diese Seite behandelt, was sich für ein Produktions-Deployment ändert.
14.3.2.1. Den Watchdog starten¶
machine.WDT ist die API. Sie ist hardwaregestützt: einmal konstruiert, läuft der Timer bis zum nächsten Reset. Es gibt kein stop(), kein deinit(), keinen Ausweg per Strg-C. Das ist der Sinn der Sache.
Eine typische Einrichtung am Anfang von main.py, unmittelbar vor der Schleife, die sie schützt:
from machine import WDT
wdt = WDT(timeout=10_000) # milliseconds
main.py ist der richtige Ort für den Watchdog, weil dort die Schleife lebt. Ein Watchdog-Reset ist ein Hardware-Reset, also läuft der Kaltstart-Pfad erneut und main.py betritt die Schleife von selbst wieder – die Wiederherstellung funktioniert ohne jegliche Verdrahtung in boot.py. Den Watchdog stattdessen in boot.py zu starten bedeutet, dass jeder Soft-Reset (zum Beispiel das Strg-D eines Entwicklers) der Anwendung einen Hardware-Timer überreicht, den sie nicht stoppen kann – was am Entwicklungsrechner ein Ärgernis ist und in produktivem Setup-Code, der vor der Bereitschaft der Schleife läuft, eine Falle.
Wählen Sie das Timeout 2- bis 3-mal länger als die schlechteste beobachtete Iterationszeit der Hauptschleife. Bildraten-Jitter, eine langsame Sensorabfrage bei einem kalten Sensor, ein kurzer Wi-Fi-Aussetzer – nichts davon sollte den Watchdog auslösen. Eine echte Blockade (eine Endlosschleife, ein blockierter I/O-Aufruf) sollte es. Zu kurze Timeouts machen den Watchdog zu einer Quelle falscher Resets; zu lange Timeouts lassen die Kamera minutenlang reaktionslos verharren, bevor die Wiederherstellung greift.
14.3.2.2. Ihn füttern¶
wdt.feed() setzt den Countdown zurück. Rufen Sie es einmal pro Iteration der Hauptschleife auf, am Anfang des Schleifenkörpers, sodass das Füttern bedingungslos vor jeglicher Arbeit erfolgt, die hängen bleiben könnte:
while True:
wdt.feed()
frame = csi0.snapshot()
process(frame)
14.3.2.3. Ausnahmen überleben¶
Der Watchdog kümmert sich um Blockaden. Ausnahmen sind ein anderer Fehlerzustand. Eine unbehandelte Ausnahme steigt bis zur obersten Ebene des Skripts auf, main.py wird beendet, und die Kamera fällt zur REPL zurück. Der Watchdog löst dann nach seinem Timeout aus, weil ihn von der REPL aus nichts füttert, die Kamera setzt sich zurück, und main.py läuft erneut – die Wiederherstellung funktioniert also durchaus, aber das Feld bezahlt für jeden Absturz ein volles Timeout plus Neustart, der Traceback geht an den USB-stdout, den niemand liest, und jeglicher Zustand, den die Anwendung im Speicher hielt, ist verloren.
Die Hauptschleife in ein try / except auf oberster Ebene zu hüllen, verwandelt einen Absturz in ein protokolliertes Ereignis, durch das die Anwendung hindurch weiterläuft, ohne für einen Reset zu bezahlen:
import logging
log = logging.getLogger(__name__)
while True:
wdt.feed()
try:
frame = csi0.snapshot()
process(frame)
except Exception:
log.exception("frame loop iteration failed")
Das Abfangen von Exception (nicht BaseException) hält KeyboardInterrupt und SystemExit funktionsfähig, was genau das ist, was ein über USB angeschlossener Entwickler möchte.
Dieses Muster ist die Software-Hälfte der Lebendigkeit: der Watchdog fängt die Blockaden ab, der Wrapper fängt die Abstürze ab, und das Protokoll hält fest, was beide abgefangen haben.
14.3.2.4. Wissen, warum ein Boot stattfand¶
Jeder Soft-Reset und jeder Watchdog-Reset zeigt sich letztlich als frischer Boot. Der Helfer für die Boot-Zeit-Diagnostik protokolliert machine.reset_cause() bei jedem Kaltstart; die reset cause-Zeile ist das, was dem Feld verrät, ob die Wiederherstellung tatsächlich gegriffen hat oder die Kamera nur normal aus- und eingeschaltet wurde.
Die Reset-Cause-Zeile ist das, was die Arbeit des Watchdogs im Protokoll sichtbar macht. Ein Protokoll voller watchdog timeout-Resets sagt aus, dass die Anwendung hängt und der Watchdog sie wiederhergestellt hat. Ein Protokoll ohne sie sagt aus, dass der Watchdog nicht auslösen musste – was normalerweise bedeutet, dass die Anwendung gesund ist, aber auch bedeuten kann, dass das Timeout zu lang eingestellt ist, um die Blockaden zu erfassen, die tatsächlich auftreten.
14.3.2.5. Ein vollständiger Einstieg¶
Ein main.py, das Watchdog, Logging-Einrichtung, Boot-Zeit-Diagnostik und den Wrapper zusammenführt, sieht so aus:
import logging
from machine import WDT
from app.logging_setup import setup_logging, log_boot_diagnostics
setup_logging('/sdcard/logs/app.log')
log_boot_diagnostics()
log = logging.getLogger(__name__)
wdt = WDT(timeout=10_000)
while True:
wdt.feed()
try:
step()
except Exception:
log.exception("loop iteration failed")
step() ist die Arbeit der Anwendung pro Iteration; der Rest dieses Gerüsts ändert sich zwischen Produkten nicht. Härtung ist ein Watchdog, ein Wrapper und ein protokollierter Boot bei jedem Kaltstart – nicht viel Code, und der Unterschied zwischen einer Kamera, die sich von selbst erholt, und einer, die einen Serviceeinsatz benötigt.