Switches that need debounce before and after pull up

Below is some sample code that I found in the forum that was originally written to monitor one button which I've modified to monitor 10 switches. Normal game play creates a lot of "false" status changes when reading switch statuses. My question is this... is there a better approach or Library (e.g. ezButton) that would simplify this code?

I am using a Mega Arduino with most of the pins being used for other items not shown here.

In my arcade game application, I have 10 targets that rest against individual switches that are in the "closed" position when the target has NOT been knocked down. When the status of the switch is read, LOW returns when the target is still standing (normal state) and HIGH returns when the target has been knocked over. During normal play, vibrations give "false" readings and tell the circuit the target has been knocked down when in fact the target may have teetered but not fallen. I'm going to need to essentially debounce the input pullup at the first potential status change to filter out the noise and again at the end for typical switch debouncing needs.

/*
  Debounce

  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  press),. There's a  minimum delay between toggles to debounce the circuit (i.e. to ignore noise).

  - switches on 10 targets.
  - switch is mechanically in "closed" position in at rest.
  - switch reads "HIGH" when Target is knocked down
  -  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  - If target is really down, then need to debounce the circuit
*/

const int switchPin[] = {23,  24, 25, 26, 27, 28, 29, 30, 31}; // the number of the pushswitch pin

// Variables
int switchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};            // the current reading from the input pin
int lastSwitchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW} ;  // the previous reading from the input pin
int reading[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {

  pinMode(switchPin[1], INPUT_PULLUP); // NC Switch for Target01
  pinMode(switchPin[2], INPUT_PULLUP); // NC Switch for Target02
  pinMode(switchPin[3], INPUT_PULLUP); // NC Switch for Target03
  pinMode(switchPin[4], INPUT_PULLUP); // NC Switch for Target04
  pinMode(switchPin[5], INPUT_PULLUP); // NC Switch for Target05
  pinMode(switchPin[6], INPUT_PULLUP); // NC Switch for Target06
  pinMode(switchPin[7], INPUT_PULLUP); // NC Switch for Target07
  pinMode(switchPin[8], INPUT_PULLUP); // NC Switch for Target08
  pinMode(switchPin[9], INPUT_PULLUP); // NC Switch for Target09
  pinMode(switchPin[10], INPUT_PULLUP); // NC Switch for Target10
}

void loop() {
  // read the state of the switch into a local variable:
  for (int i = 1; i < 11; i++) {
    reading[i] = digitalRead(switchPin[i]);

    // If the switch changed, due to noise or pressing:
    if (reading != lastSwitchState[i]) {
      // reset the debouncing timer
      lastDebounceTime[i] = millis();
    }

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      // whatever the reading is at, it's been there for longer than the debounce
      // delay, so take it as the actual current state:

      // if the switch state has changed:
      if (reading != switchState[i]) {
        switchState[i] = reading;

        // only perform the following code if the new switch state is HIGH
        if (switchState[i] == HIGH) {
          //  Add to score, etc.
        }
      }
    }
  }

  // save the reading. Next time through the loop, it'll be the lastSwitchState[i]:
  for (int i = 1; i < 11; i++) {
    lastSwitchState[i] = reading[i];
  }
}

You say you have ten targets. I count only nine pin numbers in this array.

Array indices start from 0, not from 1. So a 10-element array will have its elements numbered from 0 through 9, not from 1 through 10. But you still have to fix the switchPin array so that it contains 10 elements.

2 Likes

There's a lot of off-by-one errors, confusion between reading[i] and reading.

Fix those, and move the state and time updating bits into the place where you detect the change.

No, I don't think so. I would not use a library in this circumstance as you may find it beneficial completely own the problem, so you can tinker and adjust it with full knowledge of what you are trying to do.

Arrays in C++ begin at zero, so your 10 switches should be 0 to 9 as indices into your arrays.

You only have nine pins in the pin array or I am being blinded by the sun here.

Parallel arrays work well, you might take a moment to look into structured variables, the struct concept in C++ allows combining variables of different types to be packaged so they travel around together.

But parallel arrays work fine, and can leave less visual clutter around. The big step you have taken is using arrays, use them whenever you can:

void setup() {
  for (int ii = 0; ii < 10; ii++) 
    pinMode(switchPin[ii], INPUT_PULLUP); // switches for targets
}

