Chapter 6
IN THIS CHAPTER
Considering keypads
Discovering how keypads work
Using the Keypad library for easier keypad programming
Teaching an Arduino how to pretend to be a computer keyboard
This chapter shows you how to use a common input device called a keypad. It also shows you how to use a unique feature of some Arduino boards, including the popular Leonardo, to send information to a computer as if the Arduino were a keyboard.
A keypad is a common form of input for devices built with microcontrollers such as the Arduino. You’re probably most familiar with keypads from devices such as calculators and phones — they consist of an array of push buttons arranged in rows and columns. Figure 6-1 shows a typical keypad, with four rows of four push buttons each.
You can purchase keypads such as this one from many different suppliers, including Amazon, Jameco Electronics (www.jameco.com
), and others. Just search the web for “Arduino keypad,” and you’ll find a wide variety to choose from. Simple keypads such as the one shown in Figure 6-1 cost just a few dollars apiece. You can get nicer ones for a bit more.
If each push button in the keypad used two leads, a 4 x 4 keypad would have 32 leads. Even if the keypad used a common ground lead for all 16 buttons, a total of 17 leads would be required, taking up 16 digital I/O ports on the Arduino. Unfortunately, a 4 x 4 keypad would use nearly all of an UNO’s available I/O ports if it is wired in this way.
Fortunately, some clever folks have figured out a more efficient way to wire a keypad. The key (pun intended) is to realize that the buttons in a keypad are arranged in an array, with rows and columns. They can be wired with a single lead for each row and a single lead for each column. So, a 4 x 4 keypad can be wired with just eight leads, and a 3 x 3 keypad requires just six leads.
Figure 6-2 shows how the wiring works for a 4 x 4 keypad. As you can see, one lead connects all the buttons in each separate row, and one lead connects all the buttons in each separate column. When any one of the push buttons is pressed, a circuit is completed between the lead that’s connected to the button’s row and the lead that’s connected to the button’s column.
For example, if the button labeled 6 is pressed, a circuit is completed between the lead on row 2 (R2) and the lead on column 2 (C3).
Connecting a keypad to an Arduino is a simple task, but it can require a lot of digital I/O pins depending on the number of rows and columns in the keypad. A 4 x 4 keypad requires a total of eight digital I/O pins: four for the row pins and four for the column pins.
Although it’s not required, consecutive pins are commonly used for the keypad, with the lower-numbered pins used for the columns and the higher-numbered pins used for the rows. Avoid pins 0 and 1, because those pins are often used for serial communications.
The leads on a keypad typically have female connectors, which means you need male-to-male adapters to connect them to the female terminal strips on the Arduino or to a solderless breadboard. For a 4 x 4 keypad, you need an eight-pin adapter. For a 3 x 4 keypad, a six-pin adapter will do the trick.
After you get a keypad connected to an Arduino, you’ll need to add code to your sketch to detect button presses. The programming required for that task can be a bit complicated, but fortunately there are libraries available that simplify the programming.
Before we look at a Keypad library, it helps to have a general understanding of how a Keypad library is able to determine when a key is pressed. Here’s how it works in a nutshell:
Set up all the digital I/O pins for the rows as OUTPUT pins and all the I/O pins for the columns as INPUT_PULLUP pins.
Active-Low switch logic will be used, and the Arduino’s internal pullup resistors eliminate the need for external resistors.
Set the output value of the I/O pin for row 1 to LOW; then check each of the input pins for a LOW value.
If you find a LOW value, you’ve found the row and column for the button that’s pressed.
Here’s how this may work in actual Arduino code. Steps 1 and 2 are done in the setup
function:
void setup() {
for (int row = 2; row<6; row++) // Set the row pins to OUTPUT
{
pinMode(row, OUTPUT);
digitalWrite(row, LOW);
}
for (int col = 6; col<10; col++) // Set the column pins to INPUT
{
pinMode(col, INPUT_PULLUP); // Use INPUT_PULLUP so external
} // resistors are not required
}
Steps 3 through 5 are done in the loop
function:
void loop() {
// Look for a completed circuit
String ButtonPushed = "NONE";
for (int row = 2; row<6; row++)
{
digitalWrite(row, LOW); // Set the row to LOW
for (int col = 6, col<10) // Test each column
{
If (digitalRead(col) == LOW) // Button is pressed!
{
ButtonPushed = 'R’ + row + 'C' + col; // Save the row and column
delay(50); // Debounce the button
}
}
digitalWrite(row, HIGH); // Set the row to HIGH
}
After one pass through this loop
function, the ButtonPushed
String variable indicates the status of the keypad. If no button has been pressed, ButtonPushed
will be NONE
. But if a button has been pressed, ButtonPushed
will indicate the button's row and column. For example, a value of R2C3 indicates that the button at row 2 and column 3 has been pressed.
Fortunately, you don’t have to write all that code every time you want to use a keypad. Instead, you can use one of several libraries that handle the details for you. The one I present in this chapter is known simply as Keypad
. You can install it by following these steps:
Choose Tools⇒ Manage Libraries.
The Library Manager dialog box, shown in Figure 6-3, appears.
Type Keypad in the search box at the top of the Library Manager and press Enter.
The Library Manager searches its vast store of libraries and displays every library that includes the word keypad.
Select the Keypad library.
Scroll through the list of libraries until you find the one identified simply as Keypad.
Select the current version.
Use the Select Version drop-down list to choose the version you want to install. At the time of this writing, the current version was 3.1.0.
Click Install.
The library is installed.
You’re done!
After you’ve installed the Keypad library, you can use it to access matrix keypads. To do so, you’ll need to include the following line near the top of your sketch:
#include <keypad.h>
After you’ve installed and included the Keypad library, you can write the code necessary to define the characteristics of the keypad. You’ll need to define five things:
After you’ve set up these five things, you can create a variable of type Keypad
, which you can then use to read key presses from the keypad.
To define the number or rows and columns in the keypad, just create a pair of int
variables that represent the number of rows and the number of columns in the keypad, like this:
int rows = 4;
int cols = 4;
To identify the pins used to connect the keypad’s rows and columns, you must use an Arduino programming feature called an array. An array is a variable that can hold more than one value. The array of row pins will hold one value for each row on your keypad, and the array of column pins will hold one value for each column. The data type of these arrays should be byte
.
The individual values in an array are identified using an integer value known as an index. The index value is enclosed in square brackets after the variable name, like this: rowPins[0]
. Index values always begin with zero, so rowPins[0]
refers to the first value in the array named rowPins
.
To create an array, you must declare it using a data type, a variable name, and the number of values in the array. For example:
byte rowPins[4];
byte colPins[4];
In the preceding example, two arrays are defined — one to hold the row pins, and the other to hold the column pins. Both arrays will hold four byte
values.
You can assign all the values as part of an array's declaration like this:
byte colPins[4] = {6, 7, 8, 9};
byte rowPins[4] = {2, 3, 4, 5};
In the preceding example, the colPins
values are 6, 7, 8, and 9, and the rowPins
values are 2, 3, 4, and 5. These values correspond to the Arduino digital I/O pins that the keypad's row and column leads will be connected to.
The keymap uses a more complex type of array, called a two-dimensional array, also known as a matrix. This type of array requires two indexes to access a value: The first index is a row index, and the second index is a column index.
You use a variation of the special syntax used to initialize the values of a simple array with a two-dimensional array as well, like this:
char keymap[rows][cols] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
As you can see, the matrix of key values closely resembles the physical arrangement of keys on the 4 x 4 keypad.
Keypad
variableThe Keypad
variable is what allows your program to detect key presses on a keypad. You'll need to define and initialize this variable before you can read the status of the keypad. Here’s an example that defines and initializes a Keypad
variable named kp
:
Keypad kp = Keypad(makeKeymap(keymap), rowPins, colPins, rows, cols);
The Keypad
variable is initialized using the keymap
, rowPins
, colPins
, rows
, and cols
variables you've created, as described in the previous sections.
After you’ve created a Keypad
variable (see the preceding section), you can use that variable to read the status of the keypad by calling the Keypad
variable's waitForKey
function. This function waits until one of the keys on the keypad is pressed. It then returns the char
value that corresponds to the key that was pressed, according to the Keymap
array.
You'll want to call the waitForKey
function somewhere in the loop
function, like this:
void loop() {
char key = kp.waitForKey();
// Do something based on which key was pressed.
}
Here's a simple example that just echoes the key value on the serial port, which you can view by opening the Serial Monitor in the Arduino integrated development environment (IDE):
void loop() {
char key = kp.waitForKey();
Serial.println(key);
}
Note that for this to work, you’ll need to initialize the serial port and provide a baud rate. This is usually done in the setup
function with a line like this:
Serial.begin(9600);
If you open the serial monitor in the IDE (choose Tools⇒ Serial Monitor), you’ll see the character that corresponds to each button pressed on the keypad.
In this section, you build a simple project that connects a 4 x 4 keypad to an Arduino UNO. Key presses are echoed on the serial port, which you can monitor from the Arduino IDE via the Serial Port Monitor.
The details for building the project are presented in Project 51, and the complete program to run the project is shown in Listing 6-1.
In this project, you connect a 4 x 4 keypad to an Arduino UNO and learn how to develop sketches that use it.
From | To |
---|---|
Digital pin D2 on the right header | J1 |
Digital pin D3 on the right header | J2 |
Digital pin D4 on the right header | J3 |
Digital pin D5 on the right header | J4 |
Digital pin D6 on the right header | J5 |
Digital pin D7 on the right header | J6 |
Digital pin D8 on the right header | J7 |
Digital pin D9 on the right header | J8 |
Insert the eight-pin header on the prototype shield.
The header should be inserted into pins H1 through H8.
Connect the keypad to the eight-pin header.
The pin for column 1 should be on the H1 side of the header.
Connect the UNO to the computer.
Use the mini-B USB connector.
Press each button on the keypad.
As you press each button, the serial monitor will show the button that was pressed (refer to Figure 6-4).
LISTING 6-1: Testing a Keypad
// Keypad Tester
// Doug Lowe
// December 5, 2021
#include <Keypad.h>
const byte rows = 4;
const byte cols = 4;
char keymap[rows][cols] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte colPins[rows] = {6, 7, 8, 9};
byte rowPins[cols] = {2, 3, 4, 5};
Keypad keypad = Keypad(makeKeymap(keymap), rowPins, colPins, rows, cols);
void setup() {
Serial.begin(9600);
}
void loop() {
char x = keypad.waitForKey();
Serial.println(x);
}
All Arduino boards have a USB port that you use to upload sketches from a Windows computer to the Arduino. Wouldn’t it be great if you could write sketches that would reverse the flow of this USB port, sending data back to the Windows computer?
You can’t with an UNO, but some Arduino boards do provide this capability. The most popular is the Arduino Leonardo, shown in Figure 6-5.
In many respects, the Leonardo is very similar to the UNO. In particular, the I/O pin headers are the same, which means that most programs and circuits that will work on an UNO will also work on a Leonardo. All the projects presented so far in this minibook are interchangeable between UNO and Leonardo. In other words, any of the prototype boards built for the projects can be attached to a Leonardo instead of an UNO, and any of the sketches can be run on a Leonardo instead of an UNO.
One major difference, however, is that a Leonardo board contains the same USB circuitry that is found in most computer keyboards. As a result, the Leonardo can emulate a standard computer keyboard. That means you can write sketches that send keystrokes to a Windows computer. The program that happens to be running on the computer will receive those keystrokes and act as if you had typed them on a normal keyboard.
This type of Arduino project can be incredibly useful. For example, you could adapt Project 47 (see Book 6, Chapter 4) to send range information to a computer. If Excel is open on the computer, the range data will fill up the rows of the spreadsheet.
In this section, I show you how to create a Leonardo project that uses a 4 x 4 keypad to send Windows shortcut keys to the computer so you can use the keypad to lock or unlock the computer, minimize windows to reveal the desktop, or start programs from the taskbar.
One other difference between a Leonardo and an UNO is the type of USB port that’s installed on the board. The UNO uses an old-fashioned USB-B connector, the same type that’s commonly used on printers. The Leonardo, in contrast, uses a more modern and smaller USB-C connector. You’ll need to pick up a USB-C cable to connect a Leonardo to a computer, both to upload sketches to the Leonardo and to enable the Leonardo to send keyboard input to the computer.
The secret to emulating a computer keyboard on a Leonardo lies in the Keyboard library. This library is a built-in part of the Arduino IDE, so you don’t have to use the Library Manager to install it. However, you do have to tell the Arduino IDE that you’re working with a Leonardo board rather than an UNO board. If you forget to do this, the compiler won’t recognize any of the Keyboard calls.
The Keyboard library includes the following functions to help your sketches send keyboard input to a computer attached via the USB port:
Keyboard.begin
: Starts a keyboard emulation session. You'll typically call this function from the sketch’s setup
function.Keyboard.end
: Ends a keyboard session. You may not need this in your sketch, but if you want the Leonardo to act like a keyboard at some times but not others, you can use Keyboard.end
to turn off the keyboard.Keyboard.print
: Sends a complete string, as if you had typed the string on the computer.Keyboard.println
: Works just like Keyboard.print
, but also sends the Enter key at the end of the string.Keyboard.write
: Sends a single keystroke.Keyboard.press
: Presses and holds a key. The key is not released until you call the Keyboard.release
or Keyboard.releaseAll
function.Keyboard.release
: Releases a specific key.Keyboard.releaseAll
: Releases all keys that are currently pressed.With that warning duly noted, let’s look at some code. To use the Keyboard library, you must include it. Use this command near the start of your sketch:
#include <Keyboard.h>
Then use a command such as this in the setup
function to start a Keyboard session:
void setup() {
Keyboard.begin();
}
Here’s a snippet of code from a loop
function that waits for a key to be pressed on a keypad. Then the program types one of four Shakespearean insults based on which key was pressed on the keypad:
char x = keypad.waitForKey();
if (x=='A')
{
Keyboard.println("Thine face is not worth sunburning.");
}
if (x=='B')
{
Keyboard.println("Would thou were clean enough to spit upon.");
}
if (x=='C')
{
Keyboard.println("I'd beat thee, but I would infect my hands.");
}
if (x=='D')
{
Keyboard.println("The tartness of his face sours ripe grapes.");
}
switch
statement to simplify choicesInput via a keypad cries out for a simpler way to determine which key was pressed than a bunch of if
statements, one after the other. Fortunately, the Arduino language provides such a simplification via a statement called switch
. The switch
statement tests the value of a single variable and provides several different outcomes based on that value.
The snippet of code in the preceding section, which generated Shakespearean insults based on a key press, can be written with a switch
statement like this:
char x = keypad.waitForKey();
switch (x)
{
case 'A':
Keyboard.println("Thine face is not worth sunburning.");
break;
case 'B':
Keyboard.println("Would thou were clean enough to spit upon.");
break;
case 'C':
Keyboard.println("I'd beat thee, but I would infect my hands.");
break;
case 'D':
Keyboard.println("The tartness of his face sours ripe grapes.");
break;
}
Here are the important rules to follow when composing switch
statements:
switch
names a variable in parentheses and is followed by one or more case
statements, all of which are enclosed within a set of curly brackets.case
statement provides a value that is matched with the variable. The value is not enclosed in parentheses. The value is followed by a colon.case
statement. You can also use other structural statements, such as if
statements or for
loops.break
statement to mark the end of a group of statements that should be executed for each case
value. When the break
statement is executed, the entire switch
statement finished and no other case
statements are processed.case
statement, the next case
statement is evaluated. If that doesn't match, the case
statement after that is evaluated, until all case
statements have been checked.One of the important features of a standard keyboard is to use keys in combination. For example, pressing Ctrl+Alt+Del brings up a menu that lets you select actions such as locking the computer, signing out, switching users, or calling up the Windows Task Manager.
The Keyboard.press
function lets you use keys in combination, by pressing and holding one or more keys and then using Keyboard.print
or Keyboard.write
to send keystrokes along with the modifier keys.
To facilitate this, the Arduino language defines a set of constants that represent the modifier keys. These constants are listed here. Note that some keys have two versions — one on the left side of the keyboard and the other on the right.
KEY_LEFT_CTRL
KEY_LEFT_SHIFT
KEY_LEFT_ALT
KEY_LEFT_GUI
KEY_RIGHT_CTRL
KEY_RIGHT_SHIFT
KEY_RIGHT_ALT
KEY_RIGHT_GUI
KEY_UP_ARROW
KEY_DOWN_ARROW
KEY_LEFT_ARROW
KEY_RIGHT_ARROW
KEY_BACKSPACE
KEY_TAB
KEY_RETURN
KEY_ESC
KEY_INSERT
KEY_DELETE
KEY_PAGE_UP
KEY_PAGE_DOWN
KEY_HOME
KEY_END
KEY_CAPS_LOCK
KEY_F1
KEY_F2
KEY_F3
KEY_F4
KEY_F5
KEY_F6
KEY_F7
KEY_F8
KEY_F9
KEY_F10
KEY_F11
KEY_F12
Here's an example of a code snippet that types the Windows shortcut sequence Windows+L, which locks the computer:
Keyboard.press(KEY_LEFT_GUI);
Keyboard.print("l");
Keyboard.releaseAll();
In this example, the Keyboard.press
function is called to press and hold the left graphical user interface (GUI) key, which is the Windows key. Then the letter l is sent. Finally, all keys are released. The result is that the computer is immediately locked.
I want to wrap up this chapter with a project that uses a 4 x 4 keypad to provide handy shortcuts to several commonly used Windows shortcut key combinations. This project actually uses the same prototype breadboard built for Project 51. The only difference is that the breadboard is placed on a Leonardo rather than an UNO, and a different program is used.
The following functions are provided by this gadget:
Keypad Press | Windows Function |
---|---|
A | Locks the computer. |
B | Unlocks the computer by entering a PIN or password. |
C | Minimizes all windows. |
D | Restores all windows. |
1–9 | Opens one of the programs pinned to the taskbar. (The programs are opened in the order in which they appear on the taskbar.) |
In this project, you connect a 4 x 4 keypad to an Arduino Leonardo and learn how to use it to send keystrokes to a Windows computer.
Connect the Leonardo to the computer.
Use the USB-C USB connector.
Upload the Keypad Gadget program (Listing 6-2) to the Leonardo.
Note: You’ll need to change the PIN or password supplied in the program to your computer’s PIN or password if you want the Unlock function to work.
Press each button on the keypad.
As you press each button, the Windows shortcut keys will be invoked.
LISTING 6-2: The Windows Keypad Gadget Sketch
// Windows Keypad Gadget
// Doug Lowe
// December 10, 2021
#include <Keyboard.h>
#include <Keypad.h>
// Keypad setup
const byte rows = 4;
const byte cols = 4;
byte colPins[rows] = {6, 7, 8, 9};
byte rowPins[cols] = {2, 3, 4, 5};
char keymap[rows][cols] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
Keypad kp = Keypad( makeKeymap(keymap), rowPins, colPins, rows, cols);
void setup() {
Keyboard.begin();
}
void loop() {
char x = kp.waitForKey();
int number = 0;
switch(x) {
case 'A': // Lock computer
Keyboard.press(KEY_LEFT_GUI);
Keyboard.print("l");
Keyboard.releaseAll();
break;
case 'B': // Unlock computer
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_DELETE);
Keyboard.releaseAll();
delay(100);
Keyboard.println("******"); // Use your PIN here!
break;
case 'C': // Minimize all windows
Keyboard.press(KEY_LEFT_GUI);
Keyboard.print("m");
Keyboard.releaseAll();
break;
case 'D': // Restore all windows
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.print("m");
Keyboard.releaseAll();
break;
case '1':
OpenProgram(1);
break;
case '2':
OpenProgram(2);
break;
case '3':
OpenProgram(3);
break;
case '4':
OpenProgram(4);
break;
case '5':
OpenProgram(5);
break;
case '6':
OpenProgram(6);
break;
case '7':
OpenProgram(7);
break;
case '8':
OpenProgram(8);
break;
case '9':
OpenProgram(9);
break;
}
delay(200);
}
void OpenProgram(int num) {
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.print(num);
Keyboard.releaseAll();
}