Debouncing a SPST switch

(well, technically a SPDT switch, but only one side is wired up).

I build a variation of the example "push a button and turn on an LED" circuit and wired it up to a switch instead. Then, instead of simply turning on an LED when the switch is on, I had it turn on the LED for a few seconds and then turn off. (This is an initial building block of a larger project, so if I can't get this straight I may have to rethink the whole thing).

The good news is that the entire rig generally works. When I put the switch to on, the LED stays on for a few seconds and then turns off. Even if I turn the switch off in the middle of the "on" countdown, it keeps going.

However, every once in a while everything reverses. Suddenly setting the switch to "off" starts the countdown and setting it to "on" keeps the LED off. After a few switches on/off, it will go back to the expected behavior.

I can't figure out if this is a problem with how I have implemented debouncing or with how I have implemented the rest of the code. Does anyone have any insight that might allow me to at least narrow down where the problem might be?

Thanks!

#define LED 13
#define BUTTON 2

int val = 0;
int old_val = 0;
int state = 0;
int counter = 0;

void setup()
{
  pinMode(LED, OUTPUT);
  pinMode(BUTTON, INPUT);
  
}

void loop()
{
  val = digitalRead(BUTTON);

  if ((val == HIGH) && (old_val == LOW))
  {
    state = 1 - state;
    delay(30);
 
  }

  old_val = val;

  if (state == 1 && counter ==0)
  {
    digitalWrite(LED, HIGH);
    delay(3000);
    counter = 1;
    digitalWrite(LED, LOW);
  }
  if (state == 1 && counter == 1)
  {
    digitalWrite(LED, LOW);
  }
  else
  {
    digitalWrite(LED, LOW);
    counter = 0;
  }
  
}

Why do you delay only when the state appears to have changed? Typically, debouncing means ignoring state changes that happen too close together.

One way to avoid having to write code that keeps track of when the state changes occur is to simply delay every time you read.

Why are you using an int for a boolean? Why are you using 1 and 0 instead of true or false?

How IS the switch wired?

Thanks! The debounce code is from the "Getting Started with Arduino" book, so I guess the reason I do it is because that's what was in the book. I'm happy to avoid having to write code that keeps track of the state and simply delay every read. Any chance you have a link to an example? Is it as simple as something like this:

val1 = digitalRead(pin)
delay(50)
val2 = digitalRead(pin)

if (val1 == val2) {
      use the reading
}

if (val1 != val2) {
      don't use the reading
}

?

I'm using the int for a boolean because that's the way that occurred to me to do it. If there is a reason to use true/false (or if that might be contributing to the problem) I'm happy to do that instead.

The switch is wired so that only one of the two terminals is live. Switching it to terminal 1 is "on" because it connects 5v and ground (the reading wire is connected to the ground side, along with a resistor). Switching it to terminal 2 is "off" because there is nothing connected to the terminal 2 lead.

If there is a reason to use true/false (or if that might be contributing to the problem) I'm happy to do that instead.

A boolean is half the size of an int. A boolean represents whether something did happen/should happen. Therefore, true or false makes more sense than 0 or 1.

If you delay after every read, there is no possibility that contact bounce will be an issue. It is, therefore, not necessary to read twice.

It may be necessary to use the state change detection philosophy.

What does delaying every read look like?

And once again, the boilerplate question when I see "pinMode(pin,INPUT) then,
"if(digitalRead(pin) == HIGH)".

Do you have a pulldown resistor (10k) connected from GND to the input pin?

What does delaying every read look like?

  int pinState = digitalRead(pin);
  delay(someTime);

@outsider I believe so. I'm just using this circuit with a switch instead of a pushbutton

@PaulS why does waiting in that situation overcome the problem? Both

int pinState = digitalRead(pin);
delay(someTime);
take_some_action;

and

int pinState = digitalRead(pin);
take_some_action;

take a single reading and act on it. Why would waiting for a period of time make the single reading more accurate? It is still just looking once.

No debouncing needed if you connect and read the unconnected switch terminal. Just some simple logic needed in an if statement.

If your program code, including the wait, takes longer than your debounce code, then the debounce code does nothing. Don't you agree?

Paul

mweinberg:
@PaulS why does waiting in that situation overcome the problem? Both

int pinState = digitalRead(pin);

delay(someTime);
take_some_action;




and 



int pinState = digitalRead(pin);
take_some_action;




take a single reading and act on it. Why would waiting for a period of time make the single reading more accurate? It is still just looking once.

If the switch is triggering an action then you both need to debounce and to detect
only the transition from off to on, otherwise a quick action will repeatedly fire while
your fingers on the switch. The StateChangeDetection example shows how to detect
a change, and you simply need to guard that detection against bounces by either
delaying or going deliberately deaf to the pin for a short while on seeing either edge.

Thanks all!

In case it is helpful to anyone else, here's what I ended up with. For almost any other purpose, you can delete all of the tracker foolishness.

// this constant won't change:
const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
boolean tracker = false;  // apparently booleans use up less data than ints, since this is just a 1 or 0 
// to track if the HIGH function is still running I used the boolean


void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state and see if it changed
  if (buttonState != lastButtonState) {
    // Delay a little bit to avoid bouncing
    delay(50);
    // if the state has is high and the countdown isn't running
    if ((buttonState == HIGH) && (tracker == false)) {

      
      Serial.println("HIGH");
      digitalWrite(ledPin, HIGH);
      delay(3000);
      tracker = true;
      digitalWrite(ledPin, LOW);
      Serial.println("HIGH off");
      tracker = false;
      
    } 
    else
    
    {
      // if the current state is LOW then the button
      // went from on to off:
      Serial.println("LOW");
      digitalWrite(ledPin, LOW);
    }
   
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;


}

mweinberg:
However, every once in a while everything reverses. Suddenly setting the switch to "off" starts the countdown and setting it to "on" keeps the LED off.

As far as I can see this has nothing to do with debouncing.

You flip the value of 'state' when the button becomes high, and start the countdown when state is true. So every second button push starts the countdown. If you have a toggle switch, I would guess that it becomes high momentarily as you switch it.

The problem is that your sketch does not watch the button during that three-second delay. So if there's a switch during that time, then the sketch will lose count of how many switches there has been.

So you can reliably make your sketch switch sense by flicking the switch an odd number of times during the 3-second delay.

How to fix? As with so many other posts on this board - go visit the "blink without delay" sketch and learn how to time an event without using blocking delays.