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

Avoiding False Hits with RPi.GPIO Edge Detection

Fri Jan 29, 2016 8:47 am

There are two main challenges when using the RPi.GPIO event or edge detection.
First, due to a current bug in the library code, Rising edge detection acts like the BOTH detection, meaning that rising edges and falling edges are recognized as valid edges.

Second, there is an up until now unreported and obscure phenomena that I have found while characterizing the GPIO inputs. For a detailed summery, have a look here :viewtopic.php?f=29&t=133740 This phenomena will result in false edge triggering if there are no adequate counter measures taken.

Edge detection with a falling edge will start to detect a valid edge at approx. 1.16V, and will no longer recognize the edge when the voltage has dropped below approx. 1.12V. Within this small window however, edge detection will continuously fire.
A rising edge will be detected at approx. 1.25V. However, when the voltage lowers again and the input voltage drops below the 1.16V level, a stream of interrupts will start, and continue until the level is below 1.12V.

This is most dramatic when slower rising or falling edges are present, as an example, when excessive anti-debounce measures have been taken. A common mistake is to add a capacitor with a value that is too high. (more is NOT better) The dis-charching or charging slope as the result of the R/C combination will "spend" time in this small "automatic firing region" and misfiring of events is guaranteed. It can be explained best by this scope capture:
Too much debounce-ckt-2.gif
Too much debounce-ckt-2.gif (12.41 KiB) Viewed 8206 times
Red is the falling edge of a button, blue represents the edge recognition events. The de-bounce capacitor is "only" 1uF.

You should be aware that the Pi processor, when running at 700MHz for the Model B Rev2, is running at cycles that can be as fast as 1.4nS. Event recognition by the processor hardware can even be faster in the a-synchronous mode. This means that the smallest glitch or spike can cause the unwanted detection of an edge. Think about that again when you are interfacing relatively "slow" devices with events in the mSec and slower range to a processor that is running at a break-neck speed in the nSec area. Who says that the Pi is sluggish in dealing with the outside world?

It should also be noted that edge recognition is different from the Logic Level detection, which is what you use when you read an input. i.e. GPIO.input(channel)
The Logic Levels for a low signal going high start to detect a logical high above 1.25V. When the signal lowers again, a logical low will be detected below 1.17V. The difference of 0.08V is the hysteresis. Consequently, a logical high signal going low will become a low below 1.25V and when it goes up again, it will change to a high above 1.17V.

I have spend some time to create solutions for the three different edge detection routines in the RPi.GPIO package.
The scripts I present basically all center around the avoidance of false hits. The first measure is to use the proper hardware filtering circuit to counter glitches and some switch de-bounce.
debounce-ckts.png
debounce-ckts.png (28.08 KiB) Viewed 8133 times
Be careful when you use Circuit-1, or if you can, avoid it all together. Circuit-2 and Circuit-3 are the proper and adequate filtering and de-bounce methods for most buttons and switches in combination with the proper software.

In software, I use two other techniques to get rid of the false hits. First, after the edge has been recognized by the system, we don't know if it is the one we expect. By waiting for 5mSec, and then reading the input again but now with a Logic Level command, we can determine if the edge is indeed what we expect and we then avoid and discard all other (false) edges. This is also the work-around for the bug in the Rising edge detection.

Here are the Python script examples for the three event detection functions.

wait_for_edge() function

Falling Edge:

Code: Select all

#!/usr/bin/env python2.7

# file: wait_for_edge-falling-fix.py
#
# wait_for_edge solution for falling edges
# a falling edge is detected at approx. 1.16V
#
# The input pin should be defined as active low.
#
# you can use the timeout parameter (timeout=n in mSec)
#
# caveat:
# you cannot use GPIO.remove_event_detect(Input_Sig)
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
# if there is no external pull, use the internal one
GPIO.setup(Input_Sig, GPIO.IN)


def main():

    try:
        while True:
            pass # your code

            # setup the edge detection
            GPIO.wait_for_edge(Input_Sig, GPIO.FALLING)
            print "done"
            # if we're here, an edge was recognized
            sleep(0.005) # debounce for 5mSec
            # only show valid edges
            if GPIO.input(Input_Sig) == 0:
                print "FALLING"


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Rising Edge :

Code: Select all

#!/usr/bin/env python2.7

# file: wait_for_edge-rising-fix.py
#
# wait_for_edge solution for rising edges
# a rising edge is detected at approx. 1.25V
#
# The input pin should be defined as active high.
#
# you can use the timeout parameter (timeout=n in mSec)
#
# caveat:
# you cannot use GPIO.remove_event_detect(Input_Sig)
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
# if there is no external pull, use the internal one
GPIO.setup(Input_Sig, GPIO.IN)


