Python spawning thread for one function - python

I'm trying to run one function concurrently from all the other code and this function do not return anything it just measure time and make sure some gpio pins are on/off so I don't need to join threads or anything I guess but I can't make it work. Main code is executing on some condition it calls timeToRun function this function make sure that certain GPIO pin or whatever is on for example 10 seconds and after that thread/process is killed without stopping main code.
In my mind it should look like that.
main code ----------------------------------------------------
\-------------------X
For now I got something like that but this in fact turn GPIO pin to LOW but never turn it back to HIGH
import RPi.GPIO as GPIO
import time
import concurrent.futures
def initPins(pin):
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.HIGH)
def clean():
GPIO.cleanup()
initPins(26)
def timeToRun(timeToSpin):
GPIO.output(26, GPIO.LOW)
initTime = time.perf_counter_ns()/1000000 # convert to ms
while True:
currentTime = time.perf_counter_ns()/1000000
if currentTime - initTime >= timeToSpin:
GPIO.output(26, GPIO.HIGH)
print((currentTime - initTime) - timeToSpin)
user = [2000]
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.submit(timeToRun, user)
for i in range(0, 10):
print(i)
time.sleep(0.5)
clean()
I have tried with ProcessPool too but with that code stop and wait until time passes. Is this even possible to do as I think?
EDIT:
I didn't add this but function is working as expected the problem is when I try to multi-thread it.
SOLUTION:
I found the solution
import threading
class MyWorker(threading.Thread):
def __init__(self, timeToSpin):
threading.Thread.__init__(self)
self.timeToSpin = timeToSpin
def run(self):
timeToRun(self.timeToSpin)
def timeToRun(timeToSpin):
GPIO.output(26, GPIO.LOW)
initTime = time.perf_counter_ns()/1000000 # in ms
while True:
currentTime = time.perf_counter_ns()/1000000
if currentTime - initTime >= timeToSpin:
GPIO.output(26, GPIO.HIGH)
break
user = 10000
thread1 = MyWorker(user)
thread1.start()
for i in range(0, 100):
print(i)
time.sleep(0.5)
I would say that is close enough time is a bit off sometimes but that was expected.
BETTER SOLUTION by JohanL:
import threading
def test():
GPIO.output(26, GPIO.HIGH)
user = 10.0
GPIO.output(26, GPIO.LOW)
timer = threading.Timer(user, test)
timer.start()
for i in range(0, 100):
print(i)
time.sleep(0.5)

Related

Runtimewarning This Channel is already in use

