Improved button library

Creator: Andrew Mascolo (HazardsMind)
Date: 7/16/2014

Version 2.0
**Version 2 is now available. Now gives the user Double and Multiclick options. However, because the new structure, the code does not handle onRelease so well, so I took it out. Sorry :/ Also no more precision mode, now it is just using milliseconds, that is until I can figure out the bug the code has when using microseconds.

Version: 1.04
**Added Rollover check, and a few other odds and ends

Version: 1.03
**Added SetDebounceTime function

Version: 1.02
**fixed checkbutton function

DOWNLOAD VERSION 2 HERE

DOWNLOAD VERSION 1 HERE

What it can do:

This library can return the state of the button as Waiting, Pressed, Held or Released. Users can also assign that button to execute functions depending on those state (Except Waiting). Users can also execute multiple functions based on the time the button is pressed, held or released.


Abstract sketch

#define WAITING 0
#define PRESSED 1
#define HELD 2
#define RELEASED 3

#define SECONDS 1000000
#define MILLISECONDS 1000
#define MICROSECONDS 1

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
//just in case someone is still using old versions
#include "WProgram.h"
#endif

class Button
{
  private:
    bool            State, lastState;
    unsigned long   onTime, holdTime, heldTime, DBTime, DBInterval, RO_Time;
    byte            _P;
    byte            ButtonState;
    void            (*F_on)();
    void            (*F_hold)();
    void            (*F_off)();

  public:
    Button(byte Precision = LOW) : _P(Precision) { } // precision default = LOW or Milliseconds

    void SetStateAndTime(byte S = HIGH, unsigned long T = 500)
    {
      State = S; // Set the preferred state of the button. (Is the button HIGH or LOW when pressed?)
      lastState = !State; //lastState should be inverted from State
      holdTime = _P ? (T * 1000) : T; // Set the hold time in seconds
    }

    void SetDebounceTime(unsigned long T = 50)
    {
      DBInterval = _P ? (T * 1000) : T;
    }

    void onPressed(void (*ON)() )
    {
      F_on = ON;
    }

    void onHold(void (*HOLD)() )
    {
      F_hold = HOLD;
    }

    void onReleased(void (*OFF)() )
    {
      F_off = OFF;
    }

    byte checkButton(byte _buttonPin )
    {
      static byte _button;
      // Button has not been pressed yet
      ButtonState = WAITING;
      _button = digitalRead(_buttonPin);

      // First check to see if the button is pressed ie. State -> (LOW or HIGH)
      // and if it is different from it's lastState
      if (_button == State && lastState == !State)
      {
        DBTime = _P ? micros() : millis();
        // record time
        onTime = _P ? micros() : millis();

        // Execute ON function
        if (*F_on) // if something was assigned to that function, do it
          F_on();

        // This prevents the code from entering this IF statement,
        // until lastState is set to the preferred State again.
        lastState = State;

        // Button was pressed
        ButtonState = PRESSED;
      }

      if ((_P ? micros() : millis()) - DBTime  >= DBInterval)
      {
        heldTime = 0;
        // button is still held down
        if (_button == State && lastState == State)
        {
          // Check for Rollover
          RO_Time = (_P ? micros() : millis()); // current time into RollOver variable
          if (RO_Time < onTime) // is the RollOver variable smaller than ontime?
            onTime = 0; // if yes,  reset ontime to zero

          // check to see if button is held down for X seconds
          if ( (heldTime = (RO_Time - onTime)) >= (holdTime - DBInterval)) // 1 second is 1,000 milliseconds or 1,000,000 microseconds
          {
            if (*F_hold) // if something was assigned to that function, do it
              F_hold(); // if button is held down for X seconds, execute the HOLD function

            ButtonState = HELD;
          }
        }

        if (_button == !State && lastState == State)//Button was released
        {
          if (*F_off) // if something was assigned to that function, do it
            F_off(); // otherwise execute the OFF function

          ButtonState = RELEASED;
          lastState = !State; // Update lastState
        }
      }
      return ButtonState;
    }

    // This returns the elapsed held time
    float GetHeldTime(float divisor = SECONDS)
    {
      if (divisor > 0)
        return heldTime / divisor;
      else
        return -1;
    }
};// End Of CLass

//========================================================================================
#define buttonPin 2
#define ledPin 13

unsigned long blinkTime = millis();

Button B(HIGH); // LOW = milliseconds, HIGH = microseconds, default is LOW
//========================================================================================
//
//========================================================================================
void LED_ON()
{
  digitalWrite(ledPin, HIGH);
}

void LED_OFF()
{
  digitalWrite(ledPin, LOW);
}

void BLINK()
{
  if (millis() - blinkTime >= 100)
  {
    blinkTime += 100;
    digitalWrite(ledPin, !digitalRead(ledPin));
  }
}
//========================================================================================

void setup()
{
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);

  // Set what the button will be when pressed (default is HIGH)
  // and set the hold time (default is 500)
  B.SetStateAndTime(LOW, 0);

  //  B.onPressed(LED_ON);
  //  B.onHold(BLINK);
  //  B.onReleased(LED_OFF);
}

void loop()
{
  byte myButton = B.checkButton(buttonPin);

  if (myButton) // if myButton is anything but 0, it is true
  {
    unsigned long timer = B.GetHeldTime(SECONDS);
    switch (timer)
    {
      case 0: Serial.println("Quick press"); break;
      case 6 ... 10: Serial.println("Longer than 5 seconds"); break;
      default: Serial.print(timer); Serial.println(" Seconds"); break;
    }
  }
}


Sketch with library

#include<Button.h>

#define buttonPin 2
#define ledPin 13

unsigned long blinkTime = millis();

Button B(HIGH); // LOW = milliseconds, HIGH = microseconds, default is LOW
//========================================================================================
//
//========================================================================================
void LED_ON()
{
  digitalWrite(ledPin, HIGH);
}

void LED_OFF()
{
  digitalWrite(ledPin, LOW);
}

void BLINK()
{
  if (millis() - blinkTime >= 100)
  {
    blinkTime += 100;
    digitalWrite(ledPin, !digitalRead(ledPin));
  }
}
//========================================================================================

void setup()
{
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);

  // Set what the button will be when pressed (default is HIGH)
  // and set the hold time (default is 500)
  B.SetStateAndTime(LOW, 1000);

  B.onPressed(LED_ON);
  B.onHold(BLINK);
  B.onReleased(LED_OFF);
}

void loop()
{
  byte myButton = B.checkButton(buttonPin);

  if (myButton) // if myButton is anything but 0, it is true
  {
    switch (myButton)
    {
      case PRESSED:
        Serial.print("Button was Pressed ");
        break;
      case HELD:
        Serial.print("Buttons is Held:");
        break;
      case RELEASED:
        Serial.print("Button was Released ");
        break;
      default: break;
    }
    Serial.println(B.GetHeldTime(SECONDS));
  }
}

Share