alexcroox
Posts: 18
Joined: Sun Jun 02, 2013 10:57 pm

Multiple GPIO.add_event_detect, one callback function

Wed Jul 24, 2013 10:44 pm

I have several lever switches that represent scores. I want a single callback function when pressed that acts on the value of that lever. I have this working except it seems to call the function twice, I think I'm failing to understand callback functions correctly.

Code: Select all

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    
def buttonPressed(value):
    print('Scored! %s'%value)
    
GPIO.add_event_detect(17, GPIO.FALLING, callback=lambda x: buttonPressed(50), bouncetime=2000)
GPIO.add_event_detect(27, GPIO.FALLING, callback=lambda x: buttonPressed(150), bouncetime=2000)
When I push the switch once on channel 17 I get outputted:

Code: Select all

Scored! 50
 Scored! 50
Note the extra space on the second line, not sure if that has any importance to the issue.

How can I make sure it only gets called once when the lever is pressed?

PiGraham
Posts: 3610
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Multiple GPIO.add_event_detect, one callback function

Wed Jul 24, 2013 10:55 pm

Maybe your switches bounce. Is it any different if you double the bounce value, or double it again?

alexcroox
Posts: 18
Joined: Sun Jun 02, 2013 10:57 pm

Re: Multiple GPIO.add_event_detect, one callback function

Wed Jul 24, 2013 11:09 pm

I thought originally it might be bouncing but a bounce time of 2 seconds is pretty high, plus it only double outputs once, if I try to press it multiple times in 2 seconds it will only output that duplicate once until the debounce time is up.

alexcroox
Posts: 18
Joined: Sun Jun 02, 2013 10:57 pm

Re: Multiple GPIO.add_event_detect, one callback function

Thu Jul 25, 2013 7:59 am

I added a print(time.time()) to the function to work out how close they were being called...at the exact same time it turns out

Code: Select all

def buttonPressed(value):
    print('Scored! %s'%value)
    print(time.time())

Scored! 50
1374739140.93
 Scored! 50
1374739140.93
If I remove the second add_event_detect for the other channel it stops firing twice...

User avatar
alexeames
Forum Moderator
Forum Moderator
Posts: 2869
Joined: Sat Mar 03, 2012 11:57 am
Location: UK
Contact: Website

Re: Multiple GPIO.add_event_detect, one callback function

Thu Jul 25, 2013 6:59 pm

how is your button connected?

If you've got it pulled down to begin with and connecting to 3V3 when you press, it's actually detecting when you release the button isn't it? Shouldn't make much difference though. Have you tried a bouncetime of about 300? There were some bugs with long bouncetimes in a previous version of RPi.GPIO.

Do you have the latest version? 0.5.3a?
Alex Eames RasPi.TV, RasP.iO

PiGraham
Posts: 3610
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Multiple GPIO.add_event_detect, one callback function

Thu Jul 25, 2013 8:12 pm

alexcroox wrote:I have several lever switches that represent scores. I want a single callback function when pressed that acts on the value of that lever. I have this working except it seems to call the function twice, I think I'm failing to understand callback functions correctly.

Code: Select all

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    
def buttonPressed(value):
    print('Scored! %s'%value)
    
GPIO.add_event_detect(17, GPIO.FALLING, callback=lambda x: buttonPressed(50), bouncetime=2000)
GPIO.add_event_detect(27, GPIO.FALLING, callback=lambda x: buttonPressed(150), bouncetime=2000)
When I push the switch once on channel 17 I get outputted:

Code: Select all

Scored! 50
 Scored! 50
Note the extra space on the second line, not sure if that has any importance to the issue.

How can I make sure it only gets called once when the lever is pressed?
I'm not very familiar with the intricacies of python, but the way you are registering a callback function with a parameter looks odd to me. The documentation indicates that the callback is a function with one parameter that is the channel number that initiated the event. You seem to be forcing another parameter (onto the call stack?)

Why not something like this:

Code: Select all

def buttonPressed(channel):
    if(channel==17):
        print('Scored! 50')
    elif(channel==27):
        print('Scored! 150')
    
    
GPIO.add_event_detect(17, GPIO.FALLING, buttonPressed, bouncetime=2000)
GPIO.add_event_detect(27, GPIO.FALLING, buttonPressed, bouncetime=2000)
[edited for '==']
Last edited by PiGraham on Fri Jul 26, 2013 7:41 am, edited 1 time in total.

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: Multiple GPIO.add_event_detect, one callback function

Fri Jul 26, 2013 7:13 am

And remember to substitute those '=' for '==' in the comparison.

PiGraham
Posts: 3610
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Multiple GPIO.add_event_detect, one callback function

Fri Jul 26, 2013 7:43 am

timhoffman wrote:And remember to substitute those '=' for '==' in the comparison.
Thanks.
I'm very used to == in c/c++ :oops:

none
Posts: 5
Joined: Sat Dec 01, 2012 5:50 pm

