Taster-Implementierung

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
TheLüddy
User
Beiträge: 20
Registriert: Freitag 27. April 2018, 10:17

Hallo, ich habe letztens mein Problem schon im Raspberry Teil gepostet, aber inzwischen neue Kenntnisse erlangt. Zu meinem Problem:

Ich habe in einer endlos while Schlaufe eine Ampel programmiert, welche von grün auf rot schaltet und wieder zurück. Weiterhin möchte ich jetzt einen Taster dazu addieren, welcher, sobald er zu irgendeiner Zeit gedrückt wird, eine zusätzliche blaue LED zum blinken bringt während die Ampel auf rot gestellt ist. Um dauernd zu überpfrüfen ob der Taster gedrückt wurde, wollte ich dies in einem zweiten Thread implementieren. Da ich aber ein Python- bzw Programmieranfänger bin, ist mir wohl ein Fehler unterlaufen. Nun bräuchte ich Hilfe um den Code auszubessern.

Vielen Dank im Voraus!

Code: Alles auswählen

import RPi.GPIO as GPIO
import time
import _thread

GPIO.setmode(GPIO.BCM)

rot = 0; gelb = 1; gruen = 2; blau = 3; taster = 4

Ampel=[4,18,23,24,25]
GPIO.setup(Ampel[rot], GPIO.OUT, initial = False)
GPIO.setup(Ampel[gelb], GPIO.OUT, initial = False)
GPIO.setup(Ampel[gruen], GPIO.OUT, initial = True)
GPIO.setup(Ampel[blau], GPIO.OUT, initial = False)
GPIO.setup(25, GPIO.IN)

print("Taster drücken für Fussgängerblinklicht, Ctrl+C beendet das Programm")

fussg = False

def taste():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(25, GPIO.IN)
    while True:
        if GPIO.input(25) == 1:
            fussg = True

try:
    while True:
        if fussg == True:                    
            GPIO.output(Ampel[gruen], False)
            GPIO.output(Ampel[gelb], True)
            time.sleep(1)
            GPIO.output(Ampel[gelb], False)
            GPIO.output(Ampel[rot], True)
            time.sleep(0.6)
            for i in range(10):
                GPIO.output(Ampel[blau], True); time.sleep(0.05)
                GPIO.output(Ampel[blau], False); time.sleep(0.05)
            time.sleep(0.6)
            GPIO.output(Ampel[rot], False)
            GPIO.output(Ampel[gelb], False)
            GPIO.output(Ampel[gruen], True)
            time.sleep(3)
            fussg = False
        else:
            time.sleep(3)
            GPIO.output(Ampel[gruen], False)
            GPIO.output(Ampel[gelb], True)
            time.sleep(1)
            GPIO.output(Ampel[gelb], False)
            GPIO.output(Ampel[rot], True)
            time.sleep(2)
            GPIO.output(Ampel[gelb], True)
            time.sleep(0.6)
            GPIO.output(Ampel[rot], False)
            GPIO.output(Ampel[gelb], False)
            GPIO.output(Ampel[gruen], True)
except KeyboardInterrupt:
    GPIO.cleanup()
    
_thread.start_new_thread(taste, ())
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ein paar grundsätzliche Sachen:

* `_thread` ist die low-level Implementierung, die man _nicht_ benutzt. Wo auch immer du das her hast -> das ist hier falsch. Wenn Threads, dann über das `threading` Modul.
* Man schreibt bei Python üblicherweise nicht mehrere Befehle mittels Semikolon getrennt in eine Zeile, weil schlechter Stil und schlecht lesbar. Jeder Befehl darf in einen eigene Zeile.
* Statt des alten RPi.GPIO Modul solltest du das neuere gpiozero Modul nutzen.Das hat auch eine schönere API. Und da kannst statt des Threads ggf. eine Callback-Funktion nutzen.
* Statt der Liste `Ampel` und der Variablendefinitionen davor kannst du ein Dict benutzen.

