Switch management class

As part of a recent project I found myself testing a number of switches, needing to debounce them, and act differently depending on a long or short press. Hence a small class below was written.

You can manage multiple switches by making instance of the class. In your setup function call the begin function to specify the required pin number, and a switch handler.

Then, each time through loop call check to check for switch presses. When required, your callback function will be called. It is passed LOW or HIGH (which is the new switch state) plus how long has passed since it last changed state. Hence you could find out if the switch was pressed for a short or long time, or if a short or long time elapsed between two presses. The class sets the pin to INPUT_PULLUP, for ease of wiring.

Example code:

#include <SwitchManager.h>

// pin assignments
const byte testSwitch = 2;
const byte blueLED = 3;
const byte greenLED = 4;

SwitchManager mySwitch; 
 
// newState will be LOW or HIGH (the is the state the switch is now in)
// interval will be how many mS between the opposite state and this one
 
void handleSwitchPress (const byte newState, const unsigned long interval)
  {
  if (newState == LOW)
     {
     digitalWrite (blueLED, LOW); 
     digitalWrite (greenLED, LOW); 
     return;
     }
     
  // switch must be HIGH
  if (interval >= 1000)
    digitalWrite (blueLED, HIGH); 
  else
    digitalWrite (greenLED, HIGH); 
    
  }  // end of handleSwitchPress
 
void setup ()
  {
  mySwitch.begin (testSwitch, handleSwitchPress);
  pinMode (blueLED, OUTPUT);
  pinMode (greenLED, OUTPUT);
  }
 
void loop ()
  {
  mySwitch.check ();  // check for presses
   
  // do other stuff here
  }

This turns on one LED if the switch is pressed for a brief time, and the other LED if it is pressed for a longer time.


Save below as SwitchManager.h and put that into a folder called SwitchManager inside your libraries folder.

// Class for managing switch presses
// Author: Nick Gammon
// Date: 18 December 2013

/*
 Example:
 
 #include <SwitchManager.h>
 
 SwitchManager mySwitch;  // declare
 
 // newState will be LOW or HIGH (the is the state the switch is now in)
 // interval will be how many mS between the opposite state and this one
 
 void handleSwitchPress (const byte newState, const unsigned long interval)
   {
   
   }
 
 void setup ()
   {
   mySwitch.begin (2, handleSwitchPress);
   }
 
 void loop ()
   {
   mySwitch.check ();  // check for presses
   }
 
 
 */

#include <Arduino.h>


class SwitchManager
  {
  enum { debounceTime = 10, noSwitch = -1 };
  typedef void (*handlerFunction) (const byte newState, const unsigned long interval);
  
  int pinNumber_;
  handlerFunction f_;
  byte oldSwitchState_;
  unsigned long switchPressTime_;  // when the switch last changed state
  unsigned long lastLowTime_;
  unsigned long lastHighTime_;
  
  public:
  
     // constructor
     SwitchManager () 
       {
       pinNumber_ = noSwitch;
       f_ = NULL;
       oldSwitchState_  = HIGH;
       switchPressTime_ = 0;
       lastLowTime_  = 0;
       lastHighTime_ = 0;
       }
       
     void begin (const int pinNumber, handlerFunction f)
       {
       pinNumber_ = pinNumber;
       f_ = f;
       if (pinNumber_ != noSwitch)
         pinMode (pinNumber_, INPUT_PULLUP);
       }  // end of begin
       
     void check ()
       {
       // we need a valid pin number and a valid function to call
       if (pinNumber_ == noSwitch || f_ == NULL)
         return;
        
        // see if switch is open or closed
        byte switchState = digitalRead (pinNumber_);
        
        // has it changed since last time?
        if (switchState != oldSwitchState_)
          {
          // debounce
          if (millis () - switchPressTime_ >= debounceTime)
             {
             switchPressTime_ = millis ();  // when we closed the switch 
             oldSwitchState_ =  switchState;  // remember for next time 
             if (switchState == LOW)
               {
               lastLowTime_ = switchPressTime_;
               f_ (LOW, lastLowTime_ -  lastHighTime_);
               }
             else
               {
               lastHighTime_ = switchPressTime_;
               f_ (HIGH, lastHighTime_ - lastLowTime_);
               }
             
             }  // end if debounce time up
          }  // end of state change
       }  // end of operator ()
       
  };  // class SwitchManager

Great stuff Nick! What a timesaver (and memory saver) that one is! It's in my library and in my list of references to point folks to.

Nice!
Maybe save a tiny bit of memory and limit it to 256 pins?

Works great Nick. 8)

AWOL:
Nice!
Maybe save a tiny bit of memory and limit it to 256 pins?

I wanted to have -1 as "no pin".

Use int8_t instead of int. All the benefits of int without the girth.

the debounce time could be a parameter of the begin(), that allows some tuning of the switches.

    void begin (const int pinNumber, handlerFunction f, uint8_t debounceTime=10)
       {
       debounceTime_ = debounceTime;

       pinNumber_ = pinNumber;
       f_ = f;
       if (pinNumber_ != noSwitch)
         pinMode (pinNumber_, INPUT_PULLUP);
       }  // end of begin

some other adjustments needed too of course,

my 2 cents,

If you put the pin and callback in the constructor, and had no arguments for "begin", you wouldn't need "no_pin", would you?
All "begin" would do would be set pinMode.

the handler in begin allows for resetting the handler to another one without destroying the object...