Skip to content

avdwebLibraries/avdweb_Switch

Repository files navigation

avdweb_Switch Library

Arduino switch and button library with Short/Long Press, Double Click and Beep. Supports defining callback functions.

General features of the Switch library

  • Performs not just de-bouncing, but also de-glitching against EMC pulses.
  • External pull-up resistors are not required.
  • Supports also long press, double click, and single click detection.
  • Callback functions.

Introduction

Switches and push buttons need debouncing. It is straightforward to do this with software, using this library.

For a complete description see:

https://www.avdweb.nl/arduino/hardware-interfacing/simple-switch-debouncer

Using the Switch library

Declare each switch or button, by indicating its GPIO number:

Switch buttonGND = Switch(4); // GPIO 4

The library sets GPIO in input mode with an internal pull-up resistor by default. The constructor accepts several arguments including the particular input mode (e.g. without pull-up), whether voltage level high must be interpreted as on or off (argument polarity must indicate the pushed voltage level), and the duration of the time periods that define the behavior.

All switches have to be repeatedly polled individually to update the status. This is usually done in the loop() function.

buttonGND.poll();

After refreshing the status, it can be checked with get functions. For example:

if(buttonGND.pushed()) Serial.print("pushed");

Any event occurs based on the de-glitched and debounced pin signal. See below for details on this function of the library that happens transparently.

Each possible event (each switch-status) has its own get function:

switched()

It returns true if the pin voltage level changed, no matter the direction (from low to high or from high to low).

on()

It returns true if the pin voltage agrees with the one defined by polarity (which is LOW by default). Use only for toggle switches. It returns "true" as long as the switch is in the "on" position. The polarity of the switch in the "on" position has to be filled in correctly. There is no off() function, this is simply !on().

pushed()

It returns true if a button was pushed (switched towards the on position). Use only for push buttons. Note that this provides instant response and can be used as "single-click" if "double-click" and "long-press" are not watched events. "Pushed" occurs simultaneously with "double-click" and "long-press".

released()

It returns true if a push button was released (switched towards the off position), this will however rarely be used.

longPress()

It returns true if a push button is pressed longer than 300ms (by default). Note that a longPress() always will be preceded by pushed() from the first push.

doubleClick()

It returns true if a push button is double clicked within 250ms (by default). Note that a doubleClick() always will be preceded by pushed() from the first push.

The anteceding pushed() event can't be avoided, because to restrict the pushed() function to single clicks it would have to wait for a possible second click, which would introduce an annoying delay. So, the action on doubleClick() has to undo the previous action on pushed().

singleClick()

It returns "true" if a push button is clicked once and the requirements for doubleClick and longPress are not met. The event thus occur several milliseconds after the actual push, because it depends on the other events not happening after the push. Use this if three different interactions are needed in combination with doubleClick and longPress. Note that a singleClick() always will be preceded by pushed(). Use pushed() instead if immediate response is required and there is no interest in monitoring doubleClick and longPress.

Examples

See the example in the library for a complete working program.

Transparent signal filtering

De-glitching

Sudden short changes in the microcontroller power supply may create inexistent switched() events if the pin is polled in that instant. De-glitching is achieved because the software will not believe that a voltage change has happened (a switched event) until the voltage remains in the new level for a minimum period. A voltage change will be ignored if it returns to the original level before that period.

Debouncing

Due to the elastic properties of the metals in the contacts they bounce away from each other after the initial contact creating false switched() events after an initial true switch, until the voltage stabilizes in the new level. Debouncing forces a refractory period after an initial switch, during which any voltage level change is ignored.

Using callback functions to be triggered by button events

Callbacks can be used, instead of needing to check with an if statement whether the relevant event has occurred after calling switch.poll().

With a callback registered, Switch will run the defined function when the conditions are met at the time poll() is called. Calling poll() periodically is the only requirement.

