Endlose Whileschleife bricht durch Variable ab?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
paul.rene
User
Beiträge: 3
Registriert: Dienstag 31. Mai 2022, 17:05

Hallo zusammen,

wahrscheinlich ist es nur irgendein dummer Fehler meinerseits, aber ich komme gerade einfach nicht weiter und dachte ich stelle auch mal eine Frage in so einem Forum :)

Ich habe an meinen RaspberryPi ein LCD Display angeschlossen und moechte auf diesem im 0.2 Sekundentakt in einem Zweizeiler die Temperatur der Pi-CPU in Grad ausgeben.
Hierfuer will ich eine simple Endlos-Whileschleife verwenden, also While 1: und fange die Abbruchbedingung ueber Strg + C der Tastatur mit try und except ab. Allerdings laeuft die While-Schleife immer nur einmal durch, sobald ich in der While-Schleife mit der Variable arbeite, welche den Wert der CPU-Temperatur enthaelt (cpuTemp). Weiss vielleicht jemand woran das liegt? Das normale Ausgeben der Temperatur ohne die Endlos-While-Schleife drumherum funktioniert wie gewuenscht...

Ich habe meinen Code so gut es geht kommentiert, damit dieser moeglichst einfach nachzuvollziehen ist, und fuege in im Anhang bei.

Danke fuer die Hilfe und lieben Gruß,
Paul :)



import RPi.GPIO as gpio
import time
import sys #Importabfolge fuer die Displaydateien
sys.path.append("/home/pi/Displaydateien/hd44780")
import lcddriver
import os #Import fuer das Auslesen der Temperatur

tempFile = os.popen("sudo more /sys/class/thermal/thermal_zone0/temp") #Temporaere Datei zum Temperaturauslesen


#Ausgabe der CPU-Temperatur auf dem angeschlossenen Display
lcd = lcddriver.lcd()


#Abfangen einer Abbruchbedingung ueber KeyboardInterrupt
try:
while 1: #Temperatur-Messung soll bis zum Abbrechen durchlaufen
cpuTemp = tempFile.read() #Variable wird mit dem Temperaturwert befuellt
cpuTemp = int(cpuTemp) #Konvertierung in eine Int-Variable, damit Rechnung moeglich ist
cpuTemp = cpuTemp/1000 #Temperaturwert wird in Grad Celsius umgerechnet
cpuTemp = str(cpuTemp) #Rueckwandlung in einen String

lcd.lcd_display_string("CPU-Temperatur:", 1)
lcd.lcd_display_string(cpuTemp + " Grad", 2) #Das cpuTemp in diesen Zeilen verhindert irgendwie, dass die While-Schleife weiter ausgefuehrt wird...
time.sleep(0.2)
print("Test Print in der While Schleife...")


except: #Falls das Programm ueber die Tastatur abgebrochen wird
KeyboardInterrupt
tempFile.close() #Sitzung der temporaeren Temperatur-Datei wieder schliessen
lcd.lcd_clear() #Display wieder auf Standard zuruecksetzen


#Irgendwie kann ich die Variable nicht in der While-Schleife verwenden, ohne dass diese abgebrochen wird...
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@paul.rene: Der Kommentar beim ``except:`` ist falsch weil das Programm durch *jede* Ausnahme abgebrochen wird, aber Du unterdrückst damit dann auch jegliche Fehlermeldung die Dir verraten könnte was das für eine Ausnahme ist. Hinter dem ``except`` muss die Ausnahme stehen die da behandelt werden soll. Und nicht sinnlos in der nächsten Zeile als Ausdruck der keinerlei Effekt hat.

Abkürzungen in Namen sind nicht gut. `tempFile` zum Beispiel, da steht als Kommentar es wäre eine temporäre Datei, was aber nicht stimmt, das `temp` steht hier für „Temperatur“ und nicht für „temporär“.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Verwende keine Zahlen wenn Du eigentlich Wahrheitswerte meinst. Also ``while True:``.

Man muss nicht jedes kleine Zwischenergebnis an einen Namen binden. Erst recht nicht, wenn das immer der gleiche Name ist, und dann auch noch unterschiedliche Typen an diesen Namen gebunden werden. Und man würde die Zahl auch nicht vor der Ausgabe wieder in eine Zeichenkette umwandeln, sondern *bei* der Ausgabe in eine Zeichenkette formatieren. Als f-Zeichenkettenliteral und nicht per ``+``.

`os.popen()` sollte man nicht mehr verwenden. Ich würde zum Auslesen einer Datei überhaupt keinen externen Prozess starten. Wenn es an Rechten scheitert, sorgt man dafür, dass das Python-Programm die notwendigen Rechte besitzt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
paul.rene
User
Beiträge: 3
Registriert: Dienstag 31. Mai 2022, 17:05

@__blackjack__

Erstmal danke, dass du dir die Zeit genommen hast, dich mit meinem Problem auseinanderzusetzen!

Der Kommentar beim except ist vielleicht nicht ganz richtig, aber ob das KeyboardInterrupt direkt hinter dem except oder in der nächsten Zeile steht zeigt bei mir keine Veränderung in der Handhabung. Beide Male ist die Ausgabe gleich, wenn ich das Programm dann über Strg + C beende.

Das mit dem Unterschied zwischen Temperatur- und Temporaeredatei habe ich mir schon gedacht, hat ja aber auch keine direkt Relevanz fuer mein Beispiel.

Das scheint dann ja Gang und Gebe zu sein, merk ich mir mal, aber ich will ja auch kein Profi-Programmierer werden.

Habe ich selbstverstaendlich auch mit "While True:" probiert, aber dann laeuft die Schleife auch nicht durch, sondern wie beschrieben nur einmal.

