Introduction
If you are interested in Arduino, you have possibly heard about matrix keypads. They are widely used for entering numeric data in Arduino projects. An example for a matrix keypad (which I have one) is here and the official Arduino library for matrix keypad is here.
Background
I didn't like the library provided by Arduino, because it is unnecessarily complicated. A matrix keypad is very simple actually. As an example, assume that you have a "4 rows X 3 columns" keypad. It has 12 keys and 7 pins. These pins correspond to rows and columns.
You can find the datasheet for my keypad here. Keys and their corresponding <rowpin, columnpin> pairs for my keypad is following:
Key: RowPin + ColumnPin
1: 2 + 3
2: 2 + 1
3: 2 + 5
4: 7 + 3
5: 7 + 1
6: 7 + 5
7: 6 + 3
8: 6 + 1
9: 6 + 5
*: 4 + 3
0: 4 + 1
#: 4 + 5
When you press a key, let's say "1", its corresponding pins are shorted. That means, when you signal the row pin of the key, you read the same value from the column pin of that key, and vice versa. If you press "1" on the keypad, you will see that pins 2 & 3 are shorted.
As you notice, keys [1..3] share the same row pin which is pin2, and keys[1,4,7,*] share the same column pin which is pin3.
Your keypad may differ in terms of number of keys, pins and their mappings onto keys. Therefore, if you have one and don't have any document, you should first find out the relation between keys & pins by using a multimeter.
Hardware Setup
In this section, I will try to explain how to connect your matrix keypad to the Arduino board by taking my keypad as an example. Your keypad structure may differ from mine, but the same rules apply.
I have 7 pins on my keypad and therefore I have dedicated 7 digital pins of my Arduino board starting from 2 to 8.
First, connect row pins of the keypad to the Arduino. But pay attention to the order of pins. Row1 pin (which is pin 2) of the keypad will be connected to the digital pin 2 of the Arduino, so on and so forth. All connections for my keypad:
Keypad Pin => Arduino Digital Pin
2 (row1) => 2
7 (row2) => 3
6 (row3) => 4
4 (row4) => 5
3 (col1) => 6
1 (col2) => 7
5 (col3) => 8
Now, you are ready to test the library. Don't forget that the default configuration for the library is according to my keypad which I explained just above. If your keypad is different from mine, your hardware setup and software configuration must be adapted. How to make a new software configuration is explained below the basic usage.
Using the code
What do you expect from a matrix keypad? Simple. Which key is pressed, which key is released?
Let's start.
When you place the library in your libraries folder of your Arduino IDE, you are ready to use it.
First, include the library and create an instance.
#include <Keypad2.h>
Keypad2 kp(1);
In the setup, specify your callback function for the press & release events.
void setup() {
Serial.begin(9600); kp.setPressCallback(kpPressCallback); }
And the callback function implementation:
void kpPressCallback(char c, uint8_t state) {
Serial.print(c);
Serial.print(" ");
Serial.print(state == KP2_KEYUP ? "up":"down");
Serial.println();
}
Don't forget to monitor your keypad in the main loop.
void loop() {
kp.update();
}
Your keypad may not have the same layout (number of keys, pins, key&pins relations) with the default one, of course. In this case, you have to provide a new configuration by calling setLayout
function. Its prototype in the header file:
void setLayout(int rowc, int colc, char **layout, uint8_t *keyState, \
int *rowp, int *colp, uint8_t useInternalPullup);
And the default configuration of the library (which describes my keypad):
const char kp2DefaultLayout[4][3] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
const uint8_t kp2DefaultStateHolder[3] = {0, 0, 0};
const int kp2RowPins[4] = {2,3,4,5}; const int kp2ColPins[3] = {6,7,8};
When you called the constructor with 1 (true) as we did at the beginning of the section, what is done behind the scene is to call setLayout
function with the default parameters as the following:
if(useDefaultLayout) {
this->setLayout(4, 3, (char **)kp2DefaultLayout, (uint8_t *)kp2DefaultStateHolder, \
(int *)kp2RowPins, (int *)kp2ColPins, 1);
}
If your configuration is different from the default one, you have to call setLayout
function with proper parameters. What I would do in such a case is that
- copy default values from the header file & paste into my code :)
- edit default values according to the new keypad
- rename parameter names
Let's study on an example. Assume that our keypad is a matrix keypad with hexadecimal values as keys. 4x4 hexadecimal matrix keypad would be something like that:
1 | 2 | 3 | 4
5 | 6 | 7 | 8
9 | 0 | A | B
C | D | E | F
Therefore there must be 8 pins for 4 rows & 4 columns. Let's say the relation between pins and rows&columns:
Pin | RowCol
1 | row1
2 | row3
3 | row2
4 | row4
5 | col2
6 | col4
7 | col1
8 | col3
We have to dedicate 8 digital pins on Arduino board. Let's say pins [2..9] are dedicated. Our parameters should be:
const char layout[4][4] = {
{'1','2','3','4'},
{'5','6','7','8'},
{'9','0','A','B'},
{'C','D','E','F'}
};
const uint8_t stateHolder[4] = {0, 0, 0, 0};
const int rowPins[4] = {2,3,4,5}; const int colPins[4] = {6,7,8,9};
We have prepared our hardware, made the connections, prepared layout configuration, and we are ready to create an instance:
#include <Keypad2.h>
Keypad2 kp(0);
In the setup function, call setLayout:
this->setLayout(4, 4, (char **)layout, (uint8_t *)stateHolder, \
(int *)rowPins, (int *)colPins, 1);
That is it. You are ready to use your hex keypad. As a note, stateHolder
is for internal usage. You just give some memory to the library to trace the keys.
Points of Interest
I have seen many discussions about pullups and where to put them on the circuitry related to matrix keypads. Actually, you don't need them, just use built-in pullups!
The algorithm for the key-press detection is 'scanning rows&columns'. As I said in the introduction section, row and column pins are shorted when you press a key. By setting row pins as INPUT and column pins as OUTPUT, one can try signalling each column and read from each row. If you read the same value from the row, that means their corresponding key is pressed, else released.
In fact, I could use dynamic memory allocation for the state holder to lessen the number of parameters passed to setLayout
function. But I don't use this technique if I don't really need it. It is very error-prone and if you make a mistake, very hard to detect.
What I hate about a matrix keypad is that it is an enemy for your Arduino board pins. You need to allocate too many digital pins to use it. It would be very nice to have a serial version of it, which informs my Arduino when a key is pressed. Do you know any?