Introducing KeyPlexLib (or, 'Wayneplexing' on Arduino Made Easy)
In a previous article, WaynePlexing Push Buttons (or, read 6 buttons using only 3 I/O pins), I introduced a technique for connecting more input buttons to an Arduino than you'd normally think possible. I shamelessly named this technique "Wayneplexing" after the "Charlieplexing" technique that's sometimes used with LEDs . And, in a later article, Key Input Library for Arduino, I described my KeyLib2 Arduino library that helps you easily connect pushbuttons by handling all the steps needed to scan, debounce and read inputs from each pushbutton key.
This article extends KeyLib2 to create a new Arduino library KeyPLexLib (download link at bottom of this page) that works as before, but also adds support for Wayneplexing of pushbuttons. In addition, KeyPLexLib adds the ability to define interrupt-like callbacks which report when individual pushbuttons are both pressed and released. The API for KeyPLexLib is the same as for KeyLib2, with the addition of the following new functions, scanPlexKeys(), getPinCount() and setCallback(). The library includes two example programs, DirectDemo and CallbackDemo, that demonstrate the available features. The demo programs are designed to work with the following circuit:
Figure 1 - Click for larger view
Here's an Arduino sketch that shows how simple it is to use KeyPlexLib to scan 6 pushbuttons using only 3 I/O pins:
#include <KeyPlexLib.h>
KeyPlexLib buttons(3, 2, 3, 4);
void setup () {
buttons.scanPlexKeys(true);
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB
}
Serial.println("Ready");
}
void loop () {
if (buttons.keyAvailable()) {
byte idx = buttons.getKeyIndex();
Serial.print("Pressed key ");
Serial.println(idx, DEC);
}
}
Wiring Wayneplexed Keys
One way to visualize how to connect all the pushbuttons when using Wayneplexing is to draw a regular polygon which has the same number of vertices as the number of I/O pins. Then, for polygons with more than 3 sides, draw in additional lines to connect all vertices to each other. These lines represent where Wayneplexed pushbuttons can go. For example, if you draw a square and then add the two diagonal lines needed to connect one corner to another, you'll have 6 lines that show where Wayneplexed pushbuttons can connect. Then, you add one pushbutton from each of the 4 vertices (I/O pins) to ground to get a total of 10 pushbuttons that can be scanned using only 4 I/O pins. The following diagram shows the connections for 4 I/O pins in schematic form where each pushbutton is labelled with the index value that will be returned by getKeyIndex() when that pushbutton is pressed.
Figure 2 - Click image for a larger view
For more, or less pushbuttons you can use the following formula to calculate the number of pushbuttons you can connect to 'n' I/O pins using Wayneplexing:
pushbuttons = (n * (n - 1) / 2 + n
or use the following table to lookup values for typical uses:
Key Index Numbering
The way the index numbers returned from a call getKeyIndex() may seem a bit odd, but you can use a simple diagram to figure it out. For example, for the 4 pin, 10 pushbutton configuration shown in Figure 2, you would start by making a chart like the one shown below:
Index values for 4 I/O pins (2-5) and 10 Pushbuttons
Notice how the left column and the bottom row are labelled with left column being labelled with the I/O pin numbers listed from the bottom up in the same order they were passed to the KeyPlexLib constructor. The bottom row starts with a column for the ground-connected pins followed by the I/O pins which are also listed in the same order as they were passed to the constructor. However the last pin assigned is omitted from the row. Then, some of the remaining cells are labelled in the pattern shown. Using this diagram you can figure out the index code for any switch combination by selecting as an number and then tracing left and down to see the I/O pins connect to the switch that will return this number when pressed. For example, index 7 is return when the pushbutton connecting pins 3 & 4 is pressed. Here some additional examples:
Index values for 5 I/O pins (2-6) and 15 Pushbuttons
Index values for 6 I/O pins (2-7) and 21 Pushbuttons
Index values for 7 I/O pins (2-8) and 28 Pushbuttons
The example sketch DirectDemo, which is included in the KeyPlexLib.zip file you can download below, includes code that enumerates the same assignments you can get from the charts like the ones shown above, but using the actual pin values assigned in the constructor. This is an alternate method you can use to determine the index to pushbutton assignments. However, if you use any of the Arduino analog pins A0 - A5, be aware that the example sketch will print back these pins as values 14 - 19, respectively.
Wayneplexing is Different from Conventional Keyboard Multiplexing
Many embedded systems use matrix-based multiplexing to connect an array of pushbuttons to I/O pins. The following schematic shows a typical way this is done using a 3x4, numeric keypad as an example:
Figure 3 - Click image for larger view
While logical and orderly, this approach wastes the full potential of the I/O pins. With Wayneplexing, the same 7 I/O pins could be used to scan 28 pushbuttons instead of only 12... While this would not be practical because Wayneplexing does not support rollover, 28 keys could, in theory, let you implement a QUERTY-style keyboard, complete with a spacebar and an enter key.
Note: it's certainly possible that this technique has been invented before and documented somewhere but, other than one web article with broken links to circuit diagrams, I haven't yet found any information on it. I think it's a valuable technique, so I've taken the time to code and write KeyPlexLib for use on Arduino,as well try to document all the details on this web page in the hopes that this enable others to benefit. If it offends you, you don't have to call it "Wayneplexing," though. This is one of the reasons I chose to name the library KeyPlexLib and not WaynePlexLib and to not encumber the API with similar names (although I refer to it as Waynepexing in the library's source code comments.) If you're aware of any prior art on this topic, please let me know at wayne dot holder at googly-eyed-company's-well-known-email-service dot com.
Practical Considerations and Limitations
Like KeyLib2, KeyPlexLib supports chording (pressing and holding multiple pushbuttons at the same time) but only with pushbuttons that connect from an I/O pin to ground and only as long as Wayneplexing has not been enabled by calling scanPlexKeys(true). If you enable Wayneplexing, you'll only be able to press one pushbutton at a time. This is not a big problem for many applications, but there are cases where inadvertently pressing a pushbutton before the previous one has been release can result in missing the second pushbutton press.
If you have a lot of I/O pins available, Wayneplexing may not offer much to your design. However, it can be a real lifesaver when working with micro controllers like the ATtiny85 that can have fewer than 6 I/O pins available. For example, here's a small PCB I built as a test device that uses an ATtiny10 (which has only 4 I/O pins) to implement a 6 button keyboard that outputs a 9600 baud, numeric ASCII serial code for each key press.
Figure 4 - Click image for larger view
The Library API (for Arduino)
Note: for efficiency and portability, this library use the types uint8_t and bool, which are defined in the header file inttypes.h. If you're unfamiliar with these types, just pretend they are type byte, or int.
KeyPlexLib (uint8_t numKeys, uint8_t ...)
This is the constructor for the KeyPlexLib library. It should be declared as a global variable, like this:
KeyPlexLib buttons(3, 2, 3, 4);
The first parameter specifies the number of I/O pins used to connect pusbbuttons and the parameters after this specify each I/O pin, in sequence. For example, the declaration above specifies 3 pins, which are 2. 3 and 4. As shown in the diagram above, Wayneplexing allows you connect and read input from up to 6 pushbuttons using just these 3 I/O pins.
Note: you can declare multiple instances of this constructor to create different sets of pushbuttons. See example code for details.
bool keyAvailable(void)
Call this function in the loop() section of an Arduino Sketch. It returns true if a pushbutton was pressed and can be read by getKeyIndex(). Note: this function is placed in loop() so that it's guaranteed to be called on a regular basis, which is necessary for KeyPlexLib to be able to scan and process pushbutton. You also need to call this function when using the callback feature, but you can ignore the return value.
uint8_t getKeyIndex(void)
You call getKeyIndex() after keyAvailable() returns true to get the 0-based index that indicates which key was pressed. In the schematic above, pressing key S0 will return an index value of 0, pressing S1 will return an index of 1, and so on. Note: it can be tricky to determine what index value will be returned by a Wayneplexed pushbutton, especially when 4, or more pins are being used. However, the example program DirectDemo includes code in the init() section that enumerates which pushbutton is connected to which pin.
Note: Calling getKeyIndex() without having first received a value of true from a call to keyAvailable() will cause the call not to return until a key is pressed, which will probably freeze your code.
uint8_t getKeyPin(uint8_t idx)
The example program DirectDemo includes code in the init() section that uses this function to enumerate which pushbutton is connected to which pin. If passed a value of 1 - n, where n is the number of I/O pins defined in the constructor, this function will return the number of the nth I/O pin that was passed to the constructor. You should not normally need to use this function in normal code.
void scanPlexKeys(bool enable)
You call this function to enable, or disable Wayneplexing on the specified KeyPlexLib object. By default, Wayneplexing is off and any Wayneplex-connected pushbuttons will not operate until this function is called with true as the parameter passed.
Important: When Wayneplexing is enabled, KeyPlexLib switches to an alternate mode in which chording (pressing and holding multiple pushbuttons at the same time) of the ground-connected buttons is disabled and only one pushbutton in the entire set of buttons can be pressed at a time. Chording is supported when Wayneplexind disabled, but only ground-connected keys will function. Please understand the implications of this limitation in your design and plan accordingly.
uint8_t getPinCount(void)
This function returns the number of pins used by the specified KeyPlexLib object. You shouldn't normally need to call this function. It provided to support the example code that enumerates how pushbuttons are connected to the I/O pins (see the example program DirectDemo.)
void setCallback (void (*keyCallback)(uint8_t key, bool pressed))
Call this function to configure a callback from the specified KeyPlexLib object whenever a key is pressed, or released. See the example program CallbackDemo for details on how to use this function.