Zur Frage: welche Fehler ist dir denn unterlaufen? Was funktioniert nicht? Welche Fehlermeldung bekommst du?

Grundsätzlich kann ein Thread zwar mit anderen Threads / Programmteilen kommunizieren, aber nicht so. Das macht man normalerweise über eine Queue.

Gruß, noisefloor
TheLüddy
User
Beiträge: 20
Registriert: Freitag 27. April 2018, 10:17

Also das mit dem Semikolon und dem Dict habe ich verstanden. Natürlich wäre es so besser programmiert, löst aber konkret mein Problem nicht. Trotzdem vielen Dank.
Den Rest habe ich leider nicht verstanden, bzw mir noch nicht erarbeitet. Ich habe bemerkt dass 'threading' gebraucht wird anstatt 'threat', verstehe aber die Anwendung nicht ganz.

Im Prinzip läuft das Programm sauber ohne Fehlermeldung, nur funktioniert es halt nicht wie gewollt. Ob ich die Taste gedrückt habe oder nicht, es spielt keine Rolle den es passiert effektiv nichts. Die Ampel funktioniert wie gewollt nur geht das Fussgängerblinklicht nicht.
Könntest du mir vielleicht ein Beispiel geben wie du dieses Programm in etwa realisieren würdest? Vielleicht auch die Queue erläutern welche du genannt hast.
TheLüddy
User
Beiträge: 20
Registriert: Freitag 27. April 2018, 10:17

Nun mein Code sieht mittlerweile so aus:

Code: Alles auswählen

import RPi.GPIO as GPIO
import time
from threading import Thread

GPIO.setmode(GPIO.BCM)

rot = 0; gelb = 1; gruen = 2; blau = 3; taster = 4

Ampel=[4,18,23,24,25]
GPIO.setup(Ampel[rot], GPIO.OUT, initial = False)
GPIO.setup(Ampel[gelb], GPIO.OUT, initial = False)
GPIO.setup(Ampel[gruen], GPIO.OUT, initial = True)
GPIO.setup(Ampel[blau], GPIO.OUT, initial = False)
GPIO.setup(Ampel[taster], GPIO.IN)

print("Taster drücken für Fussgängerblinklicht, Ctrl+C beendet das Programm")

fussg = False

def tast():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(Ampel[taster], GPIO.IN)
    while True:
        if GPIO.input(25) == 1:
              fussg = True
    return fussg


def schleife():
    try:
        while True:
            if fussg == True:                    
                GPIO.output(Ampel[gruen], False)
                GPIO.output(Ampel[gelb], True)
                time.sleep(1)
                GPIO.output(Ampel[gelb], False)
                GPIO.output(Ampel[rot], True)
                time.sleep(0.6)
                for i in range(10):
                    GPIO.output(Ampel[blau], True); time.sleep(0.05)
                    GPIO.output(Ampel[blau], False); time.sleep(0.05)
                time.sleep(0.6)
                GPIO.output(Ampel[rot], False)
                GPIO.output(Ampel[gelb], False)
                GPIO.output(Ampel[gruen], True)
                time.sleep(3)
                fussg = False
            else:
                time.sleep(3)
                GPIO.output(Ampel[gruen], False)
                GPIO.output(Ampel[gelb], True)
                time.sleep(1)
                GPIO.output(Ampel[gelb], False)
                GPIO.output(Ampel[rot], True)
                time.sleep(2)
                GPIO.output(Ampel[gelb], True)
                time.sleep(0.6)
                GPIO.output(Ampel[rot], False)
                GPIO.output(Ampel[gelb], False)
                GPIO.output(Ampel[gruen], True)
    except KeyboardInterrupt:
        GPIO.cleanup()

t = Thread(target=schleife,args=())
f = Thread(target=tast,args=())

f.start()
t.start()
Nun bekomme ich jedoch die Fehlermeldung:

