Raspberry Pi Python pause a loop sequence, when button pushed - python

I have a raspberry PI 2. With a relay board, what i use to for a switch sequence (like a traffic light).
I use a tool, called "webiopi" what create buttons on a website. When the button is clicked the function of the python script below is started.
What i want is to break out of the loop (or pause it) when another button is clicked. However, as long this loop is running, the tool don't look at the webpage
A kind of similar question is asked here Exiting a continuous loop in python via webiopi but this is for a single event and the solution doesn't work in my case.
Question is. How can I make this script look at a button what is clicked (can be a gpio switch as well) while the loop is running
GPIO_nek=11
GPIO_schouder=12
GPIO_rug1=8
GPIO_ONOFF=18
interval1 = 2
interval2 = 4
for x in range(0, 20):
GPIO.digitalWrite(GPIO_nek, GPIO.LOW)
time.sleep(interval1)
GPIO.digitalWrite(GPIO_schouder, GPIO.LOW)
time.sleep(interval1)
GPIO.digitalWrite(GPIO_nek, GPIO.HIGH)
time.sleep(interval1)
GPIO.digitalWrite(GPIO_rug1, GPIO.LOW)
time.sleep(interval2)
GPIO.digitalWrite(GPIO_schouder, GPIO.HIGH)
if (GPIO.digitalRead(GPIO_ONOFF) == GPIO.LOW):
GPIO.digitalWrite(GPIO_ONOFF, GPIO.HIGH)
break

When monitoring a real time event such as sensors or your button your best solution will be setting up a separate thread or process that contains nothing but an infinite loop that watches the resource and sets a flag when something interesting happens.
The example below sets up a process that automatically takes a picture on the RPI aprox. every minute.
#!/usr/bin/python
#Threading Prototype - Running a background thread that will take
# pictures at the appropriate time
#------------------------------------------------------------------
from multiprocessing import Queue
from multiprocessing import Process
from time import sleep
from datetime import datetime
from datetime import timedelta
from subprocess import call
#Doing the work ---------------------------------------------------
#Global Variables ---
messages = Queue()
start_time = datetime.now()
#Returns number of Milliseconds since start of program
def milliSinceStart():
global start_time
dt = datetime.now() - start_time
ms = (dt.days * 24 * 60 * 60 + dt.seconds) * 1000 + dt.microseconds / 1000.0
return ms
#Process Methods --------------------------------------------------
def processMessages():
print "Message Processor Launched"
while True:
print messages.get() #should halt until message in queue
sleep(0.1) #sleep for a tick just to avoid a run away process
def processPicutres():
print "Picture Taker Launched"
pictureCycleStart = milliSinceStart()
index = 0
while True:
if milliSinceStart() - pictureCycleStart > 10000: #once a minute
a = "blip" + str(index) + ".jpg"
b = "raspistill -n -t 100 -o " + a
messages.put("Click")
call ([b], shell=True)
messages.put("picture taken - " + b)
index = index + 1
pictureCycleStart = milliSinceStart()
sleep(0.1) #wait a tick -- don't hog processor time
def main():
print "MultiProcessing Prototype"
print "Launching message process"
Process(target=processMessages).start()
print "Back from launch"
print "Launching picture taking process"
Process(target=processPicutres).start()
print "Back from launch"
cycleStart = milliSinceStart()
index = 0
while True:
if milliSinceStart() - cycleStart > 1000:
messages.put("Tick " + str(index))
cycleStart = milliSinceStart()
index = index + 1
if __name__ == "__main__":
main()
The main method launches the Messaging and Picture processes and then sets up its own little infinite loop that does nothing more that display the message "Tick" every second. The picture process sets up a separate infinite loop, watching the clock and taking a picture periodically. The Message process monitors the picture process (again, an infinite loop) and when it detects that a picture has been taken, it outputs the fact to the screen.
The important part of this for your purpose is the message queue. The process queue is what is allowing the Picture and Message processes to communicate.
And because the task take place in different processes, it matters not if one process pauses as the others are always active. If you set up a button monitor process you can be checking message queue for this fact and halting your main program when the button is pressed. This pause in the main program would not effect the button process which could then pick up on the fact that the button is pressed again.

