trying to debounce

I came across this as I was trying to figure out how to debounce some buttons.

My problem with it is that it seems to me that it will initiate what ever the button initiates the first time the button bounces, then let the button settle down. I feel like I would rather let the button settle down and if the button has been settled for 10ms then initiate what ever the button initiates.

I am not sure weather this is something I should worry about, the only thing I think it would effect is if stray voltage caused a 1ms change in switchState without the button actually being pressed.

I will be using a 10k pull down resistor. Will this stop stray voltage from messing with my UI?

How would you initiate actions AFTER the debounce?

const byte switchPin = 8;
byte oldSwitchState = HIGH;  // assume switch open because of pull-up resistor
const unsigned long debounceTime = 10;  // milliseconds
unsigned long switchPressTime;  // when the switch last changed state

void setup ()
  {
  Serial.begin (115200);
  pinMode (switchPin, INPUT_PULLUP);
  }  // end of setup

void loop ()
  {
  // see if switch is open or closed
  byte switchState = digitalRead (switchPin);
  
  // has it changed since last time?
  if (switchState != oldSwitchState)
    {
    // debounce
    if (millis () - switchPressTime >= debounceTime)
       {
       switchPressTime = millis ();  // when we closed the switch 
       oldSwitchState =  switchState;  // remember for next time 
       if (switchState == LOW)
          {
          Serial.println ("Switch closed.");
          }  // end if switchState is LOW
       else
          {
          Serial.println ("Switch opened.");
          }  // end if switchState is HIGH
           
       }  // end if debounce time up
        
    }  // end of state change
     
  // other code here ...
   
  }  // end of loop

If you want to see the actual code I am trying to debounce, here ya go.

int menu = 1;
int subMenu = 1;
int subMenuPrev = 0;
int menuPrev = 0;
int varNumb=0;
int pumpMins=30;
unsigned long pumpDelay = pumpMins*60*1000UL;
int soilMoistureSetpoint = 430;
int soilMoisture = 500;
int rh = 60;

int mornHours = 2;
int dayCount = 0;
int hoursOff;
int hoursOn;

boolean morning = false;
boolean day = true;

int uvOnSecs = 3;

int increase = 23;
int decrease = 24;
int left = 25;
int right = 26;
int up = 27;
int down = 28;

float temp_f;
long co2ppm = 1100;

long co2Low;
long co2High;

int fanMins = 15;
unsigned long fanDelay = fanMins * 60 * 1000UL;

float tempLow;
float tempHigh;

long dayCO2Low = 1000;
long dayCO2High = 1200;
long nightCO2Low = 500;
long nightCO2High = 600;

float dayTempLow = 90;
float dayTempHigh = 100;
float mornTempLow = 65;
float mornTempHigh = 75;
float nightTempLow = 75;
float nightTempHigh = 85;

// include the library code:
#include "Wire.h"
#include "Adafruit_LiquidCrystal.h"

// Connect via i2c, default address #0 (A0-A2 not jumpered)
Adafruit_LiquidCrystal lcd(0);



void setup() {

  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  // set up the LCD's number of rows and columns:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("hello, world!");
}

