EmbeddedRelated.com
Forums
The 2026 Embedded Online Conference

Taming Multi-Switch Bounce in Software

Started by MaxMaxfield 2 months ago11 replieslatest reply 1 month ago146 views

Here’s a scalable, elegant software shift-register technique for debouncing multiple noisy switches.

https://www.designnews.com/gadget-freak/taming-mul...


I know this is simple, but it's amazing how many people run into switch bounce-induced issues. This solution nicely decouples noise rejection and switch bounce handling from any application-specific actions that are to be performed.

Happy Friday -- Max

[ - ]
Reply by igendelMarch 7, 2026

While it's obviously better to be safe than sorry, I must admit that I've never encountered "random noise" in a switch outside the regular bounce region, so in makeshift, personal projects I just wait for a state change and then ignore the switch for ~10ms. Mileage may vary in vibration-rich environments. 

[ - ]
Reply by MaxMaxfieldMarch 9, 2026

I know what you mean -- I think random noise outside the bounce region is more common in industrial environments where lots of heavy gear is switching on and off and generating EMI -- but there's still the chance in a hobby environment that something like a nearby lightning strike might trigger something. Another consideration is "do we care" -- if all we're doing is using a switch to turn a light on and off at home, maybe not -- but if we are launching a missile, we probably do. The great thing about this solution is that it handles everything, so you don't need to worry about it, and once you have it, it's easy to adapt to new projects :-)  

[ - ]
Reply by MichaelKellettMarch 10, 2026

Hello Max,

I was looking at your code and wondering why you go to the trouble of making a SwitchStuff structure but don't make any use of it in ProcessSwitches().

// ================= Switch A =====================
  if (Switches[SW_A].swState == JUST_GONE_ACTIVE)
  {
    // Mandatory actions
...........

  // ================= Switch B =====================
  if (Switches[SW_B].swState == JUST_GONE_ACTIVE)
  {
    // Mandatory actions
..........

and so on for every switch

If you were to use a loop it would be easier to scale (three chunks of repeated code as in your example are tolerable (just)) but what if you want 128 ?

And of course when you need to fix a bug ....

My rule when copying and pasting code is that probably you shouldn't !

MK

[ - ]
Reply by MaxMaxfieldMarch 10, 2026

Hi Michael, I'm not sure what you mean when you say "...why you go to the trouble of making a SwitchStuff structure but don't make any use of it in ProcessSwitches()."

But I do make use of it. In the UpdateSwitches() function when I read and debounce the switches, we move the state machine from WAITNG_FOR_ALL_ZEROS (the active state) to JUST_GONE_ACTIVE, and from WAITING_FOR_ALL_ONES (the inactive state) to JUST_GONE_INACTIVE.

Then, in ProcessSwitches(), we perform any optional actions while also transitioning the state from JUST_GONE_ACTIVE to WAITING_FOR_ALL_ONES, and from JUST_GONE_INACTIVE to WAITING_FOR_ALL_ZEROS. See below...

  // ================= Switch A =====================
  if (Switches[SW_A].swState == JUST_GONE_ACTIVE)
  {
    // Mandatory actions
    Switches[SW_A].swState = WAITING_FOR_ALL_ONES;

    // Optional actions
    Serial.println("Switch A just went active");
  }
  else if (Switches[SW_A].swState == JUST_GONE_INACTIVE)
  {
    // Mandatory actions
    Switches[SW_A].swState = WAITING_FOR_ALL_ZEROS;

    // Optional actions
    Serial.println("Switch A just went inactive");        
  }

We use a loop in the UpdateSwitches() function. The reason I treat each switch independently in the ProcessSwitches() function is that I may wish to treat them differently, performing a variety of actions when they go active or inactive.

Having said this, I like your rule "when copying and pasting code, probably you shouldn't!" -- I've been caught out by this more times than I care to remember :-)

[ - ]
Reply by MaxMaxfieldMarch 10, 2026

Also -- remember the Serial.println() statements are just there for debugging purposes -- these would be commented out later.

[ - ]
Reply by MichaelKellettMarch 10, 2026

If you add a pointer to a function field to your structure then you can easily call a different function for each switch, or the same for each one, or anything in between.

Then you could easily add a common error handler to process any errors that the switch processing functions might return.

That way you avoid the code repetition and make the ProcessSwitch function much more scalable.

It makes little difference with a few switches but when you have a lot it will save a lot of time.

MK

[ - ]
Reply by MaxMaxfieldMarch 10, 2026

Ah -- now I see where our problem lies -- you assume I know how to do esoteric programming things like "add a pointer to a function field in my structure". Silly rabbit. I'm a hardware designer by trade. I never mastered the eldritch art of the pointer, although I do have a book on the subject that I fully intend to read one day.

Could I persuade you to modify my original code (https://www.clivemaxfield.com/area51/design-news/d...) so I can feast my orbs on your pointer-based solution?

[ - ]
Reply by MichaelKellettMarch 10, 2026
typedef struct
{
  uint16_t swData;  // 16-bit SR (shift register)
  uint8_t swState;  // One of SW states
  SpecialSwitchFunction swFunction;
} SwitchStuff;
typedef struct
{
  uint16_t swData;  // 16-bit SR (shift register)
  uint8_t swState;  // One of SW states
  SpecialSwitchFunction swFunction;
} SwitchStuff;typedef struct
{
  uint16_t swData;  // 16-bit SR (shift register)
  uint8_t swState;  // One of SW states
  SpecialSwitchFunction swFunction;
} SwitchStuff;

I would but you are coding in Arduino speak and I use C. I did just try writing it in Arduino but I hesitate to offer it as an example, because I have no way to test it.

I found this on the Arduino forum:

https://forum.arduino.cc/t/pointers-to-function/58...

The key is that you define a new type like this:

typedef int (*GeneralFunction) (const int arg1, const int arg2);

and then you can define new functions that take 2 arguments and return an int like this:

int Add (const int arg1, const int arg2)
{
 return arg1 + arg2; 
} // end of Add
GeneralFunction fAdd = Add;

(All the above copied from the link)

You don't need an Add but a print the change of switch state function.

You can modify your SwitchStuff definition like this:

I seem to have run out of space in this reply.

[ - ]
Reply by MichaelKellettMarch 10, 2026
typedef struct
{
  uint16_t swData;  // 16-bit SR (shift register)
  uint8_t swState;  // One of SW states
  SpecialSwitchFunction swFunction;
} SwitchStuff;


It's a bit presumptuous of me to offer code in Arduino whenI've never written any - so please forgive any mistakes.

And kudos to nickgammon on the Arduino forum who just taught me all I know about function pointers on the Arduino.

MK

[ - ]
Reply by MichaelKellettMarch 10, 2026

I've just seen where things went wrong and the editor started inserting things at the beginning of the reply. How confusing.

Sorry.

[ - ]
Reply by MaxMaxfieldMarch 10, 2026
"I seem to have run out of space in this reply."


RORLOL -- no worries -- I get the idea -- one point I just thought of is that I write my columns for absolute beginners -- so I may stay away from pointers -- but I would like to learn how to do it more professionally for my own satisfaction (and I could always use this as the basis for a follow-up column).

The 2026 Embedded Online Conference