Skip to content

Commit

Permalink
Version 3.0 Button release handler
Browse files Browse the repository at this point in the history
  • Loading branch information
ArminJo committed May 28, 2020
1 parent 30ffd66 commit da8cc97
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 179 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/LibraryBuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# This is the name of the workflow, visible on GitHub UI.
name: LibraryBuild

on:
on:
push: # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request
paths:
- '**.ino'
Expand Down Expand Up @@ -51,7 +51,7 @@ jobs:
examples-build-properties:
EasyButtonExample:
-DTX_PIN=PB0

- arduino-boards-fqbn: digistump:avr:digispark-pro
platform-url: https://raw.githubusercontent.com/ArminJo/DigistumpArduino/master/package_digistump_index.json

Expand All @@ -75,4 +75,3 @@ jobs:
platform-url: ${{ matrix.platform-url }}
examples-exclude: ${{ matrix.examples-exclude }}
examples-build-properties: ${{ toJson(matrix.examples-build-properties) }}

71 changes: 44 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# [EasyButton](https://github.com/ArminJo/EasyButtonAtInt01)
Available as Arduino library "EasyButtonAtInt01"

### [Version 2.1.0](https://github.com/ArminJo/EasyButtonAtInt01/releases)
### [Version 3.0.0](https://github.com/ArminJo/EasyButtonAtInt01/releases)