void loop()
{
  unsigned long uvDelay = uvOnSecs * 1000UL;
  userInt();
  assignSetpoints();
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 0);
  lcd.setBacklight(HIGH);
}
//#######################################################
void userInt()
{
  if(digitalRead(up)==HIGH)
  {
    subMenu++;
    updateLCD();
  }
  if(digitalRead(down)==HIGH)
  {
    subMenu--;
    updateLCD();
  }
  if(digitalRead(right)==HIGH)
  {
    menu++;
    updateLCD();
  }
  if(digitalRead(left)==HIGH)
  {
    menu--;
    updateLCD();
  }
  if(digitalRead(increase)==HIGH)
  {
    
  }
}
void assignSetpoints()
{
  if (day)
  {
    co2Low = 1000;
    co2High = 1200;
    tempLow = 90;
    tempHigh = 100;
  }
  if (morning)
  {
    tempLow = 65;
    tempHigh = 75;
  }
  if (!day)
  {
    co2Low = 500;
    co2High = 600;
    tempLow = 75;
    tempHigh = 85;
  }
}
void updateLCD()
{
   if (menu == 1 && subMenu == 1)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("co2");
    lcd.setCursor(0, 1);
    lcd.print(co2ppm );
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=0; 
  }

  if (menu == 1 && subMenu == 2)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("dayCO2Low");
    lcd.setCursor(0, 1);
    lcd.print(dayCO2Low);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=1;
  }
  if (menu == 1 && subMenu == 3)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("dayCO2High");
    lcd.setCursor(0, 1);
    lcd.print(dayCO2High);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=2;
  }
  if (menu == 1 && subMenu == 4)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("nightCO2Low");
    lcd.setCursor(0, 1);
    lcd.print(nightCO2Low);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=3;
  }
  if (menu == 1 && subMenu == 5)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("nightCO2High");
    lcd.setCursor(0, 1);
    lcd.print(nightCO2High);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=4;
  }
  if (menu == 1 && subMenu > 5)
  {
    subMenu = 1;
  }
  if (menu == 1 && subMenu < 1)
  {
    subMenu = 5;
  }
  //_________________________________________________________
  if (menu == 2 && subMenu == 1)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Temperature");
    lcd.setCursor(0, 1);
    lcd.print(temp_f);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=0;
  }
  if (menu == 2 && subMenu == 2)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("dayTempLow");
    lcd.setCursor(0, 1);
    lcd.print(dayTempLow);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=5;
  }
  if (menu == 2 && subMenu == 3)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("dayTempHigh");
    lcd.setCursor(0, 1);
    lcd.print(dayTempHigh);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=6;
  }
  if (menu == 2 && subMenu == 4)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("mornTempLow");
    lcd.setCursor(0, 1);
    lcd.print(mornTempLow);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=7;
  }
  if (menu == 2 && subMenu == 5)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("mornTempHigh");
    lcd.setCursor(0, 1);
    lcd.print(mornTempHigh);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=8;
  }
  if (menu == 2 && subMenu == 6)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("nightTempLow");
    lcd.setCursor(0, 1);
    lcd.print(nightTempLow);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=9;
  }
  if (menu == 2 && subMenu == 7)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("nightTempHigh");
    lcd.setCursor(0, 1);
    lcd.print(nightTempHigh);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=10;
  }
  if (menu == 2 && subMenu > 7)
  {
    subMenu = 1;
  }
  if (menu == 2 && subMenu < 1)
  {
    subMenu = 7;
  }
  //_______________________________________________________
  if (menu == 3 && subMenu == 1)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("RH");
    lcd.setCursor(0, 1);
    lcd.print(rh);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=0;
  }
  if (menu == 3 && subMenu > 7)
  {
    subMenu = 1;
  }
  if (menu == 3 && subMenu < 1)
  {
    subMenu = 7;
  }
  //___________________________________________________________
  if (menu == 4 && subMenu == 1)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Soil Moisture");
    lcd.setCursor(0, 1);
    lcd.print(soilMoisture);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=0;
  }
  if (menu == 4 && subMenu == 2)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("soilMoisture");
    lcd.setCursor(0, 1);
    lcd.print(soilMoistureSetpoint);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=11;
  }
  if (menu == 4 && subMenu == 3)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("pumpMins");
    lcd.setCursor(0, 1);
    lcd.print(pumpMins);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=12;
  }
  if (menu == 4 && subMenu > 3)
  {
    subMenu = 1;
  }
  if (menu == 4 && subMenu < 1)
  {
    subMenu = 3;
  }
  if (menu == 5 && subMenu == 1)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Lighting");
    lcd.setCursor(0, 1);
    if (day)
    {
      lcd.print("on");
    }
    else
    {
      lcd.print("off");
    }
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=0;
  }
  if (menu == 5 && subMenu == 2)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("hoursOn");
    lcd.setCursor(0, 1);
    lcd.print(hoursOn);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=13;
  }
  if (menu == 5 && subMenu == 3)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("hoursOff");
    lcd.setCursor(0, 1);
    lcd.print(hoursOff);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=14;
  }
  if (menu == 5 && subMenu == 4)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("mornHours");
    lcd.setCursor(0, 1);
    lcd.print(mornHours);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=15;
  }
  if (menu == 5 && subMenu == 5)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("fanMins");
    lcd.setCursor(0, 1);
    lcd.print(fanMins);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=16;
  }
  if (menu == 5 && subMenu == 6)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("uvOnSecs");
    lcd.setCursor(0, 1);
    lcd.print(uvOnSecs);
    menuPrev = menu;
    subMenuPrev = subMenu;
    varNumb=17;
  }
  if (menu == 5 && subMenu < 1)
  {
    subMenu = 6;
  }
  if (menu == 5 && subMenu > 6)
  {
    subMenu = 1;
  }
}