HTH

a7

1 Like

Mrs. Smith would be very disappointed to see how well I took to typing classes.

a7

ok i believe i've made the initial corrections needed. I made these changes to the sketch:

  • corrected pin array to include 10 (was 9)
  • for loops now 0 to < 10
  • added [] to "reading" variable where missing
  • used array for pinMode setup

@alto777 I'm not sure what you mean by "a7"

@DaveX not sure i followed this statement:

Here is a better starting code from which us to work:

/*
  Debounce

  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  press),. There's a  minimum delay between toggles to debounce the circuit (i.e. to ignore noise).

  - switches on 10 targets.
  - switch is mechanically in "closed" position in at rest.
  - switch reads "HIGH" when Target is knocked down
  -  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  - If target is really down, then need to debounce the circuit
*/

const int switchPin[] = {23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; // the number of the pushswitch pin

// Variables
int switchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};            // the current reading from the input pin
int lastSwitchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW} ;  // the previous reading from the input pin
int reading[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {

  for (int ii = 0; ii < 10; ii++) 
    pinMode(switchPin[ii], INPUT_PULLUP); // switches for targets
}

void loop() {
  // read the state of the switch into a local variable:
  for (int i = 0; i < 10; i++) {
    reading[i] = digitalRead(switchPin[i]);

    // If the switch changed, due to noise or pressing:
    if (reading[i] != lastSwitchState[i]) {
      // reset the debouncing timer
      lastDebounceTime[i] = millis();
    }

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      // whatever the reading is at, it's been there for longer than the debounce
      // delay, so take it as the actual current state:

      // if the switch state has changed:
      if (reading[i] != switchState[i]) {
        switchState[i] = reading[i];

        // only perform the following code if the new switch state is HIGH
        if (switchState[i] == HIGH) {
          //  Add to score, etc.
        }
      }
    }
  }

  // save the reading. Next time through the loop, it'll be the lastSwitchState[i]:
  for (int i = 0; i < 10; i++) {
    lastSwitchState[i] = reading[i];
  }
}

haha, sry, just my stupid sig:

a.....lto77......7


I'm all for reducing ink. So global variables are initialised to 0 (and LOW as it happens) implicitly, and arrays can tell you their size at a good moment:

const int switchPin[] = {23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; // the number of the pushswitch pin
const int NUM_PINS = sizeof switchPin / sizeof switchPin[0];

// Variables
int switchState[NUM_PINS];           // the current reading from the input pin
int lastSwitchState[NUM_PINS];    // the previous reading from the input pin
int reading[NUM_PINS];

Then use *NUM*_*PINS* everywhere you now use 10. Adding another pin will be easier and less fraught.

a7

1 Like

I'd instrument it with some Serial.prints():

/*
  Debounce

  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  press),. There's a  minimum delay between toggles to debounce the circuit (i.e. to ignore noise).

  - switches on 10 targets.
  - switch is mechanically in "closed" position in at rest.
  - switch reads "HIGH" when Target is knocked down
  -  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  - If target is really down, then need to debounce the circuit
*/

const int switchPin[] = {23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; // the number of the pushswitch pin

// Variables
int switchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};            // the current reading from the input pin
int lastSwitchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW} ;  // the previous reading from the input pin
int reading[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {
  Serial.begin(115200);
  for (int ii = 0; ii < 10; ii++)
    pinMode(switchPin[ii], INPUT_PULLUP); // switches for targets
}

void loop() {
  // read the state of the switch into a local variable:
  for (int i = 0; i < 10; i++) {
    reading[i] = digitalRead(switchPin[i]);

    // If the switch changed, due to noise or pressing:
    if (reading[i] != lastSwitchState[i]) {
      // reset the debouncing timer
      Serial.print('x');
      lastDebounceTime[i] = millis();
    }

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      // whatever the reading is at, it's been there for longer than the debounce
      // delay, so take it as the actual current state:
      // Serial.print('t');

      // if the switch state has changed:
      if (reading[i] != switchState[i]) {
        switchState[i] = reading[i];
        Serial.print('X');
        // only perform the following code if the new switch state is HIGH
        if (switchState[i] == HIGH) {
          Serial.print('H');
          //  Add to score, etc.
        }
      }
    }
  }

  // save the reading. Next time through the loop, it'll be the lastSwitchState[i]:
  for (int i = 0; i < 10; i++) {
    lastSwitchState[i] = reading[i];
  }
}

