Toggle Library for Debouncing Data, Buttons and Switches

Toggle

arduino-library-badge PlatformIO Registry

Arduino switch and button library for SPST, SPDT or SP3T contacts. Simple to use, provides debouncing, deglitching and uses very little memory. Captures one-shot transitions (depicting direction) and current position status.

Simple to use because pinMode, input pullups, de-glitch period, bounce period, and switch type using 1 or 2 inputs is automatically detected and configured.

Memory Comparison on Leonardo with 2 buttons attached :

Library Version Bytes Bytes Used
Empty sketch -- 149 --
Toggle.h 2.1.1 177 28
JC_Button.h 2.1.2 186 37
Bounce2.h 2.71.0 193 44
AceButton.h 1.9.2 205 56
ezButton.h 1.0.3 331 182

Connections shown below are for 2-position switches (SPST, SPDT, DPDT) using 1 input:


Connections shown below are for 3-position switches (SP3T, DP3T) using 2 inputs:

Toggle Switch (SP3T, On-Off-On):

2 Likes

New version: Toggle 2.2.0

  • New feature to debounce byte data as an input.
  • New example

Would this work with microswitches?

thanks for sharing !

noticed in the .h

    uint8_t _inputMode = static_cast<uint8_t>(inMode::input_pullup);

and

    inMode inputMode = inMode::input_pullup;

do you really need both?

and why all the static_cast if you have both in the code?

(note that the conversion from an unscoped enumeration type to an arithmetic type is an implicit conversion; it is possible, but not necessary, to use static_cast).

also you could "optimize" (may be the compiler does it for you) all your masking stuff

for example instead of

bool Toggle::isOFF() {
  if ((statesOne & 0b00000001) == 0b00000001) return true;
  return false;
}

you could do

bool Toggle::isOFF() {
  return statesOne & 0b00000001;
}
1 Like

I don't see why not. Microswitches come in all types, but from this reference it mentions contact switching times of 5-15ms. The library won't add considerable time to detecting switched ON status - it takes 2 samples which adds 10ms (default). The sample time is now configurable, but I'm sure the default will work well.

Thanks for taking a look at this and for your comments and suggestions!

My liberal use of static_cast came about from an issue that occurred in another library where the user had a warning with a specific compiler, but, of course, I can't even find it. Looks like there's opportunity to do some cleanup here, because static_cast<uint8_t> used where not necessary can sure make things look ugly!

Re:

return statesOne & 0b00000001;

Wow, thanks!

1 Like

New version: Toggle 2.3.0

  • A more responsive algorithm:
    1 sample glitches are ignored.
    2 samples to detect pressed state.
    3 samples to detect released state.

  • New setInvertMode() function. Set true if button or switch forces input high when pressed. Also inverts input data if using input_logic mode

  • New input_pulldown mode enables pulldown resistor for ESP32: sw1.setInputMode(sw1.inMode::input_pulldown);

  • New examples

New version: Toggle 2.4.0

New feature;

Digital Port Debouncing:

image
Now you can easily debounce a complete 8-bit port (8 signals) at a time in just one Toggle object. Several instances of Toggle can debounce 16-bit I/O expanders or other hardware, sensor data or stored data. The function debouncePort()is used to return the debounced data byte.

From the Port_Debouncer_Test example, the following is the serial print with leading 0's added:

In: 00000000 Out: 00000000
In: 10000001 Out: 00000000
In: 10100011 Out: 00000000
In: 00100111 Out: 00000001
In: 00001110 Out: 00000011
In: 00011110 Out: 00000111
In: 00101111 Out: 00001111
In: 01110110 Out: 00001111
In: 11111100 Out: 00101111
In: 11011000 Out: 01111110
In: 01110000 Out: 01111100
In: 01100011 Out: 01111000
In: 11000000 Out: 01110000
In: 10000100 Out: 01100000
In: 00000000 Out: 01000000

Looking at the columns (bit data) top to bottom, it can be seen that the debounced Out data lags by only 2 samples (rows). It also can be seen that the port debouncer can tolerate a very noisy signal with up to 2 consecutive 1's or 0's that are anomalous or spurious in the In data.

Toggle 3.0.0

New major release with new functions, function reference and updated examples.
Simple to use, stingy on memory.

in this constructor (from the .h)

    Toggle(uint8_t inA, uint8_t inB = 255) {
      _inA = inA;
      _inB = inB;
      begin(_inA, _inB);
    };

you call begin() directly.

