Embed With Elliot: Debounce Your Noisy Buttons, Part I

“Psst…hey buddy! Wanna see the sweetest little debouncing routine this side of Spokane? C’mon over here. Step right over those bit-shift operators, they don’t bite. Now look at this beauty right here: I call her The Ultimate Debouncer(tm)!”

Everybody who works with microcontrollers eventually runs into the issue of switch bounce or “chatter”, and nearly everyone has their own favorite solution. Some fix it in hardware, others fix it in software. Some hackers understand chatter, and others just cut-and-paste the classic routines. Some folks even try to ignore it, and they might even get lucky, but everyone’s luck runs out sometimes.

In the next two “Embed with Elliot” installments, I’ll look a little bit at bouncing, look into doing hardware debouncing both the simple way and the right way, and build up a basic software routine that demonstrates some of the principles and which works just fine, though it’s not optimized. We’ll be laying the groundwork.

In the next installment, I’ll let you in on my personal favorite debounce routine. It’s a minor tweak on a standard, but with some special sauce that’s worth spreading around. I’ll call it the Ultimate Debouncer(tm), but will it stand up to the scrutiny of the Hackaday commenteers? (How’s that for a cliffhanger?!?)

For now, though, let’s look into switch bounce and the standard ways to fix it in hardware and software.

The Problem

To you and me, when you press a pushbutton, it goes from an open circuit to a closed one, and that single transition defines the button “press”. But the reason that it’s so clear-cut to us is because we perceive things pretty slowly. To a microcontroller, which is able to test a button’s state as often as a few million times per second, it’s not on-off at all.

When you physically press a normal pushbutton, two pieces of metal come into contact with each other. If these two tiny sheets of metal aren’t perfectly flat or perfectly aligned (and they’re not) then they can make and break contact a few times before being firmly enough squished together that they’re always conducting. To a microcontroller the button appears to be pressed many times for extremely short durations, when you think you’ve just pressed it once. Debouncing is all about making sure that you and the microcontroller agree about when a button push or release event happened.


Here’s some oscilloscope plots of me pressing and releasing a plain-vanilla tactile switch that’s hooked up to a microcontroller. This particular button is much worse on the release, bouncing around almost every time and for longer, than it is on the press. So much so that I had to use different time-scales on the scope to make them both visible. But see how the voltage transitions between logic high and low before it finally settles out? That’s bounce in action.

Before tackling the debouncing problem, let’s pause to think about when button bounce matters. As demonstrated above, it’s only the effective number of button presses that we and the microcontroller don’t agree on. After a couple of milliseconds have elapsed, we both agree that the button is or isn’t currently pressed. It’s only during the very fast transition that we disagree.

button_collectionSo if your code only needs to establish that a button has been pressed at all, button bounce may not matter all that much. To light up an LED when the button is pressed and turn it off when it’s released, you don’t need to debounce; the human being looking at the light won’t notice that it’s flickered ever so briefly. Your code may have wasted a few cycles going back and forth between the on and off states, but it’s not likely to be catastrophic.

Button bounce matters most when you care about counting the number of button presses. We all know the frustration of poorly-debounced rotary encoders that skip steps — these plagued most every car radio from the mid-90s that I’ve encountered. If the microcontroller, with it’s super-high-resolution world-view, sees more transitions than we do, things go wrong. We need to fix that.

Fix it in Hardware

The good news about contact bounce is that it can be entirely eliminated with a little bit of hardware. You can do it “good enough” or you can do it right, but if you’re going to physically debounce your buttons you can read the rest of this section and get back to work, ignoring the following software foolery and even Part II of this series. There’s a certain logic to a hardware solution to a hardware problem.

Most microcontrollers have internal pullup resistors that can be activated for any given GPIO pin. This lets you establish a definite logic state on a button when it’s not pressed by pulling the voltage back up from ground. More capable microcontrollers allow you to choose a pullup or pulldown resistor on the pin, and even select among possible values for the resistance and more. What matters is that we’ve got an internal or external pullup resistor of value Rp1 holding the microcontroller-side of the button in a logic high state when it’s not grounded.

Then we press the button. The button connects and disconnects to ground before finally settling down. We wish to smooth these wiggles out. Or as a frequency-domain engineer would say, we want to lowpass filter the input. Whichever way you look at it, the solution is as simple as adding another resistor and a capacitor.


The simplest hardware solution, which works most of the time is to debounce with a resistor and capacitor. It’s a couple-cent solution that takes up little board space using surface-mount parts. Practically, you pick R and C so that the product R*C (in ohms and farads, respectively) are in the ballpark of the time you’d like to debounce for. Here, I’m using the microcontroller’s internal pullup resistor, which is around 10K ohms, and a 100nF capacitor, which gives 0.001 seconds — a millisecond — on the release. The press uses a 1K resistor, and it has to be significantly lower than the pullup to guarantee that the switch settles on a low voltage. It’s necessary because dumping the capacitor directly through the switch produces unwanted high-frequency voltage noise.