def main():

    try:
        while True:
            pass # your code

            # setup the edge detection
            GPIO.wait_for_edge(Input_Sig, GPIO.RISING)

            # if we're here, an edge was recognized
            sleep(0.005) # debounce for 5mSec
            # only show valid edges
            if GPIO.input(Input_Sig) == 1:
                print "RISING"


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()



Both Edges:

Code: Select all

#!/usr/bin/env python2.7

# file: wait_for_edge-both-fix.py
#
# wait_for_edge solution for falling & rising edges (both)
# a falling edge is detected at approx. 1.16V, a rising edge at approx. 1.25V
#
# caveat:
# if you use the timeout parameter, the active edge will be shown after the timeout
# you cannot use GPIO.remove_event_detect(Input_Sig)
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)


def main():

    try:
        while True:
            pass # your code

            #  setup the edge detection
            GPIO.wait_for_edge(Input_Sig, GPIO.RISING)

            # if we're here, an edge was recognized
            sleep(0.005) # debounce for 5mSec
            # only show valid edges
            if GPIO.input(Input_Sig) == 1:
                print "RISING"
            else:
                print "FALLING"


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()

add_event_detected() function
Falling Edge:

Code: Select all

#!/usr/bin/env python2.7
# file: add_event_detected-falling-fix.py
#
# add_event_detected solution for falling edges
# a falling edge is detected at approx. 1.16V
#
# you can optionally use GPIO.remove_event_detect(Input_Sig)
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
# if there is no external pull, use the internal one (pull_up_down=GPIO.PUD_UP)
GPIO.setup(Input_Sig, GPIO.IN)

# set up the edge detection
GPIO.add_event_detect(Input_Sig, GPIO.FALLING)


def main():
    global event_ctr

    try:
        while True:
            pass # your code

            if GPIO.event_detected(Input_Sig):
                # if we're here, an edge was detected
                sleep(0.005) # debounce for 5mSec
                # only show valid edges
                if GPIO.input(Input_Sig) == 0:
                    print "FALLING"


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()

Rising Edge:

Code: Select all

#!/usr/bin/env python2.7
#
# file: add_event_detected-rising-fix.py
#
# add_event_detected solution for rising edges
# a rising edge is detected at approx. 1.25V
#
# you can optionally use GPIO.remove_event_detect(Input_Sig)
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
# if there is no external pull, use the internal one (pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(Input_Sig, GPIO.IN)

# set up the edge detection
GPIO.add_event_detect(Input_Sig, GPIO.RISING)


def main():

    try:
        while True:
            pass # your code

            if GPIO.event_detected(Input_Sig):
                # if we're here, an edge was detected
                sleep(0.005) # debounce for 5mSec
                # only show valid edges
                if GPIO.input(Input_Sig) == 1:
                    print "RISING"


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()

Both Edges:

Code: Select all

#!/usr/bin/env python2.7

# file: wait_for_edge-detected-both-fix.py
#
# wait_for_edge_detected solution for falling & rising edges (both)
# a falling edge is detected at approx. 1.16V, a rising edge at approx. 1.25V
#
# you can optionally use GPIO.remove_event_detect(Input_Sig)
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)

# set up the edge detection
GPIO.add_event_detect(Input_Sig, GPIO.BOTH)


def main():

    try:
        while True:
            pass # your code

            if GPIO.event_detected(Input_Sig):
                # if we're here, an edge was detected
                sleep(0.005) # debounce for 5mSec
                # only show valid edges
                if GPIO.input(Input_Sig) == 1:
                    print "RISING"
                else:
                    print "FALLING"


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()


add_event_detect() function

Falling Edge:

Code: Select all

#!/usr/bin/env python2.7

# file: add_event_detect-both-fix.py
#
# add_event_detect solution for both edges from buttons and switches
# a falling edge is detected at approx. 1.16V, a rising edge at approx. 1.25V
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)

def interrupt_service_routine(Input_Sig):
    sleep(0.005) # edge debounce of 5mSec
    # only deal with valid edges
    if GPIO.input(Input_Sig) == 0:
        print "FALLING"
    else:
        print "RISING"
    return

# define the event; bountime of 5mSec means that subsequent edges will be ignored for 5mSec
GPIO.add_event_detect(Input_Sig, GPIO.BOTH, callback=interrupt_service_routine, bouncetime=5)