My button library returns status of the button that includes current and previous states as well as if the button is bouncing. That is done as bits 0, 1 and 2 of the status.

In the sketch, only the status value needs to be checked. If status == 2 then button has just finished press&debounce.

I show a single button example below, I also have an array example.

/*
  button.h for public domain use by GoForSmoke May 29 2015
  Revised Sept 2016  
  To use:
  make a buttonclass object in your sketch
  run that object every time through loop(), it is quickly done
  when you want to know the status of the button, you read the object
  it returns state;  bit 0 = current, bit 1 = previous, bit 2 = bounce
*/

// button library, Sept 25th, 2016 by GoForSmoke

#ifndef button_h
#define button_h

#include "Arduino.h"

#define CURRENT_1 1
#define PREVIOUS_2 2
#define BOUNCE_4 4

typedef class button
{
private:
  byte arduPin;
  byte buttonState; // bit 0 = current, bit 1 = previous, bit 2 = bounce
  byte startMs;
  byte debounceMs; 

public:
  button(); // default constructor
  void setButton( byte, byte ); // pin, debounce millis
  button( byte, byte ); // pin, debounce millis
  void setUpButton( void );
  byte runButton( void ); // returns buttonState as below
  // buttonState: bit 0 = current, bit 1 = previous, bit 2 = bounce
  byte buttonRead();  // returns buttonState as above
};

#endif
// button library, Sept 25th, 2016 by GoForSmoke

#include "Arduino.h"
#include "button.h"

button::button() // default constructor for arrays, needs the full setup
{
  buttonState = CURRENT_1;
}

button::button( byte ap, byte dbm )
{
  arduPin = ap;
  debounceMs = dbm;
  buttonState = CURRENT_1;
};

void button::setButton( byte ap, byte dbm ) // pin, debounce millis
{
  arduPin = ap;
  debounceMs = dbm;
  pinMode( arduPin, INPUT_PULLUP );
};


void button::setUpButton( void )
{
  pinMode( arduPin, INPUT_PULLUP );
};

byte button::buttonRead()
{
  return buttonState;
};


byte button::runButton( void )
{
//  static byte msNow;

  buttonState &= BOUNCE_4 | CURRENT_1; // clears previous state bit
  buttonState |= ( buttonState & CURRENT_1 ) << 1; // copy current state to previous
  buttonState &= BOUNCE_4 | PREVIOUS_2; // clears current state bit
  buttonState += digitalRead( arduPin ); // current state loaded into bit 0

//  msNow = (byte) millis(); // gets the low byte of millis

  if ( buttonState & 3 == CURRENT_1 || buttonState & 3 == PREVIOUS_2 )  // state change detected
  {
    buttonState ^= BOUNCE_4;      // toggles debounce bit
    // on 1st and odd # changes since last stable state, debounce is on
    // on bounces back to original state, debounce is off 
    if ( buttonState & BOUNCE_4 )
    {
//      startMs = msNow;    // starts/restarts the bounce clock on any change.
      startMs = (byte) millis();    // starts/restarts the bounce clock on any change.
    }
  }
  else if ( buttonState & BOUNCE_4 ) // then wait to clear debounce bit
  {
//    if ( msNow - startMs >= debounceMs ) // then stable button state achieved
    if ( (byte)((byte)millis()) - startMs >= debounceMs ) // then stable button state achieved
    {
      //   understand that stable state means no change for debounceMs. When time
      //   is over the state bits are manipulated to show a state change.
      buttonState &= CURRENT_1; // clear all but the current state bit
      if ( buttonState == 0 )  buttonState = PREVIOUS_2;  // HIGH->LOW
      else                     buttonState = CURRENT_1;  // LOW->HIGH
      //   buttonState now appears as a debounced state change in bits 0 and 1
    }
  }

  return buttonState;
};