I'm configuring some leds and buttons on my raspberry pi, and when running the script the system warns me a channel is already in use. I tried looking for a solution, and a tip was to do a GPIO.Cleanup() in a finally clause. I tried, but it keeps failing. Any idea why? The error points to GPIO.setup(button_pin, GPIO.IN) but not sure to add these lines of code in my try clause?
#!/usr/bin/env python
import RPi.GPIO as GPIO
import os
import time
# Hide Warnings
# GPIO.setwarnings(False)
# Assign GPIO pin numbers
button_pin = 3 # Input Pin
button_led = 14 # Output Pin
green_led = 22 # Output Pin
red_led = 27 # Output Pin
# ir_receiver_pin = 17
# Use BCM pin numbering
GPIO.setmode(GPIO.BCM)
# 1. GPIO Setup
GPIO.setup(button_pin, GPIO.IN)
GPIO.setup(button_led, GPIO.OUT)
GPIO.setup(green_led, GPIO.OUT)
GPIO.setup(red_led, GPIO.OUT)
# 2. Button Led Behaviour on Startup
def button_led_on():
GPIO.output(button_led, GPIO.HIGH)
def button_led_off():
GPIO.output(button_led, GPIO.LOW)
def button_flicker_startup():
a = 1
while a < 4:
button_led_on()
time.sleep(0.3)
button_led_off()
time.sleep(0.3)
a = a + 1
button_led_on() # LED is high when Pi is on
# 3. Define front led behaviour on startup
def green_led_on():
GPIO.output(green_led, GPIO.HIGH)
def green_led_off():
GPIO.output(green_led, GPIO.LOW)
def red_led_on():
GPIO.output(red_led, GPIO.HIGH)
def red_led_off():
GPIO.output(red_led, GPIO.LOW)
def boot_flicker():
time.sleep(1.0)
green_led_on()
time.sleep(0.5)
green_led_off()
time.sleep(0.2)
green_led_on()
time.sleep(0.3)
green_led_off()
red_led_on()
time.sleep(0.3)
red_led_off()
time.sleep(0.2)
green_led_on() # LED is high when Pi is on
# 4. Main program
try:
button_flicker_startup()
boot_flicker()
GPIO.wait_for_edge(button_pin, GPIO.FALLING)
os.system("sudo shutdown -h now")
except:
pass
finally:
GPIO.cleanup()
I could use GPIO.setwarnings(False) but this is just hiding the error and would like to avoid that.
EDIT: My Raspberry Pi is using a Hifiberry dac who is using GPIO 2 and 3 for configuration. Could it be related to that?
I would change the main bit to this:
x = True
try:
while x:
button_flicker_startup()
boot_flicker()
GPIO.wait_for_edge(button_pin, GPIO.FALLING)
os.system("sudo shutdown -h now")
except:
pass
finally:
x = False
GPIO.cleanup()
because the functions are running, now we force them to stop running and then we cleanup.
I've used this video to help me, https://www.youtube.com/watch?v=LEi_dT9KDJI&t=257s :
I would watch it if I was you, I goes in depth about what you need/want.

Stop Python GPIO Thread with a lever

I have written code to let multiple LEDs blink the same time when the lever is activated. I tried it like this:
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
from threading import Thread
GPIO.setmode(GPIO.BOARD)
GPIO.setup(32, GPIO.IN)
def blink(port, hz):
GPIO.setup(port, GPIO.OUT)
while True:
GPIO.output(port, GPIO.HIGH)
time.sleep(0.5/hz)
GPIO.output(port, GPIO.LOW)
time.sleep(0.5/hz)
def aus(port):
GPIO.output(port, GPIO.LOW)
while True:
if GPIO.input(32) == 1:
Thread(target=blink, args=(15, 1)).start()
Thread(target=blink, args=(16, 3)).start()
Thread(target=blink, args=(18, 5)).start()
Thread(target=blink, args=(22, 8)).start()
Thread(target=blink, args=(29, 10)).start()
else:
Thread(target=aus, args=(15)).start()
Thread(target=aus, args=(16)).start()
Thread(target=aus, args=(18)).start()
Thread(target=aus, args=(22)).start()
Thread(target=aus, args=(29)).start()
Now the Problem:
I want to stop the blinking of the LEDS when I deactivate the lever.
The way I tried it did not work.
It seems you keep starting new threads for blinking, and each thread has an infinite loop, so they will keep running and blinking.
You need at most one thread per LED for the blinking, and that thread can check the lever in its loop, and then turn off the LED. The main program can do nothing in the meantime (while True: time.sleep(100) for example) or something different.

How do I run two while True statements in python?