Wirkt auf mich so, als wenn du das nunmal auf diese Weise machst. Da das Beispiel bei mir fuer die Uni ist, ist es glaub ich so einfacher nachzuvollziehen.

Schade, dass du mir bei "os.popen()" keine direkt Alternative geschrieben hast, aber auch hier: Es funktioniert ja so und es besteht in der Hinsicht kein Optimierungsbedarf.


-> Also wie gesagt: Danke fuer die ganzen Tipps und gut gemeinten Ratschlaege, aber hast du auch eine Idee/Loesung fuer mein urspruengliches Problem mit der nicht funktionierenden endlosen While-Schleife? Deswegen habe ich den Post hier ja eigentlich erstellt :(
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@paul.rene: der Unterschied zwischen `except:` und `except KeyboardError:` ist der, dass im zweiten Fall ein Traceback ausgegeben wird, der Dir sagt, was in welcher Zeile nicht funktioniert und Du so konkret versuchen kannst, diesen Fehler zu beheben.

Ich verrate den Fehler hier nicht, weil es gehört ja dazu, zu Lernen, wie man Fehler findet.

Die Alternative zu popen hat __blackjack__ sehr wohl geschrieben: das Programm mit den nötigen Rechten starten, so dass man kein sudo braucht.

So könnte das Programm aussehen (mit den selben Fehlern wie bei Dir):

Code: Alles auswählen

import time
import lcddriver


TEMPERATURE_FILENAME = "/sys/class/thermal/thermal_zone0/temp"

def main():
    with open(TEMPERATURE_FILENAME) as temperature_file:
        #Ausgabe der CPU-Temperatur auf dem angeschlossenen Display
        lcd = lcddriver.lcd()

        try:
            while True:
                # Temperatur-Messung soll bis zum Abbrechen durchlaufen
                cpu_temperature = int(temperature_file.read()) / 1000
                lcd.lcd_display_string("CPU-Temperatur:", 1)
                lcd.lcd_display_string(f"{cpu_temperature} Grad", 2)
                time.sleep(0.2)
                print("Test Print in der While Schleife...")
        except KeyboardInterrupt:
            pass
        finally:
            # Display wieder auf Standard zuruecksetzen
            lcd.lcd_clear()

if __name__ == "__main__":
    main()
paul.rene
User
Beiträge: 3
Registriert: Dienstag 31. Mai 2022, 17:05

@Sirius3 Ah dann hatte ich das mit dem except falsch verstanden, danke fuer die Erklaerung (auch dir natuerlich @__blackjack__)! :)

Ich habe den Fehler bekommen "invalid literal for int() with base 10"... Also dass ich versucht habe einen float-Wert in eine int-Variable zu stecken, wenn ich's richtig interpretiere... Ist natuerlich nh bisschen anfaengermaessig hihi...

Wenn ich das Programm dann allerdings mit der Mini-Anpassung (Ersetzen von int durch float) ausfuehre bekomme ich den Error "could not convert string to float". Wenn ich vor dem Konvertierungsausdruck testweise den Wert der Variable ausgeben lasse, laeuft der erste Durchgang der While-Schleife wieder normal durch, gibt die Variable aus, aber bei dem zweiten wird nichts mehr ausgegeben, als wenn die Variable dann leer sei. :/ Habe dazu im Netz und durch Probieren nichts gefunden... (Komme mir nur ein ganz klein bisschen dumm vor)

Habe parallel noch an meiner aelteren Version mit dem os.popen probiert und sowohl Oeffnen als auch Schliessen in die While-Schleife geschrieben, sodass die Datei quasi die ganze Zeit neu aufgemacht wird, aber diese extrem unsaubere und unperformante Version kann ja wohl kaum das sein worauf du hinauswillst oder? :/
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@paul.rene: Das mit den Zwischenergebnissen nicht alle an Namen binden und dann auch noch verschiedene Typen an den selben Namen und f-Zeichenkettenliteralen habe ich gerade wegen der Lesbarkeit und Nachvollziehbarkeit angesprochen. Die Überarbeitung von Sirius3 ist da wesentlich kürzer und übersichtlicher.

Noch was zu den externen Programmen: ``more`` ist hier auch eher falsch gewesen. Einfache Ausgabe wäre ``cat`` gewesen. Aber auch das ``sudo`` ist ingesamt sehr wahrscheinlich überflüssig, denn *Leserechte* existieren auf der Datei normalerweise für alle Benutzer:

Code: Alles auswählen

~$ ls -l /sys/class/thermal/thermal_zone0/temp
-r--r--r-- 1 root root 4096 Jun  1 11:28 /sys/class/thermal/thermal_zone0/temp
Siehe das dritte "r" bei den Rechten. Kein ``sudo`` nötig um die Datei zu lesen.

Es ist nicht so „als wenn“ die Variable beim zweiten Durchgang leer ist, sondern sie *ist* leer. Und die Lösung ist das was Du ”extrem unsauber und unperformant” nennst: die Datei neu öffnen.

Edit: `pathlib.Path` macht das einlesen einfacher/kürzer:

Code: Alles auswählen

#!/usr/bin/env python3
import time
from pathlib import Path

import lcddriver

TEMPERATURE_FILEPATH = Path("/sys/class/thermal/thermal_zone0/temp")


def main():
    lcd = lcddriver.lcd()
    try:
        lcd.lcd_display_string("CPU-Temperatur:", 1)
        while True:
            cpu_temperature = int(TEMPERATURE_FILEPATH.read_bytes()) / 1000
            lcd.lcd_display_string(f"{cpu_temperature:.3f} Grad", 2)
            time.sleep(0.2)
    except KeyboardInterrupt:
        pass
    finally:
        lcd.lcd_clear()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten