The humble switch is one of the major ways that users can interact with Arduino based code. Often the input comes from some variation of the momentary-on push switch, like the tact switch on the left, connected to an input on the microcontroller.
Users of modern GUIs will be familiar with being able to express themselves through a keyboard and a mouse. So user interface elements like double-clicks, long clicks and keyboard auto-repeat are familiar.
However, a lot of microcontroller code simply restricts the use of these switches to on/off functionality. Arduino programmers often don’t understand how to provide more features, even though a single switch can be made to do much more for a user.
Some time ago I wrote a utility library (MD_KeySwitch) that has become a mainstay in my own coding. This is a small library that reliably provides all the functionality I need to implement complex user interfaces using switch inputs – simple press, double press, long press and an auto repeat function. All but the simple press can be disabled and the library provides configurable timing for most of the functions.
The basis for the library is understanding the heirarchy of how the keypress can be processed – a key concept is that an event has to end before it can be reported. The initial press of a key is the start of the process and all timing values – from there we should be able to detect the following press sequences:
- release after a short time for a simple press
- release then press and release for a double press. In this case we really should not be detecting the initial press until we are sure that we don’t have a double!
- release after a long time for a long press
- once the switch has been held active for a time, auto repeat simple presses
I won’t be discussing the need for switch debouncing in this article. There are a number of references that cover this subject well. The best one I have is found here as web pages and here as a pdf. In this library debouncing is done using a timer long enough to allow the input to settle.
How do we detect all the different states and make sure the right type of switch press is detected? Using a Finite State Machine, of course! The FSM for the library, without the complications created by optional functionality, is shown and explained below.
The FSM starts in the IDLE state, where it watches the configured input for the switch to be in active state (LOW if configured with pull-up resistors, HIGH for pull-down). The next immediate state is DEBOUNCE1 to wait through a timeout period of a few milliseconds for switch debouncing. If the switch is no longer active after this time then the FSM just retuns to IDLE state and nothing is detected.
However if the switch is active, then we could be detecting any of the possible events so the FSM moves to the PRESS state and starts a timing process. One of a two outcomes could occur:
- The switch could be released. This means that we definitely have a simple press but we could also have a double press coming, so move on to DPRESS state.
- The switch is not released for longer then the long press threshold time. In this case move the FSM to the LPRESS state – we can either have a long press of we could be heading towards auto repeat.
The DPRESS state waits for a new press within a double press timeout period. If it detects a new switch active state, it moves to DEBOUNCE2 (similar to the IDLE to DEBOUNCE1 transition). If the switch remains active at the end of the debounce period, then we have detected a double press. At this point we need to allow the user let go of the switch, which is what the WAIT state does. Once the switch is inactive WAIT move back to IDLE, assuring that the IDLE state will start with an inactive switch.
If the switch was not active at the end of DEBOUNCE2, then all we can decide is that we detected a single press and the FSM returns to IDLE.
If in step 2 above we went to the LPRESS state, then we could have one of 2 outcomes:
- The switch becomes inactive before the auto repeat threshold time is reached. In this scenario we detect a long press and reset back to IDLE.
- The switch is still active when the auto repeat threshold has expired. In this case we enter REPEAT state.
REPEAT state is simply a timer that waits for the repeat period to expire and then returns a simple press. This continues at the configured repeat rate (time period) until the switch becomes inactive.
Clearly, adding configurable items complicates the path of the FSM. and this is reflected in the code. However, the way of thinking about the problem is the same.
Once this type of utility library code is code is written, it can be reused again and again, simplifying this aspect of any future project.