Set handler for GPIO state change using python signal module - python

I want to detect change in gpio input of raspberry pi and set handler using signal module of python. I am new to signal module and I can't understand how to use it. I am using this code now:
import RPi.GPIO as GPIO
import time
from datetime import datetime
import picamera
i=0
j=0
camera= picamera.PiCamera()
camera.resolution = (640, 480)
# handle the button event
def buttonEventHandler (pin):
global j
j+=1
#camera.close()
print "handling button event"
print("pressed",str(datetime.now()))
time.sleep(4)
camera.capture( 'clicked%02d.jpg' %j )
#camera.close()
def main():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(2,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(2,GPIO.FALLING)
GPIO.add_event_callback(2,buttonEventHandler)
# RPIO.add_interrupt_callback(2,buttonEventHandler,falling,RPIO.PUD_UP,False,None)
while True:
global i
print "Hello world! {0}".format(i)
i=i+1
time.sleep(5)
# if(GPIO.input(2)==GPIO.LOW):
# GPIO.cleanup()
if __name__=="__main__":
main()

I just changed code in a different manner tough you are free to implement same using SIGNAL module.You can start new thread and poll or register call back event their, by using following code and write whatever your functional logic in it's run() method.
import threading
import RPi.GPIO as GPIO
import time
import time
from datetime import datetime
import picamera
i=0
j=0
camera= picamera.PiCamera()
camera.resolution = (640, 480)
PIN = 2
class GPIOThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
if GPIO.input(PIN) == False: # adjust this statement as per your pin status i.e HIGH/LOW
global j
j+=1
#camera.close()
print "handling button event"
print("pressed",str(datetime.now()))
time.sleep(4)
camera.capture( 'clicked%02d.jpg' %j )
def main():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(PIN,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(PIN,GPIO.FALLING)
gpio_thread = GPIOThread()
gpio_thread.start()
while True:
global i
print "Hello world! {0}".format(i)
i=i+1
time.sleep(5)
if __name__=="__main__":
main()
The above code will iterate until PIN input goes high, so once PIN goes high the condition in while loop inside run method breaks and picture is captured.
So, in order to call above thread do this.
gpio_thread = GPIOThread()
gpio_thread.start()
this will call the thread constructor init and will initialize the variable inside constructor if any, and execute the run method.
You can also call join() method , to wait until thread completes it's execution.
gpio_thread.join()
This always works for me, so Cheers!!

Related

Running a python script at at system boot as a daemon

I am attempting to create a daemon that will execute my script at boot.
using this code as my template Running a python script
my python script works interactively when a user is logged in.
def wait_for_network():
while os.system("ping -c 1 8.8.8.8") != 0:
time.sleep(1)
return
from getmac import get_mac_address
from datetime import datetime
import RPi.GPIO as GPIO
import os
import time
import random
import RPi.GPIO as GPIO
import requests
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(5, GPIO.IN)
GPIO.setup(6, GPIO.IN)
GPIO.setup(23, GPIO.IN)
GPIO.setup(24, GPIO.IN)
GPIO.setup(14, GPIO.IN)
eth_mac = get_mac_address()
#print(eth_mac)
API_ENDPOINT = "https://xxxxxx.com/handlers/receiveStatus.ashx"
CUSTOMER_KEY = "1234567890"
# Define a callback function that will be called by the GPIO
# event system:
def onButton(channel):
if channel == 14:
dt_string = (datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
data = {'ID':CUSTOMER_KEY,
'UUID':str(eth_mac),
'DT':dt_string,
'A':str(GPIO.input(6)),
'B':str(GPIO.input(24)),
'C':str(GPIO.input(23)),
'D':str(GPIO.input(5))
}
r = requests.post(url = API_ENDPOINT, data = data)
#print r.text
#print data
GPIO.add_event_detect(14, GPIO.RISING, callback=onButton, bouncetime=20)
#input()
My question is this - the #input() do i need it when running as a daemon?
With it, the script runs until the users presses ctrl-c to break out of.
When I comment it out, the script runs once then returns to the prompt.
The GPIO is making a thread and the main thread needs to wait for it. That was done with the input(). What you can do instead is to make a loop that sleeps instead in place of the input().
while True:
time.sleep(1)
That will hold the process from exiting until a ctrl c happens.

I am having trouble adding timer to my code

I am working on this project with a motion sensor in which I would like to have the monitor turned off when there is no motion after a certain amount of time has passed. But every time there is a motion I would like the timer to reset.
I have the code working for turning the monitor on and off with motion, but how do I add the timer?
Any help will be appreciated. My code:
from gpiozero import MotionSensor
import time
from subprocess import call
pir = MotionSensor(4)
while True:
pir.wait_for_motion()
print("Screen On")
call(["/usr/bin/vcgencmd", "display_power", "1"])
time.sleep(30)
pir.wait_for_no_motion()
print("Screen Off")
call(["/usr/bin/vcgencmd", "display_power", "0"])
time.sleep(1)
As well as wait_for_motion(), gpiozero also provides a variable, motion_detected. The code below sets a variable, startpoint to the current time in seconds since 1/1/1970. It then starts a loop which:
Checks if motion is detected - if so, sets the startpoint variable back to the current time (which, of course, will be different to what it was previously) and turns on the display.
Checks if the startpoint variable is more than 30 seconds before the current time. Because every motion detection resets this variable, we know that there must have been at least 30 seconds since the last motion detection. If so, turns off the display.
startpoint = time.time()
while True:
if pir.motion_detected:
startpoint = time.time()
call(["/usr/bin/vcgencmd", "display_power", "1"])
print("Display on")
elif time.time() > (startpoint+30):
call(["/usr/bin/vcgencmd", "display_power", "0"])
print("Display off")
You could also use threading for this.
from enum import Enum
from gpiozero import MotionSensor
from subprocess import call
from threading import Timer
from time import sleep
from typing import Optional
pir = MotionSensor(4)
timer: Optional[Timer] = None
class MonitorState(Enum):
ON = "1"
OFF = "0"
def set_monitor_state(state: str):
call(["/usr/bin/vcgencmd", "display_power", state.value])
def start_timer():
global timer
if timer:
timer.cancel()
timer = Timer(30, set_monitor_state, (MonitorState.OFF,))
timer.start()
start_timer()
while True:
pir.wait_for_motion()
start_timer()
set_monitor_state(MonitorState.ON)
I'm not sure if the Timer actually counts as being done when the callback returns or before that. In the first case you could run into troubles when the set_monitor_state(MonitorState.ON) get called while the timer runs it's callback on another thread. You might want to use locking in this case.

Simulate "button pressed" an rise an event in gpiozero

I try to develop some code on a machine without GPIO. As GPIO library I selected a gpiozero to be able to write my code without access to gpio of raspberry pi.
My problem, I cant get ride of .when_pressed event in the code.
I simulate state change of the button, but the function is not called.
Device.pin_factory = MockFactory()
def interrupt_Event(channel):
print("%s puted in the queue", channel)
InputPin.Device.pin_factory.pin(channel)
InputPin.when_pressed = interrupt_Event
def main():
try:
while True:
time.sleep(1)
InputPins[channel].pull=drive_high()
time.sleep(0.1)
print("State CHANNEL %s" % channel)
print(InputPins[channel].state)
InputPins[channel].drive_low()
Till now I have no Idea what is wrong.
when_pressed function should not have arguments (see 2.7 in https://gpiozero.readthedocs.io/en/stable/recipes.html).
You could define the callback using a loop :Creating functions in a loop
(use channel=channel to force early binding of channel value as in example below)
for channel in channels:
def onpress(channel=channel):
print("%s puted in the queue", channel)
InputPins[channel].when_pressed = onpress
I am not convinced that you are using drive_high and drive_low to simulate the button pushing.
I have a almost identical problem. using Mock pins to develop a Pi program on windows, I find that the callback routines are not called.
from gpiozero.pins.mock import MockFactory
from gpiozero import Device, Button, LED
from time import sleep
Device.pin_factory = MockFactory() # set default pin
factory
btn = Button(16)
# Get a reference to mock pin 16 (used by the button)
btn_pin = Device.pin_factory.pin(16)
def pressed(): # callback
print('pressed')
def released(): # callback
print('released')
btn.when_pressed = pressed
btn.when_released = released # callback routine
for i in range(3): # now try to signal sensor
print('pushing the button')
btn_pin.drive_high
sleep(0.1)
btn_pin.drive_low
sleep(0.2)
The output has no callbacks, just
pushing the button
pushing the button
pushing the button
>>>

function called twice with urllib.urlopen

I have a small piece of code detecting event on a specific GPIO of my raspberry. When the event is detected, a function named increase_counter is called, if inside the function i only keep the "print('Flash ... ')" i only have one print per event,but if i call a url with urllib2 the whole increase_counter is executed twice, so i will have 2 flashes counted.
import RPi.GPIO as GPIO
import time
import urllib2,json
def increase_counter(ch):
print('Flash ... ')
requete='http://192.168.1.20:8080/json.htm?type=command&param=udevice&idx=52&svalue=1'
response=urllib2.urlopen(requete)
data = json.loads(response.read())
print data
IRPIN = 4
GPIO.setmode(GPIO.BCM) #GPIO SETUP
GPIO.setup(IRPIN,GPIO.IN) #GPIO SETUP
GPIO.add_event_detect(IRPIN, GPIO.FALLING, callback=increase_counter, bouncetime=3) #Calling event when flash is detected
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print(' Stopped !!!')
except:
print(' Stopped !!!')
raise
finally:
GPIO.remove_event_detect(IRPIN)
GPIO.cleanup(IRPIN) # GPIO cleanup

Async / Multi-Threading with blinker

I have a raspberry pi which I have hooked up with a 4 button keypad. Using the signal stuff from blinker I hooked it up to run some methods.
#sender
while True:
if buttonIsDown == True: signal.send()
#reciever
#signal.connect
def sayHI():
print("1")
time.sleep(10)
print("2")
This works fine, however when I push the button for the second time (Within 10 seconds of the previous button press) it does not fire the method as the thread is paused in the time.sleep(10).
How can I get it to fire the method again while the it is still paused(possibly in another thread)
It is an old question, but still it may be useful for someone else.
You can start a new thread every time the signal is emitted, in that way you will be able to catch all the events as soon as they happen. Remember that in your code, since you have a while True, the signal is never connected to the function, you should have defined them in the opposite order.
Here is a working example, based on your code:
import threading
from blinker import signal
from time import sleep
custom_signal = signal(name='custom')
#custom_signal.connect
def slot(sender):
def say_hello():
print("1")
sleep(10)
print("2")
threading.Thread(target=say_hello).start()
while True:
value = int(input('Press 1 to continue: '))
if value == 1:
custom_signal.send()
else:
break

Categories

Resources