Code: Alles auswählen

 Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.5/threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File "/home/pi/Desktop/ampel02.py", line 32, in schleife
    if fussg == True:
UnboundLocalError: local variable 'fussg' referenced before assignment
Hilfe? :roll:
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

Python hat keinen globalen Scope von Variablen. Heißt: deine Funktion `schleife` kennt `fussg` nicht und deshalb bekommst du einen Fehler. In `tast` definierst du `fussg`, da hat aber keinen Einfluss auf `fussg`, was die außerhalb der Funktionen definiert hast.

Wenn du einen Zustand wie den von `fussg` zwischen zwei Threads austauschen willst / musst, dann musst du das anders machen. Z.B. über eine Pipe oder das Attribute der Instanz einer Klasse, die du dann beiden Threads mit gibst.

Gruß, noisefloor
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich würde grundlegend anders vorgehen: statt Threads kannst du mit add_event_detect eine Benachrichtigung bekommen, wenn dein Taster gedrückt oder losgelassen wurde.

In der Hauptschleife nimmst du dann nur EINEN sleep her, der eine kurze Zeit wartet. Danach schaltest du deine LEDs abhängig von einer variablen, die die gerade aktive Phase anthält. Und kannst natürlich auch per Zeitstempel entscheiden, wann du umschalten willst.
TheLüddy
User
Beiträge: 20
Registriert: Freitag 27. April 2018, 10:17

Ich habe es endlich geschafft, klar das Programm ist nicht perfekt und sicher kann man es verbessern, aber es läuft. Hier der Code, falls jemand vor einem ähnlichen Problem steht:

Code: Alles auswählen

import RPi.GPIO as GPIO
import time
import _thread
 
GPIO.setmode(GPIO.BCM)
 
# wie oft blau geblinkt werden soll
NUMBER_OF_FLASHES = 10
rot, gelb, gruen, blau, taster = 0,1,2,3,4
col_dic = {0:"rot", 1:"gelb", 2:"grün", 3:"blau", 4:"taste"}
 
Ampel=[4, 18, 23, 24, 25]
for i, pin in enumerate(Ampel):
    init = False
    if i == gruen:
        init = True
    if i == taster:
        print("Initialisierung Taste")
        GPIO.setup(pin, GPIO.IN)
        GPIO.add_event_detect(pin, GPIO.RISING)
    else:
        print("Initialisierung Licht %s"%col_dic[i])
        GPIO.setup(pin, GPIO.OUT, initial=init)
 
print("Taster drücken für Fussgängerblinklicht")
 
fussg = False
 
def taste():
    global fussg
    while True:
        if GPIO.event_detected(Ampel[taster]):
            fussg = True
            print("Button pressed")
 
_thread.start_new_thread(taste, ())
 
try:
    while True:
        GPIO.output(Ampel[gruen], False)
        GPIO.output(Ampel[gelb], True)
        time.sleep(1)
        GPIO.output(Ampel[gelb], False)
        GPIO.output(Ampel[rot], True)
        if fussg:
            time.sleep(0.6)
            for i in range(NUMBER_OF_FLASHES):
                GPIO.output(Ampel[blau], True)
                time.sleep(0.05)
                GPIO.output(Ampel[blau], False)
                time.sleep(0.05)
            fussg = False
        else:
            time.sleep(2)
            GPIO.output(Ampel[gelb], True)
        time.sleep(0.6)
        GPIO.output(Ampel[gelb], False)
        GPIO.output(Ampel[rot], False)
        GPIO.output(Ampel[gruen], True)
        time.sleep(3)
except KeyboardInterrupt:
    GPIO.cleanup()

Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@TheLüddy: höchstwahrscheinlich läuft Dein Programm nicht. So mit Threads umzugehen hat schwer aufzufindende Fehler zur Folge. Eine for-Schleife, wo pro Schleifendurchgang per if verschiedene Sachen gemacht werden, ist nicht for-Schleifengeeignet. cleanup sollte immer in einem finally-Block stattfinden.
Antworten