def main():

    try:
        while True:
            pass # your code

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Rising Edge:

Code: Select all

#!/usr/bin/env python2.7

# add_event_detect solution for rising edges from buttons and switches
# a rising edge is detected at approx. 1.25V
#
# The input pin should be defined as active high.
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
# if there is no external pull, use the internal one (pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(Input_Sig, GPIO.IN)


def interrupt_service_routine(Input_Sig):
    sleep(0.005) # edge debounce of 5mSec
    # only deal with valid edges
    if GPIO.input(Input_Sig) == 1:
        print "RISING"
    return

# define the event; bountime of 5mSec means that subsequent edges will be ignored for 5mSec
GPIO.add_event_detect(Input_Sig, GPIO.RISING, callback=interrupt_service_routine, bouncetime=5)


def main():

    try:
        while True:
            pass # your code

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()

Both Edges:

Code: Select all

#!/usr/bin/env python2.7

# file: add_event_detect-both-fix.py
#
# add_event_detect solution for both edges from buttons and switches
# a falling edge is detected at approx. 1.16V, a rising edge at approx. 1.25V
#
__author__ = 'paulv'

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)

def interrupt_service_routine(Input_Sig):
    sleep(0.005) # edge debounce of 5mSec
    # only deal with valid edges
    if GPIO.input(Input_Sig) == 0:
        print "FALLING"
    else:
        print "RISING"
    return

# define the event; bountime of 5mSec means that subsequent edges will be ignored for 5mSec
GPIO.add_event_detect(Input_Sig, GPIO.BOTH, callback=interrupt_service_routine, bouncetime=5)

def main():

    try:
        while True:
            pass # your code

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()

Is there also a solution for very slow rising and falling edges?
Yes, one with a surprising bonus. Stay tuned for more.

Enjoy!
Last edited by paulv on Tue Sep 27, 2016 6:09 am, edited 1 time in total.

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

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Sun Jan 31, 2016 10:58 am

I promised to have a solution for slowly changing voltage levels.
As shown in the previous post, there is an area in the input voltage level range that manifests when using edge based triggering as opposed to logic level triggering.

The problem is that if the voltage level is in the narrow band of 1.12 to 1.16V, interrupts are recognized continuously, resulting in a stream of interrupts as fast as the Pi can manage.

When you are dealing with fast edges, like those generated with switches and buttons, it is fairly simple to find a solution as I've shown in the previous post.

But what about much slower moving voltages, like those coming from an LDR, battery, NTC/PTC etc.
You can refrain from using edge detection, but then your program needs to constantly poll the input.

After having found the source of the mysterious false hits, it is also fairly easy to find a solution, although a little more complicated compared to the solutions for faster edges.

So now that we can use reliable edge detection that works with very slow changing "edges", or rather voltage levels, we can even go one step further and do something that we really could not do reliably before.

With the following circuit, and the software scripts below, it is now possible to watch the above devices and use interrupts, rather than polling. In this circuit I have used component values that allow you to watch the discharge level of a Li-Ion cell. Once it is below a certain value an interrupt is fired, so you can safely shutdown the Pi without risking a corrupted SD card due to a power related crash. The example circuit can be easily adapted for other input voltage levels, as long as you make sure the used input pin of the Pi is protected against voltages above 3V3.
Threshold Detection.png
Threshold Detection.png (18.34 KiB) Viewed 8128 times
Here are the scripts with the fixes for all three edge based event triggering functions:
wait_for_edge() function
Falling Edge:

Code: Select all

#!/usr/bin/env python2.7
#
# file: wait_for_edge-falling-ramp-fix.py
#
# wait_for_edge solution for slowly falling ramps
# a falling edge is detected at approx. 1.16V
#
# The input pin should be defined as active low.
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
# if the pin has a pull-up, you can remove the one below
GPIO.setup(Input_Sig, GPIO.IN, pull_up_down=GPIO.PUD_UP)

valid_edge = True


def main():
    global valid_edge

    try:
        while True:
            pass # your code

            # setup the event recognition, (you can set a timeout)
            GPIO.wait_for_edge(Input_Sig, GPIO.FALLING)

            # if we're here, the event happened
            if valid_edge == True :
                # only the first event is valid
                # check if we have a logical low (falling edge)
                if GPIO.input(Input_Sig) == 0:
                    print "FALLING"
                    valid_edge = False
            # if we are back at a logical 1, reset the cycle
            if GPIO.input(Input_Sig) == 1:
                valid_edge = True

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Rising Edge:

Code: Select all

#!/usr/bin/env python2.7

