Making it easier to debounce switches

Hi all,

I have seen in the Forum that it is difficult for some to grasp the debounce concept and to apply the algorithms needed to defeat the bouncing effect.

I asked the Arduino devloppers group if it would be possible to add a function -- digitalDebounce(pin) --to the Arduino's IDE to tackle this problem. I got some interesting answers, but in the end, I was told that

so far, there seems to be limited interest from the "Arduino Team" to your feature request.

Most often, when the OP needs to handle just one switch, we can get the ball rolling. But, when there are more switches, the OP rewrites the whole algorithm for each switch.

I wrote an Arduino Sketch that puts the debouncing problem in a box that the user could just use. I would like to know what you think about it, or have your suggestions on how to improve it. Then, I think that it would be easier for beginners to handle their switches properly.

So, here goes:

/*
 *DebounceFunction.ino
 *Author: Jacques Bellavance
 *Date: September 28 2017
 *
 *This is to demonstrate how to debounce multiple switches using only one function.
 *The debounce algorithm is the same one as the Examples/Digital/Debounce of the Arduino's IDE
 *The function uses tables to retain all the switches' sates
 *
 */
 
#define switchCount 4

const int buttonPins[switchCount] = {2, 3, 4, 5};
const int redLedPin = 10;
const int blueLedPin = 11;
const int greenLedPin = 12;
const int yellowLedPin = 13;


unsigned long debounceDelay = 50;
int buttonState[switchCount];
int lastButtonState[switchCount];
unsigned long lastDebounceTime[switchCount];

void setup() {
  for (byte i = 0 ; i < switchCount ; i++) pinMode(buttonPins[i], INPUT);
  pinMode(redLedPin, OUTPUT);
  pinMode(blueLedPin, OUTPUT);
  pinMode(greenLedPin, OUTPUT);
  pinMode(yellowLedPin, OUTPUT);
}

int debounce(byte pin) {
  int reading = digitalRead(buttonPins[pin]);
  if (reading != lastButtonState[pin]) lastDebounceTime[pin] = millis(); 
  if ((millis() - lastDebounceTime[pin]) > debounceDelay) buttonState[pin] = reading;
  lastButtonState[pin] = reading;
  return buttonState[pin];
}

void loop() {
  digitalWrite(redLedPin, debounce(0));
  digitalWrite(blueLedPin, debounce(1));
  digitalWrite(greenLedPin, debounce(2));
  digitalWrite(yellowLedPin, debounce(3));
}

Cheers

From Redirecting to Google Groups

What I suggest is just a new tool to deal with the problem without having to resort to include a Library (often regarded as a no no on the Forum)

I'm not sure I agree with your premise here. I've never seen people here recommend against using a debounce library. The only reason I can see for doing that is that they don't think the code should be hidden away in a "black box". Your suggestion of adding a debounce function to the core does the same thing. Maybe you could explain more what you mean by "regarded as a no no".

I do agree that debouncing code is something almost every user will need and there are quite a few different options so it can be confusing to a beginner. I'm not sure about adding it to the core. Generally I prefer that libraries be used so that you can include them when only when necessary rather than getting everything with the core, along with overhead or conflicts in some cases, even if you don't use it. The downside is the extra complexity of the include statement but I think with good documentation that's not so bad. I think Paul Stoffregen had a good suggestion on the mailing list thread about Arduino picking a debounce library to add as a "built-in" library included with the Arduino IDE, or bundled with the hardware packages if architecture specific.

Hi,

Take a look at posts 34 & 35. This is just the last remark of the kind that I got about Libraries... Unless the original question is about a Library.

Some users even have a hard time to comprehend the "blink without delay" algorithm.

I don't know why, but some people are rudely answered to sometimes. Here is what I wrote to the "Arduino team":

Hi, It all depends on the definition of newbie. If the newbie is a college student that is in an engineering program, then yes, he/she should be aware of bouncing and thought how to deal with it. If the newbie is a kid, artist, enthousiast, hobbyist... then, bouncing is just an anoying extra hurdle to pass before thry can see those leds/servos/steppers... respond correctly to their switches. It is obvious that if a generic digitalDebounce(pin) is ever included in the IDE, it will be imperfect for some specific setups or environments. That is why digitalRead() is there for (amongst other things). A flat screwdriver is not usefull for Robertson screws. Neither fo Phillips, Hex, Torx... But having a Robertson screwdriver in your toolbox is not necessarely a bad thing. I am tryng to see it with the eyes of someone that does not persue the goal of earning a living coding. Jus the average Joe (or Jo-Ann).

