OLED Display friert regelmäßig ein

  • Hallo an die Runde,


    ich habe via GPIO einen 0,91" OLED-Display von AZ Delivery mit meinem Raspi verbunden zur Anzeige von CPU-Temperatur, Auslastung, IP usw. Das Script lasse ich über rc.local laufen. Das ganze funktioniert nach Neustart des Raspi soweit einige Stunden wie erwartet, bis die angezeigten Werte am OLED Screen nach einigen Stunden in unregelmäßigen Abständen einfrieren. Nach einer Weile funktioniert wieder alles tadellos (vermutlich startet da das Script aus der rc.local neu?) bis ein paar Stunden später die Anzeige wieder einfriert und das Spiel von vorne beginnt.

    In den Log-Files des Raspi konnte ich keinerlei, für mich brauchbare, Infos auslesen. Einen direkten Zusammenhang mit raspiBackup oder anderer Komponenten habe ich anfangs vermutet, konnte ich allerdings bis jetzt nicht verifizieren.


    Anbei mein Python Script sowie die rc.local Datei samt angelegter Log-Datei im Textdatei-Format.


    Folgende Hard- und Software Konfiguration:

    Hardware:

    Raspberry Pi 4B (4GB) mit Raspberry Pi OS (64-bit), verbaut in einem DeskPi Gehäuse, HDMI und USB verbunden mit Touchscreen, an der GPIO Leiste hängen das 0,91" OLED-Display von AZ Delivery, ein EnOcean Pi ein ESP32 (wird nur mit Strom versorgt), weiters ist über USB ein z-Wave Stick angebunden

    Software:

    Docker / Docker Compose, auf denen div. Container laufen

    Direkt am Raspi läuft noch Fail2Ban, ein Python Script für DeskPi, ein Shell-Skript für Kiosk-Modus am Touchscreen und raspiBackup (tägliches dd-Backup der SD-Karte, ich stoppe cron und cups Dienst vor dem Backup)


    Für jegliche Hinweise und Tipps wäre ich Euch sehr dankbar :danke_ATDE:


    Grüße

    neuling10

  • `rc.local` startet das Programm genau einmal, das startet das nicht während des Betriebs neu. Diese Vermutung ist also schon mal falsch.


    Man müsste in dem Programm mindestens an strategisch wichtigen Stellen Logging-Ausgaben machen um zu sehen wo genau es hängen bleibt. Das geht zum Beispiel mit dem `logging`-Modul aus der Standardbibliothek, oder dem externen `loguru`-Modul. Oder man schiesst mit dem `snoop`-Package auf diesen Spatz und lässt sich wirklich jede ausgeführte Zeile, zumindest in der Schleife, inklusive Werten protokollieren.


    Ansonsten ist das Programm eher gruselig weil das offenbar nicht von einem Python-Programmierer geschrieben wurde. Python kann Texte/Zeichenketten bearbeiten, und auch die Funktionalität einiger der externen Programme die da aufgerufen werden kann man in Python ausdrücken. Dazu muss man keine externe Shell starten in der dann externe Programme und ``cut`` oder ``awk`` zum zerlegen von Zeichenketten und zum Rechnen verwendet werden.


    Da gehören ganz viele Kommentare nicht rein, insbesondere so viel auskommentierter Code sollte da nicht drin stehen.


    Persönliche Anmerkungen: ”Normale” Vektorschriftarten sehen auf so kleinen Displays ja eher bescheiden aus. Ich würde da ja eher spezielle Vektorschriftarten verwenden die Pixelschriftarten ”nachahmen”. Die sehen dann zwar nur in genau der Grösse für die sie vorgesehen sind, gut aus, aber dafür dass dann auch auf so kleinen Displays.


    Zum Beispiel die ”dünnen” 8x8 oder 6x8 Schriftarten von Compaq oder HP-Geräten von https://int10h.org/oldschool-pc-fonts/ Falls der Zeichenvorrat von CP437 ausreicht, gibt es da noch mehr Auswahl.


    Oder Beispielsweise der „Berkelium (BSW) GEOS System Font“ von https://www.kreativekorp.com/software/fonts/c64/

    “Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.” — Terry Pratchett, Jingo

  • Moinsen,


    nur die Frage: hängen noch weitere Komponenten an den für das I²C-Display genutzten PINs ( GPIO 3 und GPIO 5 ) ?

    Meine Deutung würde jetzt dahingehend sein, wenn keine andere Erklärung möglich, dass es einen Konflikt im Zusammenhang mit der I²C I/O Bibliothek von Adafruit aus Zeile 12 & 13 deines Python-Scriptes besteht.

    Franky

  • Guten Abend,


    danke erstmals für Eure Tipps!


    Nein direkt auf GPIO 3 und GPIO 5 hängen keine weiteren Komponenten.


    Danke für den Tipp zum "gruseligen" Python Script. Persönlich habe ich da so gut wie keine Erfahrung im Coding bzw. bin ich generell noch ein ziemlicher Anfänger in der Raspi-Welt :D

    Ich hab mir nun die original Scripts von Adafruit geladen und werde die mal testen/vergleichen mit meinem aktuellen Script bzw. adaptieren.


    Mir fiel noch auf, dass systemctl status rc-local.service einen, im Vergleich zu anderen Services, hohen Wert bei CPU ausgibt mit 8h 52min 33.464s. Das würde wohl auch auf ein "suboptimales" Programm mit hoher Rechenleistung hindeuten, würde ich vermuten? htop gibt mir hier auch um die 6-7% CPU-Last an, was mir sehr, sehr hoch erscheint, bei gleichzeitiger fast vollständiger Auslastung des Swap-Speichers...



    Grüße

    neuling10

  • Moinsen


    nimm erst einmal die Aktualisierungsrate in der letzten Zeile hoch. Du musst mit der ganzen DRAW Malerei in deinem Programm keine Wiederholrate von 0.1 Sek, oder 10 Displayaktualisierungen po Sekunde durchknechten. Wenn dir eine sekündliche Displayaktualisierung ausreicht, dann kommt bei time.sleep() eine 1 rein, anstatt dieser 0.1.
    Ob man das Displaylöschen ( virtuelle ) durch den DRAW Befehl draw.rectangle höchst aufwendig und CPU Ressourcen-fressend machen muss, oder ob es nicht besser, schneller und CPU schonender wäre von dem erstellten Basis-Image ( Zeile 34 ) eine Kopie aufzubewahren, und dieser dann immer wieder nur zum überschreiben des schon "bemalten" Bildinhaltes nutzt, wäre jetzt auch noch eine Überlegung.


    Hier kann dir bestimmt __blackjack__ mit einem fehlerfreien und stilistisch perfekten Codebeispiel weiter helfen.

    Franky

  • nimm erst einmal die Aktualisierungsrate in der letzten Zeile hoch. Du musst mit der ganzen DRAW Malerei in deinem Programm keine Wiederholrate von 0.1 Sek, oder 10 Displayaktualisierungen po Sekunde durchknechten. Wenn dir eine sekündliche Displayaktualisierung ausreicht, dann kommt bei time.sleep() eine 1 rein, anstatt dieser 0.1.

    Dankeschön, das hat meine CPU-Last schon mal halbiert.... bin mal gespannt, wie sich das Display die nächsten Tage verhält

  • Hallo,


    nach reichlicher Beobachtungs- und Analyse konnte ich nun eingrenzen, in welchem Fall das OLED Display einfriert. Es besteht ein Zusammenhang zu raspiBackup und/oder NFS Mount eines Verzeichnisses auf meinem NAS mittels autofs, was ich absolut nicht nachvollziehen kann oder Zusammenhänge verstehen kann... :denker:


    Mein Raspi mountet mittels autofs ein NFS-Verzeichnis auf meiner Synology für Backups. Um 4 Uhr täglich wird ein dd-Backup mittels raspiBackup erstellt. Das funktioniert soweit einwandfrei. Ich stoppe die Dienste cron und cups durch raspiBackup vor der Backup-Erstellung. Meine Synology ist mittels Zeitplänen zumindest 2x täglich im Betrieb (um 4 Uhr für Raspi Backup und um 10 Uhr für Surveillance-Aufgaben), ansonsten heruntergefahren, wenn nicht benötigt.


    Folgendes habe ich nachgestellt:

    - Reboot des Raspi: Python Script läuft; OLED Display läuft

    - Booten der Synology und Durchführen einer Backup Aufgabe mittels raspiBackup: OLED Display läuft weiter während Backup-Erstellung

    - Sobald Synology nachfolgend heruntergefahren wird: OLED Display friert ein

    - Sobald Synology wieder bootet: OLED Display läuft wieder weiter

    => ab nun an läuft die Display Aktualisierung ausschließlich, wenn das NAS an ist. Sobald das NAS heruntergefahren wird, friert das Display wieder ein


    Hat jemand eine Erklärung oder Idee, wie das zusammenhängen könnte? Mein zweites Python Script, dass auch ununterbrochen läuft (für DeskPi Lüftersteuerung), läuft ohne Auffälligkeiten.


    Gibt es vielleicht weitere Dienste, die vor der Backup-Erstellung gestoppt werden sollten mittels raspiBackup?


    Könnte das gemountete NFS Backup Verzeichnis in Zusammenhang stehen? Immerhin läuft die Display-Aktualisierung nach erstmaligen Backup nur noch, wenn die Synology an ist und somit das NFS Verzeichnis gemountet ist.


    Danke Euch :)

  • neuling10 Mach mal ein df -h wenn das NAS an ist (und auf dem Raspi eingebunden) und dann noch mal wenn das NAS heruntergefahren ist. Ich vermute mal *das* hängt dann wenn der Pi denkt er sollte das eigentlich noch erreichen können.


    Das liesse sich sehr einfach fixen, allerdings verrate ich den Fix nicht, weil ich df aufrufen wie schon gesagt doof finde. Das kann man auch mit Python lösen, ohne externes Programm.


    Der ``df``-Aufruf hat sowieso noch einen Fehler. Der wird zwar nicht so wirklich relevant sein, aber die Formatierung der Zahlen geht einfach davon aus, dass schon jeder Wert in der Einheit Gigabyte sein wird und ignoriert die Einheitsangaben von ``df`` komplett. Sauber ist das nicht.

    “Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.” — Terry Pratchett, Jingo

  • Exakt, danke, das scheint der Grund zu sein:

    df -h bei ausgeschaltetem NAS und eingefrorenem Display: hängt; keine Ausgabe in der Putty-Sitzung


    df -h bei eingeschaltetem NAS und laufendem Display:


    Bin sehr gespannt auf Deinen Lösungsvorschlag :)

  • Naja die Berechnung des belegten/freien Speicherplatzes des Datenträgers der als "/" eingehängt ist, in Python selber machen. Also Python lernen, und dann mal im `os`-Modul schauen was es da so für nette Funktionen gibt. 😃

    “Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.” — Terry Pratchett, Jingo

  • Schade dass du mir den einfachen Fix von Post #8 nicht verraten wolltest, andererseits habe ich mich nun erstmals mit Coding und Python beschäftigt den vergangenen Nachmittag ^^


    Bin einige Schritte weiter gekommen und habe schon mal df erfolgreich aus meinem Programm entfernen können. Statt dem 'os' Modul habe ich jedoch 'psutil' angewandt, da ich hierfür einfach mehr passende Erklärvideos und Doku gefunden habe. Folgende Codezeilen habe ich nun in meinem Programm eingebaut für die Speicherkarten-Belegung:


    Code
    import psutil
    
    disk = psutil.disk_usage('/')
    # Umrechnung Byte auf GB
    free = round(disk.free/1024/1024/1024,0)
    total = round(disk.total/1024/1024/1024,0)
    disk_info = 'Disk: ' + str(free) + '/' + str(total) + 'GB ' + str(disk.percent) + '%'
    
    draw.text((x, top+25.5), str(disk_info,'utf-8'), font=font, fill=255)


    Ein print(disk_info) gibt mir die gewünschte Zeile erfolgreich aus ^^ . Einzig wie ich die Werte ohne Dezimalstellen darstellen kann, habe ich noch nicht rausgefunden. Gerundet habe ich die Werte schon korrekt.


    Möchte ich das ganze mit draw.text((x, top+25.5), str(disk_info,'utf-8'), font=font, fill=255) am Display anzeigen, erhalte ich "TypeError: decoding str is not supported". Ich vermute unterschiedliche Variablen. Müsste ich hierzu die Variable disk_info vorher als String umwandeln?

  • Möchte ich das ganze mit draw.text((x, top+25.5), str(disk_info,'utf-8'), font=font, fill=255) am Display anzeigen, erhalte ich "TypeError: decoding str is not supported". Ich vermute unterschiedliche Variablen. Müsste ich hierzu die Variable disk_info vorher als String umwandeln?

    disk_info ist schon ein str


    einfach mal diese dieses str(disk_info,'utf-8') einfach durch disk_info ersetzen.

    Franky

  • Hallo,


    wenn man die Nachkommastellen mit 'int' los werden will dann macht man einen mathematischen Fehler.

    Code
    print(int(3.555))
    >>>3
    print(round(3.555, 0))
    >>>4.0
    print(f'{3.555:.0f}')
    >>>4

    Man rundet auch nicht während des Rechenvorgangs, wenn man dann das Ergebnis mit einer bestimmten Anzahl an Nachkommastellen ausgeben will, nutzt man f-Strings (wie schon gezeigt) oder die format-Methode.


    neuling10 Ich denk __blackjack__ meinte das du mal in der Region suchen könntest:

    https://docs.python.org/3/libr…ile-descriptor-operations


    Grüße

    Dennis

    Das Leben ist kein Ponyhof, es ist ne Pferdebraterei! 🎧

  • Moinsen,

    Mathematische Fehler ja, aber was ist der oder das bei einem Filesystem welches mit "/" abgefragt wird ? Solange er keine Wertveränderungen bei der "%" Auswertung vornimmt, ist das alles relativ Socke.
    Es geht um eine schnöde simple ohnehin in der Datengenauigkeit reduzierte Darstellung auf einem Display. Klar kann man her bis auf das BIT genau rechnen, und dann das Ergebnis in der Darstellung wieder einkürzen. Nur wo ist der wirkliche Nutzeffekt ?

    Franky

  • Der Effekt tritt im nächsten Programm auf, das von jemandem geschrieben wird, der sich an deinen Vorschlag mit „int“ erinnert. Der sucht sich unter Umständen verrückt, weil seine Rechnung nicht stimmt.

    Wenn man öffentlich Ratschläge gibt muss man damit klar kommen, dass die verbessert werden und das ist auch aus genanntem Grund gut so.

    Sich immer um Kopf und Kragen reden und sich rechtfertigen ist reine Zeitverschwendung.


    Grüße

    Dennis

    Das Leben ist kein Ponyhof, es ist ne Pferdebraterei! 🎧

  • Wenn man das mal mit os.statsvfs alles selbst ausgerechnet hat, versteht man die Tools, die das machen, ein wenig besser.


    Code
    >>> import os
    >>> os.statvfs("/")
    os.statvfs_result(f_bsize=4096, f_frsize=4096, f_blocks=122654446, f_bfree=27651420, f_bavail=21402550, f_files=31227904, f_ffree=29666225, f_favail=29666225, f_flag=4096, f_namemax=255)
    Code
    f_bsize: Blockgröße
    f_blocks: Alle Blöcke
    f_bavail: freie verfügbare Blocks
    
    total = f_blocks * f_bsize
    free = f_bavail * f_bsize
    usage = total - free


    Mit psutil kann man das einfacher haben, aber das Verständnis wie die Tools den freien Platz berechnen, sollte vorhanden sein.

    (trifft nicht bei btrfs zu, da die Angabe von f_bavail nicht der Realität entspricht)


    Hier der Code ohne psutils:

    Quote

    81.64 GiB / 467.89 GiB | 82.6% | Usage: 386.25 GiB

  • Vielen Dank Leute, Eure Ratschläge haben mir sehr weiter geholfen. Hab nun mal eine funktionierende Lösung mit dem psutil-Modul. Das os-Modul möchte ich mir auch noch ansehen und mein Skript weiter verbessern :)