# wait_for_edge solution for slowly rising ramps
# a rising edge is detected at approx. 1.25V
#
# The input pin should be defined as active high.

# caveat: if the input level starts at a logic high, the first edge that will
# be detected is incorrectly identified as a rising edge due to a bug in the
# GPIO library code. The input pin should be low at the start.

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


def main():

    # Avoid the first false falling edge detection when accidentially
    # starting at a logical high.
    if GPIO.input(Input_Sig) == 1:
        valid_edge = False
    else:
        valid_edge = True

    try:
        while True:
            pass # your code

            # setup the event recognition
            GPIO.wait_for_edge(Input_Sig, GPIO.RISING)

            # if we're here, the event happened
            if valid_edge == True :
                # only the first event is valid
                # check if we have a logical high
                if GPIO.input(Input_Sig) == 1:
                    print "RISING"
                    valid_edge = False
            # if we are back at a logical 0, reset the cycle
            if GPIO.input(Input_Sig) == 0:
                valid_edge = True


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Both Edges:

Code: Select all

#!/usr/bin/env python2.7

# file: wait_for_edge-falling-ramp-fix.py
#
# wait_for_edge solution for slowly rising & falling ramps (both)
# a falling ramp is detected at approx. 1.16V, a rising ramp at approx. 1.25V
#
# caveat:
# You cannot use the timeout parameter for the edge detection
# It is actually easier to deal with falling and rising edges seperately,
# rather than using the GPIO.BOTH parameter
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)

valid_edge = True

def do_falling():
    global valid_edge, falling

    GPIO.wait_for_edge(Input_Sig, GPIO.FALLING)
    # if we're here, the event happened
    if valid_edge == True :
        # only the first event is valid
        # check if we have a logical high
        if GPIO.input(Input_Sig) == 1:
            print "FALLING"
            valid_edge = False

    # check if we have a logical low (reached bottom of hysteresis range)
    if GPIO.input(Input_Sig) == 0:
            GPIO.remove_event_detect(Input_Sig)
            # reverse the edge detection
            falling = False
            valid_edge = True
    return


def do_rising():
    global valid_edge, falling

    GPIO.wait_for_edge(Input_Sig, GPIO.RISING)
    # if we're here, the event happened
    if valid_edge == True :
        # only the first event is valid
        # check if we have a logical low (falling edge)
        if GPIO.input(Input_Sig) == 1:
            print "RISING"
            valid_edge = False
    # check if we have a logical high (reached top of hysteresis range)
    if GPIO.input(Input_Sig) == 1:
            GPIO.remove_event_detect(Input_Sig)
            valid_edge = True
            # reverse the edge detection
            falling = True
    return


def main():
    global falling

    # define the event, based on the active high or low condition of the pin
    if GPIO.input(Input_Sig) == 1:
        # active low
        falling = True
    else:
        # active high
        falling = False

    try:
        while True:
            pass # your code
            if falling == True :
                do_falling()
            else:
                do_rising()




    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
add_event_detected() function
Falling Edge:

Code: Select all

#!/usr/bin/env python2.7
# file: add_event_detected-falling-ramp-fix.py
#
# add_event_detected solution for slowly falling voltage levels
# a falling edge is detected at approx. 1.16V
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)

valid_edge = True

# event recognition set up
GPIO.add_event_detect(Input_Sig, GPIO.FALLING)


def main():
    global valid_edge

    try:
        while True:
            if GPIO.event_detected(Input_Sig):
                # if we're here, the event happened
                if valid_edge == True :
                    # only the first event is valid
                    # check if we have a logical low
                    if GPIO.input(Input_Sig) == 0:
                        print "FALLING"
                        valid_edge = False
                # if we are back at a logical 1, reset the cycle
                if GPIO.input(Input_Sig) == 1:
                    valid_edge = True

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Rising Edge:

Code: Select all

#!/usr/bin/env python2.7
#
# file: add_event_detected-rising-ramp-fix.py
#
# add_event_detected solution for slowly rising ramps
# a rising edge is detected at approx. 1.25V
#
# The input pin should be defined as active high and start low.
#
# caveat:
# There is a bug in the RPi.GPIO package code.
# GPIO.RISING will detect both edge types.
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)


