5

I have a simple Python script that is observing two GPIO pins, I've set the bouncetime parameter to 500ms, but I get multiple (almost always 2) calls to my callback, usually 3 or 4 ms apart.

This is my first Python script, so I may be missing some subtlety other than the GPIO handling.

#!/usr/bin/env python2.7

import os, datetime, atexit, time, urllib2,sys
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

GPIO.setup(17, GPIO.IN)
GPIO.setup(27, GPIO.IN)

def gate_moving_callback(channel): 

    if GPIO.input(channel):
        print(str(datetime.datetime.now()), " Gate closing")
        urllib2.urlopen("http://<redacted>/prog/GateClose").read()
        sys.stdout.flush()
    else:
        print(str(datetime.datetime.now()), " Gate opening")
        urllib2.urlopen("http://<redacted>/prog/GateOpen").read()
        os.system('./gatepic')
        sys.stdout.flush()

def shutdown_request(channel):
    print "change in GPIO27: ", channel
    print GPIO.input(channel)
    if GPIO.input(channel):
        print str(datetime.datetime.now()), " Shutdown request"
        sys.stdout.flush()
        os.system('/sbin/shutdown -h now')

def cleanup():
    #print("Cleaning up")
    GPIO.cleanup()

atexit.register(cleanup)

GPIO.add_event_detect(17, GPIO.BOTH, callback=gate_moving_callback, bouncetime=500)
GPIO.add_event_detect(27, GPIO.BOTH, callback=shutdown_request, bouncetime=500)

# Wait indefinitely...
print str(datetime.datetime.now()), " Ready!"
sys.stdout.flush()

while True:
    time.sleep(1);

Here's a fairly typical example from the log:

('2014-03-02 12:26:42.196143', ' Gate opening')
('2014-03-02 12:26:42.200036', ' Gate opening')
('2014-03-02 12:27:49.946151', ' Gate closing')

Note that the second gate opening message is only 4ms after the first, even though I've specified bouncetime=500.

It always reports two opens and one close.

5

The first thing to check in mysterious situations like this is do you have the latest version of the GPIO library? The newest one should be 0.5.4.

The second problem may be that you're not setting a pull-up or a pull-down. In an electrical circuit with only a switch, when the switch is closed, you know what it is. However, when the switch is open there's no physical electrical connection and so technically the Pi could read anything on the GPIO pin. (What it actually reads is some weird combination of electro-magnetic radiation from your refrigerator and static electricity from you sweater, that sort of thing.) To prevent the "unknown" state, a pull-up or pull-down resistor is used. This way, when the switch is open, the resistor comes into play and the circuit is still defined.

If the switch is connected to ground and the GPIO pin, you want a pull-up resistor. By default it "pulls the circuit up" by connecting the pin to +3.3 V, so that the pin reads 1 unless the switch is closed. The swtich has no resistance, so it short-circuits (in a good way!) the resistor and the pin now reads 0. The switch opens back up, the resistor comes back into play, and all is well reading 1 again. A pull-down resistor works the same, but exactly the opposite. It connects the pin to ground, so that the pin always reads 0. The switch then connects the pin to +3.3 V. When the switch is closed, the voltage short-circuits the ground connection and the pin reads 1. Etc.

The resistors can be added in one of two ways. A physical resistor can be put in place, or the Pi has internal resistors that can be used. The GPIO library has support for this. For example:

GPIO.setup(PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

This activates a pull up, and my switch on PIN is connected to ground.

If that doesn't fix it, you may be hitting a weird edge case in the code with threading that I wouldn't have though possible on a single-cpu system. You might need to implement your own software check for debounce or to try adding a small capacitor (0.1 uF) across the switch. A further increase of bouncetime might help, but if it doesn't I'd suspect bug.

  • Thanks for the reminder on checking the library version; upgraded from 5.2a to 5.4, but no difference. – TomG Mar 4 '14 at 2:20
  • I do have a pull-up resistor, and I don't get any false triggers, except for the second one 5-15ms after the first. I find it curious that I always get two calls from the open, and only one from the close. I have not seen it vary from this pattern yet. – TomG Mar 4 '14 at 2:23
  • I've tried your code and it works perfectly for me, once I add the pull_up_down option. All I can suggest at this point is your getting some jitter in the electrical signal that's causing it. I'd try activating the internal resistor even if you have an external one at this point. – Fred Mar 4 '14 at 7:09
  • Unfortunately, the adding the internal pull up didn't help. I'm ready to give in and limit it to one activation per second. – TomG Mar 7 '14 at 1:54
  • Darn. It may be best in that case to disable the bouncetime and implement it manually, since that's essentially what you're doing anyway. – Fred Mar 7 '14 at 7:43
4

I've had the very same problem for a long time and I couldn't figure out what the problem was.

What seemed to have fixed it for me (it's a weird fix, I'll say so myself...) in addition to the above, having a pull_up_down=GPIO.PUD_UP in your GPIO.setup was adding a small delay after the event is detected, and before you check which one, like so:

time.sleep(0.25)
if GPIO.input(channel):
    print(str(datetime.datetime.now()), " Gate closing")
    urllib2.urlopen("http://<redacted>/prog/GateClose").read()
    sys.stdout.flush()

I can't explain why this would work, but it does. I suspect that eventhough an event is detected, the system needs a little bit more time before it knows which one it is, rising or falling, or that it's two systems and one is slower to catch on that the other? I don't know...

Give it a try!

  • Thanx, that solved it for me too! – mstuebner Nov 3 '16 at 20:35
  • This works, thanks. But it would be nice if somebody could explain it. – Conffusion Sep 15 at 19:17
2

I think you should change GPIO.BOTH to either GPIO.RISING or GPIO.FALLING. I suspect that any jitter otherwise could be detected as two different interrupt event, one falling and one rising.

Regards

  • That's a great point. I'll try separate events for GPIO.RISING and GPIO.FALLING as I do want to know both when the gate opens and closes. – TomG Jul 19 '14 at 0:35
1

I had a similar situation where I was receiving multiple callbacks.

Instead of setting up a callback, I just create a loop that basically waited for the button to be pushed. When it was pushed, I removed the button from the event handler, waited a bit, then re-added it.

It looked something like this:

def setup():
    GPIO.add_event_detect(btn, GPIO.RISING, bouncetime=1)

def loop():
    while True:
        time.sleep(0.25)
            if GPIO.event_detected(btn):
                GPIO.remove_event_detect(btn)
                do_something()
                time.sleep(1)
                GPIO.add_event_detect(btn, GPIO.RISING, bouncetime=1)

Hope this helps anyone else who was getting multiple callbacks for one button click.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.