And in the Ganssle articles. I do understand, reading is not comprehending, but bit shifting...
OK, now I see the differences in the outputs and the actual meaning of a "perfect sequence". Thanks again.
What I posted updates a pin state history byte every ms as one independent task/function in loop and other tasks/functions can read and use that history to tell if there was a state change followed by 7 of the opposite state in a row as a stable period after the Last Debounce Change. There may be any length of bounces before then, what matters is the last change with no change (stable period longer than any bounce) following.
The bits form a picture over time. If the picture is a HIGH followed by LOWs, that is a button press. A LOW followed by HIGHs is button released. All LOWs is button held down and all HIGHs is button left up and anything else is bounce, try later.
When you have a clean button, bounce will happen for 2 ms and in tests I have found that 3 ms stable period is enough... it can work when ( history & 0xF ) == 0b1000 (press) or 0b111 (release).
When you read pin state every 5 ms and need 16 of those to establish change + stability... that's 80 ms to detect change.
But if it works, I guess that's good.
Switches can age, so what works today might not in years. Don't ask me how I know. ![]()
Jack Gannsle likes 20 to 50 milliseconds. With algorithms that wait out bouncing before reporting an edge, the debounce time must be kept fairly low. 50 milliseconds is about when ppl start feeling that response is sluggish, and also the debounce period limits the rate at which individual presses can be detected.
Switches don't look closed unless they are on their way to being fully closed and stable, nor do they read open unless they are on their way to being fully open and stable. The state can be switched at the first indication of the coming stability.
Other inputs are different, or circumstances may require such delay. A closed loop alarm loop needs way more time before an edge is acted on, in that case to accommodate brief openings due to vibration.
Non switch levels might need debouncing and deglitching. An OOK RF signal might have small periods of looking like they are off, but they are not on their way to being fully off and stable. Another kettle of worms altogether.
a7
And then there are contact switches used as limit switches where human speed is just too slow.
When I saw Nick Gammon's oscilloscope pics of contact switch closures, I made sketches to test switches and simulated dirty switches by tapping jumper pins on the Uno's USB port box and sliding it along.
Kudos to Jack G. for engineering human button presses but note that the solution fits situational needs, certain cases only and please note that microswitches are usually used in different cases.
My suggestion is to test buttons. Out of a box of used buttons, most won't bounce for 10 ms according to articles I have read. If I have a crap button, I'd rather replace it than make the project fit.
Also Please Note that the stable period only has to be longer than the widest gap during bounce. That is what I base my method on , not the total bounce time that can be as long as it takes so please when you test a switch, the gaps during debounce are what to look for. --- what my method looks for is the last bounce plus stable reads, not a total bounce period.
Because anything other than zero is TRUE as a boolean variable.
The relevant Jack Ganssle code is:
// from https://www.ganssle.com/debouncing-pt2.htm
// Idea courtesy Ganssle Group. Called from a 5ms timer,
// the debounced state only ever changes when the pin
// has been stable for 40ms. Initialize debounced_state
// to whatever is "inactive" for the system (HIGH or LOW)
//
uint8_t DebouncePin(uint8_t pin)
{
static uint8_t debounced_state = LOW;
static uint8_t candidate_state = 0;
candidate_state = candidate_state << 1 | digitalRead(pin);
if (candidate_state == 0xff)
debounced_state = HIGH;
else if (candidate_state == 0x00)
debounced_state = LOW;
return debounced_state;
}
without this strange end condition:
that was highlighted in:
The awkward value of using 0xFFF0 as the end condition is that it sort of does the work of state change detection--in a bounce-free release, there's only one iteration where 0xFFF0 matches the record of history. If there is a bounce on release, the release would not match and be ignored.
Jack Ganssle's code does debouncing, not the uncertain falling-edge-detection that the OP's code attempts.
If the goal is clean edge detection, consider rate-limiting the edge detection:
I think this DaveX @johnwasser code
boolean buttonIsPressed = digitalRead(ButtonPin) == LOW; // Active LOW
// Check for button state change and do debounce
if (buttonIsPressed != ButtonWasPressed &&
currentTime - ButtonStateChangeTime > DebounceTime)
{
is the same as what follows here, with the exception of not not even reading the button until it is time.Until it makes sense. It turns it into into a two step process, mind the { embracing } changes that are also necessary
if (currentTime - ButtonStateChangeTime > DebounceTime) {
boolean buttonIsPressed = digitalRead(ButtonPin) == LOW; // Active LOW
if (buttonIsPressed != ButtonWasPressed) {
if (buttonIsPrwssed) {
// actions or set a flag, button just got pressed
}
else {
// actions or set a flag, button just got released
}
ButtonStateChangeTime = currentTime;
buttonWasPressed = ButtonIsPressed;
}
}
If the process runs through this code infrequently enough, the outer time constrain if statement can be removed, like @LarryD said #3 and said #38. It bears repeating. Many loops can serve happily throttled to 50 Hz or 20 Hz; when timers get into the act such periodic examination can happen "automatically" in interrupt service routines.
a7
If you want to see bounce you have to read at high frequency.
If you read at low frequency you may only see one state change unless you have a very dirty button.
Human senses... our eyes see motion at 24 frames per second where 25 FPS is 40 ms per frame. So keep the gaps to 40 ms or less and you can't even see the debounce.
But still when a limit switch hits I want to stop the motor ASAP, just never for a line spike or --- hey my way would look for xFF to become xFE (1st change from steady 1 reads to a 0 read) or x00 to become x01 ---- stable to 1st change that will only detect after stable so still not false positive during unstable bounce. IMO that is open to reading a spike as a false positive.
Keeping pin state history means never having to do end - start = elapsed timing or keep time variables. Millis changes once per ms though the value may increase 1 or 2 just due to how millis() works and the low 8 bits of millis() is what matters so previous value can be stored and compared as a byte.
History shows patterns that reduce to numbers. The pattern you want is a number, the complexity is reduced to a compare to constant.