and see if it does what you expect.

1 Like

A better solution would be a structured array with a member function for debouncing.

1 Like

This is a very good debouncing writeup by Perry:

2 Likes

There are different flavors of debouncing depending on which end of the noisy transition you want to trigger on. Perry's code gives the result at BOUNCE_TIMEOUT past the end of the noise, which is the sort of debouncing you appear to want.

It differs from the leading edge detecting that some folks favor.

A collection and comparison of some of both methods are in this thread:

Your code looks derived closely from this:

... but extended to use an array of buttons.

As for the LOW vs HIGH, all the methods typically debounce both ends of a signal, and when they are processing the change, they test for HIGH or LOW and make the appropriate changes. Does this part of your code not act on transitions to HIGH properly?

1 Like

@DaveX

At present, my wiring circuit looks like the switch shown on the right on the image below.
image
The arcade target sits on the micro switch and keeps it in the closed position. When the arcade target gets knocked down, the switch looks like the one on the left (ignore the wiring on the left image). However, I have the wire to the NO pin like what is shown on the right image. With this setup, the micro switches of my arcade game are "LOW" when the targets are in the upright position and "HIGH" when knocked down. Is this conventional? If not, I can easily switch the wiring to the NC switch so the targets are "HIGH" when upright and "LOW" when knocked down. Could you please let me know so I can get that wired more conventionally then I'll try the code again with the various above suggestions.

Yes the code is the same copied from a different author with credit given to original author.

@farmer_giles @alto777 @paulpaulson Thanks for your above suggestions.

That looks most odd. What is the diode for and where is the Arduino input?

I can't see how that would work.

the switches could be wired either way. If you had them all wired so what you cared about was the transition to LOW, you could write that chunk as:

        // only perform the following code if the new switch state is LOW
        if (switchState[i] == LOW) {
          //  Add to score, etc.
        }

Or if you want the switches all wired differently, you could add another array and do:

        if (switchState[i] == switchImportantState[i]) {
          //  Add to score, etc.
        }

Is what you have currently working? The original code looked like it would have problems, but they were corrected.

If it is all working OK, go ahead and use it.

If you want to prematurely optimize it, there are ways to simplify it. @PerryBebbington's code (in link above) doesn't need to maintain 4 pieces of data to do the debouncing, it just needs to know the time of the last transition (lastMillis in his code, lastDebounceTime[i] in yours) and the debounced State (lastButtonState in his code, lastSwitchState[i] in yours).

@paulpaulson's structured array (post #9) makes the array maintenance easier:

I haven't tried the member function within a struct[] yet. It seems interesting since you could use new fangled C like:

   for (auto &switch ; switches){
        if(switch.debounce() == HIGH){
            Serial.print("rising edge on pin ");
            Serial.print(switch.pin);
            Serial.print(switch.message);
            Serial.print(" -- Adding ");
            Serial.println(switch.points));
            score += switch.points;
        }

@DaveX RE: Post #8

I see this post is coming in after some new posts after my post #12 so I did not try any of those suggestions. This post specifically addresses the proposed solution of Post #8 which seems to work. Here is the post I was working on....

I elected NOT to make any changes to the switch wiring (left it as normally open) and then tried the code in your post #8. I tweaked that code a bit more as follows:

  • changed Debounce to 100 (hunch that 50 was not enough given suspected static)
  • changed the baud rate to 9600 to match my com port
  • changed Serial.print("x") to Serial.println("x") because when the x's printed off the screen to the right very fast before I identified wiring problems with the switches for targets 9 and 10.
  • If I'm understanding this code correctly, Serial.print("H") is the desired outcome to accurately detect when a target is down. I added Serial.print(i+1) right below Serial.print("H") so that I could confirm which target switch was activated as well as to test each of the 10 switches one at a time.

