GPIO: Ventile parallel und unterschiedlich lange ansteuern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
DodgeRand
User
Beiträge: 6
Registriert: Mittwoch 17. April 2019, 13:05

Hallo Leute,

für ein Uni Projekt muss ich über einen Raspberry eine Relaiskarte ansteuern. Das klappt bisher auch schon ganz gut. Das Problem war nun, nachdem ich die GUI erstellt hatte, dass ich nicht weiss, wie ich ggf. mehrere Relais unterschiedlich lange ansteuern kann.

Theoretisch gibt es 12 Relais und diese sollen alle unterschiedlich lange angeschaltet bleiben, aber alle gleichzeitig starten.
Zur veranschaulichung habe ich hier mal einen Screenshot der GUI:

Bild

Also im Endeffekt sollen in der Anwendung die jeweiligen Ventile gecheckt werden, dann die entsprechende Zeit der einzelnen Ventile gesetzt werden und danach alle gleichzeitig durch den Startbutton für die gesetzte Zeit geöffnet werden. Wenn ich das mit einer Schleife probiere, dann wartet der Raspberry so lange, bis die time.sleep abgelaufen ist und macht danach mit dem nächsten Ventil weiter - so wie es eben nicht sein soll. Fällt euch vielleicht etwas dazu ein?

Danke im Vorraus
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

GUIs darf man nicht blockieren. Sondern muss mit Timern arbeiten. Da du nicht verraetst welches Toolkit du benutzt kann man nur raten - mit tkinter ist es die after-Methode, und mit Qt Qtimer. Wird hier alles oft und ausfuehlich diskutiert, such mal danach.
DodgeRand
User
Beiträge: 6
Registriert: Mittwoch 17. April 2019, 13:05

Danke für die Antwort! Es ist übrigens tkinter - sorry.
Werde mich in die Richtung erstmal belesen.
DodgeRand
User
Beiträge: 6
Registriert: Mittwoch 17. April 2019, 13:05

Hallo,

meine Ventile öffnen sich nun mit der after Methode! Alle gleichzeitig, bzw. mit 10ms verzögerung, was aber nicht weiter schlimm ist.
Wenn ich die after Methode richtig verstanden habe, dann sollte ein Ventil in meinem Code nach 4 Sekunden geschlossen werden (zweitletzte Codezeile/main.after(4000, GPIO.output(pinList[1], GPIO.HIGH))):

Code: Alles auswählen

def start(): #Function to start the Valves
    print("Start")
    pinList=[]
    if bigUpperValve1.get()==1:
          pinList.append(2)
    if bigUpperValve2.get()==1:
          pinList.append(3)
    if bigUpperValve3.get()==1:
          pinList.append(4)
    if smallUpperValve1.get()==1:
          pinList.append(17)
    if smallUpperValve2.get()==1:
          pinList.append(27)
    if smallUpperValve3.get()==1:
          pinList.append(22)
        
    
    
    print ("Auf")
    #st = bigUpperValve1Input.get()
    #stFloat = int(st)
    #st2 = bigUpperValve2Input.get()
    #stFloat2 = int(st2)
    #timeList = [stFloat, stFloat2]
    
    for j in range(len(pinList)):
        main.after(10, GPIO.output(pinList[j], GPIO.LOW))
        j=j+1
        
    main.after(4000, GPIO.output(pinList[1], GPIO.HIGH))
    
               
    print ("Zu")
Offenbar habe ich die Methode aber doch nicht durchdrungen, da das Ventil nicht nach 4 Sekunden schließt, sondern nach gefühlt 10 ms.
Natrüclich werde ich da noch eine Schleife herumbauen, damit ich die Ventile/Relais einzeln konfigurieren kann. Zunächst möchte ich das eben mit nur einem testen.
Habt ihr vielleicht einen Anreiz wie es wirklich funktioniert? Danke im Voraus!
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dein Verstaendnisproblem besteht darin, das du after mit einem CALLABLE versorgen musst. Also zB einer Funktion oder Methode. Und die soll aufgerufen werden, wenn der Timer abgelaufen ist. Was du stattdessen tust ist, einfach als zweites Argument einen Ausdruck anzugeben, der gleich ausgefuehrt wird. Und dessen Rueckgabewert dann an after uebergeben wird. Was im Zweifel None ist, und darum im Grunde gar nichts tut.