I am trying to make a door swipe card system in Python for my Raspberry Pi. I broke the program into two: A Door Alarm and a Card Swipe Recording system. The two programs work individually but how do I combine the two programs into one python file? I've tried threading but it doesn't seem to work.
Below are the programs:
1.) Door Alarm: If door is left open for a certain duration, an led will blink, then an alarm will ring
import time
import RPi.GPIO as gpio
led = 37
buzzer = 11
door = 16
gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
gpio.setup(buzzer, gpio.OUT)
gpio.setup(led, gpio.OUT)
gpio.setup(door, gpio.IN, pull_up_down=gpio.PUD_UP)
def blink(buzzer):
gpio.output(buzzer, True)
time.sleep(0.1)
gpio.output(buzzer, False)
time.sleep(0.1)
return
def blink(led):
gpio.output(led, True)
time.sleep(1)
gpio.output(led, False)
time.sleep(1)
return
while True:
if gpio.input(door):
time.sleep(3)
for i in range(0,5):
blink(led)
for i in range (0,5):
blink(buzzer)
else:
gpio.output(buzzer, False)
gpio.cleanup()
2.) Card Swipe Recording System: When someone swipes their card, the led blinks and a picture is taken
import datetime
import time
import os
import RPi.GPIO as gpio
led = 37
t = datetime.datetime.now()
gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
gpio.setup(led, gpio.OUT)
def blink(led):
gpio.output(led, True)
time.sleep(0.1)
gpio.output(led, False)
time.sleep(0.1)
while True:
card = raw_input()
f = open("Laptop Sign Out" + '.txt', 'a')
f.write("OneCard Number: " + card[1:10] + " Time: " + t.strftime("%m-%d-%Y %H:%M:%S"))
f.write('\n')
f.write(';')
f.write('\n')
f.close()
time.sleep(1)
for i in range(0,3):
blink(led)
os.system('fswebcam ~/Desktop/Photos/%H%M%S.jpeg')
time.sleep(3)
gpio.cleanup()
(UPDATE) Also, below is my attempt at threading:
import time
import RPi.GPIO as gpio
import os
import datetime
from threading import Thread
led = 37
buzzer = 11
door = 16
t = datetime.datetime.now()
gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
gpio.setup(buzzer, gpio.OUT)
gpio.setup(led, gpio.OUT)
gpio.setup(door, gpio.IN, pull_up_down=gpio.PUD_UP)
def blink(buzzer):
gpio.output(buzzer, True)
time.sleep(0.1)
gpio.output(buzzer, False)
time.sleep(0.1)
return
def blink(led):
gpio.output(led, True)
time.sleep(1)
gpio.output(led, False)
time.sleep(1)
return
def doorsensor():
while True:
if gpio.input(door):
time.sleep(3)
for i in range(0,5):
blink(led)
for i in range (0,5):
blink(buzzer)
else:
gpio.output(buzzer, False)
def cardreader():
while True:
card = raw_input()
f = open("Laptop Sign Out" + '.txt', 'a')
f.write("OneCard Number: " + card[1:10] + " Time: " + t.strftime("%m-%d-%Y %H:%M:%S"))
f.write('\n')
f.write(';')
f.write('\n')
f.close()
time.sleep(1)
for i in range(0,3):
blink(led)
os.system('fswebcam ~/Desktop/Photos/%H%M%S.jpeg')
time.sleep(3)
f1 = Thread(target = doorsensor())
f2 = Thread(target = cardreader())
f2.start()
f1.start()
gpio.cleanup()
You need to pass your thread functions as the target arguments, not their return values:
import sleep
f1 = Thread(target=doorsensor) # Remove parentheses after doorsensor
f1.daemon = True
f1.start()
f2 = Thread(target=cardreader) # Remove parentheses after cardreader
f2.daemon = True
f2.start()
# Use a try block to catch Ctrl+C
try:
# Use a while loop to keep the program from exiting and killing the threads
while True:
time.sleep(1.0)
except KeyboardInterrupt:
pass
gpio.cleanup()
The daemon property is set on each thread so that the program will exit when only the daemon threads are left:
A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left. The initial value is inherited from the creating thread. The flag can be set through the daemon property.
I'm presenting a thread-less approach.
The idea is to turn your while bodies into update functions, and call them alternatively.
First off, your door loop becomes
def update_door():
if gpio.input(door):
time.sleep(3)
for i in range(0,5):
blink(led)
for i in range (0,5):
blink(buzzer)
else:
gpio.output(buzzer, False)
Then your card swipe recording system becomes
def update_card():
card = raw_input()
f = open("Laptop Sign Out" + '.txt', 'a')
f.write("OneCard Number: " + card[1:10] + " Time: " + t.strftime("%m-%d-%Y %H:%M:%S"))
f.write('\n')
f.write(';')
f.write('\n')
f.close()
time.sleep(1)
for i in range(0,3):
blink(led)
os.system('fswebcam ~/Desktop/Photos/%H%M%S.jpeg')
time.sleep(3)
Finally, your main loop becomes:
while True:
update_door()
update_card()
But a problem arises: time.sleep in update_card will delay update_door as well.
Here, you have three solutions:
1 - It's ok if update_door is delayed
Well, ok.
2 - It's not ok if update_door is delayed, but it's ok if update_card is not delayed
Then just remove time.sleep(3).
3 - You need update_door not to be delayed, and update_card to be delayed
Then you can set a manual timer, using the time module.
lastCardUpdate = time.time()
while True:
update_door()
now = time.time()
if now - lastCardUpdate >= 3:
update_card()
lastCardUpdate = now
But raw_input in update_card is a blocking method, waiting for a user input.
If you do need this user input to happen every three seconds, then this approach cannot be used.
If you can move it before the while, ie outside of the update_card function, then it's fine.
Else, you will indeed need to use threads.
If you are attempting to run these two programs simultaneously, then you will have to use either threading or multiprocessing, which you say you have attempted. If you have, may we see your attempt as we may be then able to help you with your issue there.
One other issue is that all of your methods are named Blink, which is not allowed in Python, in python all of your methods should have different names.
Edit: For threading make sure to type threading.Thread(target = target) as your code
Regardless of any other mistake, you need to join one of your threads once they have been started.
f1 = Thread(target = doorsensor())
f2 = Thread(target = cardreader())
f2.start()
f1.start()
f1.join()
What does f1.join() do?
Basically, it tells Python to wait until f1 has finished running.
If you don't do so, the program will start f1 and f2, then will exit.
Upon exiting, Python will release all the resources, including those two threads, whose execution will stop.