This all said, I pass along the following observations when testing the arcade game and especially the targets:

  • When the game is at rest, the serial monitor is at rest
  • when there is any vibration of all (e.g. throwing balls at the target), the serial monitor displays a lot of "x" and the occasional "X". This seems to be correctly labeling this activity as "static" instead of assuming a target was knocked down so this works well.
  • When a target teeters but doesn't fall down, the serial monitor displays "xXx" or "xXxxx" or something to that effect. This tells me the "false" target reading of "X" is working correctly.
  • When the target is actually knocked down, I get a reading of "xXH6x" or something to that effect so that tells me target 6 has really been knocked down so this is actually working as expected.

In addition, when I first ran the sketch the screen filled up with "x"s and would only stop when I stopped monitoring the switches for targets 9 and 10. This tells me that I either have a switch or a wiring problem with those two switches. And finally, there is one additional switch that sometimes worked and sometimes did not so this helped me identify 3 switches that need attention.

I'm going to leave this dialog open for a few hours and then mark post #8 as the solution. Thanks to all. The current code shown has been tested and seems to work as I had intended.

/*
  Debounce

  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  press),. There's a  minimum delay between toggles to debounce the circuit (i.e. to ignore noise).

  - switches on 10 targets.
  - switch is mechanically in "closed" position in at rest.
  - switch reads "HIGH" when Target is knocked down
  -  Each time the input pin goes from LOW to HIGH , need to check to see if it is just "noise" or if Target is really down
  - If target is really down, then need to debounce the circuit
*/

const int switchPin[] = {23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; // the number of the switch pin

// Variables
int switchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};            // the current reading from the input pin
int lastSwitchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW} ;  // the previous reading from the input pin
int reading[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // the last time the output pin was toggled
unsigned long debounceDelay = 100;    // the debounce time; increase if the output flickers

void setup() {
  Serial.begin(9600);
  for (int ii = 0; ii < 10; ii++)
    pinMode(switchPin[ii], INPUT_PULLUP); // switches for targets
}

void loop() {
  // read the state of the switch into a local variable:
  for (int i = 0; i < 10; i++) {
    reading[i] = digitalRead(switchPin[i]);

    // If the switch changed, due to noise or pressing:
    if (reading[i] != lastSwitchState[i]) {
      // reset the debouncing timer
      Serial.println('x');
      // delay(500);
      lastDebounceTime[i] = millis();
    }

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      // whatever the reading is at, it's been there for longer than the debounce
      // delay, so take it as the actual current state:
      //Serial.print('t');

      // if the switch state has changed:
      if (reading[i] != switchState[i]) {
        switchState[i] = reading[i];
        Serial.print('X');
        // only perform the following code if the new switch state is HIGH
        if (switchState[i] == HIGH) {
          Serial.print('H');
          Serial.print(i+1);
          //  Add to score, etc.
        }
      }
    }
  }

  // save the reading. Next time through the loop, it'll be the lastSwitchState[i]:
  for (int i = 0; i < 10; i++) {
    lastSwitchState[i] = reading[i];
  }
}

@Grumpy_Mike
you busted me again for an incorrect/misleading image. I should have displayed this:


I intended to show the switch arm in relation to the micro switch AND to show the switch was being wired as NO. There is no diode. The switch common pin is wired to ground and the switch NO pin is wired to one of the Mega arduino pins. Sorry for the confusion.

Maybe I’m misunderstanding. But sounds to me like you simply need to make your debounce time longer. IOW, code “really down” as a confident time since your last ‘down’ edge was detected. Might slow the game down a bit, but you can’t really have it both ways. A target is either down or it’s not. Alternatively can you get targets that don’t ‘teeter’?

I like using those single-character prints to diagnose flow through a program. You can use a lot of different characters as abbreviations for something. I didn't comment them in the #8 code, but I used 'x' for a noise-like high-to-low or vice versa change, and X for a debounced definite change, and the H for a rising-edge to HIGH change processing. I often use '^' or 'v' for a rising or falling edge, 't' for time, or '.' for something frequent. Another nice thing is you can easily comment out the one-liner as needed.

1 Like

Hey everyone.... Just a quick update on my Down the Clown Arcade project that this topic was partly addressing. I have the arcade game working and here's a link to see that Click for forum posting of Down the Clown Arcade DIY project. I also have a youTube shorts video of the outcome and that link is:youTube Short Video

Looks like fun, nice work.

Thanks for posting the code on the other link.

Now… how hard would it be to have the entire row of clowns stay dead for a beat or two after the last one goes down?

a7