Raspberry Pi Interrupts Python (GPIO Library) - python

I have a little problem with my little raspberry project, I have hooked up an LCD screen and a bunch of buttons to it, from my university course about micro-controllers I've learned that interrupts always trigger, no matter where the code is (but it was an actual microprocessor written in C). I found an "equivalent" in python, GPIO library. Normally a program is in a infinite loop waiting for an interrupt.
GPIO.add_event_detect(4, GPIO.RISING, callback=interrupt, bouncetime=200)
I set them up this way, they all go to the same method and check which button was pressed and run another method that I want (e.g one is for displaying time, another for IP address etc). A thing is, one of those methods I've done is in an infinite loop that another button press should break, but from this point a method
while not GPIO.event_detected(4):
doesn't work (calling this method in any other doesn't either if its in this loop, and all other button that I have set up will not react at all too), if it was my default while loop it does tho. I don't have much experience in either micro-controllers and python, its just hobby thing at the moment. If needed I'll share my entire code but I think its quite tangled.
Ok I am providing a simplified example, same same thing happens as in my original code. After an interrupt happens, buttons will not react, doesn't matter if I use while not GPIO.event_detected(19) or GPIO.add_event_callback(26, callback=second_interrupt).
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Button 1
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Button 2
def interrupt(channel):
print "interrupt"
if channel == 26:
print "in loop"
GPIO.add_event_callback(26, callback=second_interrupt) #Trying to use this won't trigger second interrupt
while not GPIO.event_detected(19): #and this wont ever trigger
time.sleep(1)
print "inside loop"
def second_interrupt():
print "second interrupt"
GPIO.add_event_detect(26, GPIO.RISING, callback=interrupt, bouncetime=200) # add rising edge detection on a channel
GPIO.add_event_detect(19, GPIO.RISING, callback=interrupt, bouncetime=200) # add rising edge detection on a channel
while (True):
time.sleep(1)
if GPIO.event_detected(19): #this version works if I wont enter an interrupt first
second_interrupt()

There are several things going on in your code. I suspect that what's really causing the problem is that you need to exit the interrupt handler before another interrupt callback can be triggered...but there is also a confusing mix of callback-based handlers and the GPIO.event_detected method.
I think you can simplify things by performing less manipulation of your interrupt configuration. Just have a state variable that starts at 0, increment it to 1 on the first interrupt, so the next time the interrupt method is called you know it's the second interrupt. No need to try setting multiple handlers like that.
Keeping in mind that I don't actually know what you're trying to do...I imagine something like this:
import RPi.GPIO as GPIO
import time
state = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def interrupt_handler(channel):
global state
print("interrupt handler")
if channel == 19:
if state == 1:
state = 0
print("state reset by event on pin 19")
elif channel == 26:
if state == 0:
state = 1
print("state set by event on pin 26")
GPIO.add_event_detect(26, GPIO.RISING,
callback=interrupt_handler,
bouncetime=200)
GPIO.add_event_detect(19, GPIO.RISING,
callback=interrupt_handler,
bouncetime=200)
while (True):
time.sleep(0)

Related

GPIO event detect not giving output when button pressed

