Best method to trigger only one event when a button is pressed?

I am am using Particle Electron and trying to publish an event to the cloud when a button is pressed.

Currently, I am using Interrupts and to listening to them, but they get triggered several times for each button press.

What is the best method to trigger an event (code) only once when a push-button is pressed?

@Falcon, you get multiple interrupts due to contact “bounce”. Search the forum for “interrupt debounce” to look how others have addressed this. :smiley:

2 Likes

@peekay123 Thank you your suggestion, I’ve tried using ClickButton library to trigger a click and do the debouncing. It has significantly helped in debouncing the switch, but occasionally, I do have many (4 to 8) events being triggered at once i.e. just for one click.

Could you also suggest what is Debounce time ? which is set to a default value of 20ms. Could this vary between different switches ?

Here is a the Honeywell MicroSwitch GLAA26J1B that I am using along with my particle Electron.

    // This #include statement was automatically added by the Particle IDE.
#include <clickButton.h>

/* ClickButton library demo

  OUtput a message on Serial according to different clicks on one button.

  2010, 2013 raron

 GNU GPLv3 license
*/


// the Button
const int buttonPin1 = D2;  // Default 4
ClickButton button1(buttonPin1, LOW, CLICKBTN_PULLUP);

// Button results 
int function = 0;

int num = 10;

void setup()
{
  Serial.begin(9600);
  Serial.println("Hello");
  pinMode(D2, INPUT_PULLUP);

  // Setup button timers (all in milliseconds / ms)
  // (These are default if not set, but changeable for convenience)
  button1.debounceTime   = 20;   // Debounce timer in ms
  button1.multiclickTime = 250;  // Time limit for multi clicks
  button1.longClickTime  = 1000; // time until "held-down clicks" register
}


void loop()
{
  //num = num + 1;
  //Serial.println(num);
  // Update button state
  button1.Update();

  // Save click codes in LEDfunction, as click codes are reset at next Update()
  if (button1.clicks != 0) function = button1.clicks;

  if(button1.clicks == 1) Serial.println("SINGLE click");

  if(function == 2) Serial.println("DOUBLE click");

  if(function == 3) Serial.println("TRIPLE click");

  if(function == -1) Serial.println("SINGLE LONG click");

  if(function == -2) Serial.println("DOUBLE LONG click");

  if(function == -3) Serial.println("TRIPLE LONG click");

  function = 0;
  delay(5);
  

}

@Falcon, you may need to increase your bounce delay to 50ms (from 20) or more due to the large mechanical nature of this switch.

@peekay123 just tried doing that (increased the bounce delay to 50ms), but it still triggers multiple events for just 1 click . I just got 20 Double click events for just one double click. It happens similarly for single click events too.

@bko any suggestions or thoughts on how to resolve the bouncing effect that I am having using the micro switch.

First, I assume you can ignore double click events. Second, you may need to make the bounce delay even larger and make the multiclicktime and longclicktime very large.

Unable to remove Bouncing of switch/button using Click Button Library. Tested using debounce time of 50ms, 75ms & 150ms. Please see below.

Could someone please help me remove the bouncing of the switch ?

With Debounce delay of 50ms; MultiClick: 500ms; LongClick:2000ms

With Debounce delay of 75ms; MultiClick: 500ms; LongClick:2000ms

With a Debounce delay of 150ms; Multiclick: 500ms; Long Click: 2000ms

@Falcon
I just tried solving your problem, and I have come up with a solution that solves bouncing and the need for delays.

#include "Particle.h"

/*SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);*/

#define BUTTON D0

bool pressing = false;
unsigned int lastPress, lastRelease, previousLastPress, previousLastRelease;

int buttonHandler()  // 0 = not pressing, 1 = still pressing, 2 = just stopped pressing, 3 = just started pressing
{
        if (digitalRead(BUTTON) == LOW && pressing == false) // 3 = just started pressing
        {
                pressing = true;
                lastPress = millis();
                return 3;
        }

        if (digitalRead(BUTTON) == HIGH && pressing == true) // 2 = just stopped pressing
        {
                pressing = false;
                lastRelease = millis();
                return 2;
        }

        if (pressing == true) // 1 = still pressing
        {
                return 1;
        }
        else // 0 = not pressing
        {
                return 0;
        }
}

void setup() // Put setup code here to run once
{
        pinMode(BUTTON, INPUT_PULLUP);
        digitalWrite(BUTTON, HIGH);
        Serial.begin(9600);
}

void loop() // Put code here to loop forever
{
        int check = buttonHandler(); // get press status

        if (previousLastRelease != lastRelease) // Button bumped, pushed and then released at later time
        {
                // Particle.publish("click","Click took "+String(lastRelease - lastPress)+" ms");

                Serial.println("Click took "+String(lastRelease - lastPress)+" ms");
                previousLastRelease = lastRelease;
        }

}

