Seite 1 von 1

Taster-Implementierung

Verfasst: Montag 30. April 2018, 15:47
von TheLüddy
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, ())

Re: Taster-Implementierung

Verfasst: Montag 30. April 2018, 16:11
von noisefloor
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

Re: Taster-Implementierung

Verfasst: Montag 30. April 2018, 17:09
von TheLüddy
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.

Re: Taster-Implementierung

Verfasst: Montag 30. April 2018, 22:55
von TheLüddy
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:

Re: Taster-Implementierung

Verfasst: Dienstag 1. Mai 2018, 13:21
von noisefloor
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

Re: Taster-Implementierung

Verfasst: Dienstag 1. Mai 2018, 13:53
von __deets__
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.

Re: Taster-Implementierung

Verfasst: Mittwoch 2. Mai 2018, 08:49
von TheLüddy
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()


Re: Taster-Implementierung

Verfasst: Mittwoch 2. Mai 2018, 20:30
von Sirius3
@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.