[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Installation instructions](https://www.ardu-badge.com/badge/EasyButtonAtInt01.svg?)](https://www.ardu-badge.com/EasyButtonAtInt01)
Expand All @@ -16,8 +16,9 @@ Debouncing is merely done by ignoring a button change within the debouncing time
So **button state is instantly available** without debouncing delay!
- Each button press toggles a state variable, so **no external logic for implementing a toggle button is needed**.
- Support for **double press detection** is included. See EasyButtonExample and Callback example.
- Support for **long press detection**, is included. See EasyButtonExample example.
- Support to **measure maximum bouncing period of a button**. See EasyButtonExample example.
- Support for **long press detection**, is included. See Callback example.
- Support to **measure maximum bouncing period of a button**. See DebounceTest example.
- Support for **active high buttons**.

## Table of available pins for the 2 buttons
| CPU | Button 0 | Button 1 using INT1 | Button 1 using PCINT, if INT1_PIN is defined !=3 |
Expand Down Expand Up @@ -64,59 +65,68 @@ void loop() {
}
```

## Usage of callback function
The callback function is is called on every button press with ButtonToggleState as parameter.<br/>
## Usage of callback functions
The button press callback function is is called on every button press with ButtonToggleState as parameter.<br/>
**The value at the first call (after first press) is true**.<br/>
The callback function runs in an interrupt service context, which means it should be as short as possible.
But before callback function is called, interrupts are enabled.
This allows the timer interrupt for millis() to work and therfore **delay() and millis() can be used in the callback function**.
The button release callback function is called on every button release with the additional parameter ButtonPressDurationMillis.<br/>
Both callback functions run in an interrupt service context, which means they should be as short as possible.
But before a callback function is called, interrupts are enabled.
This allows the timer interrupt for millis() to work and therfore **delay() and millis() can be used in a callback function**.

```
#define USE_BUTTON_0 // Enable code for button at INT0 (pin2)
#include "EasyButtonAtInt01.cpp.h"
// initial value is false, so first call is with true
void printButtonToggleState(bool aButtonToggleState) {
// Initial value is false, so first call is with true
void handleButtonPress(bool aButtonToggleState) {
digitalWrite(LED_BUILTIN, aButtonToggleState);
}
EasyButton Button0AtPin2(&printButtonToggleState);
EasyButton Button0AtPin2(&handleButtonPress);
void setup() {}
void loop() {}
```

## Long press detection
Check it cyclical in your loop. Do not forget, that you will get a callback (if enabled) at the start of the long press.
The blocking call only blocks if button is pressed, otherwise it returns immediately.
the easiest way is to check it in the button release handler. Do not forget, that you will get a press callback (if enabled) at the start of the long press.

```
void loop() {
...
// default long press period is 400 ms
if (Button1AtPin3.checkForLongPressBlocking()) {
doSomething();
#define USE_BUTTON_0 // Enable code for button at INT0 (pin2)
#include "EasyButtonAtInt01.cpp.h"
void handleButtonRelease(bool aButtonToggleState, uint16_t aButtonPressDurationMillis);
EasyButton Button0AtPin2(true, NULL, &handleButtonRelease); // true -> button is connected to INT0 (pin2)
handleButtonRelease(bool aButtonToggleState, uint16_t aButtonPressDurationMillis) {
if (aButtonPressDurationMillis >= EASY_BUTTON_LONG_PRESS_DEFAULT_MILLIS) { // 400 ms
Serial.print(F("Long press "));
Serial.print(aButtonPressDurationMillis);
Serial.println(F(" ms detected"));
}
...
}
void setup() {}
void loop() {}
}
```

## Double press detection
Call checkForDoublePress() only from callback function. It will not work as expected called outside the callback function.
Call checkForDoublePress() only from button press callback function. It will not work as expected called outside this callback function.

```
#define USE_BUTTON_0 // Enable code for button at INT0 (pin2)
#include "EasyButtonAtInt01.cpp.h"
void printButtonToggleState(bool aButtonToggleState);
void handleButtonPress(bool aButtonToggleState);
EasyButton Button0AtPin2(&printButtonToggleState);
// initial value is false, so first call is with true
void printButtonToggleState(bool aButtonToggleState) {
digitalWrite(LED_BUILTIN, aButtonToggleState);
// This function works reliable only if called in callback function
// Initial value is false, so first call is with true
void handleButtonPress(bool aButtonToggleState) {
// This function works reliable only if called early in callback function
if (Button0AtPin2.checkForDoublePress()) {
Serial.println(F("Button 0 double press (< 400 ms) detected"));
}
digitalWrite(LED_BUILTIN, aButtonToggleState);
}
void setup() {}
Expand All @@ -134,7 +144,8 @@ If you are using Sloeber as your IDE, you can easily define global symbols at *P
## Class methods
```
EasyButton(bool aIsButtonAtINT0); // Constructor
EasyButton(bool aIsButtonAtINT0, void (*aButtonPressCallback)(bool aButtonToggleState)); // Constructor
EasyButton(bool aIsButtonAtINT0, void (*aButtonPressCallback)(bool aButtonToggleState));
EasyButton(bool aIsButtonAtINT0, void (*aButtonPressCallback)(bool aButtonToggleState), void (*aButtonReleaseCallback)(bool aButtonToggleState, uint16_t aButtonPressDurationMillis));
void init(); // used by constructors
#define EASY_BUTTON_LONG_PRESS_DEFAULT_MILLIS 400
Expand All @@ -150,13 +161,19 @@ bool checkForForButtonNotPressedTime(uint16_t aTimeoutMillis);
```

# Revision History
### Version 3.0.0
- Added button release handler and adapted examples.
- Revoke change for "only one true result per press for checkForLongPressBlocking()". It is superseded by button release handler.
- Support buttons which are active high by defining `BUTTON_IS_ACTIVE_HIGH`.
- Improved detection of maximum bouncing period used in DebounceTest.

### Version 2.1.0
- Avoid 1 ms delay for `checkForLongPressBlocking()` if button is not pressed.
- Only one true result per press for `checkForLongPressBlocking()`.

### Version 2.0.0
- Ported to ATtinyX5 and ATiny167.
- Support also PinChangeInterrupt for button 1 on Pin PA0 to PA7 for ATtiniy87/167.
- Support also PinChangeInterrupt for button 1 on Pin PA0 to PA7 for ATtiny87/167.
- Long button press detection support.
- Analyzes maximum debouncing period.
- Double button press detection support.
Expand Down
50 changes: 44 additions & 6 deletions examples/Callback/Callback.ino
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
#define USE_BUTTON_0 // Enable code for 1. button at INT0 (pin2)
#include "EasyButtonAtInt01.cpp.h"

void showButtonToggleState(bool aButtonToggleState); // The callback function
EasyButton Button0AtPin2(&showButtonToggleState); // Only 1. button (USE_BUTTON_0) enabled -> button is connected to INT0 (pin2)
void handleButtonPress(bool aButtonToggleState); // The button press callback function
void handleButtonRelease(bool aButtonToggleState, uint16_t aButtonPressDurationMillis);
EasyButton Button0AtPin2(true, &handleButtonPress, &handleButtonRelease); // true -> button is connected to INT0 (pin2)

#define VERSION_EXAMPLE "2.1"
#define VERSION_EXAMPLE "3.0"

#if defined(ARDUINO_AVR_DIGISPARK)
#define LED_BUILTIN PB1
Expand All @@ -43,6 +44,8 @@ EasyButton Button0AtPin2(&showButtonToggleState); // Only 1. button (USE_B
#elif ! defined(LED_BUILTIN)
#define LED_BUILTIN PB1 // define port of built in LED for your ATtiny
#endif
#define BLINK_SHORT_MILLIS 200
#define BLINK_LONG_MILLIS 600

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Expand All @@ -54,22 +57,57 @@ void setup() {
#endif
// Just to know which program is running on my Arduino
Serial.println(F("START " __FILE__ "\r\nVersion " VERSION_EXAMPLE " from " __DATE__));
Serial.println(F("Using library version " VERSION_EASY_BUTTON));
}

void loop() {
delay(10);
}

void blinkLEDBlocking(uint8_t aLedPin, uint16_t aDelay, uint8_t aRepetitions) {
for (uint8_t i = 0; i < aRepetitions; ++i) {
digitalWrite(aLedPin, HIGH);
delay(aDelay);
digitalWrite(aLedPin, LOW);
delay(aDelay);
}
}

/*
* The callback function is called at each button press
* Initial value is false, so first call is with true
*/
void showButtonToggleState(bool aButtonToggleState) {
digitalWrite(LED_BUILTIN, aButtonToggleState);
void handleButtonPress(bool aButtonToggleState) {
/*
* Double press (< 200 ms) detection by calling checkForForDoublePress() once at button press time.
*/
if (Button0AtPin2.checkForDoublePress(300)) {
Serial.println(F("Double press (< 300 ms) detected"));
Serial.print(F("Double press "));
Serial.print(Button0AtPin2.ButtonLastChangeMillis - Button0AtPin2.ButtonReleaseMillis);
Serial.println(F(" ms detected"));

// let the led blink twice short
blinkLEDBlocking(LED_BUILTIN, BLINK_SHORT_MILLIS, 2);
Button0AtPin2.ButtonToggleState = false;
}
Serial.println(F("Button pressed"));
digitalWrite(LED_BUILTIN, aButtonToggleState);
}

void handleButtonRelease(bool aButtonToggleState, uint16_t aButtonPressDurationMillis) {
digitalWrite(LED_BUILTIN, aButtonToggleState);
Serial.println(F("Button released"));

/*
* Simple long press (> 400 ms) detection
*/
if (aButtonPressDurationMillis >= EASY_BUTTON_LONG_PRESS_DEFAULT_MILLIS) {
Serial.print(F("Long press "));
Serial.print(aButtonPressDurationMillis);
Serial.println(F(" ms detected"));

// let the led blink long
blinkLEDBlocking(LED_BUILTIN, BLINK_LONG_MILLIS, 2);
Button0AtPin2.ButtonToggleState = false;
}
}
5 changes: 3 additions & 2 deletions examples/DebounceTest/DebounceTest.ino
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

//#define USE_ATTACH_INTERRUPT // enable it if you get the error " multiple definition of `__vector_1'" (or `__vector_2')
//#define MEASURE_INTERRUPT_TIMING
//#define DO_NOT_REQUIRE_LONG_AND_DOUBLE_PRESS

#define ANALYZE_MAX_BOUNCING_PERIOD
#define BUTTON_DEBOUNCING_MILLIS 2
Expand All @@ -38,7 +37,7 @@

EasyButton Button0AtPin2; // Only 1. button (USE_BUTTON_0) enabled -> button is connected to INT0

#define VERSION_EXAMPLE "2.0"
#define VERSION_EXAMPLE "3.0"

#if defined(ARDUINO_AVR_DIGISPARK)
#define LED_BUILTIN PB1
Expand All @@ -62,7 +61,9 @@ void setup() {
#endif
// Just to know which program is running on my Arduino
Serial.println(F("START " __FILE__ "\r\nVersion " VERSION_EXAMPLE " from " __DATE__));
Serial.println(F("Using library version " VERSION_EASY_BUTTON));
Serial.println(F("Button debouncing time is reduced to " STR(BUTTON_DEBOUNCING_MILLIS) " ms"));
Serial.println(F("Please press the button and watch for \"Bouncing, MBP=...\" output at the Serial Monitor"));
}

void loop() {
Expand Down
11 changes: 8 additions & 3 deletions examples/EasyButtonExample/ATtinySerialOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,16 @@
// +----+
//

#ifndef TINY_SERIAL_OUT_H_
#define TINY_SERIAL_OUT_H_
#ifndef ATTINY_SERIAL_OUT_H_
#define ATTINY_SERIAL_OUT_H_

#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
#include <Arduino.h>

#define VERSION_ATTINY_SERIAL_OUT "1.1.0"
#define VERSION_ATTINY_SERIAL_OUT_MAJOR 1
#define VERSION_ATTINY_SERIAL_OUT_MINOR 1

#if (F_CPU != 1000000) && (F_CPU != 8000000) && (F_CPU != 16000000)
#error "F_CPU value must be 1000000, 8000000 or 16000000."
#endif
Expand Down Expand Up @@ -179,6 +183,7 @@ class TinySerialOut

// virtual functions of Print class
size_t write(uint8_t aByte);
operator bool() { return true; } // To support "while (!Serial); // wait for serial port to connect. Needed for Leonardo only

#if !defined(TINY_SERIAL_INHERIT_FROM_PRINT)
void print(const __FlashStringHelper * aStringPtr);
Expand Down Expand Up @@ -220,6 +225,6 @@ extern TinySerialOut Serial;

#endif // defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)

#endif /* TINY_SERIAL_OUT_H_ */
#endif /* ATTINY_SERIAL_OUT_H_ */

#pragma once
Loading

0 comments on commit da8cc97

Please sign in to comment.