Switching between 2 LEDs with one switch a little 'glitchy'...

Experts,

My wife & I have different hours, but the pets need to be fed 2x/day. If she forgets to tell me they ate, they eat twice. If she forgets to tell me they didn't eat, they don't eat at all.

So I tried to find a clean circuit with just passives (Soft switch, push-on/push-off, etc.) and not to Rube Goldberg, but I already knew a uC could do it. There are just 2 LEDs (AM & PM), and one button. If you fed in AM, make AM light, etc., simple.

Here is the tiny sketch, running on an ATTiny45/85:

bool MODE = true;

void setup() {
  pinMode(3, INPUT_PULLUP); // momentary NO push button                                                          
  pinMode(1, OUTPUT); // LED
  pinMode(2, OUTPUT); // LED

  digitalWrite(1, LOW);
  digitalWrite(2, LOW);
}

void loop() {
  if (digitalRead(3) == LOW) {
    while (digitalRead(3) == LOW ){
      ;// wait for button to be released    
    }
    if (MODE == true) {
      MODE = false;
      digitalWrite(1, HIGH);
      digitalWrite(2, LOW);
    } else {
      MODE = true;
      digitalWrite(1, LOW);
      digitalWrite(2, HIGH);
    }
  } //  end of if sw depresed (LOW)
} // end of loop()

But there is an annoying problem with this... The behavior of the switch is not dependable. If you click fast, it won't switch, or appears to switch-then switch back really quickly as you are releasing the button (momentary contact). It works fine with a 1/2 second press 90% of the time.

The schematic is the sketch at this point, so I haven't drawn one up.

Should I introduce a delay somewhere? Declare switch pin differently? Add passive(s)? ...?

You have to debounce the switch, as it typically will make/break many times very quickly when you press it.
You can do it in hardware, with a cap, or use one of the software switch libraries that have debounce.

I can't recommend a library to use, but others on this forum certainly can help with that.

@nixie,

Thanks for writing. I was hoping sensing/waiting for the switch to be released (with the while() loop) would make it trigger just once (instead of acting on the down-stroke). I'll look into debouncing!

Thanks!

Have a look at this :

Although it would be best to get familiar with a button library, in this particular case I think you could get by with a quick and dirty band-aid by adding a delay (maybe half a second) right before the final bracket. This would prevent the main loop from repeating instantly. With this band-aid, holding down the button would cause the LEDs to toggle after each delay.

In your code, the while() does indeed block things during the first low, but then it goes on to toggle the LEDs and instantly repeat the whole process for each subsequent bounce of the switch. This is probably happening thousands (depending on the switch) of times for each button press, resulting in an almost random result.

hammy:
https://www.arduino.cc/en/Tutorial/Debounce

@hammy,
Great call. Exactly what I did, only modifying for pulldown and 2 LEDs.
If anyone cares, it's an instructable here. Thanks to everyone for their input!
pat
:slight_smile:

If appropriate, this is the gist of the instructable:

const int SW = 3;
const int LED1 = 1;
const int LED2 = 2;
int swState = HIGH;
int ledState = HIGH;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time
void setup() {
 pinMode(SW, INPUT_PULLUP); 
 pinMode(LED1, OUTPUT);
 pinMode(LED2, OUTPUT);
}
/**
 * loop() - I actually considered interrupts, but I got really lazy once I got debouncing working :(
 */
void loop() {
 int reading = digitalRead(SW);
 // switching button takes SW LOW 
 if (reading != lastButtonState) {
 // reset the debouncing timer
 lastDebounceTime = millis();
 }
 if ((millis() - lastDebounceTime) > debounceDelay) {
 if (reading != swState) {
 swState = reading;
 if (swState == LOW) {
 ledState = !ledState;
 }
 }
 }
 digitalWrite(LED1, ledState);
 digitalWrite(LED2, !ledState); 
 lastButtonState = reading;
}