I'm working on a little Raspberry Pi security project that has LEDs, a piezoelectric buzzer, a 9 digit keypad, and an LCD screen (Full Source Here: https://github.com/kevbo423/RPHSP/blob/master/Keypad.py).
In short, when a user enters an incorrect password with the keypad, I want the LEDs to flash and the buzzer to go off at the same time. I can't for the life of me figure out how to make that happen. I have the functions for the LEDs and the Buzzer created and individually working, but can't get them to go off simultaneously.
# Function for error LED feedback
def errorLED():
for i in range(3):
GPIO.output(6,1)
time.sleep(0.2)
GPIO.output(6,0)
GPIO.output(12,1)
time.sleep(0.2)
GPIO.output(12,0)
# Function for Buzzer Alarm Output
def buzzerAlarm():
for i in range(3):
GPIO.output(21,1)
time.sleep(0.5)
GPIO.output(21,0)
time.sleep(0.5)
I looked into using the Threading module, but ran into issues when I tried to execute the thread for the Buzzer beeping multiple times. Below is the test code I created to try and run the Buzzer thread multiple times...
# Buzzer Test
buzzerSound = threading.Thread(target=buzzerAlarm())
buzzerSound.start()
time.sleep(3)
buzzerSound.start()
time.sleep(3)
buzzerSound.start()
time.sleep(3)
...and the error message I received.
Traceback (most recent call last):
File "Keypad.py", line 277, in <module>
buzzerSound.start()
File "/usr/lib/python2.7/threading.py", line 730, in start
raise RuntimeError("threads can only be started once")
RuntimeError: threads can only be started once
Am I on the right track with the threading module? If so, should I be killing the Buzzer thread inside of the BuzzerAlarm() function or should I not be using .start() to run the function? Or do I need to be doing something else entirely? If threading isn't the best way to go, I'm open to suggestions.
From the documentation:
start()
It must be called at most once per thread object.
Therefore you should create a new Thread object if you want to start it again. For example:
buzzer_thread = threading.Thread(target=buzzerAlarm())
led_thread = threading.Thread(target=errorLED())
led_thread.start()
buzzer_thread.start()
time.sleep(3)
# Second time:
buzzer_thread = threading.Thread(target=buzzerAlarm())
led_thread = threading.Thread(target=errorLED())
led_thread.start()
buzzer_thread.start()
You may consider refactoring this into a function:
def buzzer_and_led():
buzzer_thread = threading.Thread(target=buzzerAlarm())
led_thread = threading.Thread(target=errorLED())
led_thread.start()
buzzer_thread.start()
# Wait until both the buzzer and the LED stop
while led_thread.is_alive() or buzzer_thread.is_alive():
pass
Firstly not a multi-threading answer, this answer should still work either way.
Okay, quick answer. What you could do is combine the two functions
# Function for error LED feedback
def errorLED():
for i in range(3):
GPIO.output(6,1)
time.sleep(0.2)
GPIO.output(6,0)
GPIO.output(12,1)
time.sleep(0.2)
GPIO.output(12,0)
# Function for Buzzer Alarm Output
def buzzerAlarm():
for i in range(3):
GPIO.output(21,1)
time.sleep(0.5)
GPIO.output(21,0)
time.sleep(0.5)
This would become
#Combined function
def combined():
for i in range(3):
GPIO.output(6,1) #Error LED
GPIO.output(21,1) #Alarm Buzzer
time.sleep(0.25) #Notice times changed by 0.05 so it can do the combination easyer
GPIO.output(6,0)
GPIO.output(12,1)
time.sleep(0.25)
GPIO.output(12,0)
#Repeats Code
GPIO.output(6,1)
GPIO.output(21,0) #Only difference is turning off the buzzer/alarm
time.sleep(0.25)
GPIO.output(6,0)
GPIO.output(12,1)
time.sleep(0.25)
GPIO.output(12,0)
Tell me if this works, I have not used my Raspberry PI in a while
Related
I am writing a script which sends a serial message over a websocket to a device. When I want to start the device I write:
def start(ws):
"""
Function to send the start command
"""
print("start")
command = dict()
command["commandId"] = 601
command["id"] = 54321
command["params"] = {}
send_command(ws, command)
Every 5 hours or so the device restarts, during the restart, my function start request does not run and my code stops completely.
My question is, is there a way to tell python: "If nothing has happened for 1 minute, try again"
It's not clear exactly what ws is or how you set it up; but you want to add a timeout to the socket.
https://websockets.readthedocs.io/en/stable/api.html#websockets.client.connect has a timeout keyword; refer to the documentation for details about what it does.
If this is not the websocket library you are using, please update your question with details.
You can use sleep from time module
import time
time.sleep(60) # waits for 1 minute
Also, do consider Multithreading for sleep
import threading
import time
def print_hello():
for i in range(4):
time.sleep(0.5)
print("Hello")
def print_hi():
for i in range(4):
time.sleep(0.7)
print("Hi")
t1 = threading.Thread(target=print_hello)
t2 = threading.Thread(target=print_hi)
t1.start()
t2.start()
The above program has two threads. Have used time.sleep(0.5) and time.sleep(0.75) to suspend execution of these two threads for 0.5 seconds and 0.7 seconds respectively.
more here
After some research on how to properly ask a thread to stop, I am stuck into an unexpected behavior.
I am working on a personal project. My aim is to run a program on a RaspberryPi dedicated to domotics.
My code is structured as below:
a first thread is dedicated to scheduling : everyday at the same hour, I send a signal on GPIO output
a second thread is dedicated to monitoring keyboard for manual events
whenever a specific key is pressed, I want to start a new thread that is dedicated to another routine just like my first thread
Here is how I proceed:
import schedule
from pynput import keyboard
import threading
first_thread = threading.Thread(target=heating, name="heating")
second_thread = threading.Thread(target=keyboard, name="keyboard")
first_thread.start()
second_thread.start()
stop_event = threading.Event()
My heating routine is defined by:
def heating():
def job():
GPIO.output(4,GPIO.HIGH)
return
schedule.every().day.at("01:00").do(job)
while True:
schedule.run_pending()
time.sleep(0.5)
My keyboard monitor is defined as follow:
def keyboard():
def on_press(key):
if key == keyboard.Key.f4:
shutter_thread = threading.Thread(name="shutter", target=shutter, args=(stop_event,))
shutter_thread.start()
if key == keyboard.Key.f5:
stop_event.set()
with keyboard.Listener(on_press=on_press,on_release=on_release) as listener:
listener.join()
My shutter thread target is similar to the heating one:
def shutter(stop_event):
def open():
GPIO.output(6,GPIO.HIGH)
return
t = threading.currentThread()
schedule.every().day.at("22:00").do(open)
while not stop_event.is_set():
schedule.run_pending()
time.sleep(0.5)
Problem is everytime I press the key to start my shutter thread, the shutter routine is called but:
the job within my shutter routine is executed twice
the job within the first thread is also now executed twice every time it is on schedule !
once I press the key to ask the shutter thread to stop, the heating (first) thread come back to its original (and correct) behaviour, but the shutter thread does not stop
I have no idea why starting this new thread yields such modification in the behaviour of the other thread. And why my stopping event is not working ?
What am I doing wrong ?
Since you are using the schedule framework for managing tasks a clean solution would be to use the same framework's API for canceling jobs (instead of using threading.Event). That way tasks management remains within schedule and user interaction is handled by threading.
def keyboard():
tasks = []
def on_press(key):
if key == keyboard.Key.f4:
# Shutter task.
tasks.append(
schedule.every().day.at("22:00").do(lambda: GPIO.output(6,GPIO.HIGH))
)
if key == keyboard.Key.f5:
schedule.cancel_job(tasks.pop(-1))
with keyboard.Listener(on_press=on_press,on_release=on_release) as listener:
listener.join()
# Heating task.
schedule.every().day.at("01:00").do(lambda: GPIO.output(4,GPIO.HIGH))
# Start keyboard listener.
ui = threading.Thread(target=keyboard)
ui.start()
while True:
schedule.run_pending()
time.sleep(0.5)
Even if a_guest solution is a clean one, I can share a second solution for those who can face a similar situation.
A working solution is to define a specific scheduler in the different threads instead of using the default one.
Illustration:
def heating():
def job():
GPIO.output(4,GPIO.HIGH)
return
heat_sched = schedule.Scheduler()
heat_sched.every().day.at("01:00").do(job)
while True:
heat_sched.run_pending()
time.sleep(1)
def shutter(stop_event):
def open():
GPIO.output(6,GPIO.HIGH)
return
shutter_sched = schedule.Scheduler()
shutter_sched.every().day.at("22:00").do(open)
while True:
if not stop_event.is_set():
shutter_sched.run_pending()
time.sleep(0.5)
else:
shutter_sched.clear()
return
I'm not too familiar with threading, and probably not using it correctly, but I have a script that runs a speedtest a few times and prints the average. I'm trying to use threading to call a function which displays something while the tests are running.
Everything works fine unless I try to put input() at the end of the script to keep the console window open. It causes the thread to run continuously.
I'm looking for some direction in terminating a thread correctly. Also open to any better ways to do this.
import speedtest, time, sys, datetime
from threading import Thread
s = speedtest.Speedtest()
best = s.get_best_server()
def downloadTest(tries):
x=0
downloadList = []
for x in range(tries):
downSpeed = (s.download()/1000000)
downloadList.append(downSpeed)
x+=1
results_dict = s.results.dict()
global download_avg, isp
download_avg = (sum(downloadList)/len(downloadList))
download_avg = round(download_avg,1)
isp = (results_dict['client']['isp'])
print("")
print(isp)
print(download_avg)
def progress():
while True:
print('~ ',end='', flush=True)
time.sleep(1)
def start():
now=(datetime.datetime.today().replace(microsecond=0))
print(now)
d = Thread(target= downloadTest, args=(3,))
d.start()
d1 = Thread(target = progress)
d1.daemon = True
d1.start()
d.join()
start()
input("Complete...") # this causes progress thread to keep running
There is no reason for your thread to exit, which is why it does not terminate. A daemon thread normally terminates when your programm (all other threads) terminate, which does not happen in this as the last input does not quit.
In general it is a good idea to make a thread stop by itself, rather than forcefully killing it, so you would generally kill this kind of thread with a flag. Try changing the segment at the end to:
killflag = False
start()
killflag = True
input("Complete...")
and update the progress method to:
def progress():
while not killflag:
print('~ ',end='', flush=True)
time.sleep(1)
I have a raspberry pi which I have hooked up with a 4 button keypad. Using the signal stuff from blinker I hooked it up to run some methods.
#sender
while True:
if buttonIsDown == True: signal.send()
#reciever
#signal.connect
def sayHI():
print("1")
time.sleep(10)
print("2")
This works fine, however when I push the button for the second time (Within 10 seconds of the previous button press) it does not fire the method as the thread is paused in the time.sleep(10).
How can I get it to fire the method again while the it is still paused(possibly in another thread)
It is an old question, but still it may be useful for someone else.
You can start a new thread every time the signal is emitted, in that way you will be able to catch all the events as soon as they happen. Remember that in your code, since you have a while True, the signal is never connected to the function, you should have defined them in the opposite order.
Here is a working example, based on your code:
import threading
from blinker import signal
from time import sleep
custom_signal = signal(name='custom')
#custom_signal.connect
def slot(sender):
def say_hello():
print("1")
sleep(10)
print("2")
threading.Thread(target=say_hello).start()
while True:
value = int(input('Press 1 to continue: '))
if value == 1:
custom_signal.send()
else:
break
Here is script (/shutdown.py). It monitors button press and if button is pressed more than 3 seconds, it runs poweroff command.
#!/usr/bin/python
# Import the modules to send commands to the system and access GPIO pins
from subprocess import call
from time import sleep
import RPi.GPIO as gpio
import time
# Define a function to keep script running
def loop():
raw_input()
# Define a function to run when an interrupt is called
def shutdown(pin):
button_press_timer = 0
while True:
if (gpio.input(17) == False) : # while button is still pressed down
button_press_timer += 1 # keep counting until button is released
if button_press_timer == 3:
#print "powering off"
call('poweroff', shell=True)
sleep(1)
else: # button is released, figure out for how long
#print "Poga atlaista. nospiesta bija " + str(button_press_timer) + " sekundes"
#button_press_timer = 0
return
# sleep(1) # 1 sec delay so we can count seconds
# print "powering off"
gpio.setmode(gpio.BCM) # Use BCM GPIO numbers
gpio.setup(17, gpio.IN, pull_up_down=gpio.PUD_UP) # Set up GPIO 17 as an input
gpio.add_event_detect(17, gpio.FALLING, callback=shutdown, bouncetime=200) # Set up an interrupt to look for button presses
loop() # Run the loop function to keep script running
If I run script from console like /shutdown.py all is fine. Button press is detected and system shutdown is initialed. But if i add that script to /etc/rc.local (/shutdown.py &), then it fails at startup with this error:
Traceback (most recent call last):
File "/shutdown.py", line 35, in <module>
loop() # Run the loop function to keep script running
File "/shutdown.py", line 11, in loop
raw_input()
EOFError: EOF when reading a line
If I comment out loop() line, then there is no error and script does not run in background. I just start and exit and button press not detected. So, how i can run that script at startup and keep running in background?
EDIT
I am not python guru and i think that loop() is python internal function. Now i seen that it is defined function which calls raw_input(). That script I found and modified to fit my needs.
Thanks.
What you really need is a Python daemon which runs in the background. The raw_input method you are trying to use looks like an ugly hack to me.
Have a look at python-daemon package, which is meant exactly for your use case and is quite simple to use. There is also an updated fork with Python 3 support.
After installing python-daemon, add this line to the beginning of your script
import daemon
Then substitute the loop() call at the end of your script with this code:
with daemon.DaemonContext():
while True:
time.sleep(10)
This code is untested, but you get the idea.