Button debouncing problem

Hi,

Sorry if this is the wrong section of the forum to be posting this, but I'm having a problem with button debouncing in my sketch. When I press the button once it is registering several button presses at once instead of just one.

I'm using the example debouncing code from https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce in my sketch and when I run this example in a standalone sketch it works as expected, registering just one button press each time I press the button so I think it is a problem with my sketch, specifically.

I'm running this on a Wemos D1 Mini and using platformio as my IDE. My sketch code is below:

#include <Arduino.h>

// The LED will begin blinking every second, then the interval variable will be decremented by
// 50 milliseconds each time the switch is pressed, making the LED blink progressively faster 
// until the interval variable is 50 milliseconds at which point it will reset to 1000 milliseconds 
// and pressing the switch will decrement the interval variable again

int switchstate; // stores the current state of the switch (HIGH or LOW)
int lastswitchstate = LOW; // stores the last known state of the switch
unsigned long lastdebouncetime = 0; //Stores the last time the button was pressed
unsigned long debouncedelay = 100; //the debounce time in milliseconds.  


int ledPin = 5; //led pin (D1 on wemos D1 Mini)
int switchpin = 16; //Switch pin (D0 on Wemos D1 mini)


unsigned long interval = 1000; //Starting interval between LED blinks in milliseconds

unsigned long previousMillis = 0; //stores the last time the led was updated
int ledState = LOW; // stores the state of the LED

unsigned long currentMillis = 0;


void setup() {
 // put your setup code here, to run once:

 //setup the serial port, 9600 baud
 Serial.begin(9600);

 //set led pin as an output
 pinMode(ledPin, OUTPUT);

 //set switch pin as an input
 pinMode(switchpin, INPUT);

}

void loop() {
 // put your main code here, to run repeatedly:

 unsigned long currentMillis = millis();

 // read the state of the switch pin into a local variable
 int reading = digitalRead(switchpin);

 // check to see if the button was pressed (LOW to HIGH transition)
 // and we've waited long enough since the last press to avoid any noise
 if(reading != lastswitchstate){
   //reset the debouncing timer
   lastdebouncetime = millis();
 }

 if ( (millis() - lastdebouncetime) > debouncedelay ){
     //whatever the reading is at, its been there longer than than the debounce delay
     //so this will be its actual state
     //Serial.println("millis - lastdebouncetime is more than debounce delay");
     //if the button has changed
     if(reading != switchstate){
       switchstate = reading;
       Serial.println("switchstate set equal to reading");
     }

     //Decrement the interval variable by 50 if the switch state is high (pressed)
     if(switchstate == HIGH){
       interval = interval - 50;
       Serial.println("interval reduced by 50ms");
     }  
     // reset interval to 1000 if it reaches 50ms or less than 50ms
     if(interval <= 50){
       interval = 1000;
     }    
 }

 //blink the led at the frequency defined in interval
 if(currentMillis - previousMillis > interval) {
   previousMillis = currentMillis; // Save last time we blinked the LED

   // If LED is off turn it on and visa-versa
   if(ledState == LOW){
     ledState = HIGH;
   }else{
     ledState = LOW;
   }
 
   // write state of led to the relevent pin
   digitalWrite(ledPin, ledState);

 }

 // save the switch reading. next time through the loop it will be the lastswitchstate  
 lastswitchstate = reading;

}

Example of the output from the Serial.println debug lines for one button press is below

switchstate set equal to reading
interval reduced by 50ms
interval reduced by 50ms
interval reduced by 50ms
interval reduced by 50ms
interval reduced by 50ms
switchstate set equal to reading

So it appears that the part of the sketch that decrements the interval variable is being run several times for each button press but I'm not sure why.

I like this form of state change detection and debounce:

const byte ButtonPin = 2;
const unsigned long DebounceTime = 30;


void setup()
{
  Serial.begin(115200);


  pinMode(LED_BUILTIN, OUTPUT);
  pinMode (ButtonPin, INPUT_PULLUP);  // Button between Pin and Ground
}