In this example, the Photon replies via Serial how long a click took.

Example output (from Serial) for a bunch of “normal clicks”:

Click took 84 ms
Click took 91 ms
Click took 133 ms
Click took 131 ms
Click took 123 ms
Click took 119 ms
Click took 116 ms
Click took 129 ms
Click took 128 ms
Click took 134 ms
Click took 136 ms
Click took 1 ms
Click took 145 ms
Click took 148 ms

Sometimes I get values lower than 10ms, which I assume is just noise or bouncing, but it works really well.

2 Likes

@nrobinson2000, what's the digitalWrite() for?

@nrobinson2000, awesome works great!! Thank you very much.

1 Like

Is that not the correct way to set up a button with a pullup resistor, connecting the button between the pin and ground?

@nrobinson2000, pinMode(BUTTON, INPUT_PULLUP); sets the pin as input with the pull-up. This digitalWrite(BUTTON, HIGH); does nothing, unlike Arduino land where it activates the pull-up on an input.

2 Likes

The solution suggested by @nrobinson2000 works perfectly, when I don’t have events like, publish to the particle cloud / writing data to EEPROM in the loop function.

As I have to publish data to cloud, save data to EEPROM, this solution becomes unreliable.

I have started using the below code suggested by @BulldogLowell, it still doesn’t remove the bouncing of the interrupt fully, as it still registers 2 to 3 interrupts for each button press.

Could someone please guide me how I could resolve interrupt bouncing issue ?

Here is a the MicroSwitch that I am using along with my particle Electron.

int button = D2;
void press(void);
int buttonState=1;
volatile bool flag = false;

void setup() 
{
    Serial.begin(9600);
    //while(!Serial.available()) SPARK_WLAN_Loop();
    Serial.println("Interrupt Test");
    pinMode(button, INPUT_PULLUP);
    attachInterrupt(button, press, FALLING);
}

void loop() 
{
  if (flag == true)
  {
      Serial.println("Key Pressed");
      flag = false;
  }
}

void press()
{
    //Serial.println("switched now");
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 50)  // debounce time = 50milliseconds
  {
    flag = true;
  }
  last_interrupt_time = interrupt_time;
}
if (interrupt_time - last_interrupt_time > 50)  // debounce time = 50milliseconds

Some mechanical switches are very bouncy.

Try changing the debounce time to something a bit longer:

if (interrupt_time - last_interrupt_time > 150)  // debounce time = 150milliseconds

start larger and work your way down to where it becomes troublesome.

1 Like

After increasing the debounce time to about 1000ms, the interrupt bouncing has stopped.

However, 1000ms is a really long time for my application, any ideas or ways that I could use to bring the delay down to 50ms ?

One of the options that I was considering was, to use a mechanical relay between the micro switch and controller.

Below are the signals from the Micro Switch captured on Oscilloscope, from this I understand that the total bounce time is below 2ms, the problem may not be with the bounce time of the switch.

The problem could be with the logic of debouncing.

It could be that the interrupt on a pin (set to falling) is being triggered when switch makes contact (Falling) as well as when switch breaks contact (Rising).

Any ideas on triggering the interrupt only when the switch makes contact and not when it breaks the contact.

  1. When switch makes the contact (Falling).

  2. When switch moves away from contact (Rising)

FALLING interrupts do actually only fire when there is a falling edge - that is definet.
But as your scope shows even letting go of the button causes bounce which does again trigger falling edges.
That’s the reason why most actions don’t usually trigger on the button press but rather on the release.

Is the code from this post still your current version?

I’d try this ISR instead

const    uint32_t msDebounce = 50;
volatile uint32_t msLastTrig =  0;

void press()
{
  msLastTrig = millis();
}

void loop()
{
  if (msLastTrig && millis() - msLastTrig > msDebounce)
  { // we know there was a trigger and the bouncing should have stopped by now
    msLastTrig = 0;
    if (!digitalRead(button))
      doStuffOnPress();
    else
      doStuffOnRelease();
  }
}

Whith this approach you can also use a CHANGE interrupt that would react on any edge, since the actual signal probing happens after the bounce in loop()

1 Like

Below is what happens when I implement the above code. Even the button pressed method is called several times when button is pressed for a longer time. The button released method is called several times on release of the button.

@Falcon, it may be time to consider hardware debouncing by using an RC circuit to slow the switch rise time and “absorb” the bounce in both directions (on and off). Coupling the RC with a schmitt trigger would give a clean edge on the signal. Here is a good article dealing with bounce: