esp32 friert bei Programmausführung unter Micropython ein.

  • ]moin,

    ich lese mit einem ESP32 laufend die Daten meiner Victron Laderegler aus und übertrage die benötigten Daten an einen MQTT-Server (Spannung und Status). Der ESP hängt sich nicht reproduzierbar nach unterschiedlich langer Zeit auf? Da er keinen Fehler ausgibt, kann ich nicht feststellen, wo die Schwachstelle liegt.
    Am externen MQTT-Server liegt es vermutlich nicht, weil meine AHOY DTUs und ein ESP32, der Temperaturen und einen Zählerstand überträgt, störungsfrei laufen.

    Für Testzwecke habe ich einige Print-Anweisungen eingefügt und anstelle des laufenden Auswertens der Victron-Daten einen entsprechenden String eingefügt.

    Die Ausgabe in Thonny sieht so wie unten aus. Wenn der ESP einfriert, wird kein Fehler angezeigt. Ist der MQTT-Server nicht erreichbar, startet der ESP neu.

    MPY: soft reboot
    Connection successful
    Mit WLAN verbinden...
    WebREPL server started on http://192.168.178.140:8266/
    Started webrepl in normal mode
    Connected to %s MQTT broker
    116 alles gesendet
    Stunde, Minute, Sekunde 16 52 34
    Connected to %s MQTT broker
    116 alles gesendet
    Stunde, Minute, Sekunde 16 52 42
    Connected to %s MQTT broker
    116 alles gesendet
    Stunde, Minute, Sekunde 16 52 50
    Traceback (most recent call last):
     File "main.py", line 106, in <module>
    KeyboardInterrupt: 
    MicroPython v1.26.0 on 2025-08-09; Generic ESP32 module with ESP32
    Type "help()" for more information.
    >>> 

  • esp32 friert bei Programmausführung unter Micropython ein.? Schau mal ob du hier fündig wirst!

  • Go to Best Answer
  • Hajulied Es ist in der Regel eine gute Idee Quelltexte die man irgendwo posted vorher mal aufzuräumen.

    In beiden wird time importiert und aus time dann noch mal sleep(). In der Boot-Datei wird nichts davon verwendet. Im Hauptmodul wird sleep() über das Modul und der explizite Import verwendet. Wonach wird da entschieden wann was benutzt wird? Das sollte einheitlich sein. Die Module micropython, ntptime und MQTTClient aus umqttsimple werden in der Boot-Datei importiert, aber nicht verwendet. Letzteres sollte im Hauptmodul importiert werden. Im Hauptmodul wird socket importiert aber nirgends verwendet.

    Was ist der Zweck der „garbage collection“ direkt nach dem Start? taste, topic_sub und topic_sub werden definiert aber nirgends verwendet. Im Hauptmodul werden nbCharsR und nbCharsL definiert aber nirgends verwendet.

    Und da sind einige Namen die eine posSerL sind. Namen werden in Python klein_mit_unterstrichen geschrieben, ausser Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Und sie sollten keine kryptischen Abkürzungen enthalten, oder gar nur aus solchen bestehen.

    Konstanten werden per Konvention KOMPLETT_GROSS geschrieben. Und man sollte Sachen dort definieren wo sie auch verwendet werden. Ist ja nett, dass alles aus der Boot-Datei auch automagisch im main-Modul sichtbar ist, aber übersichtlich ist das nicht. Wenn man ein main-Modul hat, dann ist das boot-Modul auch fast überflüssig.

    Man macht keine Vergleiche mit literalen Wahrheitswerten. Entweder kommt dabei der Wert heraus den man sowieso schon hatte, dann kann man den auch gleich verwenden, oder dessen Gegenteil. Dafür gibt es not.

    global hat in einem sauberen Programm nichts zu suchen. Macht hier auch gar keinen Sinn, weil man das einfach weglassen kann, ohne das sich am Programmablauf etwas ändert. Was man leicht(er) gesehen hätte wenn die Konstanten wie Konstanten geschrieben worden wären.

    Der %s Platzhalter im print() in connect() macht keinen Sinn, weil der durch nichts ersetzt wird. Diese uralte Ersetzungsmethode verwendet man aber sowieso nicht mehr wenn da nicht einen sehr guten Grund für gibt. Es gibt die format()-Methode auf Zeichenketten und f-Zeichenkettenliterale für so etwas.

    statR und statL werden als leere Zeichenkette initialisiert die aber nur verwendet wird falls in den empfangenen Daten jeweils das gesuchte nicht gefunden wird. Sowie dort etwas gefunden wurde, wird beim nächsten nicht-finden der letzte gefundene Wert verwendet. Soll das so? Das sieht falsch aus.

    str() mit einem bytes-Objekt als Argument aufrufen ist falsch. Man operiert nicht auf der Zeichenkettendarstellung von Objekten, das ist Murks.

    Das Ergebnis von find() einfach so als Index verwenden ist falsch, denn wenn nichts gefunden wurde, dann gibt die Methode -1 zurück, was aber ein gültiger Index ist, aber ziemlich sicher nicht der, den man haben wollte. Die index()-Methode löst eine Ausnahme aus, wenn nichts gefunden wurde. An der Stelle fehlt im Code dann auch was in diesem Fall überhaupt passieren soll‽

    Was soll passieren wenn das gefundene Zeichen keine der vier Ziffern ist? Momentan bleibt einfach der alte Wert bestehen und wird noch mal gesendet.

    Die Verarbeitung der beiden Datenquellen ist extrem ähnlich, das sollte da nicht alles zweimal im Code stehen.

    Zwischenstand (ungetestet):

    Und:

    Besonders robust sieht die Verarbeitung der gelesenen Daten nicht aus.

    Tradition is just peer pressure from dead people.

  • Noch mal als Nachtrag zum letzten Satz: Die Verarbeitung ist falsch/fehlerhaft. Man kann nicht sicher sein, dass

    1. beide Informationen innerhalb des mehr oder weniger zufälligen Ausschnitts, den man da aus dem Empfangspuffer liest enthalten sind, und
    2. das eine Information nicht abgeschnitten ist, weil sie gar nicht komplett im Empfangspuffer enthalten ist. Das führt dann zu einem abgeschnittenen Wert oder sogar einem IndexError wenn nicht mal der Anfangsindex im Bereich der Daten liegt.

    Dieses „Wir Suchen nach zwei oder drei Zeichen und zählen von da ausgehend Bytes, und ignorieren die Nachrichtenstruktur vollständig”, die da durch Zeilenenden und Tabulatorzeichen ja durchaus enthalten ist, ist kaputt. Es ist ja beispielsweise auch nicht garantiert, dass der Volt-Wert immer 5 Zeichen lang ist. Das die letzten drei Zeichen der Seriennummer verwendet werden, weil nach einem einzelnen "V" zu suchen noch problematischer ist, führt dazu, dass nach der Seriennummer immer die Volt-Angabe kommen muss, sonst funktioniert das auch nicht mehr. Solche Fehler erkennt das Programm dann auch gar nicht.

    Das Protokoll sind Zeilen mit jeweils einem Schlüssel/Wert-Paar das durch ein Tabulatorzeichen getrennt ist. Das ist also gar nicht so schwer robust zu verarbeiten.

    Wenn man zwei von aussen kommende Datenströme ”gleichzeitig” verarbeiten will, bietet sich das select-Modul an, statt mehr oder weniger willkürlich sleep() Aufrufe einzubauen, die auch dazu führen könnten, dass Empfangspuffer voll laufen und Daten verloren gehen können.

    Problematisch ist auch das senden von jeweils vier Werten aus zwei verschiedenen Datenquellen. Die müssen nicht synchron sein oder exakt gleich schnell/häufig diese Werte liefern, dann kann man die gar nicht so als Viererpaket garantieren/liefern.

    Tradition is just peer pressure from dead people.

  • Moin,

    vielen Dank für Deine ausführliche Antwort.
    Die Schreibweisen bei den Variablen etc. übernehme ich.

    Wenn ich mich recht erinnere, hatten wir schon vor einigen Jahren einmal ein "Gespräch" über "find" und "parse" im Zusammenhang mit dem Auslesen einer Website, die immer die gleiche Struktur hat.

    Aber Dein Lösungsansatz ist gut und ich werde versuchen, ihn bei weiteren kleinen Projekten anzuwenden. Den angehängten Code hatte ich weitgehend ausgedünnt, weil er für das Beispiel unerheblich war, zB nbchar(). Im echten Betrieb brauche ich das aber, damit nur 80 Zeichen des Strings eingelesen werden. Bei 80 Zeichen ist zu 99 % mindestens einmal die Seriennummer mit den Volt und der Status enthalten. Wenn bei den beiden Werten tatsächlich mal Fehler ausgegeben werden sollten, wird das beim nächsten Lauf schon wieder richtig gestellt. Die Daten werden nur im Dashboard grafisch dargestellt, nicht weiter verarbeitet.

    Dein Code läuft gut (genau wie meiner;)), aber leider nicht lange (genau wie meiner;(). Der ESP stellt nach wie vor ohne Fehlermeldung die Übertragung ein.

  • Immer noch moin,

    zu Deiner Antwort #3:

    Du hast insoweit recht, wenn die Datenströme unterschiedliche Strukturen haben könnten. Das ist hier aber nicht der Fall. Die Spannung ist immer 5 stellig, der Status immer 1-stellig. Und mit 80 eingelesenen Zeichen (ohne die Steuerzeichen) erwische ich immer die Spannung und den Status. Es kommt mir auch nicht auf zeitliche Genauigkeit an, der Versatz durch das Einlesen nacheinander ist unerheblich.

    Ich habe den Code jetzt noch mal mit diversen "print (Zeile)" versehen, um zu schauen, wo bzw ob er immer an der gleichen Position hängen bleibt.

    Nur info-halber habe ich eine Stringausgabe angehängt.

  • Moin!

    Da ich den ESP32 nicht mit Mikropython betreibe, habe ich eine Frage. Reicht Python die Exception-Meldungen durch?
    Z.B.: Watchdogmeldungen

    73 de Bernd

    Ich habe KEINE Ahnung und davon GANZ VIEL!!
    Bei einer Lösung freue ich mich über ein ":thumbup:"
    Vielleicht trifft man sich in der RPi-Plauderecke.
    Linux ist zum Lernen da, je mehr man lernt um so besser versteht man es.

  • Bernd666 Sieht man doch auch im ersten Beitrag, den KeyboardInterrupt der da ausgegeben wird.

    Hajulied Hat denn der Quelltext den Du hier gezeigt hast auch das Problem das der stehen bleibt? Du sagst Du hast da Code entfernt. Wenn der so ewig läuft und nicht hängen bleibt, dann ist es ja ziemlich wahrscheinlich, dass das Problem in Code steckt, den wird nicht gesehen haben, weil Du den nicht gezeigt hast. Wenn man abgespeckten Code zeigt, muss man auch testen ob der immer noch Probleme macht, denn sonst hilft das ja nicht weiter wenn wir Code bekommen der das Problem gar nicht hat.

    Tradition is just peer pressure from dead people.

  • Moin!

    __blackjack__ Ich meinte sowas:

    Display Spoiler

    sp: 3ffffde0 end: 3fffffd0 offset: 0190
    3fffff70: 4 0202f44 3ffe8892 3ffee654 3ffee6f8
    3fffff80: 3ffee654 00000001 00000005 401002bc
    3fffff90: 00000030 00000033 3ffee614 402010ac
    3fffffa0: 3fffdad0 3ffee5a8 00000004 402011f9
    3fffffb0: 3fffdad0 00000000 3ffee6cc 40203e98
    3fffffc0: feefeffe feefeffe 3fffdab0 40100dc9
    <<<stack<<<

    --------------- CUT HERE FOR EXCEPTION DECODER ---------------

    ets Jan 8 2013,rst cause:2, boot mode:(3,6)

    load 0x4010f000, len 3424, room 16
    tail 0
    chksum 0x2e
    load 0x3fff20b8, len 40, room 8
    tail 0
    chksum 0x2b
    csum 0x2b
    v000452d0
    ~ld

    Speicherfragmentierung kann man auch ausschliessen?

    73 de Bernd

    Ich habe KEINE Ahnung und davon GANZ VIEL!!
    Bei einer Lösung freue ich mich über ein ":thumbup:"
    Vielleicht trifft man sich in der RPi-Plauderecke.
    Linux ist zum Lernen da, je mehr man lernt um so besser versteht man es.

  • Moin __blackjack__ ,

    noe, ist es nicht.
    Ich denke das Micropython ein Wrapper für die ESP-IDF ist und die gibt solche Meldungen aus.
    Oder liege ich mit meiner Vermutung mit dem Wrapper total daneben?

    73 de Bernd

    Ich habe KEINE Ahnung und davon GANZ VIEL!!
    Bei einer Lösung freue ich mich über ein ":thumbup:"
    Vielleicht trifft man sich in der RPi-Plauderecke.
    Linux ist zum Lernen da, je mehr man lernt um so besser versteht man es.

  • Moin __blackjack__,

    gut, dann nutzen sie halt die API.
    Der "Fehler" wird immer dann ausgegeben, wenn es einen schwerwiegenden Fehler gibt. Z.B.: Wenn der Watchdog nicht rechtzeitig bedient wird.
    Der ESP gibt diese Trace-Punkte aus und bootet dann.

    Entschuldige, wenn ich da nicht tiefer darauf eingehe, aber es gibt so viele Möglichkeiten eine Exception auszulösen. Zumal ich, im Moment, nicht wirklich in der ESP-Welt zuhause bin. Eher bei dem Raspberry Pi Pico W....

    73 de Bernd

    Ich habe KEINE Ahnung und davon GANZ VIEL!!
    Bei einer Lösung freue ich mich über ein ":thumbup:"
    Vielleicht trifft man sich in der RPi-Plauderecke.
    Linux ist zum Lernen da, je mehr man lernt um so besser versteht man es.

  • Bernd666 Diese Ausgabe und Reboot passt nicht zu der Beschreibung „friert ein“, also vermute ich mal dass da was an dem Code hängt den wir nicht kennen. Was ich auch nicht für so unvorstellbar halte wenn ich den gezeigten Code und die Einstellung des Autors anschaue. Das gezeigte ist ja nicht wirklich robust, und ich habe nicht den Eindruck, dass das interessiert.

    Tradition is just peer pressure from dead people.

  • Bernd666 Sieht man doch auch im ersten Beitrag, den KeyboardInterrupt der da ausgegeben wird.

    Hajulied Hat denn der Quelltext den Du hier gezeigt hast auch das Problem das der stehen bleibt? Du sagst Du hast da Code entfernt. Wenn der so ewig läuft und nicht hängen bleibt, dann ist es ja ziemlich wahrscheinlich, dass das Problem in Code steckt, den wird nicht gesehen haben, weil Du den nicht gezeigt hast. Wenn man abgespeckten Code zeigt, muss man auch testen ob der immer noch Probleme macht, denn sonst hilft das ja nicht weiter wenn wir Code bekommen der das Problem gar nicht hat.

    moin,

    der ursprüngliche Code enthielt noch die Ansteuerung eines kleinen Displays und eine Website für den lokalen Zugriff. Diese beiden Dinge habe ich rausgenommen, zumal ich gelesen hatte, dass möglicherweise I2C und MQTT sich manchmal stören. Die Webseite war überflüssig. Die Fehlerbeschreibung bezog sich auf die Ursprungsversion (ohne Website), meinen ausgedünnten Code und den von Dir überarbeiten Code (vielen Dank noch mal dafür). Letzteren habe ich noch ein wenig angepasst und verwende ihn jetzt. Vielleicht bringen mich die diversen print(xx) Befehle weiter.

    Von mir aus kann die Diskussion geschlossen werden.

  • Moin Hajulied,

    ich sach ma NOE. zum schliessen der Diskussion.
    Grund dafür: Wir Helfenden wollen auch wissen, was nun ist. Also sind wir immer dankbar, wenn es eine Rückmeldung gibt.

    73 de Bernd

    Ich habe KEINE Ahnung und davon GANZ VIEL!!
    Bei einer Lösung freue ich mich über ein ":thumbup:"
    Vielleicht trifft man sich in der RPi-Plauderecke.
    Linux ist zum Lernen da, je mehr man lernt um so besser versteht man es.

  • moin,
    normalerweise gebe ich selbstverständlich ein feedback über die weitere Entwicklung, denn ich freue mich über die Unterstützung. Aber wenn hier mit Vermutungen (Code, den wir nicht kennen) oder ähnlichem argumentiert wird, habe ich keine Lust dazu.

    Daher nochmal:

    - mein Code (nicht micropython - konform) funktioniert gut.

    -_blackjacks- Code auch, den verwende ich jetzt auch mit geringfügigen Anpassungen.

    Letzteren habe ich mit vielen prints und einem Durchlaufzähler erweitert, um zu sehen, wo er stehen bleibt (s. Screenshot). Es ist immer Zeile 98 ohne Fehlerhinweis nach unterschiedlicher Anzahl von Durchläufen. Er scheint aber nicht eingefroren zu sein, denn wenn ich den grünen "ausführen" Button in Thonny drücke, kommt der Kommentar "Device is busy..." . Neustart mit Ctrl-D funktioniert.

    Der Code ist durch die Extrazeilen natürlich länger geworden.

  • Vielleicht ein Problem, dass dich ununterbrochen neue Verbindungen zu MQTTClient aufbaust. Verbinde dich ein mal und nutze die Verbindung und wenn du fertig bist, dann wieder schließen.


    Grüße
    Dennis

    🎧 Hate the jocks, the preps, the hippie fuckin' scumbags.
    Heavy-metalers with their awful, pussy hairbands.
    Counting seconds until we can get away.
    Ditching school almost every single day, oh, yeah 🎧

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!