Der erste Reflex beim Industrie-Prototyping ist oft: ESP32 anschließen, Sensor auslesen, Demo zeigen. Für die meisten Teams ist das die falsche Reihenfolge. Eine blinkende LED beweist wenig darüber, ob aus Messwerten die richtige Entscheidung entsteht. Ich würde zuerst das Verhalten mit aufgezeichneten Daten festlegen und erst dann Hardware anschließen, weil sich fachliche Fehler am Schreibtisch schneller und eindeutiger finden lassen.
Ein laufendes Board beweist noch keine richtige Entscheidung
Ein ESP32 liefert früh ein sichtbares Ergebnis: Der Sensor sendet Werte, eine Anzeige wechselt den Zustand, vielleicht schaltet ein Relais. Das motiviert, verdeckt aber eine unbequeme Frage: Was soll passieren, wenn der letzte Wert alt ist, ein Messwert fehlt oder die Reihenfolge der Nachrichten nicht stimmt? Solange das niemand entschieden hat, demonstriert die Hardware vor allem, dass Kabel und Bibliotheken zusammenarbeiten.
Industrie Prototyping mit Software von Idee zum MVP setzt einen nützlichen Zielpunkt; ich würde den ersten überprüfbaren Meilenstein allerdings nicht als laufendes Board definieren, sondern als ausführbaren Vertrag für die Entscheidung. Dieser Vertrag bekommt Eingaben, einen Zeitpunkt und ein erwartetes Ergebnis. Ein Junior Developer kann ihn mit Python 3.12 und pytest 8 prüfen, ohne zugleich GPIO-Belegung, Treiber und Sensorausfälle debuggen zu müssen.
Nehmen wir einen Prototyp, der eine Anlage bei hoher Temperatur sperren soll. 70 °C sind hier ein frei gewählter Testgrenzwert, keine Empfehlung für eine reale Anlage. Eine Messung von 69 °C erlaubt in diesem vereinfachten Modell den Betrieb, eine von 72 °C sperrt ihn. Damit ist die wichtigste Frage noch offen: Ist eine vor einer Minute gemessene Temperatur überhaupt eine gültige Grundlage für „Betrieb erlaubt“? Die Antwort gehört in den Vertrag, nicht als beiläufige Bedingung in einen Callback.
Auch der MVP-Begriff wird dadurch präziser. „Temperatur wird angezeigt“ prüft den Datenweg, aber nicht die Entscheidung. „Bei fehlendem aktuellem Wert wird keine Freigabe ausgegeben“ ist dagegen mit Testdaten widerlegbar. Wer diese Unterscheidung früh trifft, kann einer fachlich verantwortlichen Person zwei konkrete Verhaltensvarianten vorlegen, statt über eine Demo zu reden, die beide Varianten gleich aussehen lässt.
Das heißt nicht, dass Software die physische Messung ersetzt. Es heißt, dass ein Sensorwert und seine Bedeutung getrennte Probleme sind. Falls niemand sagen kann, ob ein verspäteter Wert verworfen werden muss, hilft auch eine fehlerfreie I²C-Verbindung nicht weiter. Ich würde nicht zuerst eine vollständige Arduino-Skizze für Sensor, Anzeige und Relais schreiben, weil jede ungeklärte Regel dann zwischen Initialisierung, Interrupts und Fehlerbehandlung verschwindet.
Ein kleiner Replay-Test findet früher die teuren Denkfehler
Für den ersten Vertrag reichen Ereignisse mit Zeitstempel und Wert. Als Austauschformat eignet sich JSON Lines: Jede Zeile enthält ein Ereignis, und Dateien lassen sich mit Git versionieren und im Review vergleichen. Ein JSON Schema Draft 2020-12 kann festlegen, dass t_ms eine ganze Zahl und temp_c eine Zahl ist. Das Schema verhindert noch keine falsche Entscheidung, aber es hält ungültige Eingaben vom eigentlichen Entscheidungstest fern.
Der folgende Python-3-Code läuft als Datei ohne zusätzliche Pakete. Die 500 ms sind eine einstellbare Frist für diesen Versuch; ob sie zum späteren Sensor passt, muss gemessen werden. Die Zeitpunkte 0, 200 und 800 ms sind synthetische Testdaten, keine behaupteten Anlagenmessungen.
def decide(events, now_ms, max_age_ms=500):
seen = [event for event in events if event["t_ms"] <= now_ms]
if not seen or now_ms - seen[-1]["t_ms"] > max_age_ms:
return "FEHLT"
if seen[-1]["temp_c"] >= 70:
return "SPERRE"
return "FREIGABE"
trace = [{"t_ms": 0, "temp_c": 62}, {"t_ms": 200, "temp_c": 72}]
assert decide(trace, 100) == "FREIGABE"
assert decide(trace, 250) == "SPERRE"
assert decide(trace, 800) == "FEHLT"
print("Drei Vertragsfaelle bestanden")
Der Code ist absichtlich kleiner als eine Firmware. Er legt offen, dass „Messwert fehlt“ ein anderer Zustand als „Grenzwert überschritten“ ist. Ob beide Zustände später denselben sicheren Ausgang auslösen, muss die fachliche Anforderung bestimmen; der Test sollte diese Entscheidung nicht durch einen einzigen Rückgabewert verstecken. Gerade für jemanden mit wenig Embedded-Erfahrung ist das hilfreich, weil ein fehlschlagender Test hier auf die Regel oder die Testannahme zeigt und nicht auf fünf mögliche Hardwareursachen.
Danach würde ich die Testfälle erweitern: zwei Ereignisse mit gleichem Zeitstempel, ein Wert exakt am Grenzwert, ein Ereignis aus der Zukunft und eine Lücke zwischen Messungen. Ein Arbeitsziel von 20 überprüften Sequenzen ist kein Qualitätsstandard, sondern eine handhabbare Größe für einen ersten Review. Wichtiger als die Anzahl ist, dass jede Sequenz eine Frage beantwortet, über die sich das Team sonst erst beim Vorführen der Hardware streitet.
Für reale Laufzeitmessungen müssen diese künstlichen Zeitpunkte später ersetzt werden. Auf dem Gerät eignet sich eine monotone Uhr besser als eine Wanduhr, weil eine Korrektur der Systemzeit die Altersberechnung sonst verfälschen kann. Ein Zeitstempel im übertragenen Ereignis und die Ankunftszeit am Empfänger sind außerdem verschiedene Dinge: Wer sie vermischt, kann eine verspätete Nachricht irrtümlich für frisch halten.
Python-Replay und ESP32 beantworten verschiedene Fragen
Die Wahl ist kein Glaubenskrieg zwischen „richtiger“ Hardware und „bloßer“ Simulation. Python-Replay gewinnt, wenn das Team Regeln, Grenzfälle und Bedeutung von Nachrichten klären muss; sein Preis ist, dass es weder Sensorrauschen noch Scheduling und elektrische Signale prüft. ESP32 mit ESP-IDF 5.x gewinnt, sobald Bootverhalten, Treiber, Interrupt-Latenz oder Speicherverbrauch die offene Frage sind; sein Preis sind Aufbau, Flash-Vorgänge und schwierigere Fehlersuche. Für die meisten Teams ist Replay deshalb der bessere erste Schritt, aber nicht der letzte.
Espressif gibt für den klassischen ESP32 je nach Variante und Konfiguration Taktfrequenzen bis 240 MHz an. Diese Herstellerangabe sagt nichts darüber aus, ob eine konkrete Messung rechtzeitig verarbeitet wird, weil Sensorzugriff, FreeRTOS-Tasks und blockierende Aufrufe ebenfalls Zeit beanspruchen. Sobald die Entscheidung zeitkritisch wird, würde ich ihre Laufzeit auf der Zielhardware messen und die Verteilung betrachten, statt aus dem CPU-Takt eine Reaktionszeit abzuleiten.
Microcontroller Boards programmieren mit Arduino und ESP32 bietet einen praktischen Einstieg in die Gerätearbeit; ich würde ihn erst nach dem ersten fachlichen Replay zum nächsten Schritt machen. Dann lässt sich ein sehr kleiner Adapter schreiben: Sensor lesen, Messwert mit Zeitstempel versehen, die bereits festgelegte Entscheidung aufrufen und das Ergebnis ausgeben. Wer mit Arduino-ESP32 3.x statt ESP-IDF arbeitet, kann dieselben Vertragsfälle verwenden, sofern Eingaben und Ergebnisse an der Grenze zur Firmware gleich bleiben.
Es gibt gute Gründe, früher zum Board zu greifen. Wenn unklar ist, ob der Sensor kurze Spitzen überhaupt erfasst, beantwortet kein JSON-Test diese Frage. Wenn ein Ausgang beim Reset einen gefährlichen Pegel annimmt, muss das am realen Aufbau geprüft werden. In solchen Fällen würde ich einen gezielten Hardwareversuch vorziehen: genau eine Frage, genau eine Messung und ein notiertes Ergebnis. Ich würde daraus noch keine komplette Produktdemo bauen, weil zusätzliche Funktionen die Ursache eines Fehlers wieder schwerer erkennbar machen.
Wokwi kann für einfache GPIO-Abläufe eine Zwischenstation sein, weil sich Abläufe ohne physischen Aufbau wiederholen lassen. Es ist aber kein Ersatz für den konkreten Sensor und seine elektrische Umgebung, weil ein Simulator nur das Verhalten abbildet, das sein Modell kennt. Ein USB-Logikanalysator mit Saleae Logic 2 oder ein Mitschnitt in Wireshark ist erst dann sinnvoll, wenn die offene Frage an Signalen beziehungsweise Nachrichten hängt. Das Werkzeug sollte der Frage folgen, nicht umgekehrt.
Die Übergabe zur Firmware braucht eine überprüfbare Grenze
Der Vorteil des Replay-Ansatzes geht verloren, wenn später zwei Versionen der Regel entstehen: eine in Python und eine leicht andere in C oder C++. Deshalb würde ich nicht den Python-Code zur heimlichen Spezifikation erklären. Die Spezifikation sind die versionierten Eingaben, erwarteten Ausgaben und begründeten Grenzfälle. Dieselben Fälle müssen gegen die Firmware laufen können, auch wenn dafür zunächst ein einfacher Test-Adapter nötig ist.
Bei einem Gerät mit MQTT 5 könnte dieser Adapter beispielsweise Testereignisse veröffentlichen und Entscheidungen abonnieren. Das ist nur dann ein brauchbarer Vertragstest, wenn das Team festlegt, welche Zeit der Zeitstempel beschreibt und wie mit erneut zugestellten Nachrichten umgegangen wird; das Protokoll entscheidet diese Fachfragen nicht. Für einen lokalen Prototyp reicht oft eine serielle Schnittstelle mit JSON Lines, weil sie ohne Broker auskommt und Ein- und Ausgaben direkt sichtbar macht.
Zur Grenze gehören auch Einheiten und ungültige Werte. Ein Feld namens temperature lässt Celsius und Fahrenheit offen; temp_c macht die Annahme sichtbar. Ein JSON-Schema kann das Feld verlangen, aber nicht erkennen, ob ein Sensor versehentlich Fahrenheit liefert. Ein Test mit einem bekannten Referenzwert und ein Review der Sensor-Konfiguration sind deshalb nötig, sobald echte Daten hinzukommen. Ebenso sollte ein nicht lesbarer Sensor einen definierten Fehlerzustand erzeugen und nicht den letzten gültigen Wert stillschweigend weiterreichen.
Für einen ersten Review würde ich einen kurzen Nachweis verlangen: Welche Testdatei enthält den Grenzfall, welche Firmware-Version wurde geprüft und welcher reale Versuch bleibt offen? Git hält die Vertragsfälle neben dem Code fest; pytest kann den Replay-Teil automatisieren. Ein separater Test auf dem Board belegt anschließend, dass der Adapter dieselben Ergebnisse liefert. Das ist mehr Arbeit als eine spontane Demo, aber weniger Rätselarbeit bei jeder Änderung der Entscheidungsregel.
Beginne mit einem einzigen realistischen Messverlauf und schreibe daneben auf, welche Entscheidung an jedem Zeitpunkt erwartet wird. Ergänze einen fehlenden oder verspäteten Wert und lasse den kleinen Replay-Test scheitern, bevor du die Regel korrigierst. Erst danach schließt du das Board an. Wenn dabei eine Hardwarefrage auftaucht, formuliere sie als eigenen Versuch, statt den Vertrag stillschweigend umzuschreiben.