The simple RC filter here is still only a partial solution, though. If it’s running on 5V, for instance, your microcontroller wants to switch between logic low and high at 2.5V. While the filter smooths things out fairly well, it doesn’t guarantee that you don’t get unlucky bounces that wiggle around just as the voltage on the capacitor is reaching 2.5 V. If you pick the product of RC large enough, you’ll get away with it most of the time.

Hysteresis: The Full Fix

To get it right all of the time, what you’d like is a voltage signal that moves as rapidly as possible through the murky mid-rail voltage region. It’s quite common to use a pre-conditioning logic chip with hysteresis to clean up the switching action. Logic chips like the 74HC14 or 40106 will do just that for you.

In our case, hysteresis means that instead of one voltage threshold, right in the middle, there are two: one-third and two-thirds of the maximum logic voltage are common. A switch with hysteresis uses the upper threshold when it’s last seen the low state, and the lower threshold when it’s in the high state. This leaves the range between the two thresholds as a “dead band” where no switching ever takes place, and this solves our problem perfectly.

(Note that thermostats work exactly this way — they trip the heater on at a lower set point than they turn it off again. If a thermostat didn’t work this way, your heater would be oscillating back and forth with every change of fractions of a degree.)

proper_debouncing.schThe ideal hardware solution is straightforward: a resistor and a capacitor to smooth the ripples out and a 74HC14 to provide hysteresis. A fifty-cent chip and a handful of passives can take care of up to six buttons with ease, and you don’t have to think about it at all on the software side. Sticklers will note that we have twice the pull-down current as pull-up. It’s OK — buttons usually bounce more on release than on press.

Here, the yellow trace is the smoothed output of the RC filter, and the green trace is the signal after being run through an inverter. You can see that there was considerable bounce on the press and release — this is the worst-behaving button in my collection — but that it’s mostly smoothed out by the filter. You can also see hysteresis in action. Notice the two voltage thresholds that the inverter uses: on the way up, it switches around 3.3V, and on the way down, around 1.5V.


I cherry-picked these two fairly bad transitions for the graphs here — I probably pressed the button 50 times to get these two examples. But they also illustrate what can go wrong when you only filter the bounce out. The ripple in the button press event on the left gets dangerously close to the 2.5V mid-rail switching threshold. That’s why you use an inverter with hysteresis before the microcontroller.

In conclusion, fixing contact bounce in hardware is straightforward, at least on this timescale. If you’re feeling lucky, you can filter the button’s signal with a resistor and capacitor. If you’ve got really dirty buttons, or you just want to do the job right, an inverter with hysteresis buys a lot of peace of mind and covers six buttons for just fifty cents.

Fix it in Software

I’m usually a fan of fixing hardware problems in hardware, rather than software. On the other hand, besides requiring no additional parts, the beauty of the software solution is that it’s a once-and-for-all fix: get yourself comfortable with a particular debouncing routine and you can import that one library for the rest of your life. Or maybe you’ll want a couple. Anyway, writing the code is an up-front cost that you pay just once. Besides, debouncing routines can be fun.

But keep the hardware examples above in mind, and be sure that you’re visualizing the unfiltered, bouncy signals as we work through algorithms. After all, that’s the reality that we’re trying to cope with.

There are, broadly speaking, three types of debounce routines that I’ve seen. (There may be more!)

  1. The first, and simplest, attempts to wait until after the bouncing has stopped before declaring a button press or release. If the switch is still bouncing after a delay time, it delays again until the switch is stable. I’ll call these routines delay debouncers for obvious reasons, and that’s what I’ll work through today.
  2. There’s also counter- or integrator-based debouncers, with or without hysteresis in the switching threshold. These are the equivalent of the RC filter above, or the RC filter with inverter respectively. We’ll not cover these in detail, but I’ve got links below.
  3. Finally, and we’ll cover this in the next installment, is what I’d call a pattern-based debouncer, that takes the overall pattern of the switch’s voltage output over a relatively long time period into account. (I’ve said too much!)

Take a Time Out

Delay-based debouncers wait the bouncing out. After all, the mismatch between human-time and computer time isn’t all that long. Let’s say, for example, that your button never bounces for longer than five milliseconds. When the microcontroller notices a downward transition because the beginning of the bounce phase has begun, we let it wait for five milliseconds and test again. If it’s still pressed, declare that a press has happened. If not, it was just a bounce, so we repeat the cycle.

Notice what this requires, though. To detect a single button-press event, we need to know what the button state was before the press, what it is currently, and then we need to wait and check again. Even in the “simplest” routine, there’s a lot going on. We need to keep track not only of the current button state, but its past state as well.

For clarity, I’m assuming that you know how to read the button state on your particular microcontroller, and the function read_button() takes care of that. With a pull-up resistor, pressing the button will read logic low, so you might also need to invert the logic. For an AVR target, this could be as simple as testing ( (PORTD & (1 << PD2)) == 0 );. For Arduino, we’re talking digitalRead(pin_whatever) == 0.

enum ButtonStates { UP, DOWN, PRESS, RELEASE };