I feel the same way about Libraries. Now that I know that I won't see a digitalDebounce(pin) in a near future, I feel that we should make it easier to the non-programmer to make their ideas a reality.

I also thought that Paul's suggestion should be considered. Bounce2 or any debouncer that is easy to use should be included in the Arduino's IDE.

Jacques

I very much agree with what you said. There's absolutely nothing wrong with a hobbyist not wanting to understand everything down to the deepest level. I get really annoyed with the professional electrical engineers who want to force everyone else to learn as they did. If that's your career path then of course you will need to understand how to write your own debouncing code but for a hobbyist trying to get their project finished in some limited spare time it's not necessary and just acts as a barrier. If you make things too complicated then the people who are casually interested in this stuff will see there's too steep of a learning curve and do something else. That's the genius of the Arduino project: Make it as easy as possible to get started with microcontrollers while still allowing the potential to achieve professional quality results. Once you get hooked then you can always go dig into that debouncing function's source code to see how it works or write your own using direct port manipulation, assembly, etc.

It's possible that the Arduino team will consider your proposal and there has been no official response because they're busy and it's not a high priority. The best way to push it forward would be to submit a pull request to the Arduino AVR Boards hardware package, which is located in the Arduino IDE repository. The most important thing is to determine whether this will add any overhead when the function is not used and add that information to the PR as that will be a primary consideration. If that is accepted then function(s) with the same signature will need to be added to every other core library to keep the API consistent. The documentation will also need to be updated. This will require quite a bit of work from the Arduino developers and also 3rd party hardware package maintainers. You can reduce the amount of work for them (and thus increase the chances of it happening) but doing as much of the work as possible yourself but even so they will be responsible for reviewing all proposals, merging them, and maintaining the code in perpetuity. It's best just to start with a single pull request to the Arduino AVR Boards core library as this is used somewhat as a model for every other core but it would be a good idea to state your commitment to contributing to further work that will be required in the pull request description.

As you can see, it's so much easier just to write a 3rd party library, which requires no approval or effort from the Arduino developers, no long drawn out discussions/bike shedding on the mailing list, etc.

1 Like

Hi Pert,

Although I would'nt mind doing some of the work that you suggest (I am retired, so I have the time) I don't think that I have the knowledge that is required to persue the task. (I don't even know what a "pull request" is).

I think that I will just keep helping users thru the Forum. I wrote a few Libraries, and I will offer them whenever it seems to be an easier solution...

And keep my cool when I see posts that I dislike.

Thanks for your ears, and your thoughts.

Jacques

1 Like

I understand, and it's still worth making proposals as the Arduino team may act on them or someone else might take up the volunteer effort to move it forward. I was just explaining how you could personally make it more likely that your proposal would happen.

If you want to get an idea of what a pull request is, take a look at the outstanding ones for the Arduino IDE repository:
https://github.com/arduino/Arduino/pulls

Something like this perhaps?

Pete

Quoting myself:

I also thought that Paul's suggestion should be considered. Bounce2 or any debouncer that is easy to use should be included in the Arduino's IDE.

Yes, this algorithm is by far the most popular one amongst Arduino users around the world.

Jacques

Paul Stoffregen mentioned in the mailing list discussion that his Teensyduino installer adds the Bounce2 library as one of the "built-in" libraries in the Arduino IDE installation. I've only ever used it in one project (the others I've just written my own debounce function for some reason) but Paul's endorsement is good enough for me.

I usually just stick a cap across my buttons so the code can pretend bounce doesn't exist.

Most of this is above me and I tend to rely on libs.

That said from my POV there is debounce and there is debounce.

For simple buttons quite often a delay of some kind will suffice.
rotary encoders on the other hand I found needed a little more and I ended up playing with maybe 6 libs (all supposedly for encoders) until I found one that suited my needs just right with minor tweaks.

Having a basic debounce built in with a couple of simple parameters would be great as already mentioned for many beginners.

Just my POV is that not all switches will debounce the same.

jbellavance:
I wrote an Arduino Sketch that puts the debouncing problem in a box that the user could just use. I would like to know what you think about it, or have your suggestions on how to improve it. Then, I think that it would be easier for beginners to handle their switches properly.

