Unit D - Sensors & Buses

24. 4x4 Matrix Keypad

Read a membrane keypad with the Keypad library and print each press on Serial.

Estimated time 2-3 hours

Learning outcomes

  • Explain how a matrix keypad shares row and column wires
  • Wire an 8-pin 4x4 keypad to Uno digital pins without using D0/D1
  • Install and use the Keypad library (Mark Stanley / Alexander Brevig)
  • Match the keys[] map to the printed labels on your pad

Parts and preparation

Arduino Uno (on-board LED on pin 13), USB data cable, 4x4 membrane keypad (8-pin ribbon), jumper wires, and Keypad by Mark Stanley / Alexander Brevig (Library Manager).

Before power: inspect wiring, confirm supply voltage and ensure all connected circuits share GND.

Libraries for this lesson

In Arduino IDE 2 open Tools → Manage Libraries…. Search by the Library Manager name and install the package by the exact author below. Similar names from other authors can use different APIs and will break the example.

IncludeLibrary Manager nameAuthorInstall note
Keypad.hKeypadMark Stanley, Alexander BrevigLibrary Manager: search Keypad and install Keypad by Mark Stanley, Alexander Brevig.
4x4 Matrix Keypad instructional connection diagram
Open the diagram for a larger view. Trace every connection before building.

Matrix scanning

A 4x4 pad has 16 switches but only eight wires: four rows and four columns. Each key sits at one row-column crossing. The Keypad library drives rows and reads columns (with internal pull-ups) so you get one character when a key closes that intersection.

Pin order and keymap

Count keypad pins from left to right on the ribbon as seen from the button side in the course diagram: pins 1-4 are rows, pins 5-8 are columns. This lesson uses rowPins D9-D6 and colPins D5-D2. The keys[] array must match the labels printed on your pad. If a press prints the wrong character, swap the map or check that row/column wires are not reversed.

Arduino Uno wired to a 4x4 membrane keypad on digital pins 2 through 9
Course wiring: keypad left-to-right pins 1-4 to D9, D8, D7, D6 (rows); pins 5-8 to D5, D4, D3, D2 (columns).
Keypad pin (L→R)RoleUno pin
1Row 0D9
2Row 1D8
3Row 2D7
4Row 3D6
5Col 0D5
6Col 1D4
7Col 2D3
8Col 3D2

getKey() behaviour

getKey() is non-blocking: it returns the pressed character once per press, or 0 / NO_KEY when idle. Use if (pressedKey) so you only print when a new key is detected. Leave D0 and D1 free while using the Serial Monitor.

Wiring and safe build sequence

Breadboard wiring for lesson 24: 4x4 Matrix Keypad
Breadboard layout for this lesson. Match colours and pins before powering the circuit. Click the image for a larger view.
  1. Keypad pin 1 (leftmost) -> D9
  2. Keypad pin 2 -> D8
  3. Keypad pin 3 -> D7
  4. Keypad pin 4 -> D6
  5. Keypad pin 5 -> D5
  6. Keypad pin 6 -> D4
  7. Keypad pin 7 -> D3
  8. Keypad pin 8 (rightmost) -> D2
  9. No separate VCC/GND on a typical membrane pad - only the eight matrix lines
Power rule: switch off before moving wires. Arduino I/O pins are control signals; high-current loads require a driver and suitable external supply.
#include <Keypad.h>

const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

Keypad myKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void setup() {
  Serial.begin(9600);
  Serial.println("Keypad ready. Press any key...");
}

void loop() {
  char pressedKey = myKeypad.getKey();

  if (pressedKey) {
    Serial.print("Key pressed: ");
    Serial.println(pressedKey);
  }
}

How the code works

  1. Install Keypad by Mark Stanley / Alexander Brevig before Verify.
  2. makeKeymap(keys) links your label grid to the scanned matrix.
  3. getKey() returns 0 when no new press is available; if (pressedKey) is enough for beginners.
  4. Match Serial Monitor baud to 9600.

Example 2: PIN lock on LED 13

Download .ino sketch

Same keypad wiring. Uses the built-in LED on pin 13 (LED_BUILTIN). Type up to four digits (Serial shows * for each), then press # to check. Correct code 1234# turns the LED on for 3 seconds. Any other entry ending with # flashes the LED three times at 100 ms on / 100 ms off.

#include <Keypad.h>

// ---- Keypad setup ----
const byte ROWS = 4;  // 4 rows on the keypad
const byte COLS = 4;  // 4 columns on the keypad

// What each button on the keypad represents
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

// Which Arduino pins are connected to the keypad's rows and columns
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

Keypad myKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// ---- Code / password setup ----
const String secretCode = "1234";  // the correct code
String inputCode = "";             // what the user has typed so far

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  Serial.begin(9600);
  Serial.println("Enter code, then press #");
}

void loop() {
  char key = myKeypad.getKey();

  // If no button was pressed, do nothing this round
  if (key == NO_KEY) {
    return;
  }

  // '#' means "check the code now"
  if (key == '#') {
    Serial.println();  // move to a new line in Serial Monitor

    if (inputCode == secretCode) {
      Serial.println("Access Granted!");
      digitalWrite(LED_BUILTIN, HIGH);
      delay(3000);
      digitalWrite(LED_BUILTIN, LOW);
    } else {
      Serial.println("Access Denied!");
      flashErrorLED();
    }

    inputCode = "";  // reset for next attempt
  }
  // Only accept digits 0-9, and only if there's room left
  else if (key >= '0' && key <= '9' && inputCode.length() < 4) {
    inputCode += key;   // add the digit to the code
    Serial.print("*");  // print * to hide the code (use key instead to show it)
  }
}

// Blinks the LED quickly 3 times to show a wrong code
void flashErrorLED() {
  for (byte i = 0; i < 3; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  }
}

How the code works

  1. Digits build inputCode; # is Enter and compares it to secretCode.
  2. Serial prints * for each digit so the PIN is not shown in the Monitor.
  3. Correct (1234#): LED on for 3000 ms. Wrong: flashErrorLED() does three 100 ms flashes.
  4. A B C D * are ignored. Change Serial.print("*") to Serial.print(key) if you want to see the digits while testing.

Test and record evidence

Expected result: First sketch: each press prints once on Serial. PIN lock sketch: digits show as *; 1234# lights the on-board LED for 3 seconds; any other code then # triple-flashes.

Practical evidence checklist

Common faults and checks
  • Install failed or Keypad.h missing: use Library Manager and restart the IDE.
  • Wrong character for a key: check the keys[] layout against the printed pad, and confirm row/column pin order.
  • No response: reseat the ribbon, confirm D2-D9 wiring, and avoid using D0/D1 for the pad.
  • Ghost or stuck keys: press one key at a time; check for bent ribbon pins or a short on the breadboard adapters.
  • PIN lock: type digits then # to enter. Without # nothing is checked. Letters and * are ignored.
Extension challenge: Clear the entry when * is pressed, before #.

Check your understanding

Q1. Why does a 4x4 pad need only eight wires?

Show answer

Keys share four row and four column lines in a matrix.

Q2. What does getKey() return when no key was newly pressed?

Show answer

0 / NO_KEY (treated as false in if (pressedKey)).

Q3. Why avoid D0 and D1 for the keypad in this lesson?

Show answer

Those pins are used by USB Serial on the Uno.