I have a nice project that has about 12 button inputs, and sends musical notes over midi BLE. Latency is very important as the human ear can detect latency as slow at 30-50ms.
I just realized I can greatly reduce my latency, if I were to remove the 25ms debounce time from my buttons. I still need debounce, but I don't need it on the button push, if that makes sense. In other words, as soon as the code were to detect a button press, it would be okay to signal it as a button press. It could debounce the release of the button. maybe another way to explain it would be that the debounce would only monitor the state change of the release. So, the instant the button is pressed, it will fire a button flag, but then debounce the button after that. It seems like it wouldn't be too hard to maybe just use the same debounce2 library, but add in a line of two that would do something like:
If the button hasn't been pushed in awhile (no state change for the debounce time 25ms), then go ahead a pass it as Pressed. I think I can press this, but it seems a bit mind boggling. Maybe I'd be better to write my own little ButtonPressed function rather than using the library. There's so much coding in the library I don't understand the syntax of, as I'm just a self taught arduino tinkerer that was born before the age of computers. If anyone has a suggestion to the best way to go about this, I think the current 25ms debounce time would be greatly noticed if it were gone.
thanks
Here is my setup for my buttons, using the bounce2 library.
#include <Bounce2.h>
#define BUTTON_A_PIN 1
#define BUTTON_B_PIN 26
#define BUTTON_C_PIN 19
#define BUTTON_D_PIN 9
#define BUTTON_E_PIN 17
#define BUTTON_F_PIN 16
#define BUTTON_G_PIN 18
#define BUTTON_7th_PIN 11
#define BUTTON_MINOR_PIN 12
#define BUTTON_MENU_SINGLE_PIN 15
#define BUTTON_SELECT_SINGLE_PIN 14
Bounce2::Button ButtonA = Bounce2::Button();
Bounce2::Button ButtonB = Bounce2::Button();
Bounce2::Button ButtonC = Bounce2::Button();
Bounce2::Button ButtonD = Bounce2::Button();
Bounce2::Button ButtonE = Bounce2::Button();
Bounce2::Button ButtonF = Bounce2::Button();
Bounce2::Button ButtonG = Bounce2::Button();
Bounce2::Button Button7th = Bounce2::Button();
Bounce2::Button ButtonMinor = Bounce2::Button();
Bounce2::Button ButtonMenu = Bounce2::Button();
Bounce2::Button ButtonSelect = Bounce2::Button();
void setup_buttons() {
ButtonA.attach(BUTTON_A_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonA.interval(25); // Use a debounce interval of 25 milliseconds
ButtonA.setPressedState(LOW);
ButtonB.attach(BUTTON_B_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonB.interval(25); // Use a debounce interval of 25 milliseconds
ButtonB.setPressedState(LOW);
ButtonC.attach(BUTTON_C_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonC.interval(25); // Use a debounce interval of 25 milliseconds
ButtonC.setPressedState(LOW);
ButtonD.attach(BUTTON_D_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonD.interval(25); // Use a debounce interval of 25 milliseconds
ButtonD.setPressedState(LOW);
ButtonE.attach(BUTTON_E_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonE.interval(25); // Use a debounce interval of 25 milliseconds
ButtonE.setPressedState(LOW);
ButtonF.attach(BUTTON_F_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonF.interval(25); // Use a debounce interval of 25 milliseconds
ButtonF.setPressedState(LOW);
ButtonG.attach(BUTTON_G_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonG.interval(25); // Use a debounce interval of 25 milliseconds
ButtonG.setPressedState(LOW);
Button7th.attach(BUTTON_7th_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
Button7th.interval(25); // Use a debounce interval of 25 milliseconds
Button7th.setPressedState(LOW);
ButtonMinor.attach(BUTTON_MINOR_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonMinor.interval(25); // Use a debounce interval of 25 milliseconds
ButtonMinor.setPressedState(LOW);
ButtonMenu.attach(BUTTON_MENU_SINGLE_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonMenu.interval(25); // Use a debounce interval of 25 milliseconds
ButtonMenu.setPressedState(LOW);
ButtonSelect.attach(BUTTON_SELECT_SINGLE_PIN, INPUT_PULLUP); // Attach with INPUT_PULLUP mode
ButtonSelect.interval(25); // Use a debounce interval of 25 milliseconds
ButtonSelect.setPressedState(LOW);
}
void update_Buttons() {
ButtonA.update(); // Update the button instance
ButtonB.update(); // Update the button instance
ButtonC.update(); // Update the button instance
ButtonD.update(); // Update the button instance
ButtonE.update(); // Update the button instance
ButtonF.update(); // Update the button instance
ButtonG.update(); // Update the button instance
Button7th.update(); // Update the button instance
ButtonMinor.update(); // Update the button instance
ButtonMenu.update(); // Update the button instance
ButtonSelect.update(); // Update the button instance
}
Here is the main coding in the debounce2 library. It's a very good button library.
// Please read Bounce2.h for information about the liscence and authors
#include "Bounce2.h"
//////////////
// DEBOUNCE //
//////////////
Debouncer::Debouncer():previous_millis(0)
, interval_millis(10)
, state(0) {}
void Debouncer::interval(uint16_t interval_millis)
{
this->interval_millis = interval_millis;
}
void Debouncer::begin() {
state = 0;
if (readCurrentState()) {
setStateFlag(DEBOUNCED_STATE | UNSTABLE_STATE);
}
#ifdef BOUNCE_LOCK_OUT
previous_millis = 0;
#else
previous_millis = millis();
#endif
}
bool Debouncer::update()
{
unsetStateFlag(CHANGED_STATE);
#ifdef BOUNCE_LOCK_OUT
// Ignore everything if we are locked out
if (millis() - previous_millis >= interval_millis) {
bool currentState = readCurrentState();
if ( currentState != getStateFlag(DEBOUNCED_STATE) ) {
previous_millis = millis();
changeState();
}
}
#elif defined BOUNCE_WITH_PROMPT_DETECTION
// Read the state of the switch port into a temporary variable.
bool readState = readCurrentState();
if ( readState != getStateFlag(DEBOUNCED_STATE) ) {
// We have seen a change from the current button state.
if ( millis() - previous_millis >= interval_millis ) {
// We have passed the time threshold, so a new change of state is allowed.
// set the STATE_CHANGED flag and the new DEBOUNCED_STATE.
// This will be prompt as long as there has been greater than interval_misllis ms since last change of input.
// Otherwise debounced state will not change again until bouncing is stable for the timeout period.
changeState();
}
}
// If the readState is different from previous readState, reset the debounce timer - as input is still unstable
// and we want to prevent new button state changes until the previous one has remained stable for the timeout.
if ( readState != getStateFlag(UNSTABLE_STATE) ) {
// Update Unstable Bit to macth readState
toggleStateFlag(UNSTABLE_STATE);
previous_millis = millis();
}
#else
// Read the state of the switch in a temporary variable.
bool currentState = readCurrentState();
// If the reading is different from last reading, reset the debounce counter
if ( currentState != getStateFlag(UNSTABLE_STATE) ) {
previous_millis = millis();
toggleStateFlag(UNSTABLE_STATE);
} else
if ( millis() - previous_millis >= interval_millis ) {
// We have passed the threshold time, so the input is now stable
// If it is different from last state, set the STATE_CHANGED flag
if (currentState != getStateFlag(DEBOUNCED_STATE) ) {
previous_millis = millis();
changeState();
}
}
#endif
return changed();
}
// WIP HELD
unsigned long Debouncer::previousDuration() const {
return durationOfPreviousState;
}
unsigned long Debouncer::currentDuration() const {
return (millis() - stateChangeLastTime);
}
inline void Debouncer::changeState() {
toggleStateFlag(DEBOUNCED_STATE);
setStateFlag(CHANGED_STATE) ;
durationOfPreviousState = millis() - stateChangeLastTime;
stateChangeLastTime = millis();
}
bool Debouncer::read() const
{
return getStateFlag(DEBOUNCED_STATE);
}
bool Debouncer::rose() const
{
return getStateFlag(DEBOUNCED_STATE) && getStateFlag(CHANGED_STATE);
}
bool Debouncer::fell() const
{
return !getStateFlag(DEBOUNCED_STATE) && getStateFlag(CHANGED_STATE);
}
////////////
// BOUNCE //
////////////
Bounce::Bounce()
: pin(0)
{}
void Bounce::attach(int pin) {
this->pin = pin;
// SET INITIAL STATE
begin();
}
void Bounce::attach(int pin, int mode){
setPinMode(pin, mode);
this->attach(pin);
}