Announcement - Debounce library

(apologies for the double post, but this looks like the correct forum to put this announcement in)

I have built a debounce library based on cactusjack’s algorithm ().

The library is available at github.

The library works and is usable. Goals are: fast (not using delays), small(ish), robust and consistent behaviour (using timed samples), easy to use for the most common cases.

At this stage, I am looking for comments on if people think the names of the methods are unclear, if the README needs to be better, etc. Oh, and if it is wrong or buggy, that’s kinda important, too. :frowning: If people seem to be mostly happy with the API, I’ll change the version to 1 and say “job done (for now)”.

For comparison, the existing Bounce library is here on github, and its announcement post is here on this forum.

// simple "do something when the button is pressed"

#include <DebounceInput.h>

DebouncedInput pinA(4);
DebouncedInput pinA(5);

void setup() { /* no setup required */ }

void loop() {
  // simple case
  pinA.read(); // sample the input

  if(pinA.low()) ; // button currently held down

  if(pinA.falling()) ; // button pressed
  else if(pinA.rising()) ; // button released 

  // simpler case - combine the read and the test
  if(pinB.readFalling()) ; // the other button was pressed
}

I didn’t test it :frowning: but I noticed a few things.
You use INPUT_PULLUP, assuming that the button is connected to a pin and GND. What if the button is connected to 5V ?
The millis() is reduced to maximum 1 second with a resolution of 4ms. On my Arduino Mega with Ethernet Shield, that might not be enough. When I open a large log file in my browser, it is retrieved from the SD card and send to my computer, that might take a while.

I was looking for a simple example, and “InfoToSerial.ino” is easier than “SimpleOnOff.ino”. To avoid scaring new users, perhaps you could change the names, and call InfoToSerial a simple demonstration, or make “SimpleOnOff.ino” super simple.

My ideal debouncer would have a filter that takes into account that a button has bad contacts :smiley_cat: . When a button is connected to GND, a LOW input value has a higher reliability than a HIGH value. A low could be electric noise, so perhaps LOW would be 90% reliable and HIGH would be 50% reliable.

Questions about data transfer over very long wires are often asked on the forum. Could the filter be used for a button or an Arduino output pin over a cable of hundreds meters ? In other words: could the timing of the filter be adapted ?

This is more in the spirit of 'notes to self' than arguing. Some of this stuff can be addressed by better doco.

Peter_n: You use INPUT_PULLUP, assuming that the button is connected to a pin and GND. What if the button is connected to 5V ?

Constructors are executed before your setup(). If you change the mode to INPUT in the setup(), then this will override the setting that was done in the construcor.

But I notice that the Bounce2 library actualy changes from using a constructor to using an attach() method to specify the pin. I suppose it's more in the spirit of how people code up arduino sketches. Maybe I should just do it that way.

Peter_n: The millis() is reduced to maximum 1 second with a resolution of 4ms. On my Arduino Mega with Ethernet Shield, that might not be enough. When I open a large log file in my browser, it is retrieved from the SD card and send to my computer, that might take a while.

This won't affect anything important. The time is used to not take a sample if we are still in the same 4ms bracket of time. At worst, if your long job takes exactly enought time to (falsely) make the algorithm think that it's back in the same 4ms time period again, all it means is that you lose 4ms of sensing.

Peter_n: I was looking for a simple example, and "InfoToSerial.ino" is easier than "SimpleOnOff.ino". To avoid scaring new users, perhaps you could change the names, and call InfoToSerial a simple demonstration, or make "SimpleOnOff.ino" super simple.

Yeah, the "simple on off" is demonstrating using the filter class without the wrapping (and simlpifying) DebouncedInput class. I stuffed it in a subdirectory to make it less on top. Originally the name of the example included 'DebounceFilter' - the name of the class, and I trimmed it.

You're right - even though it does less than the InfoToSerial sketch, it's more involved because it involves output.

Peter_n: My ideal debouncer would have a filter that takes into account that a button has bad contacts :smiley_cat: . When a button is connected to GND, a LOW input value has a higher reliability than a HIGH value. A low could be electric noise, so perhaps LOW would be 90% reliable and HIGH would be 50% reliable.

The filter could be made more configurable by allowing the upper and lower trigger bounds to be set. I haven't done this because a constant in the code is faster, and because it's just going to confuse people. But you're right - people are simply going to want it. Even though it blows out the size of the object by two bytes ;( .

Altering the .75/.25 ratio is another way the general algorithm could be altered, but the way the bit-fiddling works really ties the code down to doing it that way. It would be a different implementation altogether. Realistically your want to use a float to specify the ratio, and this implementation is about using integer math and bit-fiddling.

Peter_n: Questions about data transfer over very long wires are often asked on the forum. Could the filter be used for a button or an Arduino output pin over a cable of hundreds meters ? In other words: could the timing of the filter be adapted ?

I don't think that I could simply make the sample time configurable without sacrificing the neatness of how it works now - varying the bit shift will only give delays that are powers of 2, and allowing any size of sample delay - again - means reimplementing the way the thing works. Taking a modulus is slow, and I wanted this thing to be fast.

But this is exactly what the 'forceSample' method in the filter class is for. You can do your own timing and feed samples in, using the 'state' and 'statechanged' methods on the filter object to get the output.

So maybe the docs need to cite this as an example of why you would want to use it.

Cool 8) Good job. Maybe a little more explanation, also about the filtering method perhaps.

About the names: You use different names for the ojbects in the examples. Can you use just one ?
What about:
“myDebouncedButton” : no, that’s silly.
“pinA”, “pinB” : that might work, but I like to see that it is a debounced input.
“DI_start”, “DI_stop” : that’s ugly.
Hmmm, I can’t think of a good name.

Peter_n: Hmmm, I can't think of a good name.

Naming variables is hard. I remember a job I had, we would sit around for half an hour or more deciding what we were going to name the main classes and methods for new functionality, and consider it time very well spent. The names of your classes, variables, and methods are the first line of documentation, sometimes the only documentation that a programmer reads.

OK!

I have applied Peter_n’s suggestions.

  • trigger threshholds are now settable
  • support for attaching and detaching a pin without setting the pin mode
  • notes in the doco with respect to bypassing the 4ms rate limiter

Peter_n: Cool 8) Good job. Maybe a little more explanation, also about the filtering method perhaps.

Well, the doco is in the github wiki, and there's an "Algorithm" page. Don't know what I could add to it to make it more complete that wouldn't be you may as well read the code.

Awesome. I have read the wiki (just now :-[ ).

Ok. I have broken out the rate limiting into a subclass. The easy wrapper class uses this subclass, as it is the most usual case.

Version changed to 1.0.0, and may God have mercy on my soul. Job done.