The following python script is supposed to wait for a button press, print a button press message, and then exit.
However, when I press the button nothing is printed. Then when I press the enter key, the script prints "Button push detected" then stops.
How might I fix this code so that 'Button push detected' is printed when I push the button?
I followed a tutorial to make this code:
#Button input detection
#by
#Start date: 11th February 2021
#End date: 11th February 2021
#Importing GPIO andtime libraries as per usual with a python script making use of RPi GPIO
import RPi.GPIO as GPIO
import time
#Callback function to print 'Button push detected' when called
def button_callback(channel):
print("Button push detected")
#Disabling annoying GPIO warnings and setting GPIO mode to board
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#GPIO event detector that detects a rising edge on pin 11
GPIO.add_event_detect(11,GPIO.FALLING,callback=button_callback)
#Command that does not stop until someone presses enter
quit = input("Press enter to quit")
#Clean up the pins used
GPIO.cleanup()
The problem is that your script needs a loop that waits until the button is pressed so that when the button press is detected, your script will be able to react to the event.
Right now your script sets the event detection but then sits waiting for input and as soon as the input is made, your script exits.
See this Raspberry Pi forum post, https://www.raspberrypi.org/forums/viewtopic.php?t=201747
which has a program for lighting an LED on and off with button presses.
However it looks to me like you would probably need to make a change to the call back function to raise an exception rather than light the LED if you want the script to end when you press the button.
See below the modified program with some additional annotation along with the LED light/unlight commented out.
However this modified program uses a busy loop that just runs continuously until the button is pressed. Using a busy loop, while it works for simple programs such as yours, is generally not a good idea.
An alternative is to use the GPIO.wait_for_edge() function that will pause the script until the button is pressed. See Raspberry Pi StackExchange - Pausing code execution till a button is pressed which removes the busy loop.
First the busy loop version.
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
btn_input = 4; # button to monitor for button presses.
LED_output = 17; # LED to light or not depending on button presses.
# GPIO btn_input set up as input.
GPIO.setup(btn_input, GPIO.IN)
# GPIO.setup(LED_output, GPIO.OUT)
# handle the button event
def buttonEventHandler_rising (pin):
# turn LED on
# GPIO.output(LED_output,True)
raise Exception('button pressed')
def buttonEventHandler_falling (pin):
# turn LED off
# GPIO.output(LED_output,False)
raise Exception('button released')
# set up the event handlers so that when there is a button press event, we
# the specified call back is invoked.
# for your purposes you may only want to detect the falling
# indicating that the button was released.
# GPIO.add_event_detect(btn_input, GPIO.RISING, callback=buttonEventHandler_rising)
GPIO.add_event_detect(btn_input, GPIO.FALLING, callback=buttonEventHandler_falling)
# we have now set our even handlers and we now need to wait until
# the event we have registered for actually happens.
# This is an infinite loop that waits for an exception to happen and
# and when the exception happens, the except of the try is triggered
# and then execution continues after the except statement.
try:
while True : pass
except:
GPIO.cleanup()
See as well the explanations as to what is happening in these forum posts
https://www.raspberrypi.org/forums/viewtopic.php?t=128510
https://www.raspberrypi.org/forums/viewtopic.php?t=141520
And this article about exceptions in python. https://realpython.com/python-exceptions/

Moisture detection not resetting

I have a friend setting up an irrigation system at his house. He is telling me it won't detect moisture after the first run-through, regardless of whether there is moisture or not. In his own words:
"I’m trying to figure out how to make my program restarts the detection process. Like say my sensor is in soil and it detects water then after a certain period I need it to run again. My problems is that when I run it again and it’s sitting in a cup of water, it’s doesn’t detect the water. I read something about maybe checking the state of the sensor but I couldn’t get anywhere.
Also would I would the delay function to continuously run it in intervals."
He sent me his code today, and I would like to help him, although I am new to Python. Here is the code.
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
#GPIO SETUP
channel = 20
GPIO.setmode(GPIO.BCM)
GPIO.setup(channel, GPIO.IN)
def callback(channel):
if GPIO.input(channel)==0:
print (" Water Detected!")
else:
print ( 'NO Water Detected!')
GPIO.add_event_detect(channel, GPIO.FALLING, bouncetime=300) # let us know when the pin goes HIGH or LOW
GPIO.add_event_callback(channel, callback) # assign function to GPIO PIN, Run function on change
# infinite loop
while True:
time.sleep(1)
import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library
from time import sleep # Import the sleep function from the time module
GPIO.setwarnings(False) # Ignore warning for now
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
GPIO.setup(3, GPIO.OUT, initial=GPIO.LOW) # Set pin 8 to be an outputpin and set initial value to low (off)
while True: # Run forever
GPIO.output(3, GPIO.HIGH) # Turn on
sleep(1) # Sleep for 1 second
GPIO.output(3, GPIO.LOW) # Turn off
sleep(1) # Sleep for 1 second

Python - scheduler blocks interrupt

