Interrupt button debounce with long / short press
-
@crankshaft That's strange, I have been using this code for more than 2 weeks now, without a single double press or any crashes! What are using as button? A micro switch?
-
@mohpor - it's one of those common, small through-hole tactile buttons, but anyway I have your code working now with what appears to be 100% reliability and no double presses:
I added a check for last value and ignored it if received:
last = 0 def btn_press_detected(btn): global chrono, timer, last try: val = btn.value() if 0 == val: chrono.reset() chrono.start() timer.callback(long_press_handler) else: timer.callback(None) chrono.stop() t = chrono.read_ms() if(t==last): return last = t if (t > 30) & (t < 200): single_press_handler() finally: gc.collect()Thanks so much for your help, although it does seem incredibly long-winded to achieve this when the same thing can be done on the RPI pins with a built in debounce option !
-
@crankshaft
Happy to help and glad it's over.100% agreed on the comment, if the button is debounced, that's two less interrupt handlers required and at least some bytes freed from memory.
Cheers,
M. -
@crankshaft
good to see that you finally solve it
but i have question - because i saw this behavior self when i have too big/too small pullup/pulldown.
Do you use internal pin pullup or pull down. Or do you use external resistor? If external - what value?EDIT
+1 for debounce option
add it to:
https://forum.pycom.io/topic/433/features-wish-list -
I came up with another version that also works:
def long(count): print('Long Press',count) def short(count): print('Short Press',count) count = 0 butms = 0 def btn_press_detected(pin): global butms, count now = time.ticks_ms() if butms == 0: butms = now else: if butms == now: return i = 0 while i < 10: time.sleep_ms(1) if pin() == 1: i = 0 else: i+=1 while pin() == 0: i+=1 if(i > 1000): break time.sleep_ms(1) count +=1 ## debug check for double-trigger only if(i>1000): long(count) else: short(count) -
In case anyone is interested I created a class:
from time import sleep_ms, ticks_ms from machine import Pin import gc class BUTTON: def __init__(self,pid='P12',longms=1000): self.longms = longms self.butms = 0 self.pin = Pin(pid, mode=Pin.IN, pull=Pin.PULL_UP) self.pin.callback(Pin.IRQ_FALLING | Pin.IRQ_RISING, self.press) def long(self): pass def short(self): pass def press(self,pin): now = ticks_ms() if self.butms == 0: self.butms = now else: if self.butms == now: return i = 0 while i < 10: sleep_ms(1) if self.pin() == 1: i = 0 else: i+=1 while self.pin() == 0: i+=1 if(i > self.longms): break sleep_ms(1) if(i>1000): self.long() else: self.short() while self.pin() == 0: pass gc.collect()And to call it:
def short(): print('short') def long(): print('long') import button but = button.BUTTON() but.short = short but.long = long -
@livius - I am using internal pullup, I could also add a decoupling capacitor across the switch but I wanted to do it all in software instead.
-
@crankshaft
Hello !
I've tried to use your class with 3 different buttons but it seems once a button interrupt is triggered, all other buttons interrupts don't work anymore and only the future interrupts from the first pressed button are triggered.
I've been searching a lot for an explanation but couldn't find any.
Did you experience that kind of behavior ? Do you have a clue as to where that can come from ?
Thanks for your help ! -
@gael-wogenstahl
can you show how you declare all?
Code for three buttons? -
@livius
here are the BUTTON class code (slightly modified from your version, mainly to add comments)from time import sleep_ms, ticks_ms, ticks_diff from machine import Pin from _thread import start_new_thread import gc class BUTTON: def __init__(self, pid='P10', longms=1000): self.pressms = 0 self.longms = longms self.pin = Pin(pid, mode=Pin.IN, pull=Pin.PULL_UP) self.pin.callback(Pin.IRQ_FALLING | Pin.IRQ_RISING, self.press) def long(self): pass def short(self): pass def press(self, pin): # If never pressed, store press time if self.pressms == 0: self.pressms = ticks_ms() else: # If pressed within 500 ms of first press, discard (button bounce) if ticks_diff(self.pressms, ticks_ms()) < 500: return # Wait for value to stabilize for 10 ms i = 0 while i < 10: sleep_ms(1) if self.pin() == 1: i = 0 else: i+=1 # Measure button press duration while self.pin() == 0: i+=1 if(i > self.longms): break sleep_ms(1) # Trigger short or long press if(i > self.longms): start_new_thread(self.long, ()) else: start_new_thread(self.short, ()) # Wait for button release while self.pin() == 0: pass self.pressms = 0 gc.collect()and here is the code I use to instantiate the different buttons
import button def short_red(): print('Short red button press') def long_red(): print('Long red button press') def short_yellow(): print('Short yellow button press') def long_yellow(): print('Long yellow button press') def short_black(): print('Short black button press') def long_black(): print('Long black button press') red_button = button.BUTTON(pid='P22',longms=500) red_button.short = short_red red_button.long = long_red yellow_button = button.BUTTON(pid='P21',longms=500) yellow_button.short = short_yellow yellow_button.long = long_yellow black_button = button.BUTTON(pid='P10',longms=500) black_button.short = short_black black_button.long = long_black -
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login