First, define a function with the action you want to perform, with no return type (void) and one argument, a pointer to some reference (or use nullptr if you don't need it.):

void foo(void* ref) {
  //your code;
}

... and then call one of the "set callback" methods in the setup() function, like so:

void setup() {
  ... // Other setup code 
  ...
  int reference;
  switch.setPushedCallback(&foo, &reference);
  ...
}

The available callback setting functions are setPushedCallback(), setReleasedCallback(), setLongPressCallback(), setDoubleClickCallback(), and setSingleClickCallback() which allow defining the functions that will be called on such events. If using a toggle switch and not a push button, the "pushed" event will be of interest when the switch is turned on, and "released" when it is turned off.

If the conditions for more than one event occur simultaneously and there are callback functions registered for them, they will be executed in the order of the functions above.

See the example in the library for a complete working program.

Notes

The poll instruction must be called at least 50 times per second, i.e. every 20ms; it takes about 20us in Arduino.

The button states are saved till the next poll. Then all previous button states will be cleared, so the button states must be read every poll interval, after calling the poll function.

Reading a button several times within a poll interval gives the same value, so reading doesn't clear the button state.

SUGESTION: Using an interrupt service routine for polling the buttons

Polling buttons has a high priority and should be done often. Slow functions such as Serial.print() may disrupt the timing.

Polling could be done in a function triggered by a timed interrupt. Remember to keep the subsequent button actions short as you would do with any code in interrupts.

For Arduino see here how to use an ISR for polling the buttons:

#include <Arduino.h>
#include "avdweb_Switch.h"
#include <FrequencyTimer2.h>

Switch speedUpBtn(1);
Switch speedDownBtn(2);
Switch buttonLeft(3);
Switch buttonRight(4);

void setup(void)
{ Serial.begin(9600);
  FrequencyTimer2::setPeriod(1000);
  FrequencyTimer2::setOnOverflow(timer2ISR);
}

void loop(void)
{ printAll(); // run slow functions in loop()
}

void timer2ISR()
{ pollAll(); // polling buttons has priority
  buttonActions();
}

// you should implement pollAll(), buttonActions(), and printAll()

For ESP8266 use the Ticker library.

How to use successive button events

With the Switch library, you can use all button events at the same time with the same button. For example pushed(), released(), doubleClick() and longPress(). To see an example of what can be achieved with several button events in order to perform different functions, run Windows Explorer: a single click selects a file and a double click opens it.

Button pushed event followed by a longPress event

A long-press generates first a pushed event and after 300ms (by default) the longPress event. This is not a shortcoming but a logical consequence. We can, of course, not always wait 300ms to see if a push might be a long push.

Button pushed event followed by a doubleClick event

The same happens with doubleClick, which also generates two pushed() events. When doubleClick is used, ignore the second pushed() result or don't call pushed(). When doubleClick is not needed, simply don't call doubleClick().

Combining singleClick, doubleClick, and longPress events

If these three events must be used together the best strategy is to stick to their get functions and avoid using pushed().

Hardware considerations

Connecting switches to the microcontroller

There is nothing needed beyond the switches, just connect the switches between the ground and a digital pin.

Switch between GND and digital pin

This is technically speaking the best solution and it is taken by the library as default. An external pull-up resistor is not needed but allowed.

Switch between VCC(3.3V or 5V) and digital pin

For switches connected to the Arduino power supply, the settings are: polarity = HIGH and pinmode = INPUT, which disables the internal pull-up resistor. Note that we need external pull-down resistors of about 10k here.

Questions

Post questions on the Arduino board and let me know, I will reply there.

Post issues on the github repository.

Explanation of the inner working

New: software de-glitch

Normally, just debouncing is used. But debouncing doesn't make the software insensitive to noise pulses, which could lead to dangerous situations: if there is a poll() at the moment of a noise pulse, the software can detect an activated switch. To prevent this, a de-glitch function is added now: during a deglitchPeriod, all input values have to be equal.

Software debounce, how does it work

The software debounce algorithm is based on the following assumptions; the 50ms (by default) is the so-called debounce period:

  • The switch bounce time is less than 50ms.
  • The time between successive keystrokes is larger than 50ms.
  • A reaction time of 50ms is acceptable.

If the above assumptions are met, the software debounce algorithm can be quite simple: a switch event is only accepted when the elapsed time since the last switch event is larger than the debounce period.

A debounce period of 50ms is a safe value; it doesn't hurt the reaction time, and will handle even bad switches.

See the following pseudocode:

if((current_deglitched != previous_debounced) & ((current_ms - last_switchedTime) >= debouncePeriod)) current_debounced = current_deglitched;
// otherwise current_debounced stays the same as previous_debounced

Overall functioning

Several discrete signals (updated at poll times) are created. The raw signal is de-glitched, the later debounced, and that one in turn is processed to generate a signal for each one of the events.

Timing diagram

..........................................DEGLITCHING..............................

                        ________________   _
               on      |                | | |    _
                       |                | | |   | |
                       |                |_| |___| |__
 analog        off_____|_____________________________|____________________________

                        ________________   _     _
 input            _____|                |_| |___| |_______________________________

 poll            ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^

 equal           0 1 1 0 1 1 1 1 1 1 1 1 0 0 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

 deglitchPeriod          <--------><--   <--     <-  <--------><--------><--------
                                    ___________________________
 deglitched       _________________|                           |__________________

 deglitchTime            ^         ^     ^       ^   ^         ^        ^

 ..........................................DEBOUNCING.............................

 debouncePeriod                    <-------------------------------->
                                    _________________________________
 debounced        _________________|                                 |____________
                                    _                                 _
 _switched        _________________| |_______________________________| |__________

 switchedTime                      ^                                 ^


**********************************************************************************
........................................DOUBLE CLICK..............................

                           __________         ______
 debounced        ________|          |_______|      |_____________________________

 poll            ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
                           _                  _
 pushed          _________| |________________| |__________________________________

 pushedTime               ^                  ^
                                      _              _
 released        ____________________| |____________| |___________________________

 releasedTime                        ^              ^

 doubleClickPeriod         <------------------------------------->
                                              _
 _doubleClick     ___________________________| |__________________________________


........................................LONG PRESS................................

                           ___________________________
 debounced        ________|                           |___________________________

 poll            ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^

 longPressPeriod            <--------------->
                            _                           _
 _switched        _________| |_________________________| |________________________
                                              __________
 longPressDisable ___________________________|          |_________________________
                                              _
 _longPress       ___________________________| |__________________________________


........................................SINGLE CLICK..............................

                           __________                                 ______
 debounced        ________|          |_______________________________|      |_____

 poll            ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^

 longPressPeriod           <--------------->
 doubleClickPeriod         <------------------------------------->
                            _         _                               _      _
 _switched        _________| |_______| |_____________________________| |____| |___
                                                                  _____
 singleClickDisable______________________________________________|     |__________
                                                                  _
 _singleClick     _______________________________________________| |______________

Additional information

See also: A Guide To Debouncing

Developer Note

Please perform formatting before committing the code.

clang-format -i src/*.* examples/**/*.ino

Credits

Thanks to the contributors for making this library great

  • Sébastien Celles @scls19fr for helping with the Properties file.

  • Martin-Laclaustra @Martin-Laclaustra

    • Remvoing Streaming.h dependency.
    • Adding Callback function example.
    • Updates to the documentation.
    • Various Bug fixes to the Library and much more.
  • Geoff @GeoffState for helping with updating the README file.

  • Jeff Brown @j9brown for fixing Bugs and Warnings in the library.

  • Chris Pearson @themaninthesuitcase for fixes to the file names.

  • @nomakewan for helping with various issues and altering on major failures in compatibility.

License

SPDX: GPL-3.0-or-later

Switch - Arduino switch and button library with Short/Long Press, Double Click and Beep.

Copyright (C) 2012-2024 Albert van Dalen http://www.avdweb.nl

Copyright (C) 2024 Abhijit Bose (aka. Boseji) :contributor:

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.