Need advice on debouncing my code

Hi there,
i really managed to finally write working code for my Teensy-Based MIDI Footcontroller.
(the code still avoids pins 6,9,10,13 as they… just don't work)

My Issue is: I am using little pushbuttons for testing with RC-networks to hardware-debounce them. works, but with coincidentally correct Capacitors an resistors.

Now I have two routes to finish this:

a) Again go the hardware route with the real heavy-duty footswitches.
But I can't go trial and error for the right values, I don't have the resources for this or the Time.
Is there a way / code to check with my arduino how much time/Bouncing my RC-Network would have to cover?

b) use software debounce.
that would keep me flexible if anything goes wrong or values must change in the process.
But I can't find any Code/Library that I understand and is not oversimplified.
Bounce doesn't work. I don't get a bit about bounce2. I can't get FTDebounce to work...
Any suggestions?

Oh, Here is the working stage of my Code:

String schalter [] = {"LO1", "LO2", "LO3", "LO4", "PB1", "PB2", "PB3", "PB4", "SO1", "SO2", "SO3", "SO4", "FX1", "FX2", "CLR", "S16"};
bool schalterA [16];
bool schalterB [16];

byte pins [] = {0,1,2,3,4,5,7,8,11,12,14,15,16,17,18,19};

byte stati[16];

unsigned long timers [16];
unsigned long longClick = 700;

unsigned long eraserClick = 1200;
unsigned long eraserTimer = 0;
bool eraserTarget [4] = {0,0,0,0};


byte recordNotes [4] = {0,4,8,12};
byte playNotes [4] = {1,5,9,13};
byte stopNotes [4] = {2,6,10,14};
byte clearNotes [4] = {3,7,11,15};

void onOffSwitch (byte i, byte midiNote) {
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      Serial.println(schalter[i]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      usbMIDI.sendNoteOn(midiNote, 127, 1);
      delay(50);
      usbMIDI.sendNoteOff(midiNote, 127, 1);
      schalterB[i] = HIGH;
    }
  }

void holdSwitch (byte i, byte midiNote) {
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      Serial.println(schalter[i]);
      usbMIDI.sendNoteOn(midiNote, 127, 1);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      usbMIDI.sendNoteOff(midiNote, 127, 1);
      schalterB[i] = HIGH;
    }
  }

