Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

Using matrix keypad with Arduino

5.00/5 (2 votes)
14 Jan 2014CPOL6 min read 55.8K   676  
Another Arduino.Keypad library

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.

C++
#include <Keypad2.h>
Keypad2 kp(1); // create default keypad.  

In the setup, specify your callback function for the press & release events.

C++
void setup() {
  Serial.begin(9600); // initialize Serial
  kp.setPressCallback(kpPressCallback); // set keypad callback function
}

And the callback function implementation:

C++
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.

C++
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:

C++
// initialize hardware layout, both keypad & arduino
// if not called, default layout should be used in constructor
// parameters:
// 1- row count
// 2- column count
// 3- keypad layout
// 4- key states (2 bits for each key)
// 5- row pins
// 6- column pins
// 7- internal pullup usage (0 for external pullups)
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):

C++
// default layout
const char kp2DefaultLayout[4][3] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
// 2 bits for each key to store its state
// 12*2/8 = 3 bytes for default layout
const uint8_t kp2DefaultStateHolder[3] = {0, 0, 0};
const int kp2RowPins[4] = {2,3,4,5}; // connect to keypad pins 2,7,6,4 respectively
const int kp2ColPins[3] = {6,7,8};   // connect to keypad pins 3,1,5 respectively
// end of default layout  

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:

C++
// hex keypad layout
const char layout[4][4] = {
  {'1','2','3','4'},
  {'5','6','7','8'},
  {'9','0','A','B'},
  {'C','D','E','F'}
};
// 2 bits for each key to store its state
// 16*2/8 = 4 bytes for states
const uint8_t stateHolder[4] = {0, 0, 0, 0};
const int rowPins[4] = {2,3,4,5}; // connect to keypad pins 1,3,2,4 respectively
const int colPins[4] = {6,7,8,9};   // connect to keypad pins 7,5,8,6 respectively
// end of hex keypad layout  

We have prepared our hardware, made the connections, prepared layout configuration, and we are ready to create an instance:

C++
#include <Keypad2.h>
Keypad2 kp(0); // will be configured as hex keypad  

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?
 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)