Skip to content

Commit

Permalink
OneButtonTiny added
Browse files Browse the repository at this point in the history
  • Loading branch information
mathertel committed Dec 2, 2023
1 parent c6635b4 commit 5c6d3e3
Show file tree
Hide file tree
Showing 4 changed files with 458 additions and 27 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ a copy of this library. You can find more detail about installing libraries

Each physical button requires its own `OneButton` instance. You can initialize them like this:


### OneButton Tiny version

The OneButton Library was extended over time with functionality that was requested for specific
use cases. This makes the library growing over time too and therefore was limiting use cases using very small processors like attiny84.

Staring with version 2.5 the OneButton Library starts supporting these processors with limited
memory and low cpu frequencies by introducing the `OneButtonTiny` class that offers a subset of
the features of the complete `OneButton` class by exposing the following events as callbacks:

* Click event
* DoubleClick event
* LongPressStart event
* Callbacks without parameters

This saves up to 1k of binary program space that is a huge amount on these processors.

With Version 2.5 the `OneButtonTiny` class is now in a beta state.

* Any Issues or pull requests fixing problems are welcome.
* Any new feature request for the `OneButtonTiny` class will be rejected to keep size small.
* New, reasonable functionality will be added to the OneButton class only.


### Initialize a Button to GND

```CPP
Expand Down
68 changes: 41 additions & 27 deletions examples/BlinkMachine/BlinkMachine.ino
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
-------- ------ |
| OFF |<--click-+->| ON | |
-------- | ------ |
| | |
| d-click |
| | |
^ | | |
| | d-click |
longpress | | |
| V |
| ------ |
+- | SLOW | |
Expand All @@ -47,22 +47,27 @@
// 06.10.2012 created by Matthias Hertel
// 26.03.2017 state diagram added, minor changes

#include "OneButton.h"
// #include "OneButton.h"
#include "OneButtonTiny.h" // This example also works with reduced OneButtonTiny class saving.

// The actions I ca do...
typedef enum {
ACTION_OFF, // set LED "OFF".
ACTION_ON, // set LED "ON"
ACTION_SLOW, // blink LED "SLOW"
ACTION_FAST // blink LED "FAST"
}
MyActions;
ACTION_OFF, // set LED "OFF".
ACTION_ON, // set LED "ON"
ACTION_SLOW, // blink LED "SLOW"
ACTION_FAST // blink LED "FAST"
} MyActions;

#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO_EVERY)
// Example for Arduino UNO with input button on pin 2 and builtin LED on pin 13
#define PIN_INPUT A1
#define PIN_LED 13

#elif defined(ARDUINO_attiny)
// Example for Arduino UNO with input button on pin 2 and builtin LED on pin 13
#define PIN_INPUT A1
#define PIN_LED 13

#elif defined(ESP8266)
// Example for NodeMCU with input button using FLASH button on D3 and using the led on -12 module (D4).
// This LED is lighting on output level LOW.
Expand All @@ -78,36 +83,40 @@ MyActions;

#endif

// Setup a new OneButton on pin PIN_INPUT.
OneButton button(PIN_INPUT, true);
// Setup a new OneButton on pin PIN_INPUT.
// OneButton button(PIN_INPUT, true);
OneButtonTiny button(PIN_INPUT, true); // This example also works with reduced OneButtonTiny class saving.

MyActions nextAction = ACTION_OFF; // no action when starting
MyActions nextAction = ACTION_OFF; // no action when starting


// setup code here, to run once.
void setup() {
// enable the standard led on pin 13.
pinMode(PIN_LED, OUTPUT); // sets the digital pin as output
pinMode(PIN_LED, OUTPUT); // sets the digital pin as output

// link the myClickFunction function to be called on a click event.
// link the myClickFunction function to be called on a click event.
button.attachClick(myClickFunction);

// link the doubleclick function to be called on a doubleclick event.
// link the doubleclick function to be called on a doubleclick event.
button.attachDoubleClick(myDoubleClickFunction);

// link the doubleclick function to be called on a doubleclick event.
button.attachLongPressStart(myDoubleClickFunction);

// set 80 msec. debouncing time. Default is 50 msec.
button.setDebounceMs(80);
} // setup
} // setup


// main code here, to run repeatedly:
// main code here, to run repeatedly:
void loop() {
unsigned long now = millis();

// keep watching the push button:
button.tick();

// You can implement other code in here or just wait a while
// You can implement other code in here or just wait a while

if (nextAction == ACTION_OFF) {
// do nothing.
Expand All @@ -123,17 +132,17 @@ void loop() {
digitalWrite(PIN_LED, LOW);
} else {
digitalWrite(PIN_LED, HIGH);
} // if
} // if

} else if (nextAction == ACTION_FAST) {
// do a fast blinking
if (now % 200 < 100) {
digitalWrite(PIN_LED, LOW);
} else {
digitalWrite(PIN_LED, HIGH);
} // if
} // if
} // loop
} // if
} // if
} // loop


// this function will be called when the button was pressed 1 time and them some time has passed.
Expand All @@ -142,7 +151,7 @@ void myClickFunction() {
nextAction = ACTION_ON;
else
nextAction = ACTION_OFF;
} // myClickFunction
} // myClickFunction


// this function will be called when the button was pressed 2 times in a short timeframe.
Expand All @@ -155,8 +164,13 @@ void myDoubleClickFunction() {

} else if (nextAction == ACTION_FAST) {
nextAction = ACTION_ON;
} // if
} // myDoubleClickFunction
} // if
} // myDoubleClickFunction

// End

// this function will be called when a long press was detected.
void myLongPressFunction() {
nextAction = ACTION_OFF;
} // myLongPressFunction

// End
221 changes: 221 additions & 0 deletions src/OneButtonTiny.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/**
* @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.
* Ihor Nehrutsa, [email protected]
*
* 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 OneButtonTiny.h
*/

#include "OneButtonTiny.h"

// ----- Initialization and Default Values -----

/**
* 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.
*/
OneButtonTiny::OneButtonTiny(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 OneButtonTiny::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 OneButtonTiny::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 OneButtonTiny::setPressMs(const unsigned int ms) {
_press_ms = ms;
} // setPressMs


// save function for click event
void OneButtonTiny::attachClick(callbackFunction newFunction) {
_clickFunc = newFunction;
} // attachClick


// save function for doubleClick event
void OneButtonTiny::attachDoubleClick(callbackFunction newFunction) {
_doubleClickFunc = newFunction;
} // attachDoubleClick


// save function for longPressStart event
void OneButtonTiny::attachLongPressStart(callbackFunction newFunction) {
_longPressStartFunc = newFunction;
} // attachLongPressStart


void OneButtonTiny::reset(void) {
_state = OneButtonTiny::OCS_INIT;
_nClicks = 0;
_startTime = 0;
}


/**
* @brief Debounce input pin level for use in SpecialInput.
*/
int OneButtonTiny::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 OneButtonTiny::tick(void) {
if (_pin >= 0) {
_fsm(debounce(digitalRead(_pin)) == _buttonPressed);
}
} // tick()


void OneButtonTiny::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 OneButtonTiny::_newState(stateMachine_t nextState) {
_state = nextState;
} // _newState()


/**
* @brief Run the finite state machine (FSM) using the given level.
*/
void OneButtonTiny::_fsm(bool activeLevel) {
unsigned long waitTime = (now - _startTime);

// Implementation of the state machine
switch (_state) {
case OneButtonTiny::OCS_INIT:
// waiting for level to become active.
if (activeLevel) {
_newState(OneButtonTiny::OCS_DOWN);
_startTime = now; // remember starting time
_nClicks = 0;
} // if
break;

case OneButtonTiny::OCS_DOWN:
// waiting for level to become inactive.

if (!activeLevel) {
_newState(OneButtonTiny::OCS_UP);
_startTime = now; // remember starting time

} else if ((activeLevel) && (waitTime > _press_ms)) {
if (_longPressStartFunc) _longPressStartFunc();
_newState(OneButtonTiny::OCS_PRESS);
} // if
break;

case OneButtonTiny::OCS_UP:
// level is inactive

// count as a short button down
_nClicks++;
_newState(OneButtonTiny::OCS_COUNT);
break;

case OneButtonTiny::OCS_COUNT:
// dobounce time is over, count clicks

if (activeLevel) {
// button is down again
_newState(OneButtonTiny::OCS_DOWN);
_startTime = now; // remember starting time

} else if ((waitTime >= _click_ms) || (_nClicks == 2)) {
// now we know how many clicks have been made.

if (_nClicks == 1) {
// this was 1 click only.
if (_clickFunc) _clickFunc();

} else if (_nClicks == 2) {
// this was a 2 click sequence.
if (_doubleClickFunc) _doubleClickFunc();

} // if

reset();
} // if
break;

case OneButtonTiny::OCS_PRESS:
// waiting for pin being release after long press.

if (!activeLevel) {
_newState(OneButtonTiny::OCS_PRESSEND);
_startTime = now;
} // if
break;

case OneButtonTiny::OCS_PRESSEND:
// button was released.
reset();
break;

default:
// unknown state detected -> reset state machine
_newState(OneButtonTiny::OCS_INIT);
break;
} // if

} // OneButton.tick()


// end.
Loading

0 comments on commit 5c6d3e3

Please sign in to comment.