Milis and debounce

hi i play with button and milis, i understand the way is working but something is not fit in my mind.

buttonCState[i] = digitalRead(BUTTON_ARDUINO_PIN); 

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {
        lastDebounceTime[i] = millis();

        if (buttonCState[i] == LOW) {

how i understand it. buttoncstate is been read, lets say is high.
now millis it has start up since the arduino is power up. lets say millis is at 10000ms , last
debounce is 0

debounce delay is declared 100ms in the sketch. so 10000-0 is more that 100 so is true and go to next line

if (buttonPState[i] != buttonCState[i]) {

button is preset when mills was in 10000ms

because buttonpstate is not equal to buttoncstate then it move to the next parameter

lastDebounceTime[i] = millis();

in this faction millis will been write to lastdebounce, what time will be writen 10000ms?

if it wite 10000ms then 10000-1000=0 so is les than debouncedelay. so the program is stack
if the button is preset continiue then each time a new lastdebounce will be writen, so we are in endless loop.

this is in my mind.. i know is working but please explain the theory in simple words.

do you really want a debounce time of 1 sec? how about 10 msec

What do you want?
If you want to debounce the botton you have to reload the time when the debounce timer has triggert.
If you want a timer function you have to code this function separately from the debouncing.
Have a nice day and enjoy coding in C++.
p.s. You can use a struct for both timers.

Let's write it in a normal way:

buttonState = digitalRead(BUTTON_ARDUINO_PIN); 

if (millis() - previousMillis > debounceDelay) 
  if (lastButtonState != buttonState) 
    previousMillis = millis();

    if (buttonState == LOW) 

The 'previousMillis' may not be larger than the millis(), and that does never happen.
To update the 'previousMillis', the new button state should be different than the old button state.
If the button is pressed for 50 days, then the code is not correct anymore.

There are many ways for debouncing. I don't know if I would do it this way.

debouncedelay is 150ms i know is to mutch. 50ms is the normal but i set it to 150 for expirament.

button is preset after arduino is power up. and it pass 10000ms or 10 sec time. so i assume millis count is 10000ms, at this moment i press the button.

here is my confusion, if millis and previousmillis are the same, how the debounce will come.
if button not been preset milis will count and previousmillis will be the old value.

so next time a button presset the faction is allready true. because millis is match bigger than previousmillis.

assume lastDecounceTime starts at zero. millis() would then need to be > debounceDelay for the following to be true

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

lets say the button was pressed at time 12000. the condition will be true and lastDebounceTime will be set to 12000.

the condition won't be true again until millis() is > 13000.

and this is also true when millis() wraps at 2^32 and is actually < lastDebounceTime. the subtraction isn't negative but truncated

is crazy, how the debounce will accrued. if i press again the button at 13000 condition is true again.
if the switch is bouncing will be not deference.
what the meaning to compare it with debouncedelay

ohh i think i got it... if the switch is bouncing, on/off on/off lets say 5 times. then lastdebounce will be change 5 times, so the condition will be false.

A little delay enought to wait bouncings stops.
You are doing things really much much more complicated than needed.

This is how I usually handle bouncing switch (exactly as stated from @gcjr)

#define BOUNCE_TIME 50
static uint32_t lastPressTime = 0;
if(digitalRead(button) && millis() - lastPressTime > BOUNCE_TIME) {
  lastPressTime = millis();
  // do something related to button press

no, the following won't be checked for debounceDelay (msec) after each button state change. within that time the button can bounce many times

yes i got it, if the switch is bouncing, a new lastpresstime time will be writen.

if i press the button at let say 13000

milis will be 13000ms lastpress will be 13000, so it wait the debounce time 50ms.
if the switch debounce again before 50ms lets say it bounce at 20ms


mills will be 13020 last press will be 13020, so again is wating.

ok, so is constant. fix amount of time to wait.. what has been set in the parameters.

mills is 13000 you press the button. lastdeboune will get 13000 too, and it wait for the debouncedelay. so 50ms are more than enough for debouncing.

so when press button again.. at 25000 then lastdebounce will get the value off 25000 and will wait again.

Almost, but not quite like that

You press button at 13000, so lastPressTime assume the same value 13000.
On the next loop millis() will be 13001 so the condition millis() - lastPressTime > 50 will not be true.

13001 - 13000 > 50

When millis() equals to 13051 the condition is true again, but in the meantime bounces stops (hopefully) so a new button press can be processed.


Yes, now you got it!
You can save the amount of time to wait also in a variable if you prefer.

buttonCState[i] = digitalRead(BUTTON_ARDUINO_PIN); 

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {
        lastDebounceTime[i] = millis();

        if (buttonCState[i] == LOW) {

first line in sketch is readding the button,
second line is doing the maths for the debouncing
third line is to tell if the button is preset
forth line is to update the lastdebounce parameter.

buttonCState[i] = digitalRead(BUTTON_ARDUINO_PIN);

if (buttonPState[i] != buttonCState[i]) {

  lastDebounceTime[i] = millis();

if ((millis() - lastDebounceTime[i]) > debounceDelay) {

  if (buttonCState[i] == LOW) {

in my mind will be more normal to
read button
check button
update lastdebounce
do the maths
and if is true to go on..

or the series docent matter? arduino is goin from line 1 to line 2 then 3 and so one.?

What is not clear to me is the reason you are putting the button reading and time into arrays.
Is the intention to use the same portion of code to read more buttons?

In this case, I think it would be much more efficient to use a small class made on purpose or use a function (there are also libraries, but DIY is cooler :sweat_smile: )

If you want, I can share the code of what I did for my purposes.

please do so...

Ok. This is the C++ class
You can put in a specific .h file and include it, or paste and bin at the beginning of your sketch.

class PushButton
    // Class constructor, default active state LOW, defaul debounce 50ms
    PushButton(uint8_t pin, uint8_t mode = INPUT_PULLUP, uint32_t bounceTime = 50) {
      m_pin = pin;
      m_activeState = mode;
      m_debounceTime = bounceTime;
      pinMode(m_pin, mode);
      if(mode == INPUT_PULLUP)
        m_activeState = LOW;
        m_activeState = HIGH;

    bool rising() {
      m_state = digitalRead(m_pin);
      if( (m_state != m_rising) && (millis() - m_riseTime > m_debounceTime)) {
        m_riseTime = millis();
        m_rising = m_state;
        return true;
      return false;

    bool falling() {
      m_state = digitalRead(m_pin);
      if( (m_state != m_falling) && (millis() - m_fallTime > m_debounceTime)) {
        m_fallTime = millis();
        m_falling = m_state;
        return true;
      return false;

    bool pushed() {
      return (digitalRead(m_pin) == m_activeState);

    bool released() {
      return (digitalRead(m_pin) != m_activeState);

    void setDebounceTime(uint32_t _time) {
      m_debounceTime = _time;

    /** Operator shorthand for pushed()
     * @code
     *      PushButton  button(BUTTON1, INPUT_PULLUP);
     *      bool state = button;   // Equivalent to button.pushed();
     * @endcode
    operator bool() {
      return pushed();

    uint8_t m_pin;
    uint8_t m_activeState;
    uint32_t m_debounceTime;
    uint32_t m_fallTime;
    uint32_t m_riseTime;

    bool m_state;
    bool m_rising = false;
    bool m_falling = true;

And this is an example of usage

PushButton button1(5, INPUT_PULLUP);   // Pin active state LOW (button close contact to GND)
PushButton button2(6, INPUT, 150);     // Pin active state HIGH, debounce 150 ms

void setup() {
  // pinMode() not needed, it's done inside class constructor

void loop() {
  // Due to the bool operator overloading, this is the same as if(button1.pushed())
  if(button1) {
    Serial.println("Button 1 pushed and this is true while button remain pressed");

  if(button2.falling()) {
    // this will fire once
    Serial.println("Button 2 has changed from HIGH to LOW");

  if(button2.rising()) {
    // this will fire once
    Serial.println("Button 2 has changed from LOW to HIGH");

What you do is much too complicated and consuming unnecessary resources when reading/debounceing more than one button. To debounce a button, it is sufficent to read it not too often. You must not read it twice during the debounce time, that's all. So have one millis()-if for all buttons that gets true every ~20ms and read all buttons in there.

  if ( millis() - lastReadTime > debounceTime ) {
    lastReadTime = millis();
    // read all buttons here e.g.
    for( byte i=0; i<nbrOfButtons; i++ ) {
      buttonState[i] = digitalRead(buttonPins[i]);
      // you can do edge detection here if necessary

Nothing more is necessary to debounce buttons.

in most cases debouncing has no real-time requirements and can be supported using a delay

chkButtons ()
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
    return -1;