def main():

    try:
        # Avoid the first false falling edge detection when accidentially
        # starting at a logical high.
        if GPIO.input(Input_Sig) == 1:
            valid_edge = False
        else:
            valid_edge = True

        # event recognition set up
        GPIO.add_event_detect(Input_Sig, GPIO.RISING)

        while True:
            if GPIO.event_detected(Input_Sig):
                # if we're here, the event happened
                if valid_edge == True :
                    # only the first event is valid
                    # use a logical high to avoid false falling edges
                    if GPIO.input(Input_Sig) == 1:
                        print "RISING"
                        valid_edge = False
                # if we are back at a logical 0, reset the cycle
                if GPIO.input(Input_Sig) == 0:
                    valid_edge = True


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Both Edges:

Code: Select all

#!/usr/bin/env python2.7

# file: add_event_detected-both-ramp-fix.py
#
# add_event_detected solution for slowly falling & rising voltages (both)
# a falling edge is detected at approx. 1.16V, a rising edge at approx. 1.25V
#
# caveat:
# With this code you cannot use the timeout parameter for the edge detection
# It is actually easier to deal with falling and rising edges seperately,
# than using the GPIO.BOTH parameter.
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)

valid_edge = True

def do_falling():
    global valid_edge, falling

    if GPIO.event_detected(Input_Sig) :
    # if we're here, the event happened
        if valid_edge == True :
            # only the first event is valid
            # check if we have a logical high
            if GPIO.input(Input_Sig) == 1:
                print "FALLING"
                valid_edge = False

        # check if we have a logical low (reached bottom of hysteresis range)
        if GPIO.input(Input_Sig) == 0:
                GPIO.remove_event_detect(Input_Sig)
                # reverse the edge detection
                falling = False
                valid_edge = True
                GPIO.add_event_detect(Input_Sig, GPIO.RISING)
    return


def do_rising():
    global valid_edge, falling

    if GPIO.event_detected(Input_Sig):
        # if we're here, the event happened
        if valid_edge == True :
            # only the first event is valid
            # check if we have a logical low (falling edge)
            if GPIO.input(Input_Sig) == 1:
                print "RISING"
                valid_edge = False
        # check if we have a logical high (reached top of hysteresis range)
        if GPIO.input(Input_Sig) == 1:
                GPIO.remove_event_detect(Input_Sig)
                valid_edge = True
                # reverse the edge detection
                falling = True
                GPIO.add_event_detect(Input_Sig, GPIO.FALLING)
    return


def main():
    global falling

    # define the event, based on the active high or low condition of the pin
    if GPIO.input(Input_Sig) == 1:
        # active low
        falling = True
        GPIO.add_event_detect(Input_Sig, GPIO.FALLING)
    else:
        # active high
        falling = False
        GPIO.add_event_detect(Input_Sig, GPIO.RISING)

    try:
        while True:
            pass # your code
            if falling == True :
                do_falling()
            else:
                do_rising()

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
add_event_detect() function
Falling Edge:

Code: Select all

#!/usr/bin/env python2.7

# file: add_event_detect-falling-ramp-fix.py
#
# add_event_detect solution for falling edges with a ramp input
# a falling edge is detected at approx. 1.16V
#
# The input pin should be defined as active low.
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
# if there is no external pull, use the internal one
GPIO.setup(Input_Sig, GPIO.IN, pull_up_down=GPIO.PUD_UP)


def interrupt_service_routine(Input_Sig):
    # turn detection off
    GPIO.remove_event_detect(Input_Sig)

    if GPIO.input(Input_Sig) == 0: # only falling levels
        print "FALLING"
    # turn detection back on
    GPIO.add_event_detect(Input_Sig, GPIO.FALLING, callback=interrupt_service_routine, bouncetime=5)
    return


# define the event
GPIO.add_event_detect(Input_Sig, GPIO.FALLING, callback=interrupt_service_routine, bouncetime=5)


def main():

    try:
        while True:
            pass # your code

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Rising Edge:

Code: Select all

#!/usr/bin/env python2.7

# file : add_event_detect-rising-ramp-fix
#
# add_event_detect solution for slowly rising ramps
# a rising edge is detected at approx. 1.25V
#
# The input pin should be defined as active high and start low.
#
# caveat:
# There is a bug in the RPi.GPIO package code.
# GPIO.RISING will detect both edge types.
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)


def interrupt_service_routine(Input_Sig):
    global valid_edge

    if valid_edge == True :
        # only the first detected event is valid
        # check if we have a logical high to filter the false falling edges
        if GPIO.input(Input_Sig) == 1:
            print "RISING"
            valid_edge = False
    # when we are back at a logical 0, reset the cycle
    if GPIO.input(Input_Sig) == 0:
        valid_edge = True
    return

# define the event
GPIO.add_event_detect(Input_Sig, GPIO.RISING, callback=interrupt_service_routine, bouncetime=5)


