Q's regarding a generic debounce code for all pins

Hey guys.

In order to simplify my debouncing of buttons in my Arduino projects, I've been writing this code:

const byte buttonA = 5; // arduino pin 5
const byte buttonB = 10; // arduino pin 10
const byte buttonC = 6; // arduino pin 6

// - Arduino DIO pin states -
// current state of the button:
byte buttonState[13]= 
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 
// last state of the button:
byte lastButtonState[13] = 
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

unsigned long lastDebounceTime = 0;
// keeps track of the last time we checked button state.

// *** adjustable ***
const int debounceDelay = 20;
// how long to wait on button state before confirmation.
// if this variable is too long, then quick taps of the button
// may not register. Too short, and button bouncing
// could register as false button press/release.


void setup() {
  // set up the buttons as inputs:
  pinMode(buttonA, INPUT);
  pinMode(buttonB, INPUT);
  pinMode(buttonC, INPUT);

} // end of setup()


void loop() {
  // constantly check for button push:
  
  if(buttonEdgeDetectLow(buttonA)) {
    // do something now that buttonA was pressed
  }
  if(buttonEdgeDetectLow(buttonB)) {
    // do something now that buttonB was pressed
  }
  if(buttonEdgeDetectLow(buttonC)) {
    // do something now that buttonC was pressed
  }
  
} // end of loop()


// FUNCTION:
boolean buttonEdgeDetectLow(byte button)
{
  // check if the button has been pressed:
  byte reading = digitalRead(button);
  if(reading != lastButtonState[button])
  {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) 
  {
    if(reading != buttonState[button]) 
    {
      buttonState[button] = reading;
      if(buttonState[button] == LOW) 
      {
        return true;
      }
    }
    return false;
  }
  // set last button state = to reading.
  lastButtonState[button] = reading;
  
} // end of buttonEdgeDetectLow()

It works great for when you are using any DIO pins 0-13 as a button. But unfortunately my most recent project uses pins A0, A1, and A2 as buttons. (using analog pins as a digital input pin).

So my question to you guys is: How can I modify my code to allow me to debounce Analog pins? (and not just the stock digital pins 0-13)

p.s. You will notice that use of my code above means creating an array of potentially wasted-bytes that will never be used. (in the example above: an array of 13 bytes is used even though I only ever access 3). Generally this wasted memory isn't an issue for my projects, but I'd also appreciate it if someone could explain a more efficient, yet just as simple way to implement my debounce method.

Thanks. -Josh!

Not sure what you are asking but: A0-A5 is the same as D14-D19

For all my switches I use Mr. Gammon's "Switch Manager" http://gammon.com.au/Arduino/SwitchManager.zip It has the advantage of also supporting switch timing.

What is wrong with using

const byte buttonX = A0; // arduino analogue pin 0

UKHeliBob: What is wrong with using

const byte buttonX = A0; // arduino analogue pin 0

That's great for declaring the button pin, but wouldn't work with the rest of the debouncing function I had written.

sending the value A0 to the function edgeDetectHigh() wouldn't give you a desired result. I need to modify something else in the code to get debounce on analog pins used as digital inputs.

sending the value A0 to the function edgeDetectHigh() wouldn't give you a desired result.

There was no such function in your example, but in any case I still don't see the problem. You can use the Ax versions of the pin numbers as function parameters in the same way as any other number.

I think you are confusing state-change detection with debouncing.

If you want to avoid switch bounce just ensure there is a small interval (perhaps 50ms) between subsequent reads of the buttons. One interval will cover them all.

...R

sending the value A0 to the function edgeDetectHigh() wouldn’t give you a desired result. I need to modify something else in the code to get debounce on analog pins used as digital inputs.

On a Uno, A0 is an alias for the value 14. Sending 14 to the function should, if you’ve written the function correctly, produce the same results as sending 7 to the function, with the exception of the pin number that is read.

PaulS: On a Uno, A0 is an alias for the value 14. Sending 14 to the function should, if you've written the function correctly, produce the same results as sending 7 to the function, with the exception of the pin number that is read.

Thanks Paul! So I'll just increase the size of my buttonState[] array to accommodate the Analog pins as pins 14, 15, etc.

So I'll just increase the size of my buttonState[] array to accommodate the Analog pins as pins 14, 15, etc.

Why not just use A0, A1, A2 etc and let the preprocessor work out the pin numbers for you ? That way the code will be portable between Arduino boards and the pins in the array will match the markings on the board,

UKHeliBob: Why not just use A0, A1, A2 etc and let the preprocessor work out the pin numbers for you ? That way the code will be portable between Arduino boards and the pins in the array will match the markings on the board,

is the pre-processor doing the equivalent of:

define A0 14

