Tons of buttons and debouncing

I am working on a project where a driver will be using a gigantic matrix of inputs on a racing simulator. At this stage, I have 25 inputs using 10 pins. The code looks something lie this:

if (pinA == HIGH)
     if (pin1 == HIGH)
          button 1 is pressed
     if (pin2 == HIGH)
          button 2 is pressed
if (pinB == HIGH)
     if (pin1 == HIGH)
          button 3 is pressed

etc

What is the best way to debounce these? I have seen plenty examples where only 1 or 2 buttons are debounced, but this will be 25+ buttons. The buttons I have do not measure any bounce at all on my oscilloscope, but I imagine it is good practice to do so anyways.

Your input is greatly appreciated.

Best?
Simplest, read switches every 50 ms.

.

LarryD:
Best?
Simplest, read switches every 50 ms.

.

Interesting. The arduino isn't only reading inputs, its also displaying rpm onto shift lights and gear indicator and other various visual based outputs. So I could create a function that would handle the inputs, and then use millis() to run it every 50ms? Is that fast enough to not miss button presses?

90% of the time, I just use BWD set to 50ms then read switch inputs.
No debounce more than that.

If you need to have something happen on push or release, add change in state with previousState variables for each switch input.

Edit
Reading a switch every .05 seconds is more than adequate.

.

The code looks something lie this:

I hope not, that suggests you have not learned about arrays and for loops. You are going to have a hell of a lot of turgid code if you go on writing like that.

You may want to consider building something like this. A hell of a lot easier to manage if you are not expecting too many buttons pressed at the same time.

Grumpy_Mike:
I hope not, that suggests you have not learned about arrays and for loops. You are going to have a hell of a lot of turgid code if you go on writing like that.

I understand arrays quite well I believe. I was just trying to illustrate what I was doing in a simple example with some sudo code. That being said, I do need to learn more about for loops and how they would apply to button presses from a matrix. If you have any information on this, it would be great if you shared, I'd very much appreciate that. I'm always trying to learn more.

@Grumpy_Mike, I've done a little bit of looking. Wouldn't a while loop be more appropriate? What would be initialization or counting expressions for button presses? I guess I don't see exactly how a for loop applies to this situation. Thanks.

Generally, if you know how many times you want to loop and need a simple counter, a for loop is a good choice. If the loop depends on more complex conditions then a while or a do while loop is cleaner.

But, you can use any loop style for any situation. For me it is just a matter of what is easier to read, understand what is going on, and later maintain the code.

marco_c:
Generally, if you know how many times you want to loop and need a simple counter, a for loop is a good choice. If the loop depends on more complex conditions then a while or a do while loop is cleaner.

But, you can use any loop style for any situation. For me it is just a matter of what is easier to read, understand what is going on, and later maintain the code.

I fail to see why you would even use a while or a do while loop for handling button presses anyways. Wouldn't it be better to just create a function that is initiated ever 50ms that checks to see which pins are HIGH? Am I missing something?

It would be sensible to have the list of pins in an array. When you want to see which pins are high/low, you loop through the array of pins and do the same thing for each pin.

Pins numbers are DATA and checking is CODE. It is always good practice to separate the code and data wherever possible so that you can just change the data and the code will still work, or add functionality without having to change the data. Usually makes for more compact and maintable code.

Anytime that you see code like

if (variable1) <do something>
if (variable2) <do something similar>
if (variable3) <do the same thing>
etc

Then it should probably be rewritten as

for (i=0; i<end condition; i++)
  if (variable[i]) <do whatever>

marco_c:
It would be sensible to have the list of pins in an array. When you want to see which pins are high/low, you loop through the array of pins and do the same thing for each pin.

Pins numbers are DATA and checking is CODE. It is always good practice to separate the code and data wherever possible so that you can just change the data and the code will still work, or add functionality without having to change the data. Usually makes for more compact and maintable code.

Anytime that you see code like

if (variable1) <do something>

if (variable2)
if (variable3)
etc




Then it should probably be rewritten as 


for (i=0; i<end condition; i++)
  if (variable[i])

AHHHHHHHH, so this makes more sense. The end condition is equal to the number of pins in the array. Once that loop ends, it moves onto the rest of the code I assume? So handle the button presses inside of the for loop, then handle the displaying of rpm and gear data in the rest of the void loop ().

So, when using the for loop, would it be best to place it inside of a condition for the 50ms debounce delay from millis()?

  1. It would be good to see some of your code. All very theoritical at this stage.

  2. You need to start understanding Finite State Machines. This blog post may help
    Switches as User Input Devices – Arduino++

marco_c:

  1. It would be good to see some of your code. All very theoritical at this stage.

  2. You need to start understanding Finite State Machines. This blog post may help
    Switches as User Input Devices – Arduino++

As far as my code goes, I'm trying to lay the project out before I really start working up code, that way it doesn't become very messy, as this will end up being a rather large sketch, I'd like to keep it as organized and clean as possible. Here is just a quick example I threw together:

const int matrixPins = 5;
int matrixPinsX[] = {1, 2, 3, 4, 5};
int matrixPinsY[] = {6, 7, 8, 9, 10};
const int pinCount = 5;
unsigned long previousMillis = 0;
const long interval = 50;

void setup() {
  pinMode(matrixPinsX[0], INPUT);
  pinMode(matrixPinsX[1], INPUT);
  pinMode(matrixPinsX[2], INPUT);
  pinMode(matrixPinsX[3], INPUT);
  pinMode(matrixPinsX[4], INPUT);
  pinMode(matrixPinsY[0], INPUT);
  pinMode(matrixPinsY[1], INPUT);
  pinMode(matrixPinsY[2], INPUT);
  pinMode(matrixPinsY[3], INPUT);
  pinMode(matrixPinsY[4], INPUT);
}