def main():
    global valid_edge
    try:
        # Avoid the first false falling edge detection when accidentially
        # starting at a logical high.
        if GPIO.input(Input_Sig) == 1:
            valid_edge = False
        else:
            valid_edge = True

        while True:
            pass # your code

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Both Edges:

Code: Select all

#!/usr/bin/env python2.7

# file: add_event_detect-both-ramp-fix.py
#
# add_event_detect solution for both edges with slow ramps
# a falling edge is detected at approx. 1.16V, a rising edge at approx. 1.25V
#
# caveat:
# It is easier to deal with rising and falling edge detection seperately,
# than by using the GPIO.BOTH parameter
#
__author__ = 'paulv'

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

Input_Sig = 23 # any plain GPIO pin
GPIO.setup(Input_Sig, GPIO.IN)

valid_edge = True


def interrupt_service_routine(Input_Sig):
    global valid_edge, falling

    if falling == True: # falling edge detection
        # discard any subsequent falling events
        if valid_edge == True :
            # only the first event is valid
            # check if we have a logical high
            if GPIO.input(Input_Sig) == 1:
                print "FALLING"
                valid_edge = False
        # check if we have a logical low (reached bottom of hysteresis range)
        if GPIO.input(Input_Sig) == 0:
        # reverse the edge detection
            falling = False
            valid_edge = True
            GPIO.remove_event_detect(Input_Sig)
            GPIO.add_event_detect(Input_Sig, GPIO.RISING, callback=interrupt_service_routine, bouncetime=5)

    else: # rising edge detection
        # discard any subsequent rising events
        if valid_edge == True :
            # only the first event is valid
            # check if we have a logical low
            if GPIO.input(Input_Sig) == 1:
                print "RISING"
                valid_edge = False
        # check if we have a logical high (reached top of hysteresis range)
        if GPIO.input(Input_Sig) == 1:
            # reverse the edge detection
            falling = True
            valid_edge = True
            GPIO.remove_event_detect(Input_Sig)
            GPIO.add_event_detect(Input_Sig, GPIO.FALLING, callback=interrupt_service_routine, bouncetime=5)
    return


def main():
    global falling

    try:
        # define the event, based on the active high or low condition of the pin
        if GPIO.input(Input_Sig) == 1:
            # active low
            GPIO.add_event_detect(Input_Sig, GPIO.FALLING, callback=interrupt_service_routine, bouncetime=5)
            falling = True
#            event_ctr = 1
        else:
            # active high
            GPIO.add_event_detect(Input_Sig, GPIO.RISING, callback=interrupt_service_routine, bouncetime=5)
            falling = False
#            event_ctr = 1

        while True:
            pass # your code

    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([Input_Sig])


if __name__ == '__main__':
    main()
Enjoy!

User avatar
karrika
Posts: 957
Joined: Mon Oct 19, 2015 6:21 am
Location: Finland

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Mon Feb 08, 2016 10:40 am

Thank you paulv. Great work.

User avatar
GTR2Fan
Posts: 1601
Joined: Sun Feb 23, 2014 9:20 pm
Location: South East UK

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Mon Feb 08, 2016 10:53 am

Am I missing something obvious here? If you're going to add external components as a halfway house to fixing the problem, then fix the rest in software, doesn't it make more sense to just use an external Schmitt trigger and no software correction at all? They're widely available in sixes in one tiny package for well under £1.
Pi2B Mini-PC/Media Centre: ARM=1GHz (+3), Core=500MHz, v3d=500MHz, h264=333MHz, RAM=DDR2-1200 (+6/+4/+4+schmoo). Sandisk Ultra HC-I 32GB microSD card on '50=100' OCed slot (42MB/s read) running Raspbian/KODI16, Seagate 3.5" 1.5TB HDD mass storage.

User avatar
karrika
Posts: 957
Joined: Mon Oct 19, 2015 6:21 am
Location: Finland

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Mon Feb 08, 2016 11:24 am

GTR2Fan wrote:Am I missing something obvious here? If you're going to add external components as a halfway house to fixing the problem, then fix the rest in software, doesn't it make more sense to just use an external Schmitt trigger and no software correction at all? They're widely available in sixes in one tiny package for well under £1.
You are not missing anything. External Schmitt triggers is a great solution to the problem. But you may may also need filtering to get rid of key bounces.

As most tutorials are connecting buttons directly to the RPi using only passive RC circuits it may be beneficial to build in the kind of logic presented by paulv into the GPIO drivers.

