[Gelöst] Pythonscript schaltet GPIO nicht.

L I V E Stammtisch ab 20:30 Uhr im Chat
  • Ich habe hier ein pythonscript, welches mir als root den Strom an- bzw. ausschalten soll.

    Leider wird der GPIO nicht geschaltet, wie ich mit gpio readall feststellen kann.

    Trotzdem behauptet mein "Programm" das aber.

    Ich vermute einen Fehler ab Zeile 24.

    Kann mir jemand einen Tip geben ? :helpnew:

    Das Ergebnis wenn ich es ausführe ist dieses:

  • Zur hilfreichsten Antwort springen
  • Hm, habe den Code jetzt ein bisschen erweitert, damit ich den Status des GPIO zurück bekomme, der ist dann auch 0 oder 1, aber hilft nichts:

    Zudem habe ich eine .sh zum schalten des GPIOs mit os.system dazugepackt, auch die wird nicht ausgeführt.

    Einmal an, einmal aus:

    :conf: :denker: :@:wallbash:

    Wenns schattig wird und Verbraucher dran sind, behauptet er, dass der Strom aus ist, aber er ist nicht ausgegangen, wie man am Status sieht:

  • Ja, so sieht es schonmal besser aus, jetzt mal beobachten.

    Ergebnis:

  • Hallo,

    die Warnung muss du auf jeden Fall weg bekommen. Die besagt ja, dass ein andere Programm / Skript (auch) Pin 5 benutzt. Der sollte aber exklusiv für den Programm sein.

    Am Ende des Programms fehlt ein `GPIO.cleanup()`, um die Pins wieder freizugeben.

    Python
    with open('/sys/class/gpio/gpio5/value') as status:
          status = int(status.readline())
          if status == 0

    ist doch viel zu kompliziert. Ein if gpio.input(5): #liefert `True` wenn Pin 5 high ist macht das gleich, nur eleganter.

    Den Sinn des Shellskripts verstehe ich nicht. Aber `os.system` ist veraltet, Stand der Dinge ist `subprocess`.

    Gruß, noisefloor

  • fred0815 Anmerkungen zum Quelltext:

    Eingerückt wird in Python vier Leerzeichen pro Ebene, nicht zwei.

    Sternchen-Importe sind Böse™. Damit holt man sich alles ins Modul was im importierten Modul an Namen gebunden ist, aber nicht nur was *dort* definiert wurde, sondern auch alles was das Modul selbst von woanders importiert hat. Das wird schnell unübersichtlich und kann auch zu Namenskollisionen führen wenn in dem Modul etwas definiert ist was dann etwas anderes importiertes oder eingebautes überschreibt beziehungsweise verdeckt.

    Das ``import time as dt`` ist dann sehr verwirrend, weil das überhaupt gar nicht verwendet wird, dafür aber im Programm selbst dann das als `dt` importierte `time`-Modul durch `datetime.datetime`-Objekte überschrieben wird.

    `subprocess` wird importiert aber nicht verwendet. Dafür wird `os.system()` verwendet, was es nicht sollte, denn um externe Programme auszuführen gibt es das `subprocess`-Modul. Das die Probleme von `os.system()` nicht hat, weshalb in der Dokumentation von `os.system()` auch auf das `subprocess`-Modul hingewiesen wird.

    Sich wiederholende Werte und Werte die man leicht anpassen können möchte, sollte man als Konstanten am Anfang des Programms definieren. Sonst wird das aufwändig und Fehleranfällig wenn man da mal Änderungen vornehmen muss/will.

    In den ``with``-Blöcken ist jeweils deutlich mehr Code als erforderlich wäre. Es ist jeweils nur die erste Zeile, die tatsächlich das Dateiobjekt benötigt. Auch wenn es nur zwei Zeilen sind, könnte man das schon sinnvoll in eine Fuktion stecken.

    Namen sollte man nicht abkürzen. `v` ist in der Physik üblicherweise das Symbol für Beschleunigung (velocity) und V wäre per Konvention eine Konstante. Also sollte man `voltage` oder `spannung` einfach ausschreiben, dann gibt es keine Verwirrungen.

    Bei `dt` wäre `timestamp` ein passender, nicht-kryptischer Name. Und man könnte den auch gleich *einmal* in eine Zeichenkette formatieren, statt überall dort wo der (gleiche) Wert verwendet wird. Wobei ich auch nicht so wirklich den Sinn darin sehe diesen Wert mehrfach auszugeben. Der ändert sich ja nicht, liefert dem Leser von der Ausgabe also auch keinen Mehrwert.

    Wenn am Anfang oder am Ende von allen ``if``/``elif``/``else``-Zweigen gleicher Code steht, dann gehört der *einmal* vor bzw. hinter das gesamte Konstrukt und nicht in jeden Zweig.

    Zwischenstand (ungetestet):

    Mir stellen sich zwei Fragen:

    1. Warum wird die Spannung zweimal eingelesen? Ist das wirklich sinnvoll so kurz nach dem ersten Einlesen und prüfen auf Unterschreiten der Grenze den Wert noch einmal zu lesen um auf Überschreiten der Grenze zu prüfen? Kann man das nicht am gleichen Wert überprüfen?

    2. Warum wird der Zustand von Pin 5 über "sys/class/…" ausgelesen und nicht über das `GPIO`-Modul?

    Falls das System ein Python ≥3.6 verwendet würde ich aus den Zeichenketten mit den `format()`-Aufrufen f-Zeichenkettenliterale machen.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • noisefloor Warnungen und `cleanup()` sind hier ein bisschen anders. Der Zustand von Pin 5 der in dem Programm geschaltet wird, soll ja nach Programmende bestehen bleiben. Also darf man nicht `cleanup()` aufrufen. Und es ist dementsprechend ”okay(ish)” das man die Warnungen unterdrückt. Schöner wäre natürlich wenn man gezielt nur die Warnung bezüglich Pin 5 unterdrücken könnte.

    Und das unterdrücken der Warnungen habe ich in meiner Überarbeitung des Skripts mit einem Kommentar versehen warum das gemacht wurde.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Ist zwar schon als gelöst markiert, aber ich musste feststellen, dass obwohl der Strom schon aus ist, trotzdem bei jedem Programmaufruf der Strom ausgeschaltet wird, was mir jetzt erst aufgefallen ist, da ich in der strom_aus.sh und strom_aus.sh noch eine Zeile zum protokollieren eingefügt habe.

    echo $(date '+%Y-%m-%d %H:%M:%S') >> strom_aus.txt

    und

    echo $(date '+%Y-%m-%d %H:%M:%S') >> strom_an.txt

    Deshalb habe ich noch ein bisschen getippelt und hoffe, dass es jetzt so klappt, werde es aber erst heute Mittag bei Sonne feststellen können :

  • Hat nicht geklappt, nochmal getippelt (z.B. das schalten der GPIOs ausgelagert):

    /home/pi/strom_aus.py:

    Python
    #!/usr/bin/env python3
    from datetime import datetime
    from RPi import GPIO as gpio
    gpio.setmode(gpio.BCM)
    gpio.setup(24, gpio.OUT)
    gpio.output(24, gpio.HIGH)
    with open("strom_aus.txt","a") as f:
        f.write(datetime.now().strftime('%d-%m-%y %H:%M:%S'))
        f.write("\n")

    /home/pi/strom_an.py:

    Python
    #!/usr/bin/env python3
    from datetime import datetime
    from RPi import GPIO as gpio
    gpio.setmode(gpio.BCM)
    gpio.setup(24, gpio.OUT)
    gpio.output(24, gpio.LOW)
    with open("strom_an.txt","a") as f:
        f.write(datetime.now().strftime('%d-%m-%y %H:%M:%S'))
        f.write("\n")
  • Danke für den Himweis, das Ist beim copy&paste passiert, müsste jetzt richtig reinkopiert sein.

    Im Script ist es korrekt, sonst meckert Python ja zuverlässig. :lol:

    Und noch bissl zusammengefasst, damit es wieder auf eine Seite passt.

  • Es geht !

    Ich räesche misch uff du, en ganzes Wochenende und jetzt gehts ! :lol:

    Bis auf eine Kleinigkeit, siehe unten. :-/

    strom_an.txt

    Code
    25-11-20 13:01:01

    strom_aus.txt

    Code
    25-11-20 15:16:01

    Nochmal das Script:

    strom_an.py:

    Python
    #!/usr/bin/env python3
    from datetime import datetime
    from RPi import GPIO as gpio
    gpio.setwarnings(False)
    gpio.setmode(gpio.BCM)
    gpio.setup(24, gpio.OUT)
    gpio.output(24, gpio.LOW)
    with open("strom_an.txt","a") as f:
        f.write(datetime.now().strftime('%d-%m-%y %H:%M:%S'))
        f.write("\n")

    strom_aus.py:

    Python
    #!/usr/bin/env python3
    from datetime import datetime
    from RPi import GPIO as gpio
    gpio.setwarnings(False)
    gpio.setmode(gpio.BCM)
    gpio.setup(24, gpio.OUT)
    gpio.output(24, gpio.HIGH)
    with open("strom_aus.txt","a") as f:
        f.write(datetime.now().strftime('%d-%m-%y %H:%M:%S'))
        f.write("\n")

    Was jedoch auffällt, sind diese Ausgaben auf dem Bildschirm:

    Hat mir jemand einen Tip und eine Erklärung, wieso und woher das kommt ?

    :helpnew:

  • Die Ausgabe passt irgendwie nicht zum Code.

    Was ich nicht so ganz nachvollziehen kann, dass du das in 3 oder 4 Scripte unterteilst.

    Konfigurierte GPIO Ausgänge können via gpio.input auch abgefragt werden, selbst wenn es im gleichen Prozess stattfindet.

    Das Ein-/Ausschalten kann im gleichen Script durchgeführt werden und wenn man Python-Programme als eigenen Prozess startet, stimmt meisten etwas mit der Struktur nicht.

    Das Ein-/Ausschalten sind zwei Funktionen.

    Prüfen ob die Spannung eingeschaltet ist, wäre auch nur eine Funktion.

    Das Überprüfen, ob sich die Spannung innerhalb der zulässigen Betriebsspannung befindet, wäre auch eine einzelne Funktion.

    PS: Auch das Einlesen der Spannung vom ADC könnte in dem Programm stattfinden und falls es an anderen Stellen benötigt wird, kann ja weiterhin die Datei geschrieben werden.

  • Hm, die Fehlermeldung kam jetzt auch nicht mehr, habe jetzt alles andere auch auf Python umgeschrieben, damit sich mein rumgewurstel nicht gegenseitig beisst.

    Die strom_an.py und strom_aus.pyhabe ich jetzt umgeschrieben, dass nicht in eine .txt. geschrieben wird, sondern jeweils eine neue .txt mit Zeitstempel angelegt wird und zusätzlich in die Datei der Zeitstempel:

    Python
    #!/usr/bin/env python3
    from datetime import datetime
    from RPi import GPIO as gpio
    gpio.setwarnings(False)
    gpio.setmode(gpio.BCM)
    gpio.setup(24, gpio.OUT)
    gpio.output(24, gpio.LOW)
    with open("{}_an.txt".format(datetime.now().strftime('%y-%m-%d %H:%M')), "w") as f:
        f.write(datetime.now().strftime('%y-%m-%d %H:%M'))
        f.write("\n")

    Und noch je ein Script zum manuellen an- bzw. ausschalten:

    Alles in einem zusammenfassen kommt noch, ist bei meinen Pythonkenntnissen aber noch irgendwo in der Zukunft.

    Das auslesen der Spannung soll in Zukunft ein MCP3208 übernehmen, statt wie bisher der PCF8591, damit soll die Messung genauer werden.

    Den MCP3208 mit Spannungsteiler habe ich soweit schon fertig, ich muss ihn noch anschliessen und dann an die Software, wird wohl mit smbus umgesetzt, da muss ich mich erst noch reinlesen.

    Hauptsache es klappt erstmal irgendwie. :auslachen: :lol:

  • Das wichtigste wäre das Verständnis von Funktionen und deren Nutzung.

    Damit kann man eine Aufgabe in Teilaufgaben unterteilen.
    Doppelten Code soll man vermeiden, auch Dateiübergreifend.

    Bezüglich des Logging: Eine Datei würde vollkommen ausreichen. Es würde die Fehlersuche sogar erleichtern. Bezüglich Datum kann man mit ISO8601 arbeiten. Das ist ein Standard für DateTime + Zeitzone und ist aufgrund der festgelegten Struktur auch lexikografischer Reihenfolge richtig sortiert.

    Python
    from datetime import datetime as DateTime
    
    
    now = DateTime.now()
    # now ist ein datetime objekt
    # nun als ISO8601 str ausgeben
    print(now.isoformat())
    Zitat

    2020-11-27T10:57:41.900358

    Auf Zeitzonen werde ich jetzt besser nicht eingehen. Nur soviel: Die datetime-Objekte bilden die lokale Systemzeit ohne Zeitzoneninformation ab.
    Man nennt es naive datetime.

  • Ja das mit den Funktionen ist mir vom Prinzip her klar.

    Zeitzonen benötige ich nicht, die geloggten Daten sollen mir nur Aufschluss darüber geben, ab wann und bis wann die Sonne auf die Solarpanele scheint, weil ich bisher einen unflexiblen Eintrag in der crontab -e habe, da im Winter aber viel später Sonne auf die Panele kommt, als das im Sommer der Fall ist, möchte ich ein Jahr lang protokollieren und dann Tages- und Monatsabhängig das Programm starten, damit es nicht unnötig läuft.

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!