Switch Debouncing code Problem

Hi! I have been using this switch code that I found somewhere with STM32 chips, and it has been working fine, no problems so far. I really like how easy it is to use. But I am having trouble if I use it on an Arduino Pro-Mini. It behaves as if Debounce is not working. When I press a switch once, it detects 4 or 5 presses. I used some little delay on the switch function as a workaround, but I would like to understand why debouncing is not working in order to fix it. So, thank you in advance for your help and suggestions.

EDIT: This problem happens more when using "justpressed[index]"

#define DEBOUNCE 10
#define button_pins {2, 3, 4, 5} // Cofigures the Button pins and Order.

const int numberButtons = Total_Nr_Of_Switches;
int button[numberButtons] = button_pins;
byte buttons[] =  button_pins;

byte pressed[numberButtons], justReleased[numberButtons], justpressed[numberButtons], ShortPress[numberButtons], LongPress[numberButtons], XtraLongPress[numberButtons];

void setup() {

  for (int x = 0; x < numberButtons; x++)
  {
    pinMode(button[x], INPUT_PULLUP);
  }
}

void loop() {

  check_switches();

if (justpressed[3] && RelayState == false) {
    effectON();
    RelayState = true;
  }
  if (justReleased[3] && RelayState == true) {
    effectOFF();
  }


  if (ShortPress[0]) effectChangeState();

  if (LongPress[0] || XtraLongPress[0]) // Effect and WahMode
  {
    AutoWah_State = !AutoWah_State;

    if (AutoWah_State == 0) AutoWah();
    else if (AutoWah_State == 1) Manual();
  }

}

void check_switches(){

  unsigned long previousstate[numberButtons];
  unsigned long currentstate[numberButtons];
  unsigned long lasttime;
  unsigned long Timelapse = 500;
  unsigned long TimelapseLong = 1000;
  unsigned long Timestart;

  /*
    static byte previousstate[numberButtons];
    static byte currentstate[numberButtons];
    static long lasttime;
    static long Timelapse = 500;
    static long TimelapseLong = 1000;
    static long Timestart;
  */
  byte index;

  if (millis() < lasttime) {    // we wrapped around, lets just try again
    lasttime = millis();
  }
  if ((lasttime + DEBOUNCE) > millis()) {    // not enough time has passed to debounce
    return;
  }
  lasttime = millis();  // ok we have waited DEBOUNCE milliseconds, lets reset the timer
  for (index = 0; index < numberButtons; index++) {
    justReleased[index] = 0;
    justpressed[index] = 0;       //when we start, we clear out the "just" indicators
    ShortPress[index] = 0;
    LongPress[index] = 0;
    XtraLongPress[index] = 0;

    currentstate[index] = digitalRead(buttons[index]);   //read the button
    if (currentstate[index] == previousstate[index]) {
      if ((pressed[index] == HIGH) && (currentstate[index] == HIGH)) {        // just pressed
        justReleased[index] = 1;

      }
      if ((pressed[index] == LOW) && (currentstate[index] == LOW)) {        // just pressed
        justpressed[index] = 1;
        // ButtonPressed = true;
        delay(10);  **// Because Debouncing doesn't seem to work**
        Timestart = millis();
      }
      if ((pressed[index] == HIGH && currentstate[index] == HIGH) && millis() - Timestart < Timelapse) {

        justpressed[index] = 0;
        ShortPress[index] = 1; // just released ShortPress
        // ButtonPressed = true;
        //  delay(10);
      }
      else if ((pressed[index] == HIGH && currentstate[index] == HIGH) && (millis() - Timestart >= Timelapse) && (millis() - Timestart < TimelapseLong)) {
        justpressed[index] = 0;
        ShortPress[index] = 0;
        LongPress[index] = 1; // just released LongPress
        //  ButtonPressed = true;
      }

      else  if ((pressed[index] == HIGH && currentstate[index] == HIGH) && millis() - Timestart >= TimelapseLong) {
        justpressed[index] = 0;
        ShortPress[index] = 0;
        LongPress[index] = 0;
        XtraLongPress[index] = 1; // just released XtraLongPress
        //  ButtonPressed = true;
      }
      pressed[index] = !currentstate[index];  //remember, digital HIGH means NOT pressed
      // delay(4);
    }
    previousstate[index] = currentstate[index]; //keep a running tally of the buttons
  }
  // delay(3);
}

My head spins just trying to follow the logic. Why don't you just use one of the button libraries?

Mine too head spins but the logic looks plausible and fixable.

The OP may be actually interested in getting debounce to work with code they wrote or at least came to grips with.

I avoid libraries and other ppl’s code because… I get my mileage out of writing code and getting it to do what I want.