I am using a system based on a raspberry to control some stuff. At the moment I am just testing it with turning a led on and off.
My plan is: Press a button to open a valve. Press the button again to close it - but if the button is not pressed, close it after a set time. My current script is as follows: (I know that this will not turn the led off on the second press)
import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library
import time,sched
s = sched.scheduler(time.time, time.sleep)
def button_callback(channel):
print("Button was pushed!")
print(time.time())
GPIO.output(18,GPIO.HIGH)
s.enter(10, 1, turnoff,argument='')
s.run()
def turnoff():
print "LED off"
print(time.time())
GPIO.output(18,GPIO.LOW)
btpin=22
ledpin=18
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(btpin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(ledpin,GPIO.OUT)
GPIO.add_event_detect(btpin,GPIO.RISING,callback=button_callback)
message = input("Press enter to quit\n\n")
GPIO.cleanup()
If I press the button and then leaves things alone, the led will turn off after 10 secs. But, if I press the button again immideately, nothing happens before the scheduler has finished, then a new press is registered. I had expected that the scheduler was spun of in the background so that when I pressed the button again, the callback would have ran again so I would have gotten the "Button was pushed" message (and everything happening afterwards would not have had any effect as GPIO 18 already was high and the scheduled call to turnoff would have happened after turnoff already had run.
Is it possible to use the sched library to do what I want or do I have to use some other techniques? I know I can either do it the simple way, by looping looking for a pressed button rather than registering a callback, or probably a more complicated way by creating a new thread that will pull the GPIO down after a given time - but is there something I have not understood in sched or is there some other library that gives me what I want - a way to tell python do do something a bit in the future without interfering with what else is going on.
(I do not need a very accurate timing - and also, this is just a part of what I intend to make a more complex control system, I manage to do exactly what I want using an arduino, but that will limit the further development)
Thanks to the tip from #stovfl, I rewrote the first part of my code:
import time,threading
def button_callback(channel):
pin=18
print("Button was pushed!")
print(time.time())
GPIO.output(pin,GPIO.HIGH)
t = threading.Timer(10.0, turnoff)
t.start()
and it works just like I wanted

Raspberry Pi: Python try/except loop

I've just picked up my first Raspberry Pi and 2 channel relay. I'm trying to learn how to code in Python so I figured a Pi to play with would be a good starting point. I have a question regarding the timing of my relays via the GPIO pins.
Firstly though, I'm using Raspbian Pixel and am editing my scripts via Gedit. Please see below for the script I have so far:
# !/usr/bin/python
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
# init list with pin numbers
pinList = [14]
# loop through pins and set mode and state to 'high'
for i in pinList:
GPIO.setup(i, GPIO.OUT)
GPIO.output(i, GPIO.HIGH)
# time to sleep between operations in the main loop
SleepTimeL = 60 #1 minute
# main loop
try:
GPIO.output(14, GPIO.LOW)
print "open"
time.sleep(SleepTimeL);
GPIO.cleanup()
#Reset GPIO settings
GPIO.cleanup()
# end program cleanly
except KeyboardInterrupt:
print "done"
Now that works pretty well, it opens the relay attached to pin 14 no problem. It cycles through 60 seconds as requested and then ends the program. Once the program has ended, the GPIO settings are reset and the relay closes, but that's the end of the program and it's where my problem starts.
What I want this script to do is open the relay for 60 seconds, then close it for 180 seconds. Once it reaches 180 seconds it must re-run the 'try' statement and open the relay for another 60 seconds and so on. In short, I would like an infinite loop that can only be interrupted by cancelling the script from running. I am unsure of how to tell Python to close the relay for 180 seconds and then re-run the try statement, or how to make it an infinite loop for that matter.
I'd really appreciate some input from the community. Any feedback or assistance is greatly appreciated. Thanks All.
Just use a while True loop, something like:
# main loop
while True:
GPIO.output(14, GPIO.LOW)
print "open"
time.sleep(SleepTimeL);
GPIO.cleanup()
print "done"
I agree with reptilicus, you just need to add a while loop. "while True" will run forever, until you hit ctrl-C to break. You just need to add a second delay to hold the relay off for 180 seconds before looping. You can create a different sleep time variable, or I just multiply the one you have by 2.
# main loop
while True:
try:
GPIO.output(14, GPIO.LOW)
print "open"
time.sleep(SleepTimeL);
GPIO.cleanup()
#Reset GPIO settings
GPIO.cleanup()
time.sleep(2*SleepTimeL)
# end program cleanly
except KeyboardInterrupt:
print "done"

Python Script - Nesting an if / else under an if inside a while True: on - Raspberry Pi

Python is my first interaction with a programming language, I started learning yesterday.
I'm trying to control LEDs from physical momentary switches through a Raspberry Pi and it's GPIO pins.
I have wired the switches and have a piece of code that turns each of 4 LEDs on or off.
I have a 5th switch to turn on or off all 4 LEDs at once depending on the state of an unwired pin.
The code for each LED looks like this:
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(4,GPIO.OUT)
GPIO.output(4,0)
GPIO.setup(18,GPIO.OUT)
GPIO.output(18,0)
while True:
input_state = GPIO.input(10)
if input_state == False:
GPIO.output(4, not GPIO.input(4))
time.sleep(0.1)
GPIO.output(18, GPIO.input(4))
time.sleep(0.4)
Where pin 10 is an input wired to my switch, 4 is the pin that gets turned on and powers the LED and pin 18 is a pin I use to turn on/off all LEDs at the same time with another switch. I have 4 versions of this script, 1 for each LED and just the pins used are changed.
The code to turn on/off all pins at the same time looks like this:
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
chan_list = (4,17,22,27)
GPIO.setup(chan_list,GPIO.OUT)
GPIO.output(chan_list,0)
GPIO.setup(18,GPIO.OUT)
GPIO.output(18,0)
while True:
input_state = GPIO.input(26)
if input_state == False:
chan_list = (4,17,22,27)
GPIO.output(18, not GPIO.input(18))
time.sleep(0.1)
GPIO.output(chan_list, GPIO.input(18))
time.sleep(0.4)
These codes work really well. I have them all setup to launch on startup as background processes.
I want to use pin 18 to turn on an indicator LED that turns on if any of the four LEDs are turned on and that stays on if any LEDs are turned off unless all 4 of the LEDs are turned off. This works fine using the all on/off button. The problem comes up when I use the individual LED buttons to turn off individual LEDs. In the current setup as soon as I turn off any individual LED pin 18 is turned off.
I'm trying to find a code solution to get around this issue so that pin 18 (the indicator LED) is on when any of the 4 LEDs are on and is only off if all 4 LEDs are off.
I was thinking some kind of nested series of if / else: conditions but my limited knowledge is stopping my success.
An example piece of code that I have tried is this:
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(4,GPIO.OUT)
GPIO.output(4,0)
GPIO.setup(18,GPIO.OUT)
GPIO.output(18,0)
while True:
input_state = GPIO.input(10)
if input_state == False:
if GPIO.output(17) == True:
GPIO.output(4, not GPIO.input(4))
else:
if GPIO.output(22) == True:
GPIO.output(4, not GPIO.input(4))
else:
if GPIO.output(27) == True:
GPIO.output(4, not GPIO.input(4))
else:
GPIO.output(4, not GPIO.input(4))
time.sleep(0.1)
GPIO.output(18, GPIO.input(4))
time.sleep(0.4)
This gives an error:
Traceback (most recent call last):
File "pin10test.py", line 20, in <module>
if GPIO.output(17) == True:
TypeError: function takes exactly 2 arguments (1 given)
My intention is that when a button press is detected the it will go:
If pin 17 is on, toggle pin 4. If not go to next check.
If pin 22 is on, toggle pin 4. If not go to next check.
If pin 27 is on, toggle pin 4. If not go to final else:
toggle pin 4, sleep 0.1, set pin 18 to whatever pin 4 was just set to, sleep 0.4.
Then I'd adjust this piece of script into the script for each button.
This way pin 18 only changes state to off if all pins are off.
Any help in getting this script right is appreciated.
Any ideas or tips on how to improve this is also appreciated.
input_state = GPIO.input(10)
and
if input_state == False:
should be at the same level of indentation, which is exactly what the error message is saying. There is no reason to indent the second line (since the previous one doesn't open a new block, so indenting it is an error).

Categories

Resources