Execute a function every minute while not blocing hardware interrupts? - python

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.

Related

I want to know if I any mistakes?

I have a problem with the Jsn-sr04t water proof ultrasonic sensor, knowing that the connection in the raspberry pi zero W1 is well made becouse it is activated but the readings of this are totally wrong, the pins were entered correctly but this one seems to have a data entry error. I tried several programs in python that were used on this same sensor model
PD: I'm aware that this works, because I tested it on an arduino mega and the sensor worked correctly
import RPi.GPIO as GPIO
import os
import time
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO_TRIGGER = 10
GPIO_ECHO = 8
TRIGGER_TIME = 0.00001
MAX_TIME = 0.004 # max time waiting for response in case something is missed
GPIO.setup(GPIO_TRIGGER, GPIO.OUT) # Trigger
GPIO.setup(GPIO_ECHO, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Echo
GPIO.output(GPIO_TRIGGER, False)
def measure():
# Pulse the trigger/echo line to initiate a measurement
GPIO.output(GPIO_TRIGGER, True)
time.sleep(TRIGGER_TIME)
GPIO.output(GPIO_TRIGGER, False)
# ensure start time is set in case of very quick return
start = time.time()
timeout = start + MAX_TIME
while GPIO.input(GPIO_ECHO) == 0 and start <= timeout:
start = time.time()
if(start > timeout):
return -1
stop = time.time()
timeout = stop + MAX_TIME
# Wait for end of echo response
while GPIO.input(GPIO_ECHO) == 1 and stop <= timeout:
stop = time.time()
if(stop <= timeout):
elapsed = stop-start
distance = float(elapsed * 34300)/2.0
else:
return -1
return distance
if __name__ == '__main__':
try:
while True:
distance = measure()
if(distance > -1):
print("Measured Distance = %.1f cm" % distance)
else:
print("#")
time.sleep(0.5)
except KeyboardInterrupt:
print("Measurement stopped by User")
GPIO.cleanup()
constantly OUTPUT: "#"
Seems like an issue with the GPIO settings or a timing issue as the same setup works with an Arduino.
Try with higher trigger on time (maybe 20us)
After elapsed calculation, print the elapsed time on the screen and check the actual value
If it still doesn't work, then you can also checkout the alternate output options like serial UART, PWM, etc. You can get more details here -
https://tutorials.probots.co.in/communicating-with-a-waterproof-ultrasonic-sensor-aj-sr04m-jsn-sr04t/

Blinking led script with GPIO LEDS

I've been starting to learn Python on my raspberry pi.
I have 5 LEDs on GPIO pin 8, 10, 12, 16 and 18.
I have another code working, but I was trying to make it more compact. This one is not working but you can probably guess what I was trying to do.
I'm trying to make each led blink one by one by cycling through the chan_list list, but I'm stuck. How do I repeat the function for each entry of the list?
import RPi.GPIO as GPIO
from time import sleep
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
chan_list = [8,10,12,16,18]
GPIO.setup(chan_list, GPIO.OUT, initial=0)
delay = 50
#delay = float(input("50-1000ms"))
delay = delay / 1000
led = 0
while True:
def blink():
GPIO.output(chan_list, 1)
sleep(delay)
GPIO.output(chan_list, 0)
sleep(delay)
blink()
You can use the itertools.cycle() function to create an iterator that will — wait for it! — cycle through the values is the list.
This means something like this ought to work (I can't test it). Note I also moved the definition of blink() out of the loop — there's no need to do it more than once.
from itertools import cycle # ADDED.
from time import sleep
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
chan_list = [8,10,12,16,18]
chan_cycler = cycle(chan_list) # ADDED.
GPIO.setup(chan_list, GPIO.OUT, initial=0)
delay = 50
#delay = float(input("50-1000ms"))
delay = delay / 1000
delay = 1
led = 0
def blink():
chan = next(chan_cycler) # ADDED.
GPIO.output(chan, GPIO.HIGH)
sleep(delay)
GPIO.output(chan, GPIO.LOW)
sleep(delay)
while True:
blink()
def blink(): should be defined near the top, not inside of a loop. The GPIO.output() function takes a single pin number as its input, not a list of pins. The same goes for GPIO.setup(). To make things a bit easier, get rid of the function. You don't need it in this specific case. You should use a for loop to go over each element and call GPIO.setup() on it. Then, inside of the while loop, you need another for loop which goes over each of the elements in chan_list and turns them on and off. i don't have a Raspberry Pi on me right now, but I'd bet this would get the job done:
import RPi.GPIO as GPIO
from time import sleep
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
chan_list = [8,10,12,16,18]
for pin_number in chan_list:
GPIO.setup(pin_number, GPIO.OUT, initial=0)
delay = 50
#delay = float(input("50-1000ms"))
delay = delay / 1000
led = 0
while True:
for pin_number in chan_list:
GPIO.output(pin_number, 1)
sleep(delay)
GPIO.output(pin_number, 0)
sleep(delay)

QTR-8RC Reflectance Sensor Array not returning data

I purchased a QTR-8RC Reflectance Sensor Array and now trying to configure it with Python. I am trying to determine the rate of decay of the voltage that is being read by my receivers (phototransistors) so that I know when a line is being detected. I don't know why my Python code isn't returning anything. Not even a warning statement. Additional information includes that by default the GPIO is an output and the LEDs are ON. Any help is appreciated!
import RPi.GPIO as GPIO
from time import sleep
def Read():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(5, GPIO.OUT)
sleep(0.01)
count = 0
GPIO.setup(5, GPIO.IN)
while GPIO.input(5) == True:
count = count + 1
return count
while True:
Read()
print(Read())
sleep(1)
I don't have any knowledge about QTR-8RC Reflectance Sensor Array.
But looking at your python code, the problem might be with
while GPIO.input(5) == True
If the value is always true then the line with the return statement is never reached.
You could use print statement after while block to check that. Something like
while GPIO.input(5) == True:
count = count + 1
print "while loop has ended"
return count
Instead of running the program continuously run it for some time and check the outputs.
And may be you need to increase the sleep time to see the output practically.
import RPi.GPIO as GPIO
from time import sleep
def Read():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(5, GPIO.OUT)
sleep(0.01)
count = 0
GPIO.setup(5, GPIO.IN)
while GPIO.input(5) == True:
count = count + 1
print "count :", count
return count
for _ in range(100):
print(Read())
sleep(2)

Raspberry Pi Python pause a loop sequence, when button pushed

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.

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