Ein Weg das zu aendern sind lambdas:

Code: Alles auswählen

main.after(10, lambda j=j: GPIO.output(pinList[j], GPIO.LOW))
Und dann noch ein paar Anmerkungen: das inkrementieren von j ist ueberfluessig und wirkungslos. Streich also die Zeile. Dein Code enthaelt ausserdem deutlich zu viele magische Zahlen. Definier dir auf Modulebene Konstanten dafuer:

Code: Alles auswählen

LUFTSCHLEUSE_ZUR_HOELLE=23
Der Vergleich mit 1 in deinen ganzen if-Ausdruecken ist aller Wahrscheinlichkeit nach unnoetig. Python betrachtet leere Werte und 0 als False, und den Rest als True. Ein einfaches

Code: Alles auswählen

  if smallUpperValve1.get():
          pinList.append(17)
reicht.
DodgeRand
User
Beiträge: 6
Registriert: Mittwoch 17. April 2019, 13:05

Okay danke! Werde es versuchen, aber leider über die nächsten 4 Tage keine Möglichkeit mich dranzusetzten. Frohe Ostern!
DodgeRand
User
Beiträge: 6
Registriert: Mittwoch 17. April 2019, 13:05

Nochmal Hi!!!! Gute Nachricht: konnte es doch noch schnell testen und es klappt :) Bin dir sehr sehr dankbar!
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@DodgeRand: wenn die 10ms Verzögerung nicht schlimm ist, bzw. eigentlich nicht gewollt ist, warum benutzt Du dann überhaupt `after`? Und wenn Du 4 Ventile gleichzeitig schaltest, arum machst Du das in 4 getrennten `after`-Aufrufen?
Über einen Index zu iterieren, ist umständlich, weil man mit for direkt über die Elemente der Liste gehen kann.
Die if-Kette am Anfang solltest Du auch am besten über eine Datenstruktur abbilden. Funktionen sollten alles, was sie brauchen über ihre Argumente bekommen:

Code: Alles auswählen

# beim Initialisieren, irgendwo im Code den Du nicht zeigst:
    valves = {
        2: big_upper_valve1,
        3: big_upper_valve2,
        4: big_upper_valve3,
        17: small_upper_valve1,
        27: small_upper_valve2,
        22: small_upper_valve3,
    }


def start(main, valves):
    """ Function to start the Valves """
    print("Start")
    pins = []
    for pin, valve in valves.items():
        if valve.get() == 1:
            pins.append(pin)
    
    print ("Auf")
    for pin in pins:
        GPIO.output(pin, GPIO.LOW)

    main.after(4000, lambda: GPIO.output(pins[1], GPIO.HIGH))
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die `start()` liesse sich noch deutlich kürzen:

Code: Alles auswählen

def start(main, valves):
    """Start the valves."""
    print('Start')
    pins = [pin for pin, valve in valves.items() if valve.get() == 1]
    print ('Auf')
    GPIO.output(pins, GPIO.LOW)
    main.after(4000, lambda: GPIO.output(pins, GPIO.HIGH))
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
DodgeRand
User
Beiträge: 6
Registriert: Mittwoch 17. April 2019, 13:05

Abend,

sieht super aus Leute! Danke für die viele Unterstützung :)
Kenne mich leider noch nicht so gut in Python aus, daher wollte ich erstmal alles hart codiert testen. Freut mich aber, dass es auch wesentlich dynamischer geht und werde die letzten Vorschläge auch ausprobieren! Melde mich wenn alles läuft.

Danke nochmal und einen schönen restlichen Feiertag!
Antworten