Debouncing with IRQ

Questions and discussion about The WiPy 1.0 board and CC3200 boards.
Target audience: Users with a WiPy 1.0 or CC3200 board.
SpotlightKid
Posts: 463
Joined: Wed Apr 08, 2015 5:19 am

Re: Debouncing with IRQ

Post by SpotlightKid » Wed Feb 14, 2018 2:56 pm

Look at the test_machine function at the bottom of the Gist code. Just replace value for the swpin parameter with the pin number you want (i.e. 23) and the second and third arg to the DebouncedSwitch switch constructor with the callback function and its argument.

Here's a really reduced example:

Code: Select all

from debounce import DebouncedSwitch
from machine import Pin

def mycallback(arg):
    print("Switch toggled", arg)

sw = DebouncedSwitch(Pin(23, Pin.IN), mycallback, "dummy")

dubaleeiro
Posts: 7
Joined: Mon Feb 12, 2018 9:52 pm

Re: Debouncing with IRQ

Post by dubaleeiro » Wed Feb 21, 2018 8:14 pm

Thanks a los.
It worked like a charm !

sebbalex
Posts: 3
Joined: Fri Feb 23, 2018 6:37 pm

Re: Debouncing with IRQ

Post by sebbalex » Fri Feb 23, 2018 6:41 pm

@SpotlightKid
Thanks for the code, very useful!
The callback is called with only RISING event trigger, is there a chance to get it working with both events
RISING and FALLING ?
thanks a lot

SpotlightKid
Posts: 463
Joined: Wed Apr 08, 2015 5:19 am

Re: Debouncing with IRQ

Post by SpotlightKid » Sat Feb 24, 2018 7:21 pm

sebbalex wrote:
Fri Feb 23, 2018 6:41 pm
is there a chance to get it working with both events RISING and FALLING ?
Yes, for this you'll have to pass a machine.Pin object to DebouncedSwitch as the first argument and change line 37 and 43 to pass a value for the trigger parameter (note that self._set_cb is an alias name for the irq method of your Pin instance here):

