Skip to content

Commit

Permalink
Allow using OneButton with external input level.
Browse files Browse the repository at this point in the history
  • Loading branch information
mathertel committed Sep 26, 2018
1 parent ebfac8f commit cee0985
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 90 deletions.
166 changes: 100 additions & 66 deletions src/OneButton.cpp
Original file line number Diff line number Diff line change
@@ -1,71 +1,76 @@
// -----
// OneButton.cpp - Library for detecting button clicks, doubleclicks and long press pattern on a single button.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// Changelog: see OneButton.h
// -----
/**
* @file OneButton.cpp
*
* @brief Library for detecting button clicks, doubleclicks and long press
* pattern on a single button.
*
* @author Matthias Hertel, https://www.mathertel.de
* @Copyright Copyright (c) by Matthias Hertel, https://www.mathertel.de.
*
* This work is licensed under a BSD style license. See
* http://www.mathertel.de/License.aspx
*
* More information on: https://www.mathertel.de/Arduino/OneButtonLibrary.aspx
*
* Changelog: see OneButton.h
*/

#include "OneButton.h"

// ----- 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
}

OneButton::OneButton(int pin, int activeLow, bool pullupActive)
{
// OneButton();
_pin = pin;

_debounceTicks = 50; // number of millisec that have to pass by before a click is assumed as safe.
_clickTicks = 600; // number of millisec that have to pass by before a click is detected.
_pressTicks = 1000; // number of millisec that have to pass by before a long button press is detected.

_state = 0; // starting with state 0: waiting for button to be pressed
_isLongPressed = false; // Keep track of long press state

if (activeLow) {
// the button connects the input pin to GND when pressed.
_buttonReleased = HIGH; // notPressed
_buttonPressed = LOW;

// use the given pin as input and activate internal PULLUP resistor.
if(pullupActive)
pinMode( pin, INPUT_PULLUP );
else
pinMode( pin, INPUT );

} else {
// the button connects the input pin to VCC when pressed.
_buttonReleased = LOW;
_buttonPressed = HIGH;
} // if

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);
} // if

// no functions attached yet: clear all function pointers.
_clickFunc = NULL;
_doubleClickFunc = NULL;
_pressFunc = NULL;
_longPressStartFunc = NULL;
_longPressStopFunc = NULL;
_duringLongPressFunc = NULL;
} // OneButton


