A lightweight Library to easily use Multi Direction Switches.
Interrupt based triggering - all ISR logic is cross platform (AVR, ESP) and done in the background correctly. (without debouncing problems) with easy to use callback attachments. This way your code will be cleaner and safer.
MDSwitch
h also supports MODES - Meaning you can set several modes which can be switched and has different callbacks attached, perfect workflow for menu based systems and for switching the device functionality based on the required state / mode.
BORAD | Pins (int, push, ccw, cw) | Info |
---|---|---|
ESP32 | 35, 24, 36, 39 | INPUT only pins |
UNO | 3, 4, 5, 6 | Only 2,3 support interrupts |
NANO | 3, 4, 5, 6 | Only 2,3 support interrupts |
MEGA | 3, 4, 5, 6 | |
LEONARDO | 3, 4, 5, 6 | |
DUE | 3, 4, 5, 6 | *3.3v logic |
- Library Features
- Quick Installation
- Example Included
- Decalring MDSwitch
- All the key/buttons/terminals codes
- MDSwitch Initialization and MODES
- Assigning an Event callback
- Auto invoking callbacks
tick()
method - MDSwitch additional Methods
- Important Notes
- Interrupt based - Fully managed by the library - single interrupt.
- Callbacks don't requires ISR decleration volatile or whatever - Just simple function pointers.
- Smooth key/button captures - Debouncing and 'noise' is taking care of by the library.
- Dynamic Enable / Disable states.
- Modes - You can modes which stores Event -> Callbacks scheme to easily change the functionality.
- Small, Lightweight and very fast.
- Well documented code.
You can install the library through one of the following:
- Arduino or PlatformIO library manager: Search for "SIKTEC-MDSwitch" and click install.
- Download the repositories as a ZIP file and install it through the Arduino IDE by:
Sketch -> Include library -> Add .ZIP Library.
- Download the library and include it in your project folder - Then you can Include it directly:
#include "{path to}\SIKTEC_MDSwitch.h"
The example included with this library demonstrates how to declare and initialize the library, set modes, assign callbacks and alter between the modes based on the captured events. The code is well documented and should be easy to build upon and extend the functionality. The example will create 3 modes:
- MODE: 0
CCW :: will increment a counter and print to serial output.
CW :: will decrement a counter and print to serial output.
PUSH :: will change the mode (incrementing) and print a message to serial output.
ANY :: will print the current counter to serial output.
- MODE: 1
CCW :: wont do anything (no callback attached).
CW :: wont do anything (no callback attached).
PUSH :: will change the mode (incrementing) and print a message to serial output.
ANY :: will print the current counter to serial output.
- MODE: 2
CCW :: wont do anything (no callback attached).
CW :: wont do anything (no callback attached).
PUSH :: will change the mode (incrementing) and print a message to serial output.
ANY :: wont do anything (no callback attached).
Call MDSwitch
with all required pins - Make sure you are using for SW_INT an external interrupt supported pin.
the 3 terminal (key/button pins) can be any digital pin that can be set as INPUT.
#include <SIKTEC_MDSwitch.h>
...
//using namespace SIKtec; // Optional
SIKtec::MDSwitch mdswitch(
SW_PUSH, // Push button pin number -> middle switch terminal
SW_CCW, // CCW button pin number -> right switch terminal
SW_CW, // CW button pin number -> left switch terminal
SW_INT // Shared interrupt pin.
);
...
- Note: all terminals are and interrupt should be pulled low - If you are using SIKTEC's prototype board this is allready done.
- Note: Since we assume terminals are pulled down, we can use Digital pins which can be only inputs without internal pulldown/up options (e.g. ESP32 34-39)
By default the shield has 4 key events - Events are mapped using a simple enum value;
enum | Button | More info |
---|---|---|
MDS_KEYS::PUSH |
Middle switch "push in" Event | |
MDS_KEYS::CW |
Switch side push event | |
MDS_KEYS::CCW |
Switch side push event | |
MDS_KEYS::ANY |
Any of above | when attaching a callback to this event all keys will trigger the calback |
Note:
MDS_KEYS::ANY
won't replace the key/button events - It will be triggered AFTER the specific callback (if there is one attached).
Creating a MDSwitch object does not initialize it - Its necessary to call the .init(const int _modes)
method which initializes the object and does all the pin declarations + allocates memory for the callbacks pointers.
mdswitch.init(3 /* how many modes */); // each mode can take a set of callbacks.
The method expects an integer which sets how many MODES are we going to use - MODES are basically an internal state which can be switched. Each mode can have its own set of callbacks attached and invoked.
Note: By default only 1 mode will be created - (mode index 0).
To change modes and set the current enabled/used mode:
...
mdswitch.mode(0); //set mode 1
mdswitch.mode(1); //set mode 2
mdswitch.mode(2); //set mode 3
...
int usedmode = mdswitch.mode(); // returns the current enabled/used mode index.
By default there is are no callbacks when a button is pressed/captured. To assign your custom callback functions attach them using the .attach()
method.
//Attach callbacks:
mdswitch.attach(-1, MDS_KEYS::PUSH, &cb_push); // -1 means all modes - 0,1,2.
mdswitch.attach(0, MDS_KEYS::CCW, &cb_ccw_cw); // only in mode 0.
mdswitch.attach(0, MDS_KEYS::CW, &cb_ccw_cw); // only in mode 0.
mdswitch.attach(0, MDS_KEYS::ANY, &cb_any); // any will be called in mode 0 + 1
mdswitch.attach(1, MDS_KEYS::ANY, &cb_any);
...
mdswitch.detach(1, MDS_KEYS::PUSH); // will detach the push event callback from mode 1 ONLY
- Note: A good practice will be to define callbacks inside the
setup()
function.- MDS_KEYS::ANY Is a special event which is triggered when any button/key event is captured - AFTER the dedicated callback.
All callbacks function should be declared as void cb(const int mode, const MDS_KEYS key)
.
When a callback is invoked its called with the current mode and the key/button which invoked it. This way you can use the same function
for several event callbacks.
//Example of simple callbacks declaration:
void cb_ccw_cw(const int mode, const MDS_KEYS key) {
if (key == MDS_KEYS::CCW)
Serial.println("Invoked CCW callback");
if (key == MDS_KEYS::CW)
Serial.println("Invoked CW callback");
}
- You don't need to care about ISR stuff in your callbacks (e.g volatile, IRAM_ATTR etc.) thats because the callback is not directly invoked by the interrupt handler - Its being called (if needed) from the
tick()
handler.- Why cb's are not directly invoked? ISR routines should be as fast as possible. The library ISR handler only debounces the event and sets an internal flag, indicating which event should be consumed. Doing more than that directly from an interrupt routine can cause other interrupt events to be ignored or in some systems exceptions and even reset of the system. Also, the code is simpler and easier for most of the library users.
The best practice is to call the tick()
method from the loop()
- The tick method checks wether an event was captured and consumes it. By consume I mean if a callback was attached it will be triggered otherwise the event will be ignored.
void loop() {
//This is the tick - Extremely fast, just checks if a key was recorded.
//If an event was captured and it has an attached callback it will be triggered.
mdswitch.tick();
delay(20);
}
Dynamically enable/disable callbacks:
mdswitch.enable();
mdswitch.disable();
Reading the inputs programatically on demand:
MDS_KEYS key = MDSwitch::read();
Programatically invoking a specific callback:
bool invoked1 = mdswitch.invoke(MDS_KEYS::CCW); // will invoke the CCW callback in the current mode.
bool invoked2 = mdswitch.invoke(1, MDS_KEYS::CW); // will invoke the CW callback in a specific mode.
- The ability to invoke callbacks of other events in different modes is usefull when creating advanced menu based systems.
invoke()
doesn't change the internal captured event nor the current mode of the object.ANY
callback will not be invoked automatically when using theinvoke()
method.
- By default
MDSwitch
can hold up to 5 callbacks per mode. One callback for each event. - The library is implementing a non blocking delay of
80ms
for debouncing. If you need to adjust that - Change the defined value ofSIKTEC_MDS_DEBOUNCE_DELAY
inSIKTEC_MDSwitch.h
. - Initialize the object with the ACTUAL number of modes you are planing to actually use - Each additional mode allocates additional memory to store the callbacks pointers.