If the button is the GPIO switch as you mentioned at the end of the question, instead of the webpage button, then you can make use of an inbuilt GPIO interrupt function that saves your computer the resouces of constant polling:
import RPi.GPIO as GPIO
from threading import Event # We'll use it like time.sleep, but we can interrupt it.
GPIO_nek=11
GPIO_schouder=12
GPIO_rug1=8
GPIO_ONOFF=18
interval1 = 2
interval2 = 4
GPIO.setup(GPIO_ONOFF, GPIO.IN, pull_up_down=GPIO.PUD_UP)
done = False # loop control
timer = Event()
def quit_loop(): # Called by inbuilt threaded interrupt
global done
done = True
timer.set() # Interrupt the waiting
GPIO.add_event_detect(GPIO_ONOFF, GPIO.FALLING, callback=quit_loop, bouncetime=300) # Setup interrupt to call quit_loop
Because you're using this to break out of a loop, you want to shorten that loop to a single process:
tasks = [
(GPIO_nek, GPIO.LOW, interval1),
(GPIO_schouder, GPIO.LOW, interval1),
(GPIO_nek, GPIO.HIGH, interval1),
(GPIO_rug1, GPIO.LOW, interval2),
(GPIO_schouder, GPIO.HIGH, 0) ]
for pin, level, interval in tasks * 20: # Above you ran it 20 times, this notation keeps it in a single loop to break our o
if not done:
GPIO.digitalWrite(pin, level)
timer.wait(interval)
else:
timer.clear()
break
By using the threading Event().wait() and .set() instead of the standard time.sleep() you won't even have to wait for the sleep interval to finish.

Related

How to get out of a while loop with a specific key

I am trying to make an auto clicker but when i try to make my code exit it doesnt
here is my code
import mouse
import keyboard
import time
import os
os.system('cls')
def Config():
print("Click every")
hour = int(input("Hour: "))
minute = int(input("Minute: "))
second = int(input("Second: "))
total_time = hour*3600 + minute*60 + second
print("f6 to start and f10 to stop")
keyboard.wait('f6')
while True:
time.sleep(total_time)
mouse.click()
#def Fastest():
print(" Auto clicker!!!")
print(" By ze")
print("-------------------------")
print("Auto click on desired time or Fastest?")
choose = int(input("""
1. Config (No milliseconds)
2. Fastest
"""))
if choose == 1:
Config()
# elif choose == 2:
# Fastest()
#TODO:
# use mouse.click
# make it click with time
# make function fastest start with f1 and stops with f2
# create back up file
i tried an if statement with keyboard.is_pressed('key') thinking it would work but it doesnt my results are that the code exits (if key is pressed then exit)
You need to check if the key is pressed in your infinite loop. If it is pressed, you need to exit
while True:
time.sleep(total_time)
mouse.click()
if keyboard.is_pressed("f10"):
break
But this waits for the sleep function , so you'll need to hold f10 for total_time seconds.
You should use a loop to check for the key, rather than sleeping
import datetime
...
clicking = True
while clicking:
mouse.click()
s = datetime.datetime.now()
while ((datetime.datetime.now()-s).total_seconds() < total_time):
# This runs while the difference in time since you started the loop is less than the time you want to wait
if keyboard.is_pressed("f10"):
clicking = False
break
Use one thread to handle user input and another thread to do the clicking:
from threading import Thread
from msvcrt import getwch
import mouse
done = False
def auto_click():
print("Press \"q\" to quit")
global done
while True:
if getwch() == "q":
done = True
break
Thread( target = auto_click, daemon = True ).start()
while not done: # you can add whatever logic you want including a time gate here
mouse.click()

Execute a function every minute while not blocing hardware interrupts?