// explicitly set the number of millisec that have to pass by before a click is assumed as safe.
void OneButton::setDebounceTicks(int ticks) {
// explicitly set the number of millisec that have to pass by before a click is
// assumed as safe.
void OneButton::setDebounceTicks(int ticks)
{
_debounceTicks = ticks;
} // setDebounceTicks

// explicitly set the number of millisec that have to pass by before a click is detected.
void OneButton::setClickTicks(int ticks) {
// explicitly set the number of millisec that have to pass by before a click is
// detected.
void OneButton::setClickTicks(int ticks)
{
_clickTicks = ticks;
} // setClickTicks


// explicitly set the number of millisec that have to pass by before a long button press is detected.
void OneButton::setPressTicks(int ticks) {
// explicitly set the number of millisec that have to pass by before a long
// button press is detected.
void OneButton::setPressTicks(int ticks)
{
_pressTicks = ticks;
} // setPressTicks

Expand All @@ -85,7 +90,8 @@ void OneButton::attachDoubleClick(callbackFunction newFunction)


// save function for press event
// DEPRECATED, is replaced by attachLongPressStart, attachLongPressStop, attachDuringLongPress,
// DEPRECATED, is replaced by attachLongPressStart, attachLongPressStop,
// attachDuringLongPress,
void OneButton::attachPress(callbackFunction newFunction)
{
_pressFunc = newFunction;
Expand Down Expand Up @@ -125,73 +131,101 @@ void OneButton::reset(void){
_isLongPressed = false;
}


/**
* @brief Check input of the configured pin and then advance the finite state
* machine (FSM).
*/
void OneButton::tick(void)
{
// Detect the input information
int buttonLevel = digitalRead(_pin); // current button signal.
if (_pin >= 0) {
tick(digitalRead(_pin) == _buttonPressed);
}
}


/**
* @brief Advance the finite state machine (FSM) using the given level.
*/
void OneButton::tick(bool activeLevel)
{
unsigned long now = millis(); // current (relative) time in msecs.

// Implementation of the state machine

if (_state == 0) { // waiting for menu pin being pressed.
if (buttonLevel == _buttonPressed) {
if (activeLevel) {
_state = 1; // step to state 1
_startTime = now; // remember starting time
} // if

} else if (_state == 1) { // waiting for menu pin being released.

if ((buttonLevel == _buttonReleased) && ((unsigned long)(now - _startTime) < _debounceTicks)) {
if ((!activeLevel) &&
((unsigned long)(now - _startTime) < _debounceTicks)) {
// button was released to quickly so I assume some debouncing.
// go back to state 0 without calling a function.
// go back to state 0 without calling a function.
_state = 0;

} else if (buttonLevel == _buttonReleased) {
} else if (!activeLevel) {
_state = 2; // step to state 2
_stopTime = now; // remember stopping time

} else if ((buttonLevel == _buttonPressed) && ((unsigned long)(now - _startTime) > _pressTicks)) {
_isLongPressed = true; // Keep track of long press state
if (_pressFunc) _pressFunc();
if (_longPressStartFunc) _longPressStartFunc();
if (_duringLongPressFunc) _duringLongPressFunc();
} else if ((activeLevel) &&
((unsigned long)(now - _startTime) > _pressTicks)) {
_isLongPressed = true; // Keep track of long press state
if (_pressFunc)
_pressFunc();
if (_longPressStartFunc)
_longPressStartFunc();
if (_duringLongPressFunc)
_duringLongPressFunc();
_state = 6; // step to state 6
_stopTime = now; // remember stopping time
} else {
// wait. Stay in this state.
} // if

} else if (_state == 2) { // waiting for menu pin being pressed the second time or timeout.
if (_doubleClickFunc == NULL || (unsigned long)(now - _startTime) > _clickTicks) {
} else if (_state == 2) {
// waiting for menu pin being pressed the second time or timeout.
if (_doubleClickFunc == NULL ||
(unsigned long)(now - _startTime) > _clickTicks) {
// this was only a single short click
if (_clickFunc) _clickFunc();
if (_clickFunc)
_clickFunc();
_state = 0; // restart.

} else if ((buttonLevel == _buttonPressed) && ((unsigned long)(now - _stopTime) > _debounceTicks)) {
} else if ((activeLevel) &&
((unsigned long)(now - _stopTime) > _debounceTicks)) {
_state = 3; // step to state 3
_startTime = now; // remember starting time
} // if

} else if (_state == 3) { // waiting for menu pin being released finally.
// Stay here for at least _debounceTicks because else we might end up in state 1 if the
// button bounces for too long.
if (buttonLevel == _buttonReleased && ((unsigned long)(now - _startTime) > _debounceTicks)) {
// Stay here for at least _debounceTicks because else we might end up in
// state 1 if the button bounces for too long.
if ((!activeLevel) &&
((unsigned long)(now - _startTime) > _debounceTicks)) {
// this was a 2 click sequence.
if (_doubleClickFunc) _doubleClickFunc();
if (_doubleClickFunc)
_doubleClickFunc();
_state = 0; // restart.
_stopTime = now; // remember stopping time

} // if

} else if (_state == 6) { // waiting for menu pin being release after long press.
if (buttonLevel == _buttonReleased) {
_isLongPressed = false; // Keep track of long press state
if(_longPressStopFunc) _longPressStopFunc();
} else if (_state == 6) {
// waiting for menu pin being release after long press.
if (!activeLevel) {
_isLongPressed = false; // Keep track of long press state
if (_longPressStopFunc)
_longPressStopFunc();
_state = 0; // restart.
_stopTime = now; // remember stopping time
} else {
// button is being long pressed
_isLongPressed = true; // Keep track of long press state
if (_duringLongPressFunc) _duringLongPressFunc();
// button is being long pressed
_isLongPressed = true; // Keep track of long press state
if (_duringLongPressFunc)
_duringLongPressFunc();
} // if

} // if
Expand Down
71 changes: 47 additions & 24 deletions src/OneButton.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
// -----
// OneButton.h - Library for detecting button clicks, doubleclicks and long press pattern on a single button.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// OneButton.h - Library for detecting button clicks, doubleclicks and long
// press pattern on a single button. This class is implemented for use with the
// Arduino environment. Copyright (c) by Matthias Hertel,
// http://www.mathertel.de This work is licensed under a BSD style license. See
// http://www.mathertel.de/License.aspx More information on:
// http://www.mathertel.de/Arduino
// -----
// 02.10.2010 created by Matthias Hertel
// 21.04.2011 transformed into a library
// 01.12.2011 include file changed to work with the Arduino 1.0 environment
// 23.03.2014 Enhanced long press functionalities by adding longPressStart and longPressStop callbacks
// 23.03.2014 Enhanced long press functionalities by adding longPressStart and
// longPressStop callbacks
// 21.09.2015 A simple way for debounce detection added.
// 14.05.2017 Debouncing improvements.
// 25.06.2018 Optional third parameter for deactivating pullup.
// 26.09.2018 Anatoli Arkhipenko: Included solution to use library with other
// sources of input.
// 26.09.2018 Initialization moved into class declaration.
// -----

#ifndef OneButton_h
Expand All @@ -22,14 +27,16 @@
// ----- Callback function types -----

extern "C" {
typedef void (*callbackFunction)(void);
typedef void (*callbackFunction)(void);
}


class OneButton
{
public:
// ----- Constructor -----
OneButton();

OneButton(int pin, int active, bool pullupActive = true);

// ----- Set runtime parameters -----
Expand All @@ -43,44 +50,60 @@ class OneButton
// set # millisec after press is assumed.
void setPressTicks(int ticks);

// attach functions that will be called when button was pressed in the specified way.
// attach functions that will be called when button was pressed in the
// specified way.
void attachClick(callbackFunction newFunction);
void attachDoubleClick(callbackFunction newFunction);
void attachPress(callbackFunction newFunction); // DEPRECATED, replaced by longPressStart, longPressStop and duringLongPress
void attachPress(
callbackFunction newFunction); // DEPRECATED, replaced by longPressStart,
// longPressStop and duringLongPress
void attachLongPressStart(callbackFunction newFunction);
void attachLongPressStop(callbackFunction newFunction);
void attachDuringLongPress(callbackFunction newFunction);

// ----- State machine functions -----

// call this function every some milliseconds for handling button events.
/**
* @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.
*/
void tick(bool level);

bool isLongPressed();
int getPressedTicks();
void reset(void);

private:
int _pin; // hardware pin number.
int _debounceTicks; // number of ticks for debounce times.
int _clickTicks; // number of ticks that have to pass by before a click is detected
int _pressTicks; // number of ticks that have to pass by before a long button press is detected
int _pin; // hardware pin number.
int _debounceTicks = 50; // number of ticks for debounce times.
int _clickTicks = 600; // number of ticks that have to pass by
// before a click is detected.
int _pressTicks = 1000; // number of ticks that have to pass by before a long
// button press is detected

int _buttonReleased;
int _buttonPressed;

bool _isLongPressed;
bool _isLongPressed = false;

// These variables will hold functions acting as event source.
callbackFunction _clickFunc;
callbackFunction _doubleClickFunc;
callbackFunction _pressFunc;
callbackFunction _longPressStartFunc;
callbackFunction _longPressStopFunc;
callbackFunction _duringLongPressFunc;
callbackFunction _clickFunc = NULL;
callbackFunction _doubleClickFunc = NULL;
callbackFunction _pressFunc = NULL;
callbackFunction _longPressStartFunc = NULL;
callbackFunction _longPressStopFunc = NULL;
callbackFunction _duringLongPressFunc = 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.
int _state;
// They are initialized once on program start and are updated every time the
// tick function is called.
int _state = 0;
unsigned long _startTime; // will be set in state 1
unsigned long _stopTime; // will be set in state 2
};
Expand Down

0 comments on commit cee0985

Please sign in to comment.