Example:

// buttonclass2016 by GoForSmoke @ Arduino Forum
// Free for use, Sept 25th, 2016

#include <avr/io.h>
#include "button.h";  // put the library files in the same folder as the sketch

button  myButton( 7, 5 ); // makes button on pin 7 with 5ms debounce


void setup()
{
  Serial.begin( 2000000 );
  Serial.println( F( "\n\n\n\nTEST\n" ));

  myButton.setUpButton(); // this starts the button object
};


void loop()
{
  byte buttonState = myButton.runButton(); // this runs the button object

  switch ( buttonState )
  {
    case CURRENT_1 :
      Serial.print( F( "up    " ));
      Serial.println( millis());
      break;
    case PREVIOUS_2 :
      Serial.print( F( "down  " ));
      Serial.println( millis());
      break;
  }
}

I do appreciate the help, but using a library won't help me understand the concept.

Here is an example not using a library:

 //***************************************************************************
//Simple demonstration showing one way to handle switch scanning and
//how to filter noise on a pin input.
//In this case, a switch input that is less than 20ms is ignored.
//***************************************************************************
 
const byte heartBeatLED = 13;
const byte myLED        = 12;
const byte myButton     = 8;
 
byte lastButton         = HIGH;
byte sampleCounter      = 0;
byte currentButton;
 
unsigned int counter;
 
unsigned long currentMillis;
unsigned long heartBeatMillis;
unsigned long lastMillis;
 
const unsigned long heartBeatDelay = 250UL; //toggle LED every 1/4 second
const unsigned long switchDelay    = 10UL;  //read switch(es) every 10ms
 
//***************************************************************************
void setup()
{
  Serial.begin(9600);
 
  pinMode (heartBeatLED, OUTPUT);
  pinMode (myLED, OUTPUT);
 
  pinMode (myButton, INPUT_PULLUP);
 
} //END of                        s e t u p ( )
 
//***************************************************************************
void loop()
{
  currentMillis = millis();
 
  //***************************
  //The ‘Heart Beat LED’, should toggle every heartBeatDelay milliseconds if code is nonblocking
  if (currentMillis - heartBeatMillis >= heartBeatDelay)
  {
    //reset timing
    heartBeatMillis = heartBeatMillis + heartBeatDelay;
   
    //Toggle heartBeatLED
    digitalWrite(heartBeatLED, !digitalRead(heartBeatLED));
  }
 
    //go check the switches
    checkSwitches();
 
  //***************************
  // Other nonblocking code
  //***************************
 
 
} //END of                      l o o p  ( )
 
 
//***************************************************************************
void checkSwitches()
{
  //Time to check the switches? (10ms)
  if (currentMillis - lastMillis < switchDelay)
  {
    //it's not time
    return;
  }
    //reset timing for next iteration
    lastMillis = lastMillis + switchDelay;
 
  //***************************
  //We must read 2 'sequential' samples before we accept a valid button change.
  //Hence, it takes 2 X 10ms = 20ms to detect a valid button change.
  //This filters out any circuit noise less than 20ms.
 
  currentButton = digitalRead(myButton);
 
  //Has there been a button state change?
  if (lastButton == currentButton)
  {
    //no change, reset the sample counter
    sampleCounter = 0;
    
    return;
  }

    //there was a button change
    sampleCounter++; //used in filtering circuit noise
 
    //Is this the 2nd time in two sequential reads
    if (sampleCounter >= 2)
    {
      //Is the button pushed?
      if (currentButton == LOW) //Button pushed makes the pin LOW
      {
        //do something
        counter++;
        Serial.println(counter);
        digitalWrite(myLED, !digitalRead(myLED));
      }
 
      //the button is HIGH (released)
      else
      {
        //do button HIGH stuff
      }
 
      //update the lastButton state for the next read
      lastButton = currentButton;
      //finished with this button change, get ready for the next 2 samples
      sampleCounter = 0;
    }
 
  //***************************
 
} //END of             c h e c k S w i t c h e s ( )
 
