How to stop loop partially? - python

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.

Related

Python spawning thread for one function

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)

I want to execute this script every minute and send me a message every time it changes state

I am working on a raspberrypi, I want to put this code in crontab to be exectuted every minute, it should low the gpio from 19:00 to 19:minute_max (and this part works) then it should send me an email when it changes the state of the GPIO. I'm able to send me an email when its set to LOW. Whit this code:
from time import localtime
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)
ore = 19
minuti_max=30
minuti = range(0,minuti_max)
if (localtime()[3]==ore) and (localtime()[4] in minuti):
if GPIO.input(26)==1:
GPIO.output(26, GPIO.LOW)
#send_mail_to_me("ON","yeye")
print("on")
else:
GPIO.output(26, GPIO.HIGH)
If I put a send_mail_to_me function in the else statement it will send me it every minute. But I want that it is send only when it set the gpio high for the first time.
I tried the following but it doesn't work:
from time import localtime
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.OUT)
ore = 19
minuti_max=30
minuti = range(0,minuti_max)
if (localtime()[3]==ore) and (localtime()[4] in minuti):
if GPIO.input(26)==1:
GPIO.output(26, GPIO.LOW)
#send_mail_to_me("ON","yeye")
print("on")
flag=1
elif (localtime()[4] not in minuti) and flag==1:
GPIO.output(26, GPIO.HIGH)
#send_mail_to_me("OFF","yoyo")
print("off")
flag=0
else:
GPIO.output(26, GPIO.HIGH)

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.

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

Categories

Resources