(aka: replacing all A0 variables with the value 14 at the time of compile) ?

If so, then yeah, using A0, A1, etc should be the same as 14, 15, etc.

As I mentioned in Post#1, A0-A5 is the same as D14-D19 Try the code below, you will see 14 printed.

void setup()
{
Serial.begin(9600);
Serial.print(A0);
}
void loop()
{
}

LarryD: As I mentioned in Post#1, A0-A5 is the same as D14-D19 Try the code below, you will see 14 printed.

void setup()
{
Serial.begin(9600);
Serial.print(A0);
}
void loop()
{
}

thanks.

Try the code below, you will see 14 printed.

Not on my Mega.

I am going to have to buy a Mega. (Yes this is for the UNO or a Bobuino)

joshpit2003:
I’d also appreciate it if someone could explain a more efficient, yet just as simple way to implement my debounce method.

Josh-

Here is your code modified showing a different way to handle using a number of pins in a more efficient manner (at least memory-wise).

It’ might not be the best way to solve this problem but I hope it gives you some ideas.

// Structure for current and last state of a given pin
struct pinStates {
  byte currentState;
  byte lastState;
};

// Here is a list of the physical pin numbers for each button
const byte BUTTON_PINS[] = {5, 10, 6};
const byte NO_PINS = 3;

// An array of pin states (one element for each pin used)
pinStates buttonStates[NO_PINS];

unsigned long lastDebounceTime = 0;
// keeps track of the last time we checked button state.

// *** adjustable ***
const int debounceDelay = 20;
// how long to wait on button state before confirmation.
// if this variable is too long, then quick taps of the button
// may not register. Too short, and button bouncing
// could register as false button press/release.


void setup() {
  // set up the buttons as inputs:
  for (byte indx=0; indx < NO_PINS; indx++)
    pinMode(BUTTON_PINS[indx], INPUT);
} // end of setup()


void loop() {
  // constantly check each button
  for (byte indx=0; indx < NO_PINS; indx++) {
    if (buttonEdgeDetectLow(indx)) {
  // Button state has changed
 }
  }
  
  // Do whatever else here
  
} // end of loop()


// FUNCTION:
boolean buttonEdgeDetectLow(byte buttonNumber) {
  // check if the button has been pressed:
  byte reading = digitalRead(BUTTON_PINS[buttonNumber]);
  if(reading != buttonStates[buttonNumber].lastState) {
    lastDebounceTime = millis();
  }
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if(reading != buttonStates[buttonNumber].currentState) {
      buttonStates[buttonNumber].currentState = reading;  
      if(buttonStates[buttonNumber].currentState == LOW) {
        return true;
      }
    }
  }
  
  // set last button state = to reading.
  buttonStates[buttonNumber].lastState = reading;
  return false;
} // end of buttonEdgeDetectLow()

NOTE: I didn’t test this code and I know the variable names aren’t the best but I tried to keep the changes minimal just to keep things simple.

Regards,

Brad
KF7FER

PS In the code as posted, the function ‘buttonEdgeDetectLow’ didn’t always return a value. It didn’t matter in this case but that may cause you issues in the future. If you change your IDE preferences (File → Preferences, then select "Show verbose output during ‘compilation’) you’ll see warnings such as that during the compile.

Scroll back thru the compiler output and check for warnings every so often.

It might save you hours of debugging in the future. I know more than once I’ve wasted time trying to solve a problem and didn’t see the compiler warning that clearly flagged the issue.

Just a thought.

[quote author=Brad Burleson date=1447971605 link=msg=2486453]

Here is your code modified showing a different way to handle using a number of pins in a more efficient manner (at least memory-wise).

...

[/quote]

Thanks Brad for the insight.

I am not very familiar with structs, I originally thought they were only used to group different variable types (byte, int, long, etc), but I now see they can be used to group similar variables (even of the same type) to "help structure a data set". I guess that's why it's called a "structure" :o

In the past, I've declared them like this:

typedef struct myStruct
{
  byte a;
  byte b;
  byte c;
};

myStruct foo;

Anyhow... back to your code:

I noticed you ditched my button-names, and I forgot to specify that I would like to be able to send a button-name to the edgeDetectLow() function. That way, I can check in a loop for a specific button press by calling:

  if(buttonEdgeDetectLow(buttonA)) {
    // do something now that buttonA was pressed
  }

After playing with your code for 15 minutes, I couldn't figure out a good way to tweak it to allow for using button names (not just pin numbers). I'm wondering if your code requires the use of pointers to accommodate that (?).

How would you tweak your code to allow the use of button names?

Regarding your "PS" comment: Thanks for the tip... I didn't notice that my "return false;" command was in the wrong location. I'm surprised the code still functioned.