I'm making an energy meter using a Raspberry Pi, which gets its reading from a flashing LED (1000 flashes/kWh). It counts the flashes for 60 seconds through an interrupt and then it sends the data to a database. This works great, none of the flashes are missed this way, but because it just constantly checks if 60 seconds have passed it pegs the given thread to a 100% which is less than optimal for a 24/365 usecase.
Here is the important part of the code:
sampleFreqency = 60 #seconds
flashCount = 0
time1 = time.time()
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def flashCounter(self):
global flashCount
if not GPIO.input(17):
print("Light!")
flashCount = flashCount + 1
GPIO.add_event_detect(17, GPIO.BOTH, callback=flashCounter, bouncetime=50)
while True:
if time.time() > time1+sampleFreqency:
energy = flashCount #Wh
power = energy * 0.36/(sampleFreqency/10) # kW
print("Power: " + str(power) + "kW, Energy: " + str(energy) + "Wh")
logData(power, energy)
flashCount = 0
time1 = time.time()
I have tried using the threading Timer module without success, as it blocks everything else until it runs.
Turns out interrupts in fact do trigget while the program is doing time.sleep() , only my code was not functioning as I would have wanted it to. I have rewritten the ISR like this:
def flashCounter(self):
global flashCount
print("Light!")
flashCount = flashCount + 1
GPIO.add_event_detect(17, GPIO.FALLING, callback=flashCounter, bouncetime=50)
Now it works flawlessly.
The issue was that for sensing the blinking I'm using a photoresistor which has a slow reaction time, and when the interrupt triggered on the falling edge, the voltage probably has not had fallen to a level which would read as low, so the if statement did not get fulfilled.

Making a timer reset on a light