//***************************************************************************

roosterqmoney:
I will be using a 10k pull down resistor. Will this stop stray voltage from messing with my UI?

It is not stray voltage to care about. It is current used and ---- stray? Huh? If you wire bad, something might burn up on the board.

One thing I do with inputs to buttons is use the AVR chip internal 20K to 50K pullup resistor through pinMode( myPin, INPUT_PULLUP). If the pin is grounded, digitalRead() will return LOW ( zero ) and if not then HIGH ( one). At no time will the pin source enough current to be damaged and all the current it does source goes to ground, no stray anything.

AVR chips have quite a few beautiful features for such simple (compared to PC CPUs) low cost devices.

How would you initiate actions AFTER the debounce?

That's why I gave you the library and example. The code is the best way to show it!

I do appreciate the help, but using a library won't help me understand the concept.

It will if you figure out the code, especially in the run function source in button.cpp. That handles the debounce and status. Most of the lines there are comments, the code is pretty spare given what it does.

thank you larryd, that does exactly what I want it to do, and I learned about "return." Seems like return doesn't get used much in beginner stuff, this being the first time I have seen it.

this is where I got stray voltage from, its from the same place I got the first code I posted. Gammon Forum.

I just want to make sure weird stuff doesn't happen, and waiting for the button to stabilize seems like the way to go.

"This will not work properly because the switch input (digital pin 8 in this example) is "floating" if the switch is open.

The switch is definitely +5V when closed (and thus returns HIGH when tested with digitalRead) but is not necessarily at 0V when open. In fact, waving your hand over the processor board is likely to generate enough stray voltages to make it look like the switch is being opened and closed."

I don't know how to look at a the inside of a library yet.

This is one of the de facto standards when it come to switches.
http://www.gammon.com.au/forum/?id=11955

.

You don't call pinMode() on any of your switches. Unless they already have pull up or pull down resistors, that is a problem. You mentioned 10k resistors. Are you sure you have them connected correctly?

that is an excerpt from a much larger sketch, I forgot to excerpt the pinmodes. My buttons and resistors are wired up properly, arduino pin to one side of button with a resistor to ground, and 5v to the otherside.

Then how about giving us complete code, instead of excerpts?

Yep.... if you know roughly the bouncing duration of the switch .... then just make your code ignore the unwanted extra switch signals until after some time has passed (relative to the first switch signal).

I recall reading something that said don't post a large sketch if you don't have to if you can post a small portion that illustrates the problem/question.

Almost. You're supposed to make a small sketch that demonstrates the problem.

You need an oscilloscope to see what kind of bouncing is happening with your particular switch. Otherwise you are trying to fix a misunderstood problem.

A switch with no pullup or pulldown resistor is floating. This isn't a bouncing problem, it's just a problem of not knowing what state the button is in for sure. The resistors give it a known state when the button is just sitting there.

Pushing the button changes it's state. If you use a pulldown correctly, pushing the button makes it HIGH. If you use a pullup correctly, pushing the button makes it LOW.

When you use PU/PD resistor correctly, detecting state change and acting upon it is usually enough time to not even worry about debouncing. I.e., if your button has changed state in one direction, your code is busy doing things before it cares about the state of the button again.

Your idea about 'waiting out some unknown bouncy behavior' is a solution looking for a problem and is the wrong approach. You will only achieve a button that is less responsive.

