correctly toggles an LED with a noisy switch or button. But I'm struggling to understand its logic. Specifically, the two lines
lastDebounceTime = millis();
followed almost immediately by
if ((millis() - lastDebounceTime) > debounceDelay)
seem to imply that the condition will never be satisfied, as lastDebounceTime is repeatedly set to the current time?
Also, is the variable 'reading' unnecessary? But when I changed it to 'currentRC4State', and altered the subsequent coding to match, it failed after the first use of the button, with the LED remaining on.
// Based closely on original
// Sunday 16 January 2022, 1236; Works OK with noisy switch
// Although still don't fuly understand the logic
// CONSTANTS
const byte RC4Pin = 4; // From RC4 or Toggle Button (H edge to trigger)
const byte outPin = 12; // LED for testing
// Variables:
bool outState = LOW; // the current state of the output pin (?)
bool lastRC4State = LOW; // RC4 or Toggle, pin 4
bool currentRC4State;
// 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; // 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);
Serial.println("GardenLamp-Arduino-Millis-RC4-Toggle");
pinMode(RC4Pin, INPUT); // External pull-down
pinMode(outPin, OUTPUT);
// set initial LED state
digitalWrite(outPin, outState);
}
void loop()
{
// RC4: Want to toggle on H edge
// *****************************
// read the state of RC4/Toggle into a local variable:
bool reading = digitalRead(RC4Pin); // RC4 is clean, but Toggle is noisy
// check to see if the input went from LOW to HIGH), and now expected stable at high
// If the switch changed, due to noise or pressing:
if (reading != lastRC4State)
{
// reset the debouncing timer
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > 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 button state has changed:
if (reading != currentRC4State)
{
currentRC4State = reading;
// only toggle the LED if the new button state is HIGH
if (currentRC4State == HIGH)
{
outState = !outState;
}
}
}
// set the output:
digitalWrite(outPin, outState);
// save the reading. Next time through the loop, it'll be the lastButtonState:
lastRC4State = reading;
}
The time only gets reset if the current reading is not the same as the previous reading. There are two approaches to debouncing a button.
Method 1: When you detect a change (e.g. reading != lastReading) you act upon the button press, but then don't check again for a brief period of time [e.g. delay(25) or similar]. This is the way the StateChangeDetection example works [File->examples->02.digital->StateChangeDetection]. Think of this as acting upon the initial change and then delaying to skip over the settling/bouncing.
Method 2: you keep reading the button and every time it changes, you reset your timer. If the timer has expired and the current state is not the same as the reading, then you act upon a button change. This is your code. Think of it as not detecting the button press until the button has settled/debounced.
The difference in actual time will only be a few milliseconds. Act first & wait vs. Wait until settled and then act.
The difference the difference will be between near 0 milliseconds instant recognition of the first contact, and whatever number of milliseconds you use as a constant in the second method.
First method instant or nearly.
Second method always N milliseconds after first contact.
Take your pick. It matters sometimes. But buttons do not capriciously close their contacts unless going down, nor open them unless on the way up.
So I always use method one, and catch the signal I am looking for right away.
Ugh. I just had a bad experience with ezButoon, it prolly is fine but I highly recommend you get your head around doing this hands-and-knees style, no library, so you appreciate the problems and their typical solutions.
Then use the library if it’s convenient and works for you. I found it used improperly or naively, and it did not. Nor would I have expected it to.
Hello Terrypin
I did it quick and dirty. My button handler out of the tool box is scanning the button or buttons every 20msec and in case of state changes a desired function, method or what ever is called or executed.
Have a nice day and enjoy coding in C++.
I’m currently sketching input signals based on my deliberate choice of a very noisy SPDT switch. My scope says it takes some 90 ms to settle in one direction, and around 20 ms the other. Yet that sketch with 50 ms debounceTime appears to sort it consistently.
(Are you still wokwi enthusiast? Many months since I last used it.)
I prefer to always create both press and release events. You can act on the press immediately, then do the debounce, then wait for a release before allowing another press event. You don't allow a press event to occur unless there was a preceding release event, and vice-versa, This approach has the added advantage of allowing you to properly process multiple clicks, as well as press-and-hold events, allowing a single switch to perform many functions.
Thanks so much for taking the time to prepare that thorough example. I’ll try it straight after breakfast.
Excellent switch graphic. Hadn’t known about the relative merits of the basic configuration options. Think I’ll have to change my habitual default of always choosing high-when-pressed.
BTW, Is there an equivalent diagram for NC buttons?
Hello Terrypin
Either you make your own button handler using the IDE examples or you use a library like a canned soup.
It depends on your learning strategy, either you learn a lot about code design in C++ or the project is done quickly. Remember the canned soup always needs to be seasoned. Up to you.
Have a nice day and enjoy coding in C++.
Agreed, I’m no doubt making unduly heavy weather of it. The actual project is a little more complex than my simplified question probably implies, with three inputs determining the output (to light garden lamps):
They will be switched on at dusk and, if they are still on, switched off at dawn, via a separate LDR circuit.
They can however be toggled on/off at any time via a high going pulse, RC4. (That is a clean signal from another old circuit or a manual button press.)
They will be switched off with a high going brief signal from an old mechanical clock in my shed workshop, set to say 10 pm.
So I’ve impatiently tried to combine all three at once, before properly grasping the basics. With the help so far I’m now going to tackle it more methodically.