I'm very new to code and want the led.off portion to be extendable. Basically, if I hit the button again before the 10 seconds run out. So if the button is hit before lighting up, there is another 10 second wait, starting at press 2.
from time import time, sleep
from signal import pause
led = LED(18) # or whatever pin you've got it on
button = Button(23) # again, adjust as necessary
def light_off_for_10_sec():
led.off()
sleep(10)
led.on()
button.when_pressed = light_off_for_10_sec
The 10 second pause before lighting up works great, but here is what I have tried to use to stretch the led.off period.
def stretched(values):
when_pressed = 10
for value in values:
if value:
sleep(5)
pressed = time()
yield time() - pressed <= 20
led.source = stretched(button.pressed)
pause()```

GPIOZero Pi Alarm system: Need alternative for time delay

I require help with my alarm system code that I am building with GPIOZero library (my boss really likes the library). The system is meant for a laptop cart so people do not forget to close the door as there is a buzzer that rings if people leave it open for too long. So what I want it to do is really simple but it always waits for the function to finish before going to the next:
If door opens: Log time, Time Delay ,Buzzer turns on
If door is closed/closes: Buzzer is off, Log Time
Is there a way to not wait for the function to finish in the GPIOZero library. Please let me know!
from gpiozero import Button
from signal import pause
from gpiozero import Buzzer
from gpiozero import LED
from time import sleep
##### VARIABLES DEF #######
button = Button(21, pull_up=True)
buzzer = Buzzer(4)
def door_opened():
f = open("log" + '.txt', 'a')
f.write("Opened " + time.strftime("%m-%d-%Y %X"))
f.close()
print("Door Open")
sleep(100)
print("door held, alarm on")
buzzer.on
def door_closed():
f = open("log" + '.txt', 'a')
f.write(" " + time.strftime("%X"))
f.write('\n')
f.close()
print("Door Closed")
buzzer.off()
print("Buzzer off")
button.when_pressed = door_closed
button.when_released = door_opened
pause()
You will have to restructure things a little to get this done.
The idea here is that you have a pair of global variables that are updated by the GPIOZero event handlers:
door_is_open – true when the door is open
door_open_counter – counts the seconds the door has been opened (this is actually only reset to zero by the event handlers).
There's also an infinite main loop that ticks once per second which reads the door_is_open variable and increments the open counter... and finally, if the open counter reaches the threshold desired (5 seconds here because we're impatient people), it turns the buzzer on.
The door_closed handler takes care of resetting the buzzer, but that could also be done in the main loop.
I also took the liberty of refactoring the logging into a function of its own that handily also prints the same thing to the console. (As an aside, I'd suggest using ISO 8601 date formatting, but I didn't want to impose that here.)
A further refactoring might wrap this all in a neat class to avoid global variables.
(This all is dry-coded, so your mileage may vary, but the idea should work. :) )
import time
from gpiozero import Button, Buzzer
button = Button(21, pull_up=True)
buzzer = Buzzer(4)
door_is_open = False
door_open_counter = 0
def log_event(text):
print(log_line)
with open("log.txt", "a") as log_file:
log_line = "%s | %s" % (time.strftime("%m-%d-%Y %X"), text)
log_file.write(log_line + "\n")
def door_opened():
global door_is_open
global door_open_counter
log_event("Opened")
door_is_open = True
door_open_counter = 0
def door_closed():
global door_is_open
global door_open_counter
log_event("Closed")
buzzer.off()
door_is_open = False
door_open_counter = 0
button.when_pressed = door_closed
button.when_released = door_opened
while True:
time.sleep(1)
if door_is_open:
door_open_counter += 1
if door_open_counter >= 5:
buzzer.on()

Python: Run code every n seconds and restart timer on condition

This may be simpler than I think but I'd like to create timer that, upon reaching a limit (say 15 mins), some code is executed.
Meanwhile every second, I'd like to test for a condition. If the condition is met, then the timer is reset and the process begins again, otherwise the countdown continues.
If the condition is met after the countdown has reached the end, some code is executed and the timer starts counting down again.
Does this involve threading or can it be achieved with a simple time.sleep() function?
You could probably accomplish it with threading really elegantly but if you need a quick fix you could try
import time
timer = 15 * 60 # 60 seconds times 15 mins
while timer > 0:
time.sleep(0.985) # don't sleep for a full second or else you'll be off
timer -= 1
if someCondition:
timer = 15 * 60
executeCode() # called when time is zero and while loop is exited
If the whole process is as simple as you say, I would go about it like this (semi-psuedo-code):
def run_every_fifteen_minutes():
pass
def should_reset_timer():
pass
def main():
timer = 0
while True:
time.sleep(1)
timer+=1
if should_reset_timer():
timer = 0
if timer == 15*60:
run_every_fifteen_minutes()
timer = 0
Note that this won't be exactly fifteen minutes in. It might be late by a few seconds. The sleep isn't guaranteed to sleep only 1 second and the rest of the loop will take some time, too. You could add a system time compare in there if you need it to be really accurate.
Thanks for the help everyone, your answers pointed me in the right direction. In the end I came up with:
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import subprocess
GPIO.setmode(GPIO.BCM)
PIR_PIN = 4
GPIO.setup(PIR_PIN, GPIO.IN)
timer = 15 * 60 # 60 seconds times 15 mins
subprocess.call("sudo /opt/vc/bin/tvservice -o", shell=True)
try :
print "Screen Timer (CTRL+C to exit)"
time.sleep(5)
print "Ready..."
while True:
time.sleep(0.985)
# Test PIR_PIN condition
current_state = GPIO.input(PIR_PIN)
if timer > 0:
timer -= 1
if current_state: #is true
# Reset timer
timer = 15 * 60
else:
if current_state: #is true
subprocess.call("sudo /opt/vc/bin/tvservice -p", shell=True)
# Reset timer
timer = 15 * 60
else:
subprocess.call("sudo /opt/vc/bin/tvservice -o", shell=True)
except KeyboardInterrupt:
print "Quit"
GPIO.cleanup()
To put it in context, I'm using a PIR sensor to detect motion and switch on an hdmi connected monitor on a Raspberry Pi. After 15 mins of no movement I want to switch the monitor off and then if (at a later time) movement is detected, switch it back on again and restart the time.
The description sounds similar to a dead main's switch / watchdog timer. How it is implemented depends on your application: whether there is an event loop, are there blocking functions, do you need a separate process for proper isolation, etc. If no function is blocking in your code:
#!/usr/bin/env python3
import time
from time import time as timer
timeout = 900 # 15 minutes in seconds
countdown = timeout # reset the count
while True:
time.sleep(1 - timer() % 1) # lock with the timer, to avoid drift
countdown -= 1
if should_reset_count():
countdown = timeout # reset the count
if countdown <= 0: # timeout happened
countdown = timeout # reset the count
"some code is executed"
The code assumes that the sleep is never interrupted (note: before Python 3.5, the sleep may be interrupted by a signal). The code also assumes no function takes significant (around a second) time. Otherwise, you should use an explicit deadline instead (the same code structure):
deadline = timer() + timeout # reset
while True:
time.sleep(1 - timer() % 1) # lock with the timer, to avoid drift
if should_reset_count():
deadline = timer() + timeout # reset
if deadline < timer(): # timeout
deadline = timer() + timeout # reset
"some code is executed"
What is the best way to repeatedly execute a function every x seconds in Python?
How to run a function periodically in python
Maybe you should look into the Linux tool cron to schedule the execution of your script.

Categories