roosterqmoney:
My problem with it is that it seems to me that it will initiate what ever the button initiates the first time the button bounces, then let the button settle down. I feel like I would rather let the button settle down and if the button has been settled for 10ms then initiate what ever the button initiates.

Well, normally with debounce you have a variable holding the time at which the button was pressed, right? Well, just update that to the current time whenever you detect the the button is in the wrong state.

However … you may find if you do this that the button becomes 'locked' if the button is pressed and released quickly.

The ideal conditions for a button press are not the same as for a button release. Ideally, a button press should respond immediately to the first active edge. Why wait? It is not prudent to wait around at that point because "a press is a press". Generally we shoudn't care about delays in a button release except in terms of reaching the idle state where we are waiting for the next press, as soon as possible. That is because the release is not connected with an event (it is not a trigger).

1. In Electrical Engineering, we call it Switch; whereas, in Electronic Engineering we call it Button/Key. The very basic constructional foundation of these switches are mechanical. They do the same thing - either closing a circuit or opening a circuit.

2. After closing a switch we see that the 'Light is ON', and we believe that the switch has made the contact in just 'one go'. But, the actual fact is different. It is a natural phenomenon that any mechanical switch when closed/opened makes hundreds of to-and-fro movements (called bouncing) along with the generation of 'High Voltage Spikes (this is not stray voltage!)' before making the final contact (either close or open whatever has been desired). It has been reported that the bouncing time of a mechanical switch is about 200 ms (Fig-1).

Figure-1: Example of rising-edge switch bounce

3. In electrical engineering, as people are dealing with high voltage, they don't need to take much care of the bouncing events of the switches. In digital electronics, we must take care to (a) limit the amplitudes of the 'High Voltage Spikes', (b) prevent them from appearing at the physical pin of a digital chip; otherwise, the chip might break down and/or receiving unwanted multiple trigger signals. (The hard times come when one tries to demonstrate to his pupils the functionality of INTO interrupt just by connecting a button at the INT0-pin, and then injecting interrupt request signal by pressing the button!)

If we look at the reset circuit (Fig-2) of the Arduino UNO, we see that there is a diode (D2) at the node of RST/-pin, 5V, and RESET-Button. The diode limits the 'High Voltage Spikes' and saves the internal reset circuitry of the ATmega328. (Why is not there a full debounching circuit?)

Figure-2: Part of the reset-circuit of the ATmega328 of Arduino UNO

4. All of our efforts are now directed towards the successful achievement of the objectives of Step-3. known as Debouncing. People have been using one or more of the following debounching mechanisms for this purposes, but their effectiveness varies as all the switches are not alike.

(a) Hardware circuit called Debouncing Circuit
(b) Software codes called Debouncing Logic

The choice of the mechanism depends on the requirements of the stake holder. The acceptable one is always that one which just works fine!

aarg:
The ideal conditions for a button press are not the same as for a button release. Ideally, a button press should respond immediately to the first active edge. Why wait? It is not prudent to wait around at that point because "a press is a press". Generally we shoudn't care about delays in a button release except in terms of reaching the idle state where we are waiting for the next press, as soon as possible. That is because the release is not connected with an event (it is not a trigger).

I think that a spike/transient is not a press but could look like one. I dunno exactly what would cause it, but in tests I did get occasional falses that quit when I changed my code. In my debounce only every other change is timed. If the series ends up in the same state as before the status won't indicate a press/contact.

Typically I shoot for 5ms with no change but it does run at 2ms unless I drag the jumper on the port box badly. What can I say? I wrote for dirty buttons by making dirty contact (electronics porn?) and with millis sometimes you get off by 1ms timing.

Figure-1: Example of rising-edge switch bounce

I note that those spikes are the same voltage as the signal after stable contact is made, no higher.

We deal with 5V and 3V, not high voltage and we don't get higher V spikes.
What we get is sparks jumping between contacts when the get close enough or on opening, until they get far enough to quit crossing the tiny gap that 5V and 3V will cross.

If the button is crap, it won't keep solid mechanical contact and may give an occasional twitch that ends in the same state it started. You can tell if by wobbling your finger on the button you get change detection.