It's not coherent with the other constructors and risky as constructors for global objects will be called even before main() is executed and the pinMode might get lost


in the code I see

#if (defined(ESP32) || defined(ARDUINO_ARCH_ESP32))
    else if (_inputMode == inMode::input_pulldown) pinMode(_inB, INPUT_PULLDOWN);
#endif

➜ the SAMD core is also capable of pulldown

may be you can also test if INPUT_PULLDOWN is defined. that would be a hint the architecture supports it (as long as it's a #define)

1 Like

Thanks looking at this!

The contructor scheme came about after trying to initialize an array of button/switch objects, which I've found a reference here . I've added a default constructor and updated the example Eight_buttons.ino to use the default constructor, which seems to work really well.

The reason I've put begin() in the constructor mentioned was only for convenience. The reference led me to here, where lines 42-44 use this approach.

Would it be an improvement if have begin() called by the first-run of poll() which is the first function in loop()? The idea is to have the convenience of pin configuration done automatically, thereby simplifying user code.

I do have anESP32-S2 but haven't had a chance yet to test the input_pulldown option. I could add an ifdef for the SAMD core but would need to find out what the best define to test for would be.

I understand the convenience.

the risk of calling pinMode() in the constructor is that this happens before main() is called and so if what happens in main() does modify the pins' configuration then your object is no longer working correctly.

on an AVR arduino, main.cpp calls

init();
initVariant();

before calling your setup

init() for example will be used to configure the timer and disconnect pins 0 and 1 from the USART (the bootloader connects them) so that sounds fine but initVariant() is board specific and could do other stuff

This is more theoretical for usual pins you would use for a button but sh*t could happen and that's why you see many libraries dealing with hardware having a begin() method you need to call in the setup() function as you are guaranteed that this happens after init() and initVariant() and so what you do would stick.

that would require a test that will slow (a tiny bit) every poll() and an extra bool to remember if the button was initialized.... I would just encourage everyone to call begin() on their button in the setup() or just ignore the risk and in which case you could have the other constructor handle the pinMode too

Ahh, OK. Will update examples appropriately using the begin function.

EDIT: Done ... Toggle 3.0.1

You could have the csr &= ~0b00001000; // clear first run in the begin() body so that if the user called begin() you don’t do it twice upon first poll

You also then need to remove begin() from the constructor in the .h

1 Like

Toggle 3.1.1

Introducing...

pressCode()

Give this a trial run on WOKWi using an UNO here.

Description
  • Up to 225 possible codes with one button. The returned code (byte) is easy to interpret when viewed in hex format. For example, 47 is 4 long, 7 short presses. F2 is double-click, F7 is 7 Fast clicks.
  • Fast-click mode is detected if the first 2 clicks (presses) are less than 0.4 sec, then counts any extra presses if the duration is less than 1 sec, up to 15 max (code FF)
  • Detection of long (greater than 1 sec) presses and short (less than 1 sec) presses occurs if the first press is 0.4 sec or longer.
  • Detect up to 15 short presses
  • Detect up to 14 long presses
  • Returns code after button is released for 1.4 sec
  • Simplifies your code while adding maximum functionality to one button
Example
byte pCode = sw1.pressCode(1); // (1) serial print results
Example Sketch

Press_Code.ino

1 Like

Interesting idea.

Toggle 3.1.3

Some cleanup, removed some functions no longer needed, improved description of debounce algorithm, updated examples.

Algorithm

The debounce algorithm adds only 2 sample periods of time lag to the output signal. A 3-sample stable period is required for an output bit to change. Therefore, to set an output bit, 3 consecutive 1's are required. When 3 consecutive 0's are detected, that bit value is cleared.

image

ppDat pDat dat R S Q State
0 0 0 1 0 0 Reset
0 0 1 0 0 Last State No Change
0 1 0 0 0 Last State No Change
0 1 1 0 0 Last State No Change
1 0 0 0 0 Last State No Change
1 0 1 0 0 Last State No Change
1 1 0 0 0 Last State No Change
1 1 1 0 1 1 Set
1 Like

Toggle 3.1.5

Updated examples and readme. Use_Button_As_Toggle.ino
Try the latest Wokwi example.

Configure to trigger on press or release and set the initial state. Debouncer and INPUT_PULLUP configured by default. No millis() timers to deal with, so its very easy to use in your code.

Toggle 3.1.7

Updated timer functions and new Wokwi examples.

  • Your suggestions, Issues and Discussions are welcome here or here.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.