void loop() {

  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis >= interval) {

    previousMillis = currentMillis;

    for (i=0; i<5; i++) {
      if (matrixPinsX[i] == HIGH) {
        ***CHECK Y AXIS PINS***
      }
      ***TELL COMPUTER WHICH BUTTON WAS PRESSED***
    }
  }
}

My issue is, I don't see what the best way to check both axis of the button matrix doing this. Either way, it just gets messy.

You kind of missed the point about the arrays and loops ...

Here is a rewrite using more standard constructs

int matrixPinsX[] = {1, 2, 3, 4, 5};
int matrixPinsY[] = {6, 7, 8, 9, 10};

unsigned long previousMillis = 0;
const long interval = 50;

#define	ARRAY_SIZE(a)	(sizeof(a)/sizeof(a[0]))

void setup() {
  for (uint8_t i=0; i<ARRAY_SIZE(matrixPinsX); i++)
  	pinMode(matrixPinsX[i], INPUT);

  for (uint8_t i=0; i<ARRAY_SIZE(matrixPinsY); i++)
  	pinMode(matrixPinsY[i], INPUT);
}

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

  if(currentMillis - previousMillis >= interval) {

    previousMillis = currentMillis;

    for (i=0; i<ARRAY_SIZE(matrixPinsX); i++) {
      if (matrixPinsX[i] == HIGH) {
        for (uint8_t j=0; j<ARRAY_SIZE(matrixPinsY); j++) {
          ***CHECK Y AXIS PINS***
        }
      }
      ***TELL COMPUTER WHICH BUTTON WAS PRESSED***
    }
  }
}

See how the code can be more compact? Any time you have arrays, use loops.

You have a fundamental issue that you will need to track, for each switch, whether it was pressed and whether you are debouncing. This cannot be done with just one timeout value unless you are only allowing one switch to be recognized at any time. Tracking X*Y timeout values becomes very memory consuming (in your case 25 * 4 bytes = 100 bytes just on the off-chance a switch is pressed).

You should be clear about your requirements on this as I think your memory/program structure will be influenced by how flexible you want this to be.

marco_c:
You kind of missed the point about the arrays and loops ...

Here is a rewrite using more standard constructs

int matrixPinsX[] = {1, 2, 3, 4, 5};

int matrixPinsY[] = {6, 7, 8, 9, 10};

unsigned long previousMillis = 0;
const long interval = 50;

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

void setup() {
  for (uint8_t i=0; i<ARRAY_SIZE(matrixPinsX); i++)
  pinMode(matrixPinsX[i], INPUT);

for (uint8_t i=0; i<ARRAY_SIZE(matrixPinsY); i++)
  pinMode(matrixPinsY[i], INPUT);
}

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

if(currentMillis - previousMillis >= interval) {

previousMillis = currentMillis;

for (i=0; i<ARRAY_SIZE(matrixPinsX); i++) {
      if (matrixPinsX[i] == HIGH) {
        for (uint8_t j=0; j<ARRAY_SIZE(matrixPinsY); j++) {
          CHECK Y AXIS PINS
        }
      }
      TELL COMPUTER WHICH BUTTON WAS PRESSED
    }
  }
}




See how the code can be more compact? Any time you have arrays, use loops.

You have a fundamental issue that you will need to track, for each switch, whether it was pressed and whether you are debouncing. This cannot be done with just one timeout value unless you are only allowing one switch to be recognized at any time. Tracking X*Y timeout values becomes very memory consuming (in your case 25 * 4 bytes = 100 bytes just on the off-chance a switch is pressed).

You should be clear about your requirements on this as I think your memory/program structure will be influenced by how flexible you want this to be.

That's good and all, but this still doesn't indicate which button is being pressed, as in my original post. The example above continues the code regardless of which of the X pins is held HIGH. So there needs to be some sort of conditional arguments done in there.

IMO you have been given enough info, clues and resources to look up about how to do this, short of actually writing the code for you (which I doubt anyone here will do).

There is never a "best way" to do anything in software but there is "meets requirements". Please think carefully about what you really want from your keyboard and then document that in detail. "So there needs to be some sort of conditional arguments done in there" does not cut it as requirements.

marco_c:
IMO you have been given enough info, clues and resources to look up about how to do this, short of actually writing the code for you (which I doubt anyone here will do).

There is never a "best way" to do anything in software but there is "meets requirements". Please think carefully about what you really want from your keyboard and then document that in detail. "So there needs to be some sort of conditional arguments done in there" does not cut it as requirements.

I'm not looking for anyone to write my code for me, or to do my wiring. I came here asking about debouncing. You brought up all of these things up, and I am just asking to understand how this would actually make my code any more compact, because I don't see how it does.

If you want to act like helping someone is such a burden, then why do you even respond to these posts? I asked the same question multiple times, and you didn't answer it. I never once asked you to write any code for me, nor did I ask anyone else to write code for me. The code I shared earlier is almost entirely irrelevant to the project I am working on, and only wrote it and showed it to you to help me understand the concepts you were bringing up. I find this forum to be quite hostile from a few of the same people. I've seen these things in multiple posts by other people who are trying to learn something. If someone asking a question you don't like is so awful to you, don't read it or respond to it.

I came here asking about debouncing.

And you were answered completely in reply #1

Grumpy_Mike:
And you were answered completely in reply #1

Yep, sure was. Your point?