@GodsHell: Erst mal zum vorhandenen Programm:
``as`` beim Importieren ist zum umbenennen, `GPIO` wird aber gar nicht umbenannt.
`GPIO.cleanup()` sollte *immer* aufgerufen werden, nicht nur wenn der Benutzer das mit Strg+C abbricht, sondern auch wenn beispielsweise ein Fehler auftritt. Und das Aufsetzen der Pins gehört da auch schon in den ”geschützten” Bereich, denn auch wenn das dort am Anfang schon unterbrochen wird, sollten die bisher gemachten `setup()`\s wieder aufgeräumt werden.
Die Pin-Nummern sind magische Zahlen die über das Programm verteilt sind. Wenn man beispielsweise das was an Pin 12 angeschlossen ist, an einen anderen Pin anschliessen will, muss man alle 12 im Programm suchen und ändern. Alle? Nein, nicht ganz, man muss aufpassen, das man bei dem `randint()`-Aufruf die 12 nicht ändert, denn die steht nicht für den Pin. Deshalb definiert man für solche Zahlen Konstanten. Möglichst mit einem Namen der dem Leser verrät wofür der Pin da ist.
Da habe ich jetzt bei Pin 12 ein Problem, denn an einer Stelle steht ``#Bahnhof öffnet, Licht an`` als Kommentar, bei dem Kommentar ``#Bahnhof schließt, Licht aus`` wird dann aber Pin 13 geschaltet und nicht Pin 12‽ Pin 12 wird überhaupt nur einmal am Schleifenanfang geschaltet, und zwar immer auf den gleichen Wert. Warum steht das dann *in* der Schleife? Und nicht davor?
Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.
Eine ganze Reihe von Kommentaren wird man durch das ersetzen der magischen Zahlen für die Pin-Nummern los.
``i = x`` ist sinnfrei, da sollte man die Rundenzahl gleich an den richtigen Namen binden. Und zwar weder an `i` noch an `x`, sondern an `runden_anzahl`, weil man sich dann wieder einen Kommentar sparen kann, die erklärt was `x` eigentlich bedeuten soll.
Anstelle von ``while`` würde man eine ``for``-Schleife verwenden.
Die erste Abfrage von Pin 14 in der Schleife ist sinnfrei, weil das keinen Effekt hat.
Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
import random
import time
from RPi import GPIO
DC_POWER_SWITCH_PIN = 12 # active low!
TRAIN_POWER_PIN = 9 # active low!
TRAIN_STATION_LIGHT_PIN = 13 # active low!
PROXIMITY_SWITCH_PIN = 14 # active low!
def main():
try:
GPIO.setmode(GPIO.BCM)
GPIO.setup(
[TRAIN_POWER_PIN, DC_POWER_SWITCH_PIN, TRAIN_STATION_LIGHT_PIN],
GPIO.OUT,
initial=1,
)
GPIO.setup(PROXIMITY_SWITCH, GPIO.IN)
GPIO.output(DC_POWER_SWITCH_PIN, 0)
while True:
GPIO.output(TRAIN_STATION_LIGHT_PIN, 0)
GPIO.output(TRAIN_POWER_PIN, 1)
print("Bahnhof geöffnet, bitte einsteigen")
time.sleep(25) # Fahrgäste steigen ein.
GPIO.output(TRAIN_POWER_PIN, 0)
rounds_total = random.randint(3, 12)
print("Der Zug wird", rounds_total, "Runden fahren")
print("Zug fährt an")
time.sleep(0.5)
for rounds_left in reversed(range(1, rounds_total)):
time.sleep(0.06)
if not GPIO.input(PROXIMITY_SWITCH_PIN):
time.sleep(0.3) # Entprellen.
print("Der Zug fährt noch:", rounds_left, "Runden")
GPIO.output(TRAIN_POWER_PIN, 1)
print("Zug steht, bitte aussteigen")
time.sleep(20) # Fahrgäste steigen aus.
GPIO.output(TRAIN_STATION_LIGHT_PIN, 1)
print("Bahnhof geschlossen")
time.sleep(20) # Bahnhof geschlossen.
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
if __name__ == "__main__":
main()
Wenn Du das mit einer GUI verbinden willst, musst Du Python-Grundlagen bis einschliesslich objektorientieter Programmierung (OOP) drauf haben. Denn jede nicht-triviale GUI braucht das. Denn da hast nicht mehr die Kontrolle in so einer grossen linear ablaufenden Funktion, sondern die Hauptschleife des GUI-Rahmenwerks ist das was ständig laufen muss. Für bestimmte Ereignisse kannst Du dann Rückrufe registrieren, die von der Hauptschleife aufgerufen werden wenn das Ereignis eingetreten ist. Ereignisse können Klicks auf Schaltflächen sein, aber auch das eine bestimmte Zeitspanne abgelaufen ist.
Deine Ereignisbehandlung darf dann *kurz* etwas machen, und muss dann wieder zur GUI-Hauptschleife zurückkehren. Du kannst also keine länger laufenden Schleifen haben. Das muss alles in kleine Häppchen in Funktionen oder Methoden aufgeteilt werden. Wenn Funktionen, dann muss man immer alles was die anderen brauchen als Argument(e) durchreichen. Weshalb man nicht wirklich um OOP herum kommt, damit man den Zustand zu einem Objekt zusammenfassen kann.
Bei Tk kann man Rückrufe die nach einer angegebenen Zeit aufgerufen werden sollen mit der `after()`-Methode auf Widgets registrieren.
Alternativ könnte man den bisherigen Code in einem Thread ausführen und mit einer `queue.Queue`, die mit `after()` regelmässig abgefragt wird mit der GUI im Hauptthread kommunizieren. Denn die GUI selbst darf nur aus dem Thread heraus manipuliert werden, in dem dir GUI-Hauptschleife läuft.
Vorher würde ich das aber noch von `RPi.GPIO` auf `gpiozero` umstellen. Das hat eine wesentlich angenehmere API und man kann bei den entsprechenden Objekten in die man die Pins kapselt beim erstellen schon angeben, das die low active sind, und dann kann man die semantisch korrekten Methoden `on()` und `off()` verwenden, statt das man beim lesen immer daran denken muss das 1 aus und 0 an bedeutet.