void loop()
{
  unsigned long currentTime = millis();
  boolean buttonIsPressed = digitalRead(ButtonPin) == LOW;  // Active LOW
  static boolean buttonWasPressed = false;
  static unsigned long buttonStateChangeTime = 0;


  // Check for button state change and do debounce
  if (buttonIsPressed != buttonWasPressed &&
      currentTime -  buttonStateChangeTime > DebounceTime)
  {
    // Button state has changed
    buttonStateChangeTime = currentTime;
    buttonWasPressed = buttonIsPressed;


    if (buttonIsPressed)
    {
      // Button was just pressed
    }
    else
    {
      // Button was just released
    }
  }
}

consider

conventional debounce - simple delay

and just invert the state of the pin

#include <Arduino.h>
// The LED will begin blinking every second, then the interval variable will be decremented by
// 50 milliseconds each time the switch is pressed, making the LED blink progressively faster
// until the interval variable is 50 milliseconds at which point it will reset to 1000 milliseconds
// and pressing the switch will decrement the interval variable again
int lastswitchstate = LOW; // stores the last known state of the switch
int ledPin = 13; //led pin (D1 on wemos D1 Mini)
int switchpin = A1; //Switch pin (D0 on Wemos D1 mini)
unsigned long interval = 1000; //Starting interval between LED blinks in milliseconds
unsigned long previousMillis = 0; //stores the last time the led was updated
unsigned long currentMillis = 0;

// -----------------------------------------------------------------------------
void setup () {
    // put your setup code here, to run once:
    //setup the serial port, 9600 baud
    Serial.begin (9600);
    //set led pin as an output
    pinMode (ledPin, OUTPUT);
    //set switch pin as an input
    pinMode (switchpin, INPUT);
}

// -----------------------------------------------------------------------------
void loop () {
    // put your main code here, to run repeatedly:
    unsigned long currentMillis = millis ();

    // read the state of the switch pin into a local variable
    int reading = digitalRead (switchpin);

    // check to see if the button was pressed (LOW to HIGH transition)
    // and we've waited long enough since the last press to avoid any noise
    if (reading != lastswitchstate){
        lastswitchstate = reading;

        if (LOW == reading)  {
            interval = interval - 50;
            if (interval <= 50)
                interval = 1000;
        }
        
        delay (10);
    }

    //blink the led at the frequency defined in interval
    if (currentMillis - previousMillis > interval) {
        previousMillis = currentMillis; // Save last time we blinked the LED

        // toggle output
        digitalWrite (ledPin, ! digitalRead (ledPin));

        Serial.println (interval);
    }
}

Hi,

Thank you very much for your responses. I think I've figured it out somewhat. I added

 lastdebouncetime = millis(); // store the last debounce time value

to line 57 of my sketch. Now it seems to give more consistent results with each button press though it still continually triggers when I hold the button down (something the debounce example sketch doesn't seem to do for me.)

You need to use a pull-up or pull-down resistor for input pin. The simplest way is to use INPUT_PULLUP

There is a simpler way: use a button library. the code will be simple like below:

#include <ezButton.h>

ezButton button(7);  // create ezButton object that attach to pin 7;

void setup() {
  Serial.begin(9600);
  button.setDebounceTime(50); // set debounce time to 50 milliseconds
}

void loop() {
  button.loop(); // MUST call the loop() function first

  if(button.isPressed())
    Serial.println("The button is pressed");

  if(button.isReleased())
    Serial.println("The button is released");
}

Thank you, I will try that. I'm using a 10K pull-down resistor in my circuit.

plavelle2k20:
Thank you, I will try that. I'm using a 10K pull-down resistor in my circuit.

With that library, you should not use any pull-up or pull-down resister. The library already used INPUT_PULLUP

Sorry about that, thank you for letting me know.

Thank you everyone. Got it working now.