I would bet that for some noobs using a button library woukd take a comparable amount of effort to getting a dozen lines or so of straight ahead code to work.

Of course by applying the same idea, why not just go out and buy a whatever it is you trying to make?

a7

I want to use this code with the Pro Mini also. It works fine on STM32 chips, But not on ATMEL..
As I said, I do like the way this code allows me to detect the switches in several ways, like a short presss, if it was just pressed, released, etc.
And this is not a library, you can see it on the code above. The code related to the switching is all there. No library.

My keyboard 1.. 8 keys (1 input port) with debounce, auto-repeat, detection of press, release, number of successive presses, or duration of press; we see the result on the serial terminal. The last 'toggle' function is not yet perfected...


  // PRO-MINI 5v 16 Mhz
  // et toujours pas d' #include
  
  //******************************************************** // POUR LE CLAVIER  A0, A1, A2 en entrées
  #define KeyPlus          B00000001                         // l'une des touches : PC0
  #define KeyMinus         B00000010                         // l'une des touches : PC1
  #define KeyZero          B00000100                         // l'une des touches : PC2
  #define KeysAll          B00000111                         // toutes les touches sur PortC en entrée
  #define KeysAutoRepeat   B00000011                         // DĂ©finir ici les touches en auto-repeat 
  #define KeysOneShot      B00000001                         // DĂ©finir ici les touches "1 seul appui"
  #define TempoRepeatSlow  60                                // tempo pendant 1er appui, multiple 10ms
  #define TempoRepeatFast  6                                 // tempo de répétition après le 1er appui
  byte    KeySta;                                            // reflète l'état instantané des touches
  byte    KeyDown;                                           // reflète le  front  montant  des touches
  byte    KeyUp;                                             // reflète le front descendant des touches
  byte    KeyFlp;                                            // reflète l'état "flip-flop"  des touches 
  byte    KeyLast;                                           // dernière touche activée 
  byte    keymem;                                            //                (stockage intermédiaire)
  byte    TautoRep =       TempoRepeatSlow;                  //        (initial tempo pour auto-repeat)
  byte    appuiActif;                                        // doube clic, time after last key release
  byte    nbrhit;                                            // double clic counter (nbre_hit)
  byte    nbrrel;                                            // duration discrimination (nbre_release)
  byte    clicORduration;                                    // multiples-clics or duration ?
  byte    KeyPosition;                                       // for demo 
  boolean DelayAfterRelease;                                 //
  unsigned long previousmillis;                              //  
  