In some version of the manuals the input pins are drawn with a "hysteresis" symbol that looks very much like a Schmitt trigger to me. Obviously this in not the case.

So overall this experiment proves that many findings of spurious interrupts in this forum have a real cause because the input voltage is staying for too long in the gray zone.

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

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Mon Feb 08, 2016 12:13 pm

GTR2Fan,

Thanks for the input.
Let me see if I can clarify "what you're missing"...

My "intended" audience is not EE's or experienced people who should know all this, but the schoolkid or hobbyist that is trying to experiment and learn. That's the main audience this Forum is for, I think.
Judging from the amount of reported problems that surface over and over again, there is a void in the understanding I'm trying to fill (my way, but maybe not the right way).

In this particular case, the majority of Pi users may not realize that the common application of a capacitor to fix debounce glitches is making the problem worse and they will most likely not have a clue why that is.

I'm showing here a couple of ways to use software to "fix" or rather circumvent the problem, and in the process show them what goes on. My examples also hopefully show how much is involved to get it right, all in the spirit of learning by doing, experimenting and tinkering.

OK?

User avatar
GTR2Fan
Posts: 1601
Joined: Sun Feb 23, 2014 9:20 pm
Location: South East UK

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Mon Feb 08, 2016 1:35 pm

I'm clearly still missing something then, as I'd have thought that the best way to teach someone (beginner or otherwise) how to deal with a problem would be to promote the widely accepted electronically correct way of doing it, which also happens to involve far fewer external components and no requirement for software fixes.
Pi2B Mini-PC/Media Centre: ARM=1GHz (+3), Core=500MHz, v3d=500MHz, h264=333MHz, RAM=DDR2-1200 (+6/+4/+4+schmoo). Sandisk Ultra HC-I 32GB microSD card on '50=100' OCed slot (42MB/s read) running Raspbian/KODI16, Seagate 3.5" 1.5TB HDD mass storage.

ats1080
Posts: 44
Joined: Thu Sep 27, 2012 2:10 pm

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Tue Feb 09, 2016 5:25 pm

GTR2Fan wrote:I'm clearly still missing something then, as I'd have thought that the best way to teach someone (beginner or otherwise) how to deal with a problem would be to promote the widely accepted electronically correct way of doing it, which also happens to involve far fewer external components and no requirement for software fixes.
How many people a Schmitt trigger? Now...how many people have a push button, capacitor, and some resistors? Does that answer your question?

User avatar
GTR2Fan
Posts: 1601
Joined: Sun Feb 23, 2014 9:20 pm
Location: South East UK

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Tue Feb 09, 2016 5:28 pm

ats1080 wrote:How many people a Schmitt trigger? Now...how many people have a push button, capacitor, and some resistors? Does that answer your question?
No. Just because the vast majority of amateurs are doing it wrong, that doesn't make it right. Ask anyone who's ever designed digital electronics for a living how to avoid false edge triggering and they'll replace the hundreds of words used in the opening post with just four words. Use a Schmitt trigger.
Pi2B Mini-PC/Media Centre: ARM=1GHz (+3), Core=500MHz, v3d=500MHz, h264=333MHz, RAM=DDR2-1200 (+6/+4/+4+schmoo). Sandisk Ultra HC-I 32GB microSD card on '50=100' OCed slot (42MB/s read) running Raspbian/KODI16, Seagate 3.5" 1.5TB HDD mass storage.

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 18377
Joined: Sat Jul 30, 2011 7:41 pm

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Tue Feb 09, 2016 5:38 pm

