Skip to content

Commit

Permalink
Merge pull request #76 from mathertel/Improvements_59
Browse files Browse the repository at this point in the history
new version 2.0.0
  • Loading branch information
mathertel committed Jan 24, 2021
2 parents eb583d7 + 53d7cca commit c4493fd
Show file tree
Hide file tree
Showing 11 changed files with 524 additions and 220 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.DS_Store
.idea/
.vscode
_*
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Changelog

All notable changes to this project will be documented in this file starting 2021.

## [2.0.0] - 2021-01-22

* CHANGELOG created.
* Many thanks to the improvements included from #27 (**@aslobodyanuk**), #59 (**@ShaggyDog18**) and #73 (**@geeksville**).

This is a major update with breaking changes.

The **states** are re-factored to support counting the clicks.

By design only one of the events (click, doubleClick, MultiClick) are triggered within one interaction.
As a consequence a single-click interaction is detected after waiting some milliseconds (see setClickTicks()) without another click happening;
Only if you have not attached any double-click event function the waiting time can be skipped.

Detecting a long 'down' not only works with the first but always as the last click.

The number of actual clicks can be retrieved from the library any time.

The function **getPressedTicks()** was removed. See example SimpleOneButton on how to get that time by using attachLongPressStart to save starting time.

The function **attachPressStart()** is removed as **attachLongPressStart()** does the same but also supports parameters.

One additional feature has been added not to call the event functions from the interrupt routine and detect
the need for event functions to be called only when the tick() function is called from the main loop() method.
This is because some boards and processors do not support timing or Serial functions (among others) from interrupt routines.

The function **isIdle()** was added to allow detect a current interaction.

The library now supports to detect multiple (>2) clicks in a row using **attachMultiClick()** .


* The internal _state is using enum instead of plain numbers to make the library more readable.
* functions that had been marked deprecated are now removed. (attachPress->attachLongPressXXX)
* added const to constant parameters to enable meaningful compiler warnings.
* added code for de-bouncing double clicks from pull 27.
* added isIdle() function to find out that the internal state is `init`.


### Examples

* Examples run on NodeMCU boards. (the library worked already).

* The **SimpleOneButton.ino** example got some cleanup and definition to be used with ESP8266 boards as well.

* The **InterruptOneButton.ino** example now is using attachInterrupt instead of UNO specific register modifications.

* The **SpecialInput.ino** example was added to shpow how to use the OneButton algorythm and input pattern recognition with your own source of input.
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Arduino OneButton Library
===
# Arduino OneButton Library

This Arduino library is improving the usage of a singe button for input.
It shows how to use an digital input pin with a single pushbutton attached
Expand All @@ -11,6 +10,8 @@ This is also a sample for implementing simple finite-state machines by using the
You can find more details on this library at
http://www.mathertel.de/Arduino/OneButtonLibrary.aspx

The change log of this library can be found in [CHANGELOG](CHANGELOG.md).


## Getting Started

Expand Down Expand Up @@ -123,11 +124,14 @@ the following functions to change the timing.
click event is not attached, the library will assume a valid single click after one click duration,
otherwise it must wait for the double click timeout to pass.

| Function | Default (ms) | Description |
| ----------------------- | ------------ | ------------------------------------------------------------- |
| `setDebounceTicks(int)` | `50` | Period of time in which to ignore additional level changes. |
| `setClickTicks(int)` | `600` | Timeout used to distinguish single clicks from double clicks. |
| `setPressTicks(int)` | `1000` | Duration to hold a button to trigger a long press. |
| Function | Default | Description |
| ----------------------- | ---------- | ------------------------------------------------------------- |
| `setDebounceTicks(int)` | `50 msec` | Period of time in which to ignore additional level changes. |
| `setClickTicks(int)` | `500 msec` | Timeout used to distinguish single clicks from double clicks. |
| `setPressTicks(int)` | `800 msec` | Duration to hold a button to trigger a long press. |

You may change these default values but be aware that when you specify too short times
it is hard to click twice or you will create a press instead of a click.