void setup(void) {                                           //
  PORTC |= KeysAll;                                          // set internal pullups on A2, A1, A0 (PORTC)
  Serial.begin(115200);                                      // 
}                                                            //
  
  
void loop(void) {                                            // ************** LA DEMO *****************
  if (millis() - previousmillis > 10) {                      // 10 ms
    previousmillis = millis();                               //
    Keys();                                                  // appel régulier, toutes les 10..30ms  
  }                                                          //
}                                                            //
  
    
void Keys(void) { // ------------------------------------------ DEBOUNCE 10ms, rise and fall detection, and flip-flop 
  byte ii = ~PINC & KeysAll;                                 // read PORTC : A2, A1, A0   (8 keys, or 16 possible with
  byte jj = KeySta;                                          // replacement of bytes -> words) 
  KeySta  = ii & keymem;                                     // stable   subsiste pendant tout le temps de l'appui
  keymem  = ii;                                              //          (besoin interne, non exploitable)
  KeyDown = (jj ^ KeySta) & KeySta;                          // fugitif  (front appui) actif jusqu'au prochain passage
  KeyUp   = (ii ^ jj) & jj;                                  // fugitif  (relachement) actif jusqu'au prochain passage
  KeyFlp ^= KeyDown;                                         // va-et-vient subsiste jusqu'au prochain appui (bascule)
  if (KeyUp) KeyLast = KeyUp;                                // précédent appui subsiste jusqu'au prochain relachement
 
 
  if (KeySta & KeysAutoRepeat) {                             // AUTO-REPEAT   KeysAutoRepeat reflète les Keys à répéter
    TautoRep--;                                              //               (une, 2, ou toutes)
    if (TautoRep == 0) {                                     // at the end of TimeautoRep,
      TautoRep = TempoRepeatFast;                            // 
      KeyDown = KeySta;                                      // re-trigg KeyDown
    }                                                        //
  } else TautoRep = TempoRepeatSlow;                         // 

  
  if (KeyDown) {if (!appuiActif) {nbrhit=1; nbrrel=0;}       // MULTIPLES HITS or HIT DURATION (when auto-repeat is set)
                 else nbrhit++;}                             // comptage des appuis répétitifs: 1er appui, puis suivants
  if (KeySta) appuiActif = 50;                               // (re)lance tempo (50*10=0.5s) de détection de fin d'appui
  if (KeyUp) nbrrel++;                                       // pour discriminer "multiples clics" et "durée d'appui"
  if (appuiActif) {                                          //                                                         
    appuiActif--;                                            // détection de la fin de fenêtre de temps (appuiActif .5s)
    if (!appuiActif) {                                       // Ă  la fin de fenĂŞtre de temps (pas d'appuis depuis env.1s)
      if (nbrrel < 2) clicORduration = 0;                    // duration ; value of duration in variable "nbrhit"
      else            clicORduration = 1;                    // clics    ; number of clics   in variable "nbrhit"
      DelayAfterRelease = true;                              //   
    }                                                        // 0.5 secondes (appuiActif == 0) after release of last key
    
  //if (KeysOneShot & KeyPlus & KeyLast) KeyDown = KeySta = 0; // ONLY ONE SHOT ON THIS KEY (avoid multiples hits) 
    
  }  // ----------------------------------------------- END OF THE KEYBOARD TREATMENT -----------------------------------
      
      

  
  if (DelayAfterRelease) {                                   // 1 SECONDE  AFTER RELEASE OF THE LEST KEY,
    DelayAfterRelease = false;                               // treatment of the keys pressed.    
   
    if (KeyLast & KeyPlus) {                                 // derniere touche est-elle KeyPlus ? 
      if (!clicORduration) {                                 // DURATION       
        Serial.print("++++++++Key + duration : ");           //
        Serial.println(nbrhit);                              //
      }                                                      //
      if (clicORduration) {                                  // DOUBE-CLICS
        Serial.print("++++++++Key - nbre hits : ");          //
        Serial.println(nbrhit);                              //
      }                                                      //
    }                                                        //
    
    if (KeyLast & KeyMinus) {                               // derniere touche est-elle KeyMoins ? 
      if (!clicORduration) {                                 // DURATION       
        Serial.print("--------Key + duration : ");           //
        Serial.println(nbrhit);                              //
      }                                                      //
      if (clicORduration) {                                  // DOUBE-CLICS
        Serial.print("--------Key - nbre hits : ");          // 
        Serial.println(nbrhit);                              //
        if (nbrhit > 8)  Serial.println("C'est pas fini de jouer avec cette touche ?"); // faut pas exagérer non plus
      }                                                      //
    }                                                        //
  }                                                          //
  

  
                                                             // IMMEDIATLY AFTER RELEASE OF THE KEY,
                                                             // treatment of the keys pressed. 
  if (KeyDown & KeyPlus) {                                  // Front montant Touche "Plus", avec auto-repeat
    KeyPosition++;                                           //
    Serial.print("+");                                       //
  }                                                          //
  
  if (KeyDown & KeyMinus) {                                 // Front montant Touche "Moins" avec auto-repeat
    KeyPosition--;                                           //
    Serial.print("-");                                       //
  }
                                                             // 
  if (KeyUp & (KeyPlus | KeyMinus)) {                     // Relachement des Touches "Plus" et "Moins"
    Serial.print("  Value= ");                               // 
    Serial.println(KeyPosition);                             // 
  }                                                          //

  if (KeyDown & KeyZero) Serial.println(KeyPosition = 0);   // Touche remise à zéro, sans auto-repeat  
  
  
  if (KeyFlp   & KeyZero & KeyDown)                          // ... don't works ?
    Serial.println("oooooooo ON");                           // fonction Flip-Flop
                                                             //
  if ((!KeyFlp) & KeyZero & KeyDown)                         // ...fonctionne pas ?
    Serial.println("xxxxxxxx OFF");                          // ou bascule, ou "un coup oui, un coup non"
}


Thank you.. But I would like to use the code I posted, as I do like the way it can be used on my projects.

I would just like someone to help me understand why debounce works on the STM32 but not on the Pro-Mini.

Simply scanning your switches every 50ms is a very good method to avoid switch bounce.

Something like this:


  //******************************
  //is it time to check the switches ?
  if (millis() - switchMillis >= 50)
  {
    //restart the TIMER
    switchMillis = millis();

    checkSwitches();
  }

Look for a change in switch state in the checkSwitches() function.

1 Like

You mean, check for switches on the main loop every 50ms instead of all the time? and I guess millis make it non blocking, right? Because I am checking MIDI as well.
I was looking at the code I posted, and was wondering if that millis code at the beginning of the CheckSwitches() function is really doing something.
Thank you.

The code in post #7 goes in loop( ).

This example will be non blocking.

I could perhaps just replace the millis code in the checkSwitches function with the code from post #7. What do you think?