Code: Select all

        self._set_cb(self._sw_cb if cb else None, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
See the documentation on possible values for the trigger parameter:

http://docs.micropython.org/en/latest/p ... ne.Pin.irq

I'll leave it as an exercise to the reader to add an additional constructor parameter for the trigger event to the DebouncedSwitch class.

sebbalex
Posts: 3
Joined: Fri Feb 23, 2018 6:37 pm

Re: Debouncing with IRQ

Post by sebbalex » Sat Feb 24, 2018 10:22 pm

first thanks for the update!

I tried as you mentioned but I can't figure it out why it is not working, even after those change only RISING event
is fired..

this is my class instance:

[code]
v = x #dynamic id in a loop
p = machine.Pin(v, machine.Pin.IN, machine.Pin.PULL_UP)
sw = DebouncedSwitch(p, pub_cb, [v,p.value()])
[/code]

SpotlightKid
Posts: 463
Joined: Wed Apr 08, 2015 5:19 am

Re: Debouncing with IRQ

Post by SpotlightKid » Sun Feb 25, 2018 1:46 pm

Sorry, I overlooked the "if self.sw():" statement in line 35. The code will not work for handling callbacks on the rising and falling edge as is. I'll put a version below that does but it doesn't work very well, due to timing issues. The callback for the rising edge is fired with a very noticable delay. I guess this is due to the use of micropython.schedule.

So I recommend using another debouncing approach in your case. Unfortunately I don't have the time at the moment to offer a solution.

Code: Select all

#
# inspired by: https://forum.micropython.org/viewtopic.php?t=1938#p10931
#
import micropython

try:
    from machine import Timer
    timer_init = lambda t, p, cb: t.init(period=p, callback=cb)
except ImportError:
    from pyb import Timer
    timer_init = lambda t, p, cb: t.init(freq=1000 // p, callback=cb)

# uncomment when debugging callback problems
#micropython.alloc_emergency_exception_buf(100)


class DebouncedSwitch:
    def __init__(self, sw, cb, arg=None, delay=50, tid=4):
        self.sw = sw
        # Create references to bound methods beforehand
        # http://docs.micropython.org/en/latest/pyboard/library/micropython.html#micropython.schedule
        self._sw_cb = self.sw_cb
        self._tim_cb = self.tim_cb
        self._set_cb = getattr(sw, 'callback', None) or sw.irq
        self.delay = delay
        self._last = -1
        self.tim = Timer(tid)
        self.callback(cb, arg)

    def sw_cb(self, pin=None):
        self._set_cb(None)
        self._last = self.sw()
        timer_init(self.tim, self.delay, self._tim_cb)

    def tim_cb(self, tim):
        tim.deinit()
        if self.sw() == self._last:
            self._last = -1
            micropython.schedule(self.cb, self.arg)
        self._set_cb(self._sw_cb if self.cb else None)

    def callback(self, cb, arg=None):
        self.tim.deinit()
        self.cb = cb
        self.arg = arg
        self._set_cb(self._sw_cb if cb else None)


def test_pyb(ledno=1):
    import pyb
    sw = pyb.Switch()
    led = pyb.LED(ledno)
    return DebouncedSwitch(sw, lambda l: l.toggle(), led)


def test_machine(swpin=2, ledpin=16):
    from machine import Pin
    sw = Pin(swpin, Pin.IN)
    led = Pin(ledpin, Pin.OUT)
    return DebouncedSwitch(sw, lambda l: l.value(not l.value()), led)

sebbalex
Posts: 3
Joined: Fri Feb 23, 2018 6:37 pm

Re: Debouncing with IRQ

Post by sebbalex » Thu Mar 01, 2018 9:17 am

SpotlightKid wrote:
Sun Feb 25, 2018 1:46 pm
Sorry, I overlooked the "if self.sw():" statement in line 35. The code will not work for handling callbacks on the rising and falling edge as is. I'll put a version below that does but it doesn't work very well, due to timing issues. The callback for the rising edge is fired with a very noticable delay. I guess this is due to the use of micropython.schedule.

So I recommend using another debouncing approach in your case. Unfortunately I don't have the time at the moment to offer a solution.
Thank you anyway, this code is working but as expected there is a huge delay sometimes.
Maybe I should move to a different approach or use the uasyncio library that should have this behaviour built in.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Debouncing with IRQ

Post by pythoncoder » Thu Mar 01, 2018 9:24 am

I was going to suggest uasyncio but I haven't used the WiPy V1.0 for a long time and I don't know how well it is supported. If you do use it you'll find asynchronous, debounced drivers for switches and pushbuttons here (aswitch.py).
Peter Hinch
Index to my micropython libraries.

dubaleeiro
Posts: 7
Joined: Mon Feb 12, 2018 9:52 pm

Re: Debouncing with IRQ

Post by dubaleeiro » Fri Mar 02, 2018 10:55 pm

@SpotlightKid
I am trying to use the DebouncedSwitch to pause a loop which displays the current time in a 7-segment display, and when I execute the following code, the callback behaves as if I had pushed the button:

Code:
def cbButMenu(arg):
print(arg, 'value')
main_menu()
return

debMenu = DebouncedSwitch(butMenu, cbButMenu, "butMenu")

while True:
curTime = rtc.datetime()
display_time(curTime.hour, curTime.minute)
if cbButMenu("butMenu"):
break

Debbuging:
........
download ok
exec(open('./test4.py').read(),globals())
butMenu value
>>>

could you please point what am I doing wrong here...?

SpotlightKid
Posts: 463
Joined: Wed Apr 08, 2015 5:19 am

Re: Debouncing with IRQ

Post by SpotlightKid » Sun Mar 04, 2018 2:42 pm

You are calling the cButMenu callback function explicitly within the for loop (with the same argument as it would be called by the DebouncedSwitch callback). don't do that.

Post Reply