infinite while loop python

I am doing a parking sensor with raspberry pi and python
This is the code :
import RPi.GPIO as GPIO
import time
#from picamera import PiCamera
from time import sleep
from gpiozero import MotionSensor
import smtplib
sender = '*****#gmail.com'
reciever = '*****#gmail.com'
def BlueLED (): #Blue LED Function
GPIO.output(27, GPIO.HIGH)
time.sleep(3)
GPIO.output(27, GPIO.LOW)
def RedLED (): #Red LED Function
GPIO.output(22,GPIO.HIGH)
time.sleep(3)
GPIO.output(22, GPIO.LOW)
def Buzzer (): #Buzzer Function
GPIO.output(17, GPIO. HIGH)
time.sleep(3)
GPIO.output(17, GPIO.LOW)
def email(sender,reciever,msg):
try :
server = smtplib.SMTP('smtp.gmail.com',587)
server.ehlo()
server.starttls()
server.login(sender,'******')
server.sendmail(sender,reciever,msg)
server.close()
print('Email sent!')
except :
print('Error')
try :
GPIO.setmode(GPIO.BCM)
#camera = PiCamera()
pir = MotionSensor(4)
GPIO.setwarnings(False)
GPIO.setup(27, GPIO.OUT) #blueLED
GPIO.setup(22, GPIO.OUT) #redLED
GPIO.setup(17, GPIO.OUT) #buzzer
GPIO.setup(18, GPIO.OUT) #tempsensor
GPIO.setup(21, GPIO.IN, pull_up_down = GPIO.PUD_UP) #entry button
count = 0
while True :
if (pir.motion_detected):
print('Motion Detected')
#Calling the buzzer function
#Buzzer()
#The content that is going to be sent via email
msg = """Subject : Car Park
(Picture) """
email(sender,reciever,msg)
print('\nPlease press the button for the gate to open')
while True :
if(GPIO.input(21) == False):
if (count < 5):
BlueLED()
print('\nThere are ',(5-count), ' parking spaces empty ')
else :
RedLED()
print('\nSorry but the parking is full')
count = count + 1
except Exception as ex :
print('Error occured',ex)
My problem is that the first while loop is not working, i.e if the motion sensor is triggered nothing happens yet you can repress the button and the count is increased. I'm guessing there is an easy solution to this but non seem to come to mind. Would love your help, thanks
Your second while loop has no break point!!!
Your first while loop runs until it detects motion and then enters the second loop, and your second loop runs forever.
If you want both loops to work simultaneously then you should use threads!!!
If not then make a break point in second loop.
Simple example:
import time
count=0
count2=0
while True: #starting first loop
count+=1 # counting to 100 to start second loop
if count==100:
print("its 100 entering second loop")
time.sleep(1)
while True: # entering second loop
count2+=1 #counting to 100 to exit second loop
if count2==100:
print("its 100 exitin second loop")
time.sleep(1)
count=0
count2=0
break # exiting second loop this is the break point