It is curious that the code worked well with the STM32, and not with the Pro-Mini: there is no reason... not very easy to understand where is the cause of the difference

It is weird I know.. But must there be an explanation..

You commented out a block of decelerations that included

 static unsigned long lasttime = 0;

So lasttime is now

unsigned long lasttime = 0;

Since the (not satanic) variable you use now is created every time, it does not serve its purpose.

My corrections to your code were well along, but changing that one variable may be what and all you need to do. It does change things and it seems obvious that it should be declared static.

Please look closely and be sure you are using the code that worked exactly.

It would help if you placed check_switches() into a simple testing sketch that is complete, compiles and shows the problem.

I added fake stuff all around check_switches and have something running, but since I don't know what it is supposed to do, it is hard to see what it is doing wrong aside from the bouncing, which I def observe with your original.

HTH

a7

Maybe something in this partial example will help:

#define PUSHED                LOW

const byte button2          = 2;
const byte button3          = 3;
const byte button4          = 4;
const byte button5          = 5;

byte lastButton2State       = !PUSHED;
byte lastButton3State       = !PUSHED;
byte lastButton4State       = !PUSHED;
byte lastButton5State       = !PUSHED;

//timing stuff
unsigned long timePressed;

unsigned long switchMillis;
unsigned long button2Millis;
unsigned long button3Millis;
unsigned long button4Millis;
unsigned long button5Millis;

unsigned long longPress     = 1000;

//****************************************************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(button4, INPUT_PULLUP);
  pinMode(button5, INPUT_PULLUP);

} //END of setup()


//****************************************************************************************
void loop()
{
  //******************************
  //is it time to check the switches ?
  if (millis() - switchMillis >= 10)  //every 10ms, change as needed
  {
    //restart the TIMER
    switchMillis = millis();

    checkSwitches();
  }

} //END of loop()


//****************************************************************************************
void checkSwitches()
{
  //*********************************
  //                                             b u t t o n 2
  byte currentState = digitalRead(button2);

  //was there a change in state ?
  if (lastButton2State != currentState)
  {
    //update to the new state
    lastButton2State = currentState;

    //was the button pushed ?
    if (currentState == PUSHED)
    {
      //the time the button was pressed
      button2Millis = millis();

      //do something
    }

    //the button was released
    else
    {
      timePressed = millis() - button2Millis;

      Serial.print("ms button2 was pressed for ");
      Serial.println(timePressed);

      //was this a long press ?
      if (timePressed > longPress)
      {
        Serial.println("button2 had a long press. ");

        //do something
      }
    }

  } //END of button2

  //*********************************
  //                                             b u t t o n 3
  currentState = digitalRead(button3);

  //was there a change in state ?
  if (lastButton3State != currentState)
  {
    //update to the new state
    lastButton3State = currentState;

    //was the button pushed ?
    if (currentState == PUSHED)
    {
      //the time the button was pressed
      button3Millis = millis();

      //do something
    }

    //the button was released
    else
    {
      timePressed = millis() - button3Millis;

      Serial.print("ms button3 was pressed for ");
      Serial.println(timePressed);

      //was this a long press ?
      if (timePressed > longPress)
      {
        Serial.println("button3 had a long press. ");

        //do something
      }
    }

  } //END of button3

  //*********************************
  //                                             b u t t o n 4 

  //*********************************
  //                                             b u t t o n 5

} //END of checkSwitches()

//****************************************************************************************

Using which STM32 Arduino core?

Yes, the one from Roger Clark.

You should never do this. It's

  if ( millis() - lasttime > DEBOUNCE ) {    // not enough time has passed to debounce

because of millis() rollover. When I see this, I don't trust the rest of the code.

Why not just change

#define DEBOUNCE 10

to

#define DEBOUNCE 20

?

You know, I always found that line strange.. That line and the line just after that.
lasttime = millis();
I think this shouldn't be there. I did remove it and the code performed better. But it still fails sometimes. Will try your suggestion now.

At what point did the 'static' keyword get removed here?:

 unsigned long previousstate[numberButtons];
  unsigned long currentstate[numberButtons];
  unsigned long lasttime;
  unsigned long Timelapse = 500;
  unsigned long TimelapseLong = 1000;
  unsigned long Timestart;

  /*
    static byte previousstate[numberButtons];
    static byte currentstate[numberButtons];
    static long lasttime;
    static long Timelapse = 500;
    static long TimelapseLong = 1000;
    static long Timestart;
  */

Have you made extensive modifications to the program? If yes, can you explain?

I put it back after it was mentioned here, it made no difference to the problem.
Also, increasing DEBOUNCE doesn't fix the problem. still fails