### Additional Functions
Expand Down
35 changes: 24 additions & 11 deletions examples/BlinkMachine/BlinkMachine.ino
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
http://www.mathertel.de/Arduino/OneButtonLibrary.aspx
Setup a test circuit:
* Connect a pushbutton to pin A1 (ButtonPin) and ground.
* The pin 13 (StatusPin) is used for output attach a led and resistor to ground
* Connect a pushbutton to pin A1 (Uno) or D3 (ESP8266) and ground.
* The pin 13 (UNO) or D4 (ESP8266) is used for output attach a led and resistor to ground
or see the built-in led on the standard arduino board.
The Sketch shows how to setup the library and bind a "machine" that can blink the LED slow or fast.
Expand Down Expand Up @@ -58,16 +58,29 @@ typedef enum {
}
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(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.
#define PIN_INPUT D3
#define PIN_LED D4

#endif

// Setup a new OneButton on pin A1.
OneButton button(A1, true);
OneButton button(PIN_INPUT, true);

MyActions nextAction = ACTION_OFF; // no action when starting


// setup code here, to run once.
void setup() {
// enable the standard led on pin 13.
pinMode(13, 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.
button.attachClick(myClickFunction);
Expand All @@ -91,26 +104,26 @@ void loop() {

if (nextAction == ACTION_OFF) {
// do nothing.
digitalWrite(13, LOW);
digitalWrite(PIN_LED, LOW);

} else if (nextAction == ACTION_ON) {
// turn LED on
digitalWrite(13, HIGH);
digitalWrite(PIN_LED, HIGH);

} else if (nextAction == ACTION_SLOW) {
// do a slow blinking
if (now % 1000 < 500) {
digitalWrite(13, LOW);
digitalWrite(PIN_LED, LOW);
} else {
digitalWrite(13, HIGH);
digitalWrite(PIN_LED, HIGH);
} // if

} else if (nextAction == ACTION_FAST) {
// do a fast blinking
if (now % 200 < 100) {
digitalWrite(13, LOW);
digitalWrite(PIN_LED, LOW);
} else {
digitalWrite(13, HIGH);
digitalWrite(PIN_LED, HIGH);
} // if
} // if
} // loop
Expand Down Expand Up @@ -139,4 +152,4 @@ void myDoubleClickFunction() {
} // myDoubleClickFunction

// End


167 changes: 138 additions & 29 deletions examples/InterruptOneButton/InterruptOneButton.ino
Original file line number Diff line number Diff line change
Expand Up @@ -14,55 +14,164 @@
* The pin 13 (StatusPin) is used for output attach a led and resistor to ground
or see the built-in led on the standard arduino board.
The Sketch shows how to setup the library and bind a special function to the doubleclick event.
By using interrupts the doubleclick function will be called not from the main program.
The sketch shows how to setup the library and bind the functions (singleClick, doubleClick) to the events.
In the loop function the button.tick function must be called as often as you like.
By using interrupts the internal state advances even when for longer time the button.tick is not called.
*/

// 03.03.2011 created by Matthias Hertel
// 01.12.2011 extension changed to work with the Arduino 1.0 environment
// 04.11.2017 Interrupt version created.
// 04.11.2017 Interrupt version using attachInterrupt.

#include "OneButton.h"

// Setup a new OneButton on pin A1.
OneButton button(A1, true);
#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
// attachInterrupt only supports pin 2 and 3 on UNO.
// See https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
#define PIN_INPUT 2
#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.
#define PIN_INPUT D3
#define PIN_LED D4

#endif

// Setup a new OneButton on pin PIN_INPUT
// The 2. parameter activeLOW is true, because external wiring sets the button to LOW when pressed.
OneButton button(PIN_INPUT, true);

// current LED state, staring with LOW (0)
int ledState = LOW;

// save the millis when a press has started.
unsigned long pressStartTime;

// In case the momentary button puts the input to HIGH when pressed:
// The 2. parameter activeLOW is false when the external wiring sets the button to HIGH when pressed.
// The 3. parameter can be used to disable the PullUp .
// OneButton button(PIN_INPUT, false, false);


// This function is called from the interrupt when the signal on the PIN_INPUT has changed.
// do not use Serial in here.
#if defined(ARDUINO_AVR_UNO) || defined (ARDUINO_AVR_NANO_EVERY)
void checkTicks()
{
// include all buttons here to be checked
button.tick(); // just call tick() to check the state.
}

#elif defined(ESP8266)
ICACHE_RAM_ATTR void checkTicks()
{
// include all buttons here to be checked
button.tick(); // just call tick() to check the state.
}

#endif


// this function will be called when the button was pressed 1 time only.
void singleClick()
{
Serial.println("singleClick() detected.");
} // singleClick


// this function will be called when the button was pressed 2 times in a short timeframe.
void doubleClick()
{
Serial.println("doubleClick() detected.");

ledState = !ledState; // reverse the LED
digitalWrite(PIN_LED, ledState);
} // doubleClick


// this function will be called when the button was pressed 2 times in a short timeframe.
void multiClick()
{
Serial.print("multiClick(");
Serial.print(button.getNumberClicks());
Serial.println(") detected.");

ledState = !ledState; // reverse the LED
digitalWrite(PIN_LED, ledState);
} // multiClick


// this function will be called when the button was pressed 2 times in a short timeframe.
void pressStart()
{
Serial.println("pressStart()");
pressStartTime = millis() - 1000; // as set in setPressTicks()
} // pressStart()


// this function will be called when the button was pressed 2 times in a short timeframe.
void pressStop()
{
Serial.print("pressStop(");
Serial.print(millis() - pressStartTime);
Serial.println(") detected.");
} // pressStop()


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

// link the doubleclick function to be called on a doubleclick event.
button.attachDoubleClick(doubleclick);
void setup()
{
Serial.begin(115200);
Serial.println("One Button Example with interrupts.");

// enable the led output.
pinMode(PIN_LED, OUTPUT); // sets the digital pin as output
digitalWrite(PIN_LED, ledState);

// setup interrupt routine
// when not registering to the interrupt the sketch also works when the tick is called frequently.
attachInterrupt(digitalPinToInterrupt(PIN_INPUT), checkTicks, CHANGE);

// link the xxxclick functions to be called on xxxclick event.
button.attachClick(singleClick);
button.attachDoubleClick(doubleClick);
button.attachMultiClick(multiClick);

button.setPressTicks(1000); // that is the time when pressStart is called
button.attachLongPressStart(pressStart);
button.attachLongPressStop(pressStop);

// A1-Option for UNO:
// it is possible to use e.g. A1 but then some direct register modifications and an ISR has to be used:
// You may have to modify the next 2 lines if using another pin than A1
PCICR |= (1 << PCIE1); // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
PCMSK1 |= (1 << PCINT9); // This enables the interrupt for pin 1 of Port C: This is A1.
} // setup
// PCICR |= (1 << PCIE1); // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
// PCMSK1 |= (1 << PCINT9); // This enables the interrupt for pin 1 of Port C: This is A1.

} // setup

// A1-Option for UNO:
// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A1: exactly where we need to check.
ISR(PCINT1_vect) {
// keep watching the push button:
button.tick(); // just call tick() to check the state.
}
// ISR(PCINT1_vect)
// {
// // keep watching the push button:
// button.tick(); // just call tick() to check the state.
// }


// main code here, to run repeatedly:
void loop()
{
// keep watching the push button, even when no interrupt happens:
button.tick();

// main code here, to run repeatedly:
void loop() {
// You can implement other code in here or just wait a while
// You can implement other code in here or just wait a while
delay(10);
} // loop


// this function will be called when the button was pressed 2 times in a short timeframe.
void doubleclick() {
static int m = LOW;
// reverse the LED
m = !m;
digitalWrite(13, m);
} // doubleclick

// End
Loading

0 comments on commit c4493fd

Please sign in to comment.