ats1080 wrote:
GTR2Fan wrote:I'm clearly still missing something then, as I'd have thought that the best way to teach someone (beginner or otherwise) how to deal with a problem would be to promote the widely accepted electronically correct way of doing it, which also happens to involve far fewer external components and no requirement for software fixes.
How many people a Schmitt trigger? Now...how many people have a push button, capacitor, and some resistors? Does that answer your question?
With just a push button you would be able fix any bounce issues in software (it's either on or off). If you have a variable voltage, that needs a Schmitt trigger or similar to clean up the signal to on or off.
Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Please direct all questions to the forum, I do not do support via PM.

User avatar
GTR2Fan
Posts: 1601
Joined: Sun Feb 23, 2014 9:20 pm
Location: South East UK

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Tue Feb 09, 2016 8:44 pm

ats1080 wrote:Most people here are making something for fun or just learning how things work.
Then it makes perfect sense for them to learn how to make things that work properly. That can be even more fun and certainly more satisfying than bodging it and getting unpredictable results.
I think you might be logged in to the wrong forums???
Would you like the members of the admin team who've made similar suggestions in other threads to leave also?
Pi2B Mini-PC/Media Centre: ARM=1GHz (+3), Core=500MHz, v3d=500MHz, h264=333MHz, RAM=DDR2-1200 (+6/+4/+4+schmoo). Sandisk Ultra HC-I 32GB microSD card on '50=100' OCed slot (42MB/s read) running Raspbian/KODI16, Seagate 3.5" 1.5TB HDD mass storage.

User avatar
rpdom
Posts: 12003
Joined: Sun May 06, 2012 5:17 am
Location: Essex, UK

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Wed Feb 10, 2016 5:40 pm

ats1080 wrote:He has a valid solution to a bug in the rpi.gpio module many people have encountered
What is this supposed "bug" in the library? It is just reporting what the hardware is telling it.

Software debouncing is a common solution and, in most cases, the best "fix" here.

User avatar
croston
Posts: 671
Joined: Sat Nov 26, 2011 12:33 pm
Location: Blackpool
Contact: Website

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Thu Feb 11, 2016 2:35 pm

There is no bug in RPi.GPIO with edge detection. It only does what it is told by external circuits, the SOC and the kernel. The difference is that pigpio reads the GPIO input value (high/low) to confirm the edge type that was triggered before running any callbacks. This would explain the difference in voltage between the two libraries. I never put this in RPi.GPIO because I assume that the hardware+kernel can detect much shorter pulses than is possible in a non-realtime library and operating system. I also never thought that the kernel would report edges that don't exist.
Last edited by croston on Thu Feb 11, 2016 3:26 pm, edited 1 time in total.

User avatar
jojopi
Posts: 3033
Joined: Tue Oct 11, 2011 8:38 pm

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Thu Feb 11, 2016 2:56 pm

croston wrote:There is no bug in RPi.GPIO with edge detection.
Indeed.
The difference is that pigpio reads the GPIO input value (high/low) to confirm the edge type that was triggered by the kernel before running any callbacks.
In fact, pigpio does not use the hardware/kernel edge detection at all. It samples the level registers using the DMA hardware and then detects transitions in software. That gives microsecond-accurate timestamps and the ability to count multiple edges that are only microseconds apart, but at increased cost and latency and with the potential to miss extremely short pulses.

ats1080
Posts: 44
Joined: Thu Sep 27, 2012 2:10 pm

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Thu Feb 11, 2016 2:59 pm

croston wrote:There is no bug in RPi.GPIO with edge detection. It only does what it is told by external circuits, the SOC and the kernel. The difference is that pigpio reads the GPIO input value (high/low) to confirm the edge type that was triggered by the kernel before running any callbacks. This would explain the difference in voltage between the two libraries. I never put this in RPi.GPIO because I assume that the hardware+kernel can detect much shorter pulses than is possible in a non-realtime library and operating system. I also never thought that the kernel would report edges that don't exist.
First off, thanks for looking at this Ben. You're right that it only does what it's designed to do, but the end result is what I would consider undesired behavior, a bug. Hopefully you can get this updated and everyone can stop this ridiculous flame war.

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 18377
Joined: Sat Jul 30, 2011 7:41 pm

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Thu Feb 11, 2016 3:43 pm

Thread cleaned of cruft.
Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Please direct all questions to the forum, I do not do support via PM.

PsiPi
Posts: 1
Joined: Fri Oct 21, 2016 6:38 pm

Re: Avoiding False Hits with RPi.GPIO Edge Detection

Fri Oct 21, 2016 6:52 pm

Thanks paulv for your solutions to a nagging usability problem and description and info of the underlying technical reasons.

The are many roads to Rome, and although I agree there are more technical correct solutions, any solution is fine if the assumptions are made clear. It is then up to the user to ascertain whether they like those assumptions or not.

Although I think there is a bug in one of your code samples (the detecting both edges via the add_event_detect() function.
The first bit should be testing for the input to be 0:

Code: Select all

    if falling == True: # falling edge detection
        # discard any subsequent falling events
        if valid_edge == True :
            # only the first event is valid
            # check if we have a logical low
            if GPIO.input(Input_Sig) == 0:
                print "FALLING"
                valid_edge = False
I've tested this change on my circuit with a normally-open reed switch pulled high and when the switch is closed pulls down to GND,
and it seems to be working now. Previously it just detected the 'RISING' and the intermediate 'grey' areas, but never the 'FALLING'.

Return to “Troubleshooting”

Who is online

Users browsing this forum: CaptainBisquick, raspz1 and 56 guests