Built-in project · ESP32-DevKitC V4

MicroPython 4x4 keypad on an ESP32, on an ESP32-DevKitC V4

MicroPython on an ESP32: a 4x4 matrix keypad with rows on GPIO 14, 27, 26, 25 and columns on 13, 21, 22, 23 prints each key you press. The whole build, an ESP32-DevKitC V4 and 1 more part, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 15 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $11.50. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • How a matrix keypad scans rows and columns
  • Internal pull-up resistors
  • MicroPython Pin setup
  • Splitting code into a module and a main file
  • Detecting one press instead of a held key
  • Difficulty: Intermediate
  • Time: About 15 minutes
  • Board: ESP32-DevKitC V4
  • Parts: 2

How it works

A 4x4 keypad is 16 buttons wired as a grid of 4 rows and 4 columns, so it needs only 8 pins. Here the rows are on GPIO 14, 27, 26 and 25, and the columns on 13, 21, 22 and 23.

The code in keypad.py pulls one row low at a time. The columns are held high by pull-ups. If a key is down, its column reads low, and the row and column together name the key.

main.py checks every 10 ms and prints each key once per press, for example Key: 5. Holding a key does not repeat it.

Parts list

2 parts, plus the jumper wires. They retail for about $11.50; on Mokxi they're free.

Kits that can build it

These 2 starter kits have every part in the circuit. Each kit's page lists what else it can build.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Keypad, 4x4 pin R4ESP32-DevKitC V4 pin 25
Keypad, 4x4 pin R3ESP32-DevKitC V4 pin 26
Keypad, 4x4 pin R2ESP32-DevKitC V4 pin 27
Keypad, 4x4 pin R1ESP32-DevKitC V4 pin 14
Keypad, 4x4 pin C1ESP32-DevKitC V4 pin 13
Keypad, 4x4 pin C4ESP32-DevKitC V4 pin 23
Keypad, 4x4 pin C3ESP32-DevKitC V4 pin 22
Keypad, 4x4 pin C2ESP32-DevKitC V4 pin 21

The code

The MicroPython code exactly as the editor opens it. Change a line there and press Run: the board runs it on the official MicroPython firmware, with nothing to compile.

main.py · ESP32-DevKitC V4
# Matrix keypad: print each key as it is pressed. Rows on GPIO 14, 27,
# 26, 25; columns on 13, 21, 22, 23. keypad.py is in its own tab.
from keypad import Keypad
import time

pad = Keypad([14, 27, 26, 25], [13, 21, 22, 23])
print("Press a key")

while True:
    key = pad.key()
    if key:
        print("Key:", key)
    time.sleep_ms(10)
keypad.py · ESP32-DevKitC V4
# keypad.py: a 4x4 matrix keypad. Each row is pulled low in turn and the
# columns, held high by pull-ups, show which key joins them to that row.
from machine import Pin
import time

KEYS = ["123A", "456B", "789C", "*0#D"]

class Keypad:
    def __init__(self, rows, cols):
        self.rows = [Pin(n, Pin.OUT, value=1) for n in rows]
        self.cols = [Pin(n, Pin.IN, Pin.PULL_UP) for n in cols]
        self.down = None

    def _held(self):
        for r, row in enumerate(self.rows):
            row.value(0)
            for c, col in enumerate(self.cols):
                if col.value() == 0:
                    row.value(1)
                    return KEYS[r][c]
            row.value(1)
        return None

    def key(self):
        """The key just pressed, once per press, or None."""
        held = self._held()
        if held == self.down:
            return None
        self.down = held
        if held is not None:
            time.sleep_ms(10)           # let the contact settle
        return held

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • Pressing a key prints the wrong character This project
    Check the row and column order in the Keypad call matches the wiring: rows R1 to R4 on 14, 27, 26, 25 and columns C1 to C4 on 13, 21, 22, 23.
  • Nothing prints This project
    Wait for "Press a key" in the console, then click a key. Also check that keypad.py is saved in its own tab.
  • Pressing one key reports several keys, or the wrong key. Keypad
    Match the keymap and the row and column pin order to your keypad's pinout. Without diodes, pressing three keys at once can ghost a fourth.
  • No keys are detected. Keypad
    Rows need pull-ups, or use INPUT_PULLUP, while one column is driven low at a time. The Keypad library does this for you if you pass the pins in the right order.
  • The board is not found, or upload fails with a connecting message. ESP32-DevKitC V4
    Use a USB cable that carries data, not charge only. Pick the ESP32 Dev Module board. If it still fails, hold BOOT while uploading starts, then release.
  • Some pins never read right, or the board will not boot with a wire attached. ESP32-DevKitC V4
    GPIO34 to 39 are input only and have no pull-ups. GPIO6 to 11 run the flash, so leave them alone. GPIO0, 2 and 12 are boot pins; avoid pulling them the wrong way at reset.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.