void looper (int i){
  
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      timers[i] = millis();
      Serial.print(stati[i]);
      Serial.println(schalter[i]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      if ((millis() - timers[i]) < longClick) {
        
        switch (stati[i]) {
        case 0:
        usbMIDI.sendNoteOn(recordNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(recordNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Recording");
        if ((millis() - eraserTimer) > eraserClick){ 
          for (byte e=0; e<4; e++){
            eraserTarget [e] = false;
            }
           eraserTimer = millis();
        }
        eraserTarget [i] = true;
        stati[i] = 1;
        schalterB[i] = HIGH;
        break;
        
        case 1:
        usbMIDI.sendNoteOn(recordNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(recordNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Playing");
        stati[i] = 2;
        schalterB[i] = HIGH;
        break;
        
        case 2:
        usbMIDI.sendNoteOn(stopNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(stopNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Stopped");
        stati[i] = 3;
        schalterB[i] = HIGH;
        break;
        
        case 3:
        usbMIDI.sendNoteOn(playNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(playNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Playing also");
        stati[i] = 4;
        schalterB[i] = HIGH;
        break;
        
        case 4:
        usbMIDI.sendNoteOn(stopNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(stopNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Stopped also");
        stati[i] = 3;
        schalterB[i] = HIGH;
        break;
        }
      }

  if ((millis() - timers[i]) >= longClick){
    if (stati[i] > 1){
      usbMIDI.sendNoteOn(recordNotes[i], 127, 1);
      delay(50);
      usbMIDI.sendNoteOff(recordNotes[i], 127, 1);
      Serial.print (schalter[i]);
      Serial.println (" Recording");
      if ((millis() - eraserTimer) > eraserClick){ 
          for (byte e=0; e<4; e++){
            eraserTarget [e] = false;
            }
           eraserTimer = millis();
        }
      eraserTarget [i] = true;
      schalterB[i] = HIGH;
      stati[i] = 1;
      }
   else {
      schalterB[i] = HIGH;
        }    
      }
    }
  }

void playback (int i){
  
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      //Serial.print(stati[i]);
      //Serial.println(schalter[i]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      switch (stati[i]) {
        case 0:
        Serial.print (schalter[i]);
        Serial.println (" Play from Start");
        stati[i] = 1;
        schalterB[i] = HIGH;
        break;
        
        case 1:
        Serial.print (schalter[i]);
        Serial.println (" Stopped");
        stati[i] = 0;
        schalterB[i] = HIGH;
        break;
    }
  }
}

void eraser(int i) {
 if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      for (byte i=0; i<3; i++){
        Serial.print(eraserTarget[i]);
        }
        Serial.println(eraserTarget[3]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {

    for (byte i=0; i<4; i++){
        if (eraserTarget[i] == 1) { 
        Serial.print(schalter[i]);
        stati[i] = 0; 
        } 
       }
    schalterB[i] = HIGH;
    Serial.println(" gelöscht"); 
}
}

void setup() {
  for(int i=0; i<16; i++){
    pinMode(pins[i], INPUT_PULLUP);
    schalterA[i] = HIGH;
    schalterB[i] = HIGH;
    stati[i] = 0;
    }
  delay(2000);
  Serial.begin(9600);
}

void loop() {


for (int i = 0; i < 16; i++){
    schalterA[i] = digitalRead(pins[i]);
  }

for (int i = 0; i < 4; i++){
    looper(i);
    playback(i+4);
  }
 eraser(14);
 onOffSwitch (8, 2);

}

thanks a lot
Thomas

I would debounce them here. Something like:

void loop()
{
  unsigned long currentMillis = millis();

  for (int i = 0; i < 16; i++)
  {
    static unsigned long LastStateChangeTime = 0;
    const unsigned long DebounceInterval = 10;
    int newReading = digitalRead(pins[i]);

    if (newReading != schalterA[i] &&
        currentMillis - LastStateChangeTime > DebounceInterval)
    {
      LastStateChangeTime = currentMillis;
      schalterA[i] = newReading;
    }
  }

Yes, all switches share the same debounce timer. If two buttons are pressed 'at the exact same time' one will have to wait 1/100th of a second before being recognized.

Yes, yes.

So why not finish what you started and fix that? :expressionless:

a7

You could also use the MoToButtons Class of my MoBaTools Library. This class can manage up to 32 buttons in one instance, and it gives you features like pressing, releasing, long press, shortpress and more.
Because other classes of MobaTools don't run on teensy yet, you must simply copy the MoToButtons.h into your sketch directory, and include it with
#include "MoToButtons.h"

if you describe your problem one might be able to help.
Personally I prefer the OneButton Library.

By the way - here is a very active German speaking forum also: https://forum.arduino.cc/c/international/deutsch/47

huh, you are fast.

I think I understand what @johnwasser posted, good starting point.
If I get this right, this Code doesn't wait for the jitter to subside and than acts,
It uses the first Contact and Ignores everything else within the Debounce interval?

thanks everyone,
I will try one by one.

In my opinion, it is not broken. If you think my contribution can and should be improved on, please feel free to offer your own contribution.

well, I think I got it,
Every Switch now has its own timer.
next thing would be testing if pins 6,9,10, and 13 now react as expected.

String schalter [] = {"LO1", "LO2", "LO3", "LO4", "PB1", "PB2", "PB3", "PB4", "SO1", "SO2", "SO3", "SO4", "FX1", "FX2", "CLR", "S16"};
bool schalterA [16];
bool schalterB [16];

byte pins [] = {0,1,2,3,4,5,7,8,11,12,14,15,16,17,18,19};

byte stati[16];

unsigned long debounceMillis[16];
unsigned long debounceLastChange[16];

const unsigned long debounceInterval = 10;


unsigned long timers [16];
unsigned long longClick = 700;

unsigned long eraserClick = 1200;
unsigned long eraserTimer = 0;
bool eraserTarget [4] = {0,0,0,0};


byte recordNotes [4] = {0,4,8,12};
byte playNotes [4] = {1,5,9,13};
byte stopNotes [4] = {2,6,10,14};
byte clearNotes [4] = {3,7,11,15};

void onOffSwitch (byte i, byte midiNote) {
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      Serial.println(schalter[i]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      usbMIDI.sendNoteOn(midiNote, 127, 1);
      delay(50);
      usbMIDI.sendNoteOff(midiNote, 127, 1);
      schalterB[i] = HIGH;
    }
  }

void holdSwitch (byte i, byte midiNote) {
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      Serial.println(schalter[i]);
      usbMIDI.sendNoteOn(midiNote, 127, 1);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      usbMIDI.sendNoteOff(midiNote, 127, 1);
      schalterB[i] = HIGH;
    }
  }

void looper (int i){
  
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      timers[i] = millis();
      Serial.print(stati[i]);
      Serial.println(schalter[i]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      if ((millis() - timers[i]) < longClick) {
        
        switch (stati[i]) {
        case 0:
        usbMIDI.sendNoteOn(recordNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(recordNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Recording");
        if ((millis() - eraserTimer) > eraserClick){ 
          for (byte e=0; e<4; e++){
            eraserTarget [e] = false;
            }
           eraserTimer = millis();
        }
        eraserTarget [i] = true;
        stati[i] = 1;
        schalterB[i] = HIGH;
        break;
        
        case 1:
        usbMIDI.sendNoteOn(recordNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(recordNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Playing");
        stati[i] = 2;
        schalterB[i] = HIGH;
        break;
        
        case 2:
        usbMIDI.sendNoteOn(stopNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(stopNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Stopped");
        stati[i] = 3;
        schalterB[i] = HIGH;
        break;
        
        case 3:
        usbMIDI.sendNoteOn(playNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(playNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Playing also");
        stati[i] = 4;
        schalterB[i] = HIGH;
        break;
        
        case 4:
        usbMIDI.sendNoteOn(stopNotes[i], 127, 1);
        delay(50);
        usbMIDI.sendNoteOff(stopNotes[i], 127, 1);
        Serial.print (schalter[i]);
        Serial.println (" Stopped also");
        stati[i] = 3;
        schalterB[i] = HIGH;
        break;
        }
      }

  if ((millis() - timers[i]) >= longClick){
    if (stati[i] > 1){
      usbMIDI.sendNoteOn(recordNotes[i], 127, 1);
      delay(50);
      usbMIDI.sendNoteOff(recordNotes[i], 127, 1);
      Serial.print (schalter[i]);
      Serial.println (" Recording");
      if ((millis() - eraserTimer) > eraserClick){ 
          for (byte e=0; e<4; e++){
            eraserTarget [e] = false;
            }
           eraserTimer = millis();
        }
      eraserTarget [i] = true;
      schalterB[i] = HIGH;
      stati[i] = 1;
      }
   else {
      schalterB[i] = HIGH;
        }    
      }
    }
  }

void playback (int i){
  
  if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      //Serial.print(stati[i]);
      //Serial.println(schalter[i]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {
      switch (stati[i]) {
        case 0:
        Serial.print (schalter[i]);
        Serial.println (" Play from Start");
        stati[i] = 1;
        schalterB[i] = HIGH;
        break;
        
        case 1:
        Serial.print (schalter[i]);
        Serial.println (" Stopped");
        stati[i] = 0;
        schalterB[i] = HIGH;
        break;
    }
  }
}

void eraser(int i) {
 if (schalterA[i] == LOW && schalterB[i]== HIGH) {
      for (byte i=0; i<3; i++){
        Serial.print(eraserTarget[i]);
        }
        Serial.println(eraserTarget[3]);
      schalterB[i] = LOW;
      } 
  if (schalterA[i] == HIGH && schalterB[i]== LOW) {

    for (byte i=0; i<4; i++){
        if (eraserTarget[i] == 1) { 
        Serial.print(schalter[i]);
        stati[i] = 0; 
        } 
       }
    schalterB[i] = HIGH;
    Serial.println(" gelöscht"); 
}
}

void setup() {
  for(int i=0; i<16; i++){
    pinMode(pins[i], INPUT_PULLUP);
    schalterA[i] = HIGH;
    schalterB[i] = HIGH;
    stati[i] = 0;
    debounceLastChange[i] = 0;
    }
  delay(2000);
  Serial.begin(312500);
}

void loop() {

  for (int i = 0; i < 16; i++) {
    debounceMillis[i] = millis();
    bool debounceBuffer = digitalRead(pins[i]);

    if ((debounceBuffer != schalterA[i]) && (debounceMillis[i] - debounceLastChange[i]) > debounceInterval){
      debounceLastChange[i] = debounceMillis[i];
      schalterA[i] = debounceBuffer;
      }
  }

for (int i = 0; i < 4; i++){
    looper(i);
    playback(i+4);
  }
 eraser(14);
 onOffSwitch (8, 2);

}

well, what I really wanted to say was: YEAHA!!!
thanks a lot Guys, you're terrific!

It appears that you are correct. If you cared to prove it, or at least gather some evidence, increase the debounce interval to something ridiculous and see your switches act in a snappy fashion and then sorta go dark and unresponsive.

Some algorithms of debouncing or deglitching a signal would make a different choice - do not react, for example, until the switch or signal is changed for some good length of time so we sure that it is a positive change.

Something like an noisy signal from an RF receiver.

With normally open pushbuttons in a regular deployment scenario, the assumption that any closure is bound to end up being a full-on press gives the preferred behaviour.

BTW the 10 milliseconds might be a bit short. Are your switches still with a RC network?

Anyway, yay!

Edit: I used my new favorite time waster tool wokwi.com and stripped your code down to just the debouncing part, made a few changes to suit for running on a UNO. Just because.

I recommend wokwi.com for experimentation and early development of program logic. It is way easier than uploading dozens of times. Check it out. I don't know all the various Arduino simulators by any means, I wonder aloud if any are as good and getting better nearly as fast.

a7

1 Like

Thanks, a very valuable link. It worked even with a MoToButtons-Example :wink: