Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
implementation of a button
#1
Hi guys!

I bought a Raspberry Pi some time ago, and now I started a little project. First, I programmed a traffic light, which switches from green to yellow and then red, then it goes back to green and so on. It keeps going on forever. Now I want to add a light for pedestrian, which flashes when the traffic light is red and only if I pressed a button before. I read something about 2 threads which run at the same time, so one thread is always checking if the button was pressed or not. The code looks like this:

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, ())
I don't get an error message, but the program do not work. It has no effect if I press the button or not, the blue (pedestrian) light do not flash.
Can someone help me? Thanks a lot!

EDIT: Sry, I actually get an error message:
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 34, in schleife
    if fussg == True:
UnboundLocalError: local variable 'fussg' referenced before assignment
Reply
#2
You should consider starting the thread right before your main while loop. Currently your program will iterate through your loop without starting the thread. Also you already set the GPIO mode and declared PIN 25 as input, you do not need to do it in the thread again. Your thread shares all global variables with the rest of your application. So your code shall look like this
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():
    while True:
        if GPIO.input(25) == 1:
            fussg = True

_thread.start_new_thread(taste, ())

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()
Furthermore, I am not sure how the setup function is defined, so if you get True when the button is pressed or if you get False if you press the button. If you have tried this configuration before it is fine, if not try to use
GPIO.setup(25, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
instead of
GPIO.setup(25, GPIO.IN)
I have not tested the program, but I hope it works now Smile
Reply
#3
Thank you very much! unfortunately it is still not working ^^ I now get a error message like this once i stop the script:

Unhandled exception in thread started by <function taste at 0x767ad5d0>
Traceback (most recent call last):
  File "/home/pi/Desktop/ampel02.py", line 22, in taste
    if GPIO.input(25) == 1:
RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)
It think that's why I declared the GPIO Input twice. I feel like the thread don't change 'fussg' from False to True, so the if condition is never true and the light never flashes.
Reply
#4
Ok, then it really was needed in the thread :D just use the taste function as you defined it previously :) hope it works :)
Reply
#5
I tested it on my Pi and it works now. The first problem was, that the thread was started after the infite while loop, as I mentioned already. The second problem is, that eventhough threads share the same memory, variables are not shared that way. The variables have to be given as an parameter to the function (for lists it works just fine) or (in this case) they have to be declared as global. The thread creates every time the button is pressed a new variable named fussg, but that does not influence the globally declared variable fussg. The trick is to declare the variable as global so that the thread knows where to look for it. The working code is here, I modified it a bit though, first of all buttons are kind of tricky and this was the configuration in which the button recognized that it was pressed.
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, pull_up_down=GPIO.PUD_UP)
	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 not GPIO.input(Ampel[taster]):
			fussg = True

_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()
I don't know why you got the exception saying that the mode has to be set, but for me this worked just perfectly.
Have fun
cheers python
Reply
#6
I‘m currently not at home bu I‘ll try it tomorrow morning. Thank you very much!!, I nearly lost my motivation xd
Reply
#7
Well it works a lot better than before, but it's not exactly what I wanted ^^. Now the blue light flashes everytime the traffic light is red. But it should only flash if I press the button before. Furthermore the light switches from red directly to green and not first yellow anymore. Do you know what I mean?

Probably the error is in line 31 where you wrote:
if not GPIO.input(Ampel[taster]):
I think there is still a problem in the code of the button, because it changes fussg everytime to True, so the light flashes everytime.
Reply
#8
IIITT WOOORKEEED! :DD I finally realised a big problem was the button itself. Sometimes the GPIO input was detected as true altough I didn't even press the button. Furthermore I changed the if condition in the taste function to GPIO.event_detected.

Thank you so much for your help!
Reply
#9
I didn't copy you code, I typed it while looking at your code. It could be that I missed switching a light so that it went from red to green :D The button worked for me, but buttons are often a bit tricky, so as I mentioned the code worked on my Pi with this configuration of the button :D
I'm glad I could help you :)
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020