How to stop loop partially?

So im writing a basic programme to turn an LED on using a potentiometer, this is my code:
def pot():
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.OUT)
GPIO.setup(22, GPIO.IN)
GPIO.output(4, GPIO.LOW)
try:
while True:
if (GPIO.input(22) == GPIO.HIGH):
print("switch activated")
GPIO.output(4, GPIO.HIGH)
if (GPIO.input(22) == GPIO.LOW):
GPIO.output(4, GPIO.LOW)
else:
GPIO.output(4, GPIO.LOW)
except KeyboardInterrupt:
GPIO.cleanup()
pot()
When i activate the potentiometer i only want "switch activated" to be printed once but i want the LED to keep running until i deactivate the potentiometer. As the code is "switch activated" will obviously print constantly while the pot is activated. I've tried using a break and other such things but they all either ended the programme or turned off the LED as well. How can i solve this?
Since you want the LED to continue glowing, it's as simple as adding another loop. Here's a (slightly) modified version of your code that should work:
def pot():
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.OUT)
GPIO.setup(22, GPIO.IN)
GPIO.output(4, GPIO.LOW)
try:
while True:
if (GPIO.input(22) == GPIO.HIGH):
print("switch activated")
# modified part
while (GPIO.input(22) == GPIO.HIGH):
GPIO.output(4, GPIO.HIGH)
# end modification
else:
GPIO.output(4, GPIO.LOW)
except KeyboardInterrupt:
GPIO.cleanup()
pot()
Another way is to switch on the LED and wait for the input at pin 22 to turn LOW. Just change the modified part as below.
# modified part
GPIO.output(4, GPIO.HIGH)
while (GPIO.input(22) == GPIO.HIGH):
pass
# end modification
Apparently you want to have the LED react constantly to the signal, but only print the message when the signal changes.
The way to do this is to track the state with a variable.
Note: I'm somewhat confused by the nested GPIO.input calls inside your loop. It seems like these would be redundant, because if the return value of GPIO.input changed, it'd get handled in the next iteration...
try:
state = None
while True:
signal = GPIO.input(22)
GPIO.output(4, signal)
if signal != state:
if signal == GPIO.HIGH:
print("switch activated")
else:
print("switch deactivated")
state = signal
except KeyboardInterrupt:
GPIO.cleanup()
Outputs can be checked as inputs, too. So you can change your logic to only set the output high if it is currently low.
def pot():
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.OUT)
GPIO.setup(22, GPIO.IN)
GPIO.output(4, GPIO.LOW)
# Initialize state
state = GPIO.LOW
try:
while True:
# Set the output high only if the input is high and the output is low
if (GPIO.input(22) == GPIO.HIGH) and (GPIO.input(4) == GPIO.LOW):
print("switch activated")
GPIO.output(4, GPIO.HIGH)
# If you want, you could do the same here and add a check on the output to
# only turn it low if it is currently high
elif (GPIO.input(22) == GPIO.LOW):
GPIO.output(4, GPIO.LOW)
except KeyboardInterrupt:
GPIO.cleanup()
pot()
You need to move print("switch activated") outside the loop if you only want it to be executed once.

Categories

Resources