I would wrap it into a library, ideally a single header file.

Edit: e.g.

/*
 *DebounceFunction.h
 *Author: Jacques Bellavance
 *Date: September 28 2017
 *
 *This is to demonstrate how to debounce multiple switches using only one function.
 *The debounce algorithm is the same one as the Examples/Digital/Debounce of the Arduino's IDE
 *
 */

#include <Arduino.h>
#include <stdint.h>

class Debouncer
{
public:
 static uint16_t debounceDelay;

 unsigned long lastDebounceTime;
 uint8_t buttonState;
 uint8_t lastButtonState;
 uint8_t pin;

 Debouncer(byte Pin) {
 pin = Pin;
   pinMode(pin, INPUT);
 }

 int debounce (void) {
   uint8_t reading = digitalRead(pin);
   if (reading != lastButtonState) lastDebounceTime = millis();
   if ((millis() - lastDebounceTime) > debounceDelay) buttonState = reading;
   lastButtonState = reading;
   return buttonState;
 }

};

uint16_t Debouncer::debounceDelay = 50;

I would wrap it into a library, ideally a single header file.

Please see:

Those are all solutions to bouncing that also get rid of EMF interferences.

They do not use the "wait a little" debounce algorithm.

They actually block the sketch until bouncing or interference stops and immediately reports the actual status of the switch. Since bouncing occurs only at the moment that the switch is pressed or released (more than likely less than 1% of the switche's life) most of the time, you recieve a debounced reading of the switch within 90 microseconds after the call.

Jacques

Hello Jacques,

Thanks a lot for your debounce library, it works perfect. But when i compile a sketch using "EdgeDebounce.h" i get the following error

/Users/Richard/Documents/Programmation/libraries/EdgeDebounce-master/EdgeDebounce.cpp: In member function 'void EdgeDebounce::update()':
/Users/Richard/Documents/Programmation/libraries/EdgeDebounce-master/EdgeDebounce.cpp:102:12: warning: invalid conversion from 'byte {aka unsigned char}' to 'pinStatus' [-fpermissive]
MYstatus = newStatus;

^

This is not an issue as such, just for info..

Richard

i get the following error

No, you don't.

/EdgeDebounce.cpp:102:12: warning: invalid conversion from 'byte {aka unsigned char}' to 'pinStatus' [-fpermissive]

EdgeBounce has an interesting approach. I do not wish to speak too harshly of this library, but in the context of possibly recommending this library for all Arduino users, I believe some thought and testing should go into how it will perform when run on faster chips and with more advanced compiler optimizations.

As I understand the code, it appears to repetitively call digitalRead() 16 times (configurable up to 32). The timing of the repetitive reads appear to depend only on the CPU speed and the speed of digitalRead().

byte EdgeDebounce::debounce(byte pin) {
  unsigned long pinStatus;
  do {
    pinStatus = 0xffffffff;
    for (byte i = 1; i <= MYsensitivity; i++) pinStatus = (pinStatus << 1) | digitalRead(pin);
  } while ((pinStatus != debounceDontCare) && (pinStatus != 0xffffffff));
  return byte(pinStatus & 0x00000001);  
}

On slow 16 MHz AVR, with a fairly low performance digitalRead() requiring multiple PROGMEM reads, this timing probably works out fairly well.

But consider what will happen on a not-too-distant-future 1 GHz superscaler ARM microcontroller? Imagine digitalRead() implemented more efficiently. Or imagine what will happen if the C++ compiler is able to propagate all objects member variables (including the pin number) initialized with constants all the way to this function, and then optimize it and digitalRead() as a compile time const, effectively replacing the function call with just a direct register read from the hardware. Imagine a future compiler smart enough to even unroll this loop.

In that sort of best case scenario, this code could possibly end up optimized into 16 successive direct hardware register reads running at the the incredible bus speed of a very fast chip. All 16 reads might end up happening faster than an AVR could execute a single instruction!

I'm also a little concerned about what this code does in the case where the 16 reads aren't all logic high. It looks as if the case where "pinStatus" isn't all 1s simply returns the most recent reading. If "MYsensitivity" is 16 and the signal changes on the 14th or 15th reading and has random behavior for some time, wouldn't this return an unpredictable result? Or maybe I've misunderstood the algorithm?