Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    Interrupt button debounce with long / short press

    Scheduled Pinned Locked Moved WiPy 2&3
    15 Posts 5 Posters 5.5k Views 7 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • M Offline
      mohpor
      last edited by

      @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?

      C 1 Reply Last reply Reply Quote 0
      • C Offline
        crankshaft @mohpor
        last edited by

        @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 !

        1 Reply Last reply Reply Quote 0
        • M Offline
          mohpor
          last edited by

          @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.

          1 Reply Last reply Reply Quote 0
          • L Offline
            livius @crankshaft
            last edited by livius

            @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

            think about people first, then computers

            C 1 Reply Last reply Reply Quote 0
            • C Offline
              crankshaft
              last edited by

              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)
              
              1 Reply Last reply Reply Quote 0
              • C Offline
                crankshaft
                last edited by crankshaft

                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
                
                G 1 Reply Last reply Reply Quote 1
                • C Offline
                  crankshaft @livius
                  last edited by

                  @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.

                  1 Reply Last reply Reply Quote 0
                  • G Offline
                    gael-wogenstahl @crankshaft
                    last edited by

                    @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 !

                    L 1 Reply Last reply Reply Quote 0
                    • L Offline
                      livius @gael-wogenstahl
                      last edited by

                      @gael-wogenstahl
                      can you show how you declare all?
                      Code for three buttons?

                      think about people first, then computers

                      G 1 Reply Last reply Reply Quote 0
                      • G Offline
                        gael-wogenstahl @livius
                        last edited by

                        @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
                        
                        M 1 Reply Last reply Reply Quote 0
                        • M Offline
                          mahe @gael-wogenstahl
                          last edited by

                          @gael-wogenstahl

                          Hi,

                          I'm experiencing the same kind of problems - did you ever solve it?

                          Thanks

                          1 Reply Last reply Reply Quote 1

                          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
                          • 1 / 1
                          • First post
                            Last post

                          Pycom on Twitter

                          Pycom