#ifndef OneButton_h
#define OneButton_h
// ----- Callback function types -----
extern "C" {
typedef void (*callbackFunction)(void);
typedef void (*parameterizedCallbackFunction)(void *);
}
class OneButton
{
public:
// ----- Constructor -----
OneButton();
/**
* Initialize the OneButton library.
* @param pin The pin to be used for input from a momentary button.
* @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true.
* @param pullupActive Activate the internal pullup when available. Default is true.
*/
explicit OneButton(const int pin, const boolean activeLow = true, const bool pullupActive = true);
// ----- Set runtime parameters -----
/**
* set # millisec after safe click is assumed.
*/
[[deprecated("Use setDebounceMs() instead.")]]
void setDebounceTicks(const unsigned int ms) { setDebounceMs(ms); }; // deprecated
void setDebounceMs(const unsigned int ms);
/**
* set # millisec after single click is assumed.
*/
[[deprecated("Use setClickMs() instead.")]]
void setClickTicks(const unsigned int ms) { setClickMs(ms); }; // deprecated
void setClickMs(const unsigned int ms);
/**
* set # millisec after press is assumed.
*/
[[deprecated("Use setPressMs() instead.")]]
void setPressTicks(const unsigned int ms) { setPressMs(ms); }; // deprecated
void setPressMs(const unsigned int ms);
/**
* set interval in msecs between calls of the DuringLongPress event.
* 0 ms is the fastest events calls.
*/
void setLongPressIntervalMs(const unsigned int ms) { _long_press_interval_ms = ms; };
// ----- Attach events functions -----
/**
* Attach an event to be called when a single click is detected.
* @param newFunction This function will be called when the event has been detected.
*/
void attachClick(callbackFunction newFunction);
void attachClick(parameterizedCallbackFunction newFunction, void *parameter);
/**
* Attach an event to be called after a double click is detected.
* @param newFunction This function will be called when the event has been detected.
*/
void attachDoubleClick(callbackFunction newFunction);
void attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter);
/**
* Attach an event to be called after a multi click is detected.
* @param newFunction This function will be called when the event has been detected.
*/
void attachMultiClick(callbackFunction newFunction);
void attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter);
/**
* Attach an event to fire when the button is pressed and held down.
* @param newFunction
*/
void attachLongPressStart(callbackFunction newFunction);
void attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter);
/**
* Attach an event to fire as soon as the button is released after a long press.
* @param newFunction
*/
void attachLongPressStop(callbackFunction newFunction);
void attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter);
/**
* Attach an event to fire periodically while the button is held down.
* The period of calls is set by setLongPressIntervalMs(ms).
* @param newFunction
*/
void attachDuringLongPress(callbackFunction newFunction);
void attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter);
// ----- State machine functions -----
/**
* @brief Call this function every some milliseconds for checking the input
* level at the initialized digital pin.
*/
void tick(void);
/**
* @brief Call this function every time the input level has changed.
* Using this function no digital input pin is checked because the current
* level is given by the parameter.
* Run the finite state machine (FSM) using the given level.
*/
void tick(bool level);
/**
* Reset the button state machine.
*/
void reset(void);
/*
* return number of clicks in any case: single or multiple clicks
*/
int getNumberClicks(void);
/**
* @return true if we are currently handling button press flow
* (This allows power sensitive applications to know when it is safe to power down the main CPU)
*/
bool isIdle() const { return _state == OCS_INIT; }
/**
* @return true when a long press is detected
*/
bool isLongPressed() const { return _state == OCS_PRESS; };
private:
int _pin = -1; // hardware pin number.
unsigned int _debounce_ms = 50; // number of msecs for debounce times.
unsigned int _click_ms = 400; // number of msecs before a click is detected.
unsigned int _press_ms = 800; // number of msecs before a long button press is detected
int _buttonPressed = 0; // this is the level of the input pin when the button is pressed.
// LOW if the button connects the input pin to GND when pressed.
// HIGH if the button connects the input pin to VCC when pressed.
// These variables will hold functions acting as event source.
callbackFunction _clickFunc = NULL;
parameterizedCallbackFunction _paramClickFunc = NULL;
void *_clickFuncParam = NULL;
callbackFunction _doubleClickFunc = NULL;
parameterizedCallbackFunction _paramDoubleClickFunc = NULL;
void *_doubleClickFuncParam = NULL;
callbackFunction _multiClickFunc = NULL;
parameterizedCallbackFunction _paramMultiClickFunc = NULL;
void *_multiClickFuncParam = NULL;
callbackFunction _longPressStartFunc = NULL;
parameterizedCallbackFunction _paramLongPressStartFunc = NULL;
void *_longPressStartFuncParam = NULL;
callbackFunction _longPressStopFunc = NULL;
parameterizedCallbackFunction _paramLongPressStopFunc = NULL;
void *_longPressStopFuncParam = NULL;
callbackFunction _duringLongPressFunc = NULL;
parameterizedCallbackFunction _paramDuringLongPressFunc = NULL;
void *_duringLongPressFuncParam = NULL;
// These variables that hold information across the upcoming tick calls.
// They are initialized once on program start and are updated every time the
// tick function is called.
// define FiniteStateMachine
enum stateMachine_t : int {
OCS_INIT = 0,
OCS_DOWN = 1, // button is down
OCS_UP = 2, // button is up
OCS_COUNT = 3,
OCS_PRESS = 6, // button is hold down
OCS_PRESSEND = 7,
};
/**
* Run the finite state machine (FSM) using the given level.
*/
void _fsm(bool activeLevel);
/**
* Advance to a new state.
*/
void _newState(stateMachine_t nextState);
stateMachine_t _state = OCS_INIT;
int debouncedPinLevel = -1;
int _lastDebouncePinLevel = -1; // used for pin debouncing
unsigned long _lastDebounceTime = 0; // millis()
unsigned long now = 0; // millis()
unsigned long _startTime = 0; // start time of current activeLevel change
int _nClicks = 0; // count the number of clicks with this variable
int _maxClicks = 1; // max number (1, 2, multi=3) of clicks of interest by registration of event functions.
unsigned int _long_press_interval_ms = 0; // interval in msecs between calls of the DuringLongPress event
unsigned long _lastDuringLongPressTime = 0; // used to produce the DuringLongPress interval
public:
int pin() const { return _pin; };
stateMachine_t state() const { return _state; };
int debounce(const int value);
int debouncedValue() const { return debouncedPinLevel; };
/**
* @brief Use this function in the DuringLongPress and LongPressStop events to get the time since the button was pressed.
* @return milliseconds from the start of the button press.
*/
unsigned long getPressedMs() { return(millis() - _startTime); };
};
#endif
// ----- Initialization and Default Values -----
/**
* @brief Construct a new OneButton object but not (yet) initialize the IO pin.
*/
OneButton::OneButton()
{
_pin = -1;
// further initialization has moved to OneButton.h
}
/**
* Initialize the OneButton library.
* @param pin The pin to be used for input from a momentary button.
* @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true.
* @param pullupActive Activate the internal pullup when available. Default is true.
*/
OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupActive)
{
_pin = pin;
if (activeLow) {
// the button connects the input pin to GND when pressed.
_buttonPressed = LOW;
} else {
// the button connects the input pin to VCC when pressed.
_buttonPressed = HIGH;
}
if (pullupActive) {
// use the given pin as input and activate internal PULLUP resistor.
pinMode(pin, INPUT_PULLUP);
} else {
// use the given pin as input
pinMode(pin, INPUT);
}
} // OneButton
// explicitly set the number of millisec that have to pass by before a click is assumed stable.
void OneButton::setDebounceMs(const unsigned int ms)
{
_debounce_ms = ms;
} // setDebounceMs
// explicitly set the number of millisec that have to pass by before a click is detected.
void OneButton::setClickMs(const unsigned int ms)
{
_click_ms = ms;
} // setClickMs
// explicitly set the number of millisec that have to pass by before a long button press is detected.
void OneButton::setPressMs(const unsigned int ms)
{
_press_ms = ms;
} // setPressMs
// save function for click event
void OneButton::attachClick(callbackFunction newFunction)
{
_clickFunc = newFunction;
} // attachClick
// save function for parameterized click event
void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *parameter)
{
_paramClickFunc = newFunction;
_clickFuncParam = parameter;
} // attachClick
// save function for doubleClick event
void OneButton::attachDoubleClick(callbackFunction newFunction)
{
_doubleClickFunc = newFunction;
_maxClicks = max(_maxClicks, 2);
} // attachDoubleClick
// save function for parameterized doubleClick event
void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter)
{
_paramDoubleClickFunc = newFunction;
_doubleClickFuncParam = parameter;
_maxClicks = max(_maxClicks, 2);
} // attachDoubleClick
// save function for multiClick event
void OneButton::attachMultiClick(callbackFunction newFunction)
{
_multiClickFunc = newFunction;
_maxClicks = max(_maxClicks, 100);
} // attachMultiClick
// save function for parameterized MultiClick event
void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter)
{
_paramMultiClickFunc = newFunction;
_multiClickFuncParam = parameter;
_maxClicks = max(_maxClicks, 100);
} // attachMultiClick
// save function for longPressStart event
void OneButton::attachLongPressStart(callbackFunction newFunction)
{
_longPressStartFunc = newFunction;
} // attachLongPressStart
// save function for parameterized longPressStart event
void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter)
{
_paramLongPressStartFunc = newFunction;
_longPressStartFuncParam = parameter;
} // attachLongPressStart
// save function for longPressStop event
void OneButton::attachLongPressStop(callbackFunction newFunction)
{
_longPressStopFunc = newFunction;
} // attachLongPressStop
// save function for parameterized longPressStop event
void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter)
{
_paramLongPressStopFunc = newFunction;
_longPressStopFuncParam = parameter;
} // attachLongPressStop
// save function for during longPress event
void OneButton::attachDuringLongPress(callbackFunction newFunction)
{
_duringLongPressFunc = newFunction;
} // attachDuringLongPress
// save function for parameterized during longPress event
void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter)
{
_paramDuringLongPressFunc = newFunction;
_duringLongPressFuncParam = parameter;
} // attachDuringLongPress
void OneButton::reset(void)
{
_state = OneButton::OCS_INIT;
_nClicks = 0;
_startTime = 0;
}
// ShaggyDog ---- return number of clicks in any case: single or multiple clicks
int OneButton::getNumberClicks(void)
{
return _nClicks;
}
/**
* @brief Debounce input pin level for use in SpesialInput.
*/
int OneButton::debounce(const int value) {
now = millis(); // current (relative) time in msecs.
if (_lastDebouncePinLevel == value) {
if (now - _lastDebounceTime >= _debounce_ms)
debouncedPinLevel = value;
} else {
_lastDebounceTime = now;
_lastDebouncePinLevel = value;
}
return debouncedPinLevel;
};
/**
* @brief Check input of the configured pin,
* debounce input pin level and then
* advance the finite state machine (FSM).
*/
void OneButton::tick(void)
{
if (_pin >= 0) {
_fsm(debounce(digitalRead(_pin)) == _buttonPressed);
}
} // tick()
void OneButton::tick(bool activeLevel)
{
_fsm(debounce(activeLevel));
}
/**
* @brief Advance to a new state and save the last one to come back in cas of bouncing detection.
*/
void OneButton::_newState(stateMachine_t nextState)
{
_state = nextState;
} // _newState()
/**
* @brief Run the finite state machine (FSM) using the given level.
*/
void OneButton::_fsm(bool activeLevel)
{
unsigned long waitTime = (now - _startTime);
// Implementation of the state machine
switch (_state) {
case OneButton::OCS_INIT:
// waiting for level to become active.
if (activeLevel) {
_newState(OneButton::OCS_DOWN);
_startTime = now; // remember starting time
_nClicks = 0;
} // if
break;
case OneButton::OCS_DOWN:
// waiting for level to become inactive.
if (!activeLevel) {
_newState(OneButton::OCS_UP);
_startTime = now; // remember starting time
} else if ((activeLevel) && (waitTime > _press_ms)) {
if (_longPressStartFunc) _longPressStartFunc();
if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam);
_newState(OneButton::OCS_PRESS);
} // if
break;
case OneButton::OCS_UP:
// level is inactive
// count as a short button down
_nClicks++;
_newState(OneButton::OCS_COUNT);
break;
case OneButton::OCS_COUNT:
// dobounce time is over, count clicks
if (activeLevel) {
// button is down again
_newState(OneButton::OCS_DOWN);
_startTime = now; // remember starting time
} else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) {
// now we know how many clicks have been made.
if (_nClicks == 1) {
// this was 1 click only.
if (_clickFunc) _clickFunc();
if (_paramClickFunc) _paramClickFunc(_clickFuncParam);
} else if (_nClicks == 2) {
// this was a 2 click sequence.
if (_doubleClickFunc) _doubleClickFunc();
if (_paramDoubleClickFunc) _paramDoubleClickFunc(_doubleClickFuncParam);
}
else if (_nClicks == 3){
// this was a multi click sequence.
if (_multiClickFunc) _multiClickFunc();
if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam);
}
else if (_nClicks == 4){
// this was a multi click sequence.
if (_multiClickFunc) _multiClickFunc();
if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam);
}
// if
reset();
} // if
break;
case OneButton::OCS_PRESS:
// waiting for pin being release after long press.
if (!activeLevel) {
_newState(OneButton::OCS_PRESSEND);
} else {
// still the button is pressed
if ((now - _lastDuringLongPressTime) >= _long_press_interval_ms) {
if (_duringLongPressFunc) _duringLongPressFunc();
if (_paramDuringLongPressFunc) _paramDuringLongPressFunc(_duringLongPressFuncParam);
_lastDuringLongPressTime = now;
}
} // if
break;
case OneButton::OCS_PRESSEND:
// button was released.
if (_longPressStopFunc) _longPressStopFunc();
if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam);
reset();
break;
default:
// unknown state detected -> reset state machine
_newState(OneButton::OCS_INIT);
break;
} // if
} // OneButton.tick()
// end.
OneButton button(23, true); //attach a button on pin A1 to the library
OneButton button2(22, true);
#define biled1 5
int led1 = LOW;
#define biled2 4
int led2 = LOW;
#define selenoid 14
int beam = LOW;
#define drl 12
int leddrl = LOW;
#define mode 19
#define ledlevel 18
void setup() {
pinMode(biled1, OUTPUT); // sets the digital pin as output
pinMode(biled2, OUTPUT);
pinMode(selenoid, OUTPUT); // sets the digital pin as output
pinMode(drl, OUTPUT);
pinMode(mode, OUTPUT);
pinMode(ledlevel, OUTPUT);
button.attachDoubleClick(doubleclick); // link the function to be called on a doubleclick event.
button.attachClick(singleclick); // link the function to be called on a singleclick event.
button.attachMultiClick(multiclick);
button.attachMultiClick(fourclick);
button2.attachClick(passbeam);
button2.attachDuringLongPress(kedip);
button2.attachDoubleClick(up);
button2.attachMultiClick(down);
}
void loop() {
button.tick(); // check the status of the button
button2.tick();
delay(20); // a short wait between checking the button
} // loop
void doubleclick() { // what happens when button is double-clicked
led2 = ~ led2;
digitalWrite(biled2,led2);
delay(200);
}
void singleclick(){ // what happens when the button is clicked
led1 = ~ led1;
digitalWrite(biled1,led1);
delay(200); // light the red LED
}
void passbeam(){ // what happens when buton is long-pressed
digitalWrite(selenoid,HIGH);
digitalWrite(biled1,HIGH);
digitalWrite(biled2,HIGH);
delay(200);
digitalWrite(selenoid,LOW);
digitalWrite(biled1,led1);
digitalWrite(biled2,led2);
}
void multiclick(){ // what happens when buton is long-pressed
}
void fourclick(){ // what happens when buton is long-pressed
int n = button.getNumberClicks();
if (n == 3) {
leddrl = ~ leddrl;
digitalWrite(drl,leddrl);
delay(200);
}
}
void kedip(){ // what happens when buton is long-pressed
digitalWrite(selenoid,HIGH);
digitalWrite(biled1,HIGH);
digitalWrite(biled2,HIGH);
delay(100);
digitalWrite(selenoid,LOW);
digitalWrite(biled1,led1);
digitalWrite(biled2,led2);
delay(100);
}
void up(){
digitalWrite(mode,HIGH);
digitalWrite(ledlevel,HIGH);
delay(1000);
digitalWrite(mode,LOW);
delay(100);
}
void down(){
int n = button2.getNumberClicks();
if (n == 3) {
digitalWrite(mode,HIGH);
digitalWrite(ledlevel,LOW);
delay(1000);
digitalWrite(mode,LOW);
delay(100);
}
}