Multi Function Button

notsolowki:
yea i just realized i forget the opening and closing brackets of that last if statement. it seems like there is a lot of code involved in making a button detect short/long presses AND double/triple fast presses

Here is another that I wrote which instead uses callback functions (which are set in the constructor).

test sketch

#include "MultiPress.h"

#define LEDPIN 13
#define BUTTON_A 2
#define BUTTON_B 3

void A_ButtonActions(void);  // Callback function definitions are required...
void B_ButtonActions(const int value);

MultiPress pushButtonSwitches[]= {
  {BUTTON_A, 100, A_ButtonActions},  // pin, interval, callback-on-pressed
  {BUTTON_B, 500, B_ButtonActions, true},
};

void setup()
{
  Serial.begin(9600);
  pinMode(LEDPIN, OUTPUT);
  Serial.println(MultiPress::getCount());
  MultiPress::beginAll();
  MultiPress::setDebounceAll(50);
}

void loop()
{
  MultiPress::update();
}

void A_ButtonActions(void)  // simple example of registering "Pressed"
{
  digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}

void B_ButtonActions(const int value)  // example of registering Multi-Presses
{
  Serial.print(F("Button B:\t"));
  switch(value)
  {
    case -1:
      Serial.println(F("Long Press"));
      break;
    case 1:
      Serial.println(F("One Press"));
      break;
    case 2:
      Serial.println(F("Two Presses"));
      break;
    case 3:
      Serial.println(F("Three Presses")); // can do more!
      break;
    default:
      Serial.println(F("Whole Lotta Presses"));
      break;
  }
}

header:

#ifndef MULTIPRESS_H
#define MULTIPRESS_H

#include "Arduino.h"

#define MAX_BUTTON_INSTANCES 8
#define DEFAULT_DEBOUNCE_MILLISECONDS 75

class MultiPress{
  using funcPtr = void(*)(void);
  using returnFuncPtr = void(*)(const int value);
  public:
    MultiPress(int _pin, uint32_t _pressInterval, returnFuncPtr returnCallback, bool repeat = false);
    MultiPress(int _pin, uint32_t _pressInterval, funcPtr _callBack);
    bool begin();
    int setDebounce(uint8_t dbounce);
    static void setDebounceAll(uint8_t dbounce);
    int8_t pressed();
    static void update(void);
    static bool beginAll();
    static int getCount();
  private:
//    enum {
//      NOT_PRESSED,
//      PRESSED,
//    } switchState;
    bool repeatHold;
    byte pressCount;
    byte lastState;
    byte pin;
    uint32_t lastMillis;
    uint8_t debouncePeriod;
    uint16_t pressInterval;
    returnFuncPtr returnCallback;
    funcPtr callBack;
    static uint8_t instanceCount;
    static MultiPress* instances[MAX_BUTTON_INSTANCES];
};

#endif

implementation:

#include "MultiPress.h"

MultiPress* MultiPress::instances[MAX_BUTTON_INSTANCES];
uint8_t MultiPress::instanceCount = 0;

MultiPress::MultiPress(int _pin, uint32_t _pressInterval, returnFuncPtr _callback, bool repeat) : pin(_pin), pressInterval(_pressInterval), returnCallback(_callback), repeatHold(repeat){
  debouncePeriod = DEFAULT_DEBOUNCE_MILLISECONDS;
  instances[instanceCount] = this;
  instanceCount++;
}

MultiPress::MultiPress(int _pin, uint32_t _pressInterval, funcPtr _callBack)
{
  callBack = _callBack;
  pin = _pin;
  debouncePeriod = DEFAULT_DEBOUNCE_MILLISECONDS;
  pressInterval = _pressInterval;
  instances[instanceCount] = this;
  instanceCount++;
}

bool MultiPress::begin()
{
  pinMode(pin, INPUT_PULLUP);
  lastState = HIGH;
  Serial.println(pin);
  return true;
}

bool MultiPress::beginAll()
{
  for (size_t button = 0; button < instanceCount; button++)
  {
    instances[button]->begin();
  }
  return true;
}

void MultiPress::update(void)
{
  for (size_t button = 0; button < instanceCount; button++)
  {
    int8_t pressed = instances[button]->pressed();
    if(pressed)
    {
      if(instances[button]->returnCallback)
      {
        instances[button]->returnCallback(pressed);
      }
      else if(instances[button]->callBack)
      {
        instances[button]->callBack();
      }
    }
  }
}

int8_t MultiPress::pressed()
{
  byte nowState = digitalRead(pin);
  if(nowState != lastState)
  {
    //Serial.println(F("CHANGED STATE"));
    if(millis() - lastMillis < debouncePeriod) return 0;
    if(nowState == LOW)
    {
      lastMillis = millis();
      pressCount++;
    }
    else
    {
      if (millis() - lastMillis > pressInterval) // a long press
      {
        lastState = nowState;
        pressCount = 0;
        return -1;
      }
    }
  }
  if(pressCount != 0)
  {
    if(millis() - lastMillis > pressInterval and nowState == HIGH)
    {
      int presses = pressCount;
      pressCount = 0;
      return presses;
    }
  }
  lastState = nowState;
  return 0;
}

int MultiPress::setDebounce(uint8_t dbounce)
{
  debouncePeriod = dbounce;
  return debouncePeriod;
}

void MultiPress::setDebounceAll(uint8_t dbounce)
{
  for (size_t button = 0; button < instanceCount; button++)
  {
    instances[button]->setDebounce(dbounce);
  }
}

int MultiPress::getCount()
{
  return instanceCount;
}