MicroPython Button Debounce

Wemos ESP8266 MicroPython Power Consumption

Reading and processing button presses with microcontrollers is a lot harder than one could assume because of the signal noise for which we have hardware and software solutions.

I couldn’t find a performant software solution for ESP8266 running MicroPhython (Wemos D1 mini) so I came up with an idea of listening for a pin change interrupt and starting a fixed “one-shot” timer for 200ms that restarts on every interrupt and triggers the final callback once the timer ends:

from machine import Pin, Timer

def on_pressed(timer):
    print('pressed')

def debounce(pin):
    # Start or replace a timer for 200ms, and trigger on_pressed.
    timer.init(mode=Timer.ONE_SHOT, period=200, callback=on_pressed)

# Register a new hardware timer.
timer = Timer(0)

# Setup the button input pin with a pull-up resistor.
button = Pin(0, Pin.IN, Pin.PULL_UP)

# Register an interrupt on rising button input.
button.irq(debounce, Pin.IRQ_RISING)

It delays the button press event for 200ms after the last interrupt trigger. There is no way of knowing if you’ve actually released the button so it will most likely trigger another event if the button press lasts for more than 200ms.

Are there better ways of doing this? Please share you ideas in the comments.

3 Comments

  1. vdl says:
    * global variables
    - counter
    - button_pressed
    * setup button irq 
    * in irq 
    - set button_pressed true/false
    - reset counter
    * in main loop or timer :
    if button_pressed && ++counter==x 
        print "pressed"
    

    x is 200 if using timer with 1ms interval

  2. Chamith Siriwardena says:

    My implementation is slightly different and it works as I expected. I’m not an expert in either Python or Micro-controller programming, so there could be improvements to the code.

    interrupt = 1
    tim = machine.Timer(-1)
    
    def enable_interrupt(timer):
        global interrupt
        interrupt = 1
    
    def handle_pin_change(pin, value):
        pinConfig = pin_config_util.getThingByInputPin(pin)
        output_pin_id = pin_config_util.getOutputPinConfig(pinConfig.thing_id, pinConfig.thing_prop)
        # DO THE REAL WORK HERE
    
    #THIS IS WHAT WE HOOK UP WITH PIN.IRQ
    def debounce(pin):
        global interrupt
        if interrupt == 1 and pin.value() == 1:
            tim.init(period=300, mode=machine.Timer.ONE_SHOT, callback=lambda t:enable_interrupt(t))
            interrupt = 0
            handle_pin_change(pin, pin.value())
    
  3. Pessach says:

    Thank you. Worked perfectly and is very simple.

Leave a Reply