enum ButtonStates delay_debounce(enum ButtonStates button_state) {        
    if (read_button()){                      /* if pressed     */
        if (button_state == PRESS){
            button_state = DOWN;
        if (button_state == UP){
            if (read_button() == 1){
                button_state = PRESS;
    } else {                                 /* if not pressed */
        if (button_state == RELEASE){
            button_state = UP;
        if (button_state == DOWN){
            if (read_button() == 0){
                if (read_button() == 0){
                    button_state = RELEASE;
    return button_state;

We define four possible states for the button to be in: the resting states of UP and DOWN and the transition states PRESS and RELEASE. And note that the function both takes as input and returns a ButtonStates value — your code calls this function like this bs = delay_debounce(bs); With this strategy the calling code stores the button state, and the debouncer knows what the previous state was when it’s called.

If the button is currently pressed, there are two possibilities that matter — either the button was just pressed last time, and we’re in the held-down state, or the button was up last time and we’re entering a possible press event. The code waits and retests the button before coming to any conclusions. The code for the non-pressed button is a similar but opposite logic.

Note that successive calls to the debounce routine move it from the just-pressed transitional state into the “button is down” state where it will spend a lot of time when someone’s mashing it. If your application code is counting button presses, life is easy: simply test if (bs == PRESS) after an update. It is guaranteed to only be in the PRESS state once per button press, and the next call will push it into DOWN. Woot!


The fundamental weakness of this simple snippet is that you have to know how long the longest bounce event is going to be. This is tricky. Things like dirt or oil in the contacts, humidity in the air, and aging parts can change the required time-out over the life of the device. You should probably end up overestimating the bounce timeout rather than underestimating it. Is five milliseconds enough? How about ten? Jack Ganssle likes twenty to fifty milliseconds.

The other weaknesses of the particular code that I’ve written above are that it’s slow — all of those nested if statements cost. You can get rid of a couple by using elses, and I usually do, but it makes the code a tiny bit less readable.

I also use a blocking wait for the delay, which means that the CPU can’t get any work done until the button is resolved. That’s no good. The Arduino “Bounce” library, which otherwise uses a similar method, counts time with non-blocking millis() instead, which is strictly an improvement. In general, if you have a system tick at hand, it’s a better choice than using a blocking wait. Five milliseconds of time wasted is an eternity for a CPU that’s clocked in the megahertz.

Software Debouncers in the Wild

If one debounce routine isn’t enough for you, have a look at our mega-debounce roundup back in 2010. Interestingly, most of them are improved implementations of the delay debounce presented here: they sense a change and see if it’s still true in some milliseconds. Even focusing on just this one strategy, you’ve got a number of implementation options.

A couple are of the integrator type. In particular, [Kenneth Kuhn]’s debounce routine is very nice. It increments a counter every time it sees the button pressed, and decrements it when the button isn’t pressed. By using a threshold like seven counts or something, it avoids responding to initial wiggles.

By summing up the button’s count over time, it’s acting just like our RC filter hardware example — smoothing out the otherwise spiky bounces. And just like the simple RC filter example, it is possible that the counter will get right up to the threshold value and then wiggle around. Good design picks the timing so that this almost never happens, which is probably why this debouncer works so well, but we can improve it.

What this debounce routine lacks as written is the hysteresis of the ideal hardware debouncer, but that’s a simple fix. Just keep track of the button’s current state (up or down) and use different thresholds to detect button presses or releases. This complicates the otherwise beautifully simple routine, unfortunately, but it’s a very solid choice. If I couldn’t use my favorite debouncing strategy, I’d use this one.

Conclusion, and Teaser

So far, I’ve presented a few options for debouncing: a “good enough” RC filter solution, the “guaranteed” hardware solution with a filter and a switch with hysteresis, and then a standard, delay-based software debouncer that actually works better than you’d think because it keeps track of which state the button is currently in.

But I wouldn’t be writing this column unless I had something even better in mind. Keeping track of the state of every button that you’re debouncing is a hassle, and if you’re only interested in detecting button presses, for instance, the time spent on detecting releases is time wasted. But it’s hard to add hysteresis to the system without state. Or is it?

In Part II, I’ll show you a software debouncer that’s inspired by an absolute classic strategy (that I haven’t presented here yet) but with some simple improvements. It’s based on recognizing the overall pattern of a bouncing switch and adds in hysteresis to make it work, yet it’s simple enough to be implemented directly in logic (e.g. in an FPGA or CPLD or similar). I call it the Ultimate Debouncer(tm)! Join us next time to see if it can stand up to the hype.

98 thoughts on “Embed With Elliot: Debounce Your Noisy Buttons, Part I

  1. A simple alternative with a common super-loop style microcontroller program might be to read in the switch bit states once per loop into an 8, 16 or 32bit variable and rotate it one bit, once per timing loop.

    If your tick counter is 10msec for example then the switch would be ready once every 10 msec. Each pass through the loop you rotate the variable by 1 bit. Your debounced switch state is valid only when the variable =
    0x00 or 0xFF for 8bit variable
    0x0000 or 0xFFFF for 16bit variable or
    0x00000000 or 0xFFFFFFFF for a 32bit variable.

    Debounce timing in the above example would be the number of bits x the tick count delay, 8x10mSec, 16x10mSec or 32x10mSec for example.

    That’s a really easy way to do debouncing, using only 1 variable and a very fast rotate instruction.

      1. Perhaps you could accumulate ‘delta’ bits, debouncing both ‘press’ and ‘release’ states, along with some simple parallel switch state logic for a simple, fast, and elegant solution…

      2. A pair of 8-bit wide vertical counters provides independent counters for up to eight switches while simple parallel switch state logic maintains a debounced switch state latch and passes along the desired switch state flags. The parallel logic is also isochronous (no if/then or other conditional code) which may be an advantage is some situations.

      3. Set an edge interrupt and possibly a timer to check if it makes it to the end(full count of timer) If it does the press was a success, if it edge interrupts before then it was a bounce or too fast to be a successful press, unset edge INT until next press event occurs.

        Also there is some tom-foolery to be had with internal PU/PD resistors in some situations.

      4. Just test is for 0b01111111 (=>pressed) and 0b11111111 (=> down)

        In the text you mention the Arduino Bounce library. But the Bounce library is obsolete (and I can’t find it anymore). It’s replaced by the Bounce2 library. And that library does NOT use the what you call delay debouncers. It does not (and I quote) “sense a change and see if it’s still true in some milliseconds” but it does “sense a change and see if it’s true FOR some milliseconds”. That’s a major difference. The “some milliseconds” does not to be the whole bounce but just the longer then the longest spike. It continuously watches the button after a change and declares it stable (a real change in state) after “some millisecond” of being stable. But it keeps watching and resets the timer when the read_button() changes again. So it’s more like the example above and gives you a fourth style of debounce (except if you call this the pattern-based debouncer). But Bounce2 (and I can’t speak for Bounce (1)) is not a delay debouncer!

        1. Bounce2 waits until it’s seen N (is it four? I forget.) observations spaced over time. That’s a lot more certain than a single delay, and can respond faster as well.

          Not that the old delay routine was bad — they’re just different. (I wouldn’t say that working code is ever “obsolete”.) Lots of folks get along just fine by using very long debounce times.

          And yeah — what you suggested is totally the answer I was looking for. Internets for you!

          It’s subtly flawed, though, and it took a particularly crappy button in my junk drawer to show me how. That’s the topic of the next post. :)

          1. Thank you :) Hope I don’t use it all this evening :p

            But about Bounce2, observations of what? It just waits “some milliseconds” (default is 10 which is indeed long) in which the read_button() may not change. If it does, it resets the timer and starts looking again. Any how many times the button is looked at (read_button() is run) depends on how often you can the .update(). And yeay, you might miss a small spike but that’s live when you poll.

    1. This is what I do on all my MSP430 projects.
      The MSP430 architecture is great at this.

      In assembly:

      bit BIT0, &P1IN ; bit test on BIT0 or P1IN, sets/clears carry to store result of bit test
      rrc.w &debounceVariable ; rotate result through variable in memory.

      This is literally all there is to it. Now test debounceValue equal to 0 or -1. Typically I consider 0 as not pressed anything else as pressed, this gives the best response time while still providing adequate debounce capability.

    2. This is what I do. Storing the values until I get 0x00 or 0xFF as required. I don’t use interrupts, instead I poll my ports. I get more reliable behavior since I’m not diddling around for X mSecs but it does require a bit of care not dicking around with long routines to remain responsive to the user.

      I find I rarely care to know both transition states so I can stay with vertical or horizontal (depending on number of buttons) tracking however wide my memory buttons in one go. Or scale it by sacrificing memory as necessary. If I really really needed to track state changes, an extra memory is used to “flag” a button as being handled so the polling routine isn’t altered too much. For example, if a button is 0x00 but the flag is b1 then that means a transition occured and left unhandled. The transition is handled and the flag is set to b0 to wait for the next transition.

      I recently scaled the method with little alteration to deal with 27 (all the pins I had left) analog inputs as a means of adding software based hysteresis. The memory punishment isn’t too bad since I divided the input by Dilberts to keep within the processor’s native width. All of this was created on astonishly dirty power, so there was a lot of fine tuning and tweaking dealing with the noise. Once I realized the problem with that (an unavoidable change to the power source was the cause), the code has proven rock solid ever since.

      I didn’t use a hardware based solution because, quite frankly, it didn’t occur to me. A hardware solution would’ve shaved off many hours of tweaking for the analog portion.

      One neat idea I was working on before being distracted was a self calibrating routine to “degrade” the timing as the input wears out over time. More for the analog rather than the digital, the idea was to monitor the incoming analog values and be self aware to poorer performing input. The initial solution calibrated itself to the worse performing switch for each group of 8 (or 16 or 32 depending on bit width). I wasn’t too thrilled with it so it needs a little .ore work I guess.

      1. The scaling is where this shines. You can have small variances with the timing as long as the overall timing remains the same. Not like a bouncing switch makes a perfectly spaced square wave anyways. That said, you wouldn’t be using a single function for the button handling anyways. Poll at deterministic times, deal with the state at indeterminate times. Just don’t let the indeterminate times take too long.

        If you’re doing a ton of pushbuttons, your controller shouldn’t be doing much of anything else anyways. Model it after keyboards and let a small controller use a refined and tightly controlled loop for determining button state and push that data to a higher order controller for the action.

        1. “If you’re doing a ton of pushbuttons, your controller shouldn’t be doing much of anything else anyways.”

          I don’t see why not. A modern controller can keep track of 100 buttons and still have 99% capacity left over for other tasks.

          1. My point was that if you’re doing some intense processing interfering with user responsiveness, then you need to redesign the super loop or offload some of that work or figure out something else. A controller with 99% capacity is going to be useless if it can’t deal with the 100 buttons in a responsive manner because someone thought it was a good idea to write blocking code.

            Doesn’t matter how useful your doodad is, if it doesn’t respond in a seemingly timely manner to the user then it’s going to be considered a piece of shit. They’re not going to care if they only ever push a button once a day, if it doodles around trying to decide what to do, you get annoyed customers.

        2. You’ll need at least 20 or so I/O for the 100 keys. Eventually you runs out of I/O in your package and might have to go to a bigger package that can cost more money or force you to use BGA that requires a more expensive PCB.
          Chances are that you can leverage a off the shelf OTP micro for the PC market or even a full keyboard.

      1. Doesn’t matter. If it wasn’t pressed before, and you’re not seeing it pressed this iteration (because it is mid-bound) then you’ll get it next time.

        I’ve used this technique flawlessly for years. It is the simplest and works for nearly all cases. For Arduino code, just put a sleep for 20 to 50 ms per loop iteration, or if you have work to do, check a timer for when to sample the button input.

    1. Not to mention that this technique is easy to implement, tidy in code, uses very few resources (one timer for all input), easy to use with an RTOS, let’s you sleep for longer to save power, and makes debugging easy because all input updates synchronously.

      Of course the flaw is that it doesn’t adapt to the different switching speeds of every switch. That is, it’s way slower than the methods in the article, but even when I was making a video game controller updating once every millisecond was far more than required.

      I’d love to hear about applications where this approach doesn’t work or takes more effort. I can’t think of one off the top of my head.

  2. Rather than a delay debounce, would it not be better to be proactive? A transition means a change in state, pass that on immediately but ignore further transitions for the next 50ms or so.

    1. Exactly – on a transition, I would store current microseconds. Should the next transition occur within the pre-defined number of microseconds, I would simply return from the function with no action. Should the number of microseconds between last action and current time be above the threshold, I would to the action.
      Using this approach, the micro-controller does not to have 5ms delay and can do other things while the “debouncing” action is ongoing.

        1. I’ve actually implemented that one — first edge interrupt disables that button’s interrupt until a timeout has passed or until your code has handled the press. It’s a good one.

          And to Artenz: The delay is really a red herring. If you’ve got some way of keeping track of time, a system tick variable that updates, converting this code to a non-blocking version is only a few lines more complex. It can be made to work just fine, and for instance that’s what the Arduino libs do.

        2. Like this?

          Set interrupt on port
          on port interrupt set timer interrupt and do your button state tracking logic logic
          on timer interrupt set interrupt on port again.

          But not if you are doing PWM using analogWrite() at the same time?

          1. All the setting and removing of interrupts is too complicated. Just keep one repeating timer interrupt, and check all switches in that. Any delays can be measured by simple counting.

            It’s even better if you have a bunch of switches connected to the same port. You can read them all in parallel, and keep their previous and current states in bit vector, so you can manipulate them all in parallel too.

        3. The great advantage is that this allows also really low-power sleep modes, e.g. where all clocks (even low-power/async timers) are off. We wakeup on async GPIO IRQ, disable said IRQ, start the oscillators and enable HW timer and go back to sleep (in a mode that keep the timer running). On timout check button(s), do stuff, re-enable GPIO IRQ and back to power-down.

    2. My thoughts exactly. Switches, even ones which are noisy when pressed, do not generate entirely random state changes; so if you pick up a change, it necessarily means the switch was pressed/released, and that’s all you need to know for the next [insert reasonable debounce time here].

        1. It’s not nitpicking.

          I’ve got a tactile switch on my desk that will make you cry. It goes intermittent for a few tens of microseconds about 20-50 milliseconds after a press.

          If you’re keeping track of state (or as suggested above, enforcing a state-change lockout) delay debouncing will work. If you’re not, it’s a lot more succeptible to short glitches.

          1. Yes, I meant a state-change lockout (i.e. ignoring new input for some time). I used it successfully on several occasions. A switch as bad as you describe should be banned anyway… :-) it does emphasize the importance of knowing your hardware – that can help in selecting the best algorithm for the job.

            And thanks Elliot for your clear and important articles!

  3. I use the delay debouncer with a good 50 or so mS. Never had it fail. Most people pressing buttons are going to hold the button that long unless they are trying to do some sort of button pressing speed trial or something. Also no human is going to notice a 50mS delay between button press and action anyway. It works, it’s simple and uses minimal code or resources. Hardware deboucing is a waste of board space and components if you’re feeding inputs to a microcontroller.

  4. Might be worth mentioning the hardware solution of using a SPDT button. Feed this into a RS latch (or two microcontroller inputs). The NO and NC contacts may each bounce individually, but the switch will never bounce between the two. Simple hardware hysteresis.

  5. I usually have a timer interrupt (usually 5-10ms) for my code. One of the things it does is to poll the hardware key. It also keep tracks of software timers, polling tasks and display refreshes etc.
    – If a key was pressed where it wasn’t pressed before, it copy the key and increment a counter.
    – If key(s) became opened manual release or due to key bounces, it clears the counter.
    – Once the counter reaches a threshold that is longer than key bounce, then it sets a global flag for a valid key press.
    – Once the counter reaches a secondary threshold for key repeat, it sets a global flag for valid key press and reset the counter to a fixed value for the desired the auto-repeat rate.

    The auto repeat detection allows me to increment values 10 at a time in my UI while individual key presses increase it by 1.

    1. Yeah.

      I didn’t cover counter-based debouncers, but they’re great in principle. The addition is the equivalent of the RC filter, and setting / clearing the counter gives you some hysteresis.

      As you point out, where they really come into their own is where you need to know the duration of a press — long vs short, or auto-repeat or etc.

      Do you have a snippet up somewhere that you’d like to share?

      1. Here is my old 8051 code.
        bit Key_Hit; // Key pressed
        ubyte Key, OldKey, Key_Time; // Keypad

        ubyte GetKey(void)
        OldKey = Key;

        if(Key && (OldKey == Key))
        Key_Time = 0;

        if(Key_Time > Key_RepeatX1+Key_RepeatX2)
        { Key_Time = Key_RepeatX1;
        else if (Key_Time == Key_Debounce)


        1. It came from my collection of code written for my 8051 embedded design. My other targets codes are different as they use more specific hardware features such as ADC, port change and even DMA. Are you nit picking what I said?
          While code portable enough, but not obvious to your average “Arduino” users.

          1. Well yes I am nit picking if you want to call it that. I generally program really old chips like the 8051 in assembly as that’s how it was done before ‘c’ and 8051 Assembly looks nothing like your ‘c’ code obviously lol.

            I agree ‘c’ is great and it’s portable but still ‘c’ is *not* 8051 code.

    2. I use this method. It’s nice when you need to do long-duration presses that trigger different events. (ie. 5 second press performs a different action than a single press.) The “unpress” works the same way.

      When my device goes into low-power mode and the clock slows down, I adjust the rate that the button counters increment to get a similar response time. It’s not perfect.

  6. Well…

    I agree with the author about fixing hardware problem in hardware but with a caveat. Fix them in hardware when you can’t afford the overhead in your micro or you CAN’T fix it in software. Here is a real world example from a company I worked for, Fortune 500 company, for a product that had to go through UL325 certification. The product was a control board for a garage door opener.

    The code was written, debugged and went to the “destroyers”, I was the leader of the gang. It was up to us to make the micro go off into never never land, or worse, allow a garage door to go down and not reverse. After we got the obvious bugs worked out (i.e. hit the pushbutton 257 times in less than a certain amount of time causing a stack overflow in an 8 bit micro).

    Once this was done, the product prototype was built and it was subjected to 5 ESD discharges, both + and – from 4kV to 20kV in 2kV increments as well as well as radiated emissions into all IO lines (push button, line cord, IR safety beam). The radiated emissions were supposed to simulate having a noisy fluorescent light near the GDO. Under no circumstances could the GDO (Garage Door Opener) fail the following tests.

    1. Coming down and not reversing on impact
    2. Coming down and not reversing on a safety beam break
    3. Coming down and disobeying 1 and 2 and not reversing after 30 seconds of not finding the down limit for the door

    No micro known to man can withstand a barrage of ESD discharges that total 90 discharges that exceed a typical human body model. None. Show me one that has those external components built into the micro, the parts take up too much room! So we added external hardware to take care of protecting the IO lines.

    So the micro passed the destroyers, but failed 1, 2 and 3. Code was re-written, it went back to the destroyers, they found new bugs, the new bugs were fixed and it went back to 1, 2 and 3. It passed 1 and 3 but failed 2. The micro only had 6 bytes left, we could not do any better in software.

    We had two choices. Upgrade to a 16 bit micro or take out the debounce routines that worked on 1 and 3 to give us room for some last minute code changes that were required to add features (dontcha love marketing peeps?). There was a cost with this and when we looked at adding a resistor and capacitor for debounce routines and stay with an 8 bit micro, the hardware changed won out.

    It became a balance of hardware AND software. Some IO lines went on to be on A/D lines with a ladder resistor networks for the push buttons and since the A/D lines acted differently than the IO lines, we were able to configure the hardware and squeeze the code to the point that a well known low cost micro company that we used for the design wanted to use us as an example, but our company would not allow it.

    What micro? What company? I am still under NDA’s for the rest of my life, but lets just say it was a neat little micro chip, that had 2K and if their Keeloq device didn’t need so much overhead, we wouldn’t have been in such a quandry.

    Long ago in a galaxy far far away I learned a couple of good lessons.

    1. As my first mentor taught me, a good engineer will do for a dollar what an engineer will do for two dollars.
    2. As my first mentor taught me, a great engineer will do for a quarter what a good engineer does for a dollar.
    3. Listen to your mentors.
    4. Mentor new engineers but only if they meet the requirements of wanting to get to #2.
    5. Eventually, the servant becomes the master, when the mentor is “wrapped around your finger”, don’t forget the mentor or in my lucky case the mentors that got you there. Don’t stab them in the back or trample over them. Give them their due respect.
    6. I have 26 years of real world experience and I took a #3 while writing this so on a smart phone, so don’t think that #5 will happen that easy. Good engineers don’t happen they are born!

    1. Thanks for that! The mental image of an engineer trying to press a button super-fast to get to 256 is awesome.

      The ESD torture also sounds horrible, but very useful. I wonder if I could build up an ESD tester without getting myself killed in the process. Where’s that flyback transformer?

    2. ESD testing is part of the PI (Product Integrity) for most things that needs some level of reliability. They have a pointy end that is bout the size of a finger and the PI guys it to whatever opening that a finger can get into for testing ESD. I have seen designs that have caps connected across LED etc that is used for protecting against ESD.

      You only need a few thousand voltage, so even a souped up version of spite’s voltage multiplier as featured in the conference badge hack a few days earlier could do the job. You just need to build those human ESD model circuit and charge them up with a right voltage. :)

  7. I am currently using a non-blocking sampling routine for debounce that can return either a short-press or a long-press or a no_press state. The routine takes a ride on one of the timers that is currently being used for other purpose, thus releasing me from keeping track of how often the routine is called within the code. It does return a key event (short/long press) when the key is released, though.
    More details on https://hackaday.io/project/8199-key-pass/log/27390-button-control-working

  8. For a simple switch I use a state machine with 4 states. The two extra states are state transition delays.

    For quadrature I use more states – normal ones being – no action, continuing, reversing, invalid with extra states for delays as well. The invalid state waits for the next valid state and repeats that transition. This second part is often left out in other routines I have read. Some cheap rotary switches are so bad that they need it.

    1. Actually you don’t need to debounce quadrature switches if you actually understand how they work. Individual bounces on one of the signals can easily be identified and ignored. The state change is resulting from the 2 inputs signals toggling are what you are after. Everything else is just noise.

      If you have poor quality switches and the user is spinning it faster than than the contacts can settle or your code can handle, then there isn’t much you can do anyway.

      1. Indeed, I just wait for alternating states 00 and 11, and keep track of the previous state so I can see how I got there. Like this:

        if( enc == enc_next )
        enc_pos += ((enc ^ enc_prev) & 2) – 1;
        enc_next ^= 3;
        enc_prev = enc;

        enc is current encoder state in two least significant bits
        enc_next is state I am waiting for.
        enc_prev is the previous state.
        enc_pos is the current position.

        Called by 1kHz timer interrupt. Only meant for ~30 clicks/rotation user interface knobs, not for high speed motor feedback.

          1. Right now I can turn it about 100 clicks/second, and it works fine. Even if it goes down to 10 that would be acceptable. Worse than that, and I don’t think software is going to help much.

          2. @[Artenz]

            I got a shitty rotary encode / switch from China that most people would just write off as faulty.

            I took it as a challenge to write code to make it work, and I did, and it did. lol

            The benefit is that with the code that works for a shitty crappy faulty Chinese rotary switch that misses complete pulses, you can use the same code for a good switch and when it goes faulty it will still be usable.

            I will try to find the draft code that was done on something ending in ‘duino’. I have had a system crash / reload but my code should be on the backups.

          3. PS: as a side not I have a high end brand name mouse and the scroll wheel is now reversing at times and that tells me the exact state machine that is driven by the inputs.

          4. “The benefit is that with the code that works for a shitty crappy faulty Chinese rotary switch that misses complete pulses, you can use the same code for a good switch and when it goes faulty it will still be usable. ”

            Assuming that the failure mode is the same, yes. And what happens when your shitty crappy faulty switch gets worn in a few years, and gets even crappier and shittier ?

      2. Um, go buy some cheep quadrature / push button switches from China and then write your code for it and then tell me all about how I don’t need to correct for signal errors in the input stream. Weather you want to call that de-bouncing or not!

        The two quads are not evenly spaced (90°) and you get bounce in an incoming transition *WHILE* you still have bounce on a previous transition.

        The second part of your statement is exactly what I am talking about – “If you have poor quality switches and the user is spinning it faster than than the contacts can settle or your code can handle, then there isn’t much you can do anyway”

        In this situation I have written a state machine that handles this well. It had 4 state transition delays for de-bounce, 4 state transition delays for rotation changes and 8 states for the inputs, of those 8 input states were four ‘invalid’ states that were waiting for the next valid state to see if the rotation direction remained the same or reversed. there were also 4 extra states for longer delays to do with rotation speed.

        In the remaining error states when a transition is completely missed!!!! as happens with these cheap switches if you spin quickly, there was on compromise and that was to wait one state transition or continuing forward and then do a double backwards if the direction changed. I found the latter worked better because the error occurred more often when you spin quickly and when people want to reverse direction – they slow down first.

  9. Why can’t you use a bit train? Well that is what I call it.

    Here it is as pseudo code, because not everything is an Arduino (dig dig)

    shift left Train
    PinState = read(pin)
    Train = Train OR PinState

    At the end of that you get a number that is Near max value for an ON, near min value for an OFF, and you quantise it into bands to indicated a rising or falling transition. The bands may need to overlap in the bits they use to eliminate the ambiguous 50:50 scenario where you can’t tell if it is rising or falling, only that it is changing.

    Reading the port registers and then looping through a bitmask that is shifted should let you maintain multiple bit trains.

    Pretty sure it is the most compact and low cycle way to de-bounce in software. Each shift-read-OR call could be in a timer interrupt routine so long as the intervals between the calls are even enough.

      1. I value these sort of articles very highly, and I’m sure my daughter will find both very educational. She is working on a project at the moment where the knowledge will come in very handy. I could just tell her how to do it but she learns more if she does the research herself.

    1. You can skip a shift if you change to a vertical mapping by maintaining an array of desired size. You need an AND and OR at the end though. A round robin pointer is necessary as well.. easy in assembly, annoying in C.
      In short….
      Poll 8 bit port
      Store into element determined by pointer
      Increment pointer or wrap if max is met
      If the state of the buttons is required. AND all slots to get the those in a “1” state. OR to get a “0” state.

      It works really well if you use approximately a multiple of your memory width. Fall short and those “wasted” bits can be used to store other data.

  10. While it addresses a slightly different switch debounce problem, a pair of cross coupled Nand gates (SR flip-flop) provides a solid solution especially when the debounced signal will directly drive a hardware state machine (as opposed to code in a microporcessor). The more gain and speed in the nand gate, the faster the resolution. RC debounce improve, but do not eliminate the problem.

    35-years ago, an ex-student of mine, called from his summer job at NASA Cape Kennedy. When debugging code on some ancient TTL implemented computers, often they would get multiple steps when they pressed the “single-step” button. Yep, poor switch debounce using an RC circuit. Changing to the cross-coupled SR flip-flop solved the problem.

    This might not be the best, but at least you see a sample schematic.

  11. What about a latch?

    Long ago, I learned Atari computers had latched game inputs. The main program loop, or some interrupt routine sets the latch, and the button input changes it. Only one change is allowed. Once changed, it’s latched, until the program gets around to acting on it. Upon taking that action, it resets the latch.

  12. I take issue with the software debounce method presented here being belittled – I see no flaws here at all. First off, you don’t “guess” what your debounce period might be – you take the biggest-ass value you can live with (a hundred millisecond? Why not? Are you seriously pressing that button more than five time a second, mate?!?) which pretty much GUARANTEES no pushbutton in existence will out-bounce that. You just crank it up until it feels unresponsive, then take it back down a peg or two until it feels fine. Second, no offense intended to the Arduino crowd, but anyone using blocking ANYTHING for any purpose should either stick to blinking LEDs or better yet, step away from the keyboard slowly and carefully, to avoid perpetrating crimes against humanity. Point being, it’s not that one couldn’t come up with exceedingly “elegant” and contorted ways to skin the same cat just to prove that one can – but that there’s absolutely nothing wrong with this approach. It’s quite fine, no drawbacks – it’s absolutely good enough.

  13. Isn’t there a bug in the delay debounce code? If the input goes low only for one poll period, the button state will change to PRESS and remain there – even though the button is released.

  14. I’m wondering why did Elliot changed the schematic for the simple passive RC filter and the one using the comparator? The 1st one Tao of RC upon release and 1.1RC upon press (R=10k,C=100n). The circuit with the comparator has Tao of 2RC upon release and RC upon press (same values as before). Why a comparator could not have been inserted after the passive circuit?

  15. Hi,

    I used this for my wifi switch from Sonoff which has stupid double press of button to enter RF pairing mode and this is programmed in STM32 and I use this switch for controling my lamps with external push buttons soldered to onboard push buttons and this external switches are easily press twice ..

    In original there is just one 10k resistor between VCC and STM32 pin and I putted 47uF cap between ground and 10k.
    This work great and I cannot press button twice in 0.5s, but with these you get also delay. I know that comes because you are discharging that cap that long as it is his value.

    Is there some other hardware way to do that when you press button — send normal short signal to pin —- and after that wait for specified time to allow sending signal again ?


  16. Passives are so cheap, and a hex schmitt inverter is handy to have for other tasks…IMO the hardware way is the best. Set it and forget it. Let your code be lean, uncluttered, and fast!

    1. Ain’t nothing wrong with that!

      And it’s worth noting that the hex inverters & co are operating with more information (the analog voltages present in the circuit) than the digital input pins are. The ability to make two voltage comparisons instead of just one can make them more responsive if you time the R-C circuit part just right.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.