Re: Multiple GPIO.add_event_detect, one callback function

Sat Sep 21, 2013 12:52 pm

I also have the issue of duplicate callback execution when using subprocess.call inside the callback (-> thread), which is the main purpose of my python script (running/stopping the camera apps by pushing a button via GPIO).

I'm running Raspbian 5/2013 kernel 3.6.11+
('GPIO.RPI_REVISION: ', 2)
('GPIO.VERSION: ', '0.5.3a')

This also caused running raspistill twice, resulting in mmal errors. Also, somewhere during this, the camera LED now stays on and raspistill run manually runs indefinitely, so I guess I have caused something to crash..

But the question is: how to prevent running the system call twice?

Code: Select all

# shebang
# encoding

"""
Python script for Raspberry Pi with Camera

Start/Stop video recording via GPIO on pin header P1 (2x13)
Using GPIO 24 and 25 with internal pull-up resistors next to ground and detecting a falling edge.
Calling the camera apps.
Tested with Raspbian 2013/5


Links
http://www.raspberrypi.org/
http://elinux.org/Rpi_Camera_Module
http://elinux.org/Rpi_Low-level_peripherals#Python
http://code.google.com/p/raspberry-gpio-python/wiki/Inputs

P1 Pinout (incomplete, no warranty, check board revision)
        ----------+   <--- Raspberry Pi board corner
                  |
    3V3  1    2 5V
         3    4 5V
         5    6 GND
GPIO_4   7    8 GPIO_14 (TXD)
    GND  9   10 GPIO_15 (RXD)
GPIO_17 11   12 GPIO_18 (PCM_CLK)
        13   14 GND
GPIO_22 15   16 GPIO_23
    3V3 17   18 GPIO_24
GPIO_10 19   20 GND
GPIO_9  21   22 GPIO_25
GPIO_11 23   24 GPIO_8 (CE0)
    GND 25   26 GPIO_7 (CE1)

"""


print('start of script...')

import os
import sys
import time
import datetime
import subprocess


import RPi.GPIO as GPIO

print( "GPIO.RPI_REVISION: ", GPIO.RPI_REVISION)
print( "GPIO.VERSION: ", GPIO.VERSION)



def timestamp():
  """
  Return current date/time as string.
  """
  return( datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%f") )


def snap(channel):
  """
  Take a single picture and save to disk.
  Used as callback in GPIO event.
  """
  print("snap ({}) ..".format(channel))
  subprocess.call( ["raspistill --nopreview --timeout 100 --quality 85 --output {}.jpg".format(timestamp())], shell=True )


vid_toggle = False

def vid(c):
  """
  Start recording video until stopped.
  Used as callback in GPIO event.
  """
  if vid_toggle:
    vid_toggle = False
    print("snap..")
    subprocess.call( ["raspivid --nopreview --output {}.h264".format(timestamp())], shell=True )
  else:
    vid_toggle = True
    # http://www.raspberrypi.org/phpBB3/viewtopic.php?f=32&t=44672&p=355140
    subprocess.call( ["pkill raspivid"], shell=True )
  
  

GPIO.setmode(GPIO.BCM)

button = 24
GPIO.setup(button, GPIO.IN, pull_up_down=GPIO.PUD_UP)


stop = 25
#GPIO.setup(led, GPIO.OUT)
GPIO.setup(stop, GPIO.IN, pull_up_down=GPIO.PUD_UP)



  
'''
button_last = GPIO.input(button)
while True:  
  button_now = GPIO.input(button)
  if button_now != button_last and button_now == GPIO.LOW:
    snap()
  button_last = button_now
  time.sleep(0.01)
'''

'''
while True: 
  GPIO.wait_for_edge(button, GPIO.RISING)
  snap()
'''


GPIO.add_event_detect(button, GPIO.FALLING, callback=snap, bouncetime=300)
GPIO.add_event_detect(stop, GPIO.FALLING, bouncetime=300)

while True:
  try:
    time.sleep(1.0)
  except KeyboardInterrupt:
    print("except..")
    break
  
  if GPIO.event_detected(stop):
    print('stopping')
    break

    
GPIO.remove_event_detect(button)
GPIO.remove_event_detect(stop)

GPIO.cleanup()

print('end of script.')

User avatar
paddyg
Posts: 2360
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Multiple GPIO.add_event_detect, one callback function

Sat Sep 21, 2013 7:46 pm

I suspect it's not the intended behaviour of this system, as the event detection appears to pass the pin number... However I tried all ways to get this to work and in the end the only thing was to have a different function for each pin! Essentially they were all the same function but different names. I was doing 'equivalent analogue' input by timing charge of capacitors through potentiometers using several pins. After detecting a voltage rise a function reset the pin but with one function they all got reset at the same time.

What would be really useful is if some other parameter could be passed to the function, such as a pointer to some object that needed to be updated when the event is detected. The only way I could find of doing that was including it as a global variable in the function. Horrible.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

none
Posts: 5
Joined: Sat Dec 01, 2012 5:50 pm

Re: Multiple GPIO.add_event_detect, one callback function

Sun Sep 22, 2013 2:02 pm

This forum software sucks, I just lost a reply because of session-expire.

The module is buggy, the error occurs also when not using subprocess, the second call-back triggers on rising edge.
Bouncing is probably not a problem, even with debounce disabled I repeatably get two calls.

Here's something similar to my above snippet
https://github.com/raspitv/RasPiCamcord ... corder2.py

none
Posts: 5
Joined: Sat Dec 01, 2012 5:50 pm

Re: Multiple GPIO.add_event_detect, one callback function

Sun Sep 22, 2013 3:08 pm

Trying with RPIO
http://pythonhosted.org/RPIO/rpio_py.ht ... put-output
Uses GPIO.BCM.

Similar, but not identical. add_event_detect() is add_interrupt_callback():

Code: Select all

GPIO.add_interrupt_callback(button, edge='falling', debounce_timeout_ms=100)
TypeError: add_interrupt_callback() takes at least 2 arguments (3 given)
The pullup must be specified even when the pin has been configured previously, otherwise it'll float and trigger randomly because of noise (detect mains frequency and you have a touch sensor..).

Seemed to work better, but now it's crashed..

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Multiple GPIO.add_event_detect, one callback function

Wed Sep 25, 2013 4:31 pm

I have exactly the same problem, multiple instances of the call_back, regardless of the debounce in software, or hardware.
If I don't use the threading, it works fine.
I suspect that there is an issue with the call_back thread that was introduced with an update, because it worked before.

chu_man_fu
Posts: 5
Joined: Fri Nov 23, 2012 10:08 am

Re: Multiple GPIO.add_event_detect, one callback function

Tue Oct 22, 2013 7:21 am

I have had the same issue.
At first I only had 1 input (button) setup and I was getting multiple events for the one action (button press). Adding debounce didn't work so I added a capacitor. This worked but then when I setup a second input (another button) I get multiple events for the 1 button press.

I know I am repeating what other people have posted but it's nice to know you're not totally alone when software/hardware goes wrong.

Has anyone got any further with finding a fix for this?
Did anyone try RPIO and did it fix the issue?

User avatar
paddyg
Posts: 2360
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Multiple GPIO.add_event_detect, one callback function

Tue Oct 22, 2013 8:24 am

My 'fix' above worked ok for me. As I said, I had to have distinct callback functions for each pin.

I found an alternative and related issue using the GPIO module to read a quadrature encoder. I had to make the callback trigger with either up or down to detect the edges of the square wave but there is actually lots of 'jitter' so, magnified, it might look like;
00000000000000000000001011001010110110100101011111111111111111111111111111
with the BOTH option set in the callback it is then not possible to tell whether the trigger was rising or falling so the function has to read from the GPIO pin to determine whether it's up or down. Often the jitter is fast enough that the pin has changed back again by the time it's read! So what I ended up with was one callback function that checked the input on both pins whenever there was a state change on either. See code here
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

jamesgood72
Posts: 1
Joined: Tue Oct 29, 2013 12:17 am

Re: Multiple GPIO.add_event_detect, one callback function

Tue Oct 29, 2013 12:31 am

I'm seeing this 'called twice' issue too. It seems to happen if my callback function takes a significant amount of time to finish; the interrupt seems to happen again. If the callback function is trivial and does nothing except print some text and return, it is only called only. Seems to be what you guys have been seeing.

Should I report this as a bug to the GPIO project? It's a very useful library, the author has done a great job with it.

Thanks,

-James.

Marre
Posts: 6
Joined: Thu Dec 31, 2015 11:02 am

Re: Multiple GPIO.add_event_detect, one callback function

Mon Jan 18, 2016 9:33 pm

I was having problems with this in a program which the user could rerun.
Although using GPIO.cleanup() as part of a try/except/finally block under the while loop, it did not remove GPIO.add_event_detect, which was causing trouble with multiple simultanious events during reruns of the program.
It was solved with adding GPIO.remove_event_detect(channel/PIN).

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Multiple GPIO.add_event_detect, one callback function

Tue Jan 19, 2016 7:01 am

My suggestion for most users is that if you use a button or a switch as an input, do not use add_event_detect() and the call_back function if your code can't cope with multiple hits. Otherwise be prepared to jump through some hoops to make it work reliably.

I recommend you use wait_for_edge().

miket1982
Posts: 1
Joined: Tue Apr 05, 2016 4:56 pm

Re: Multiple GPIO.add_event_detect, one callback function

Mon Oct 09, 2017 10:22 am

try adding a sleep between the calls to setup the callbacks, it looks like there is a bug in the c implmentation of the callback thread.

https://rlaanemets.com/post/show/python ... ing-broken

Return to “Python”