Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

4×4 keypad with Arduino Leonardo — a USB macro pad and numpad

Wire a 4×4 membrane keypad to an Arduino Leonardo on D4–D11 and turn it into a USB numpad with copy, paste, undo, save and Alt+Tab shortcuts.

Arduino C++Verified — core avr 1.8.8, Keypad 3.1.1, 3 Nov 2026Compile-only

Community draft — report a mistake

A cheap membrane keypad plus a Leonardo is a USB macro pad: one key for copy, one for paste, one to save, and a numpad for laptops that have none. It is a good first HID project because nothing happens unless you press a key.

The keypad’s eight wires go to D4–D11. Digits type themselves; A–D send Ctrl+C, Ctrl+V, Ctrl+Z and Ctrl+S; * is Alt+Tab and # is Enter. Change the switch to your own shortcuts.

Wiring

Arduino LeonardoOnly the pins used are shown · 5 V logic4×4 matrix membrane keypadD4D5D6D7D8D9D10D11R1R2R3R4C1C2C3C4
Every connection
Arduino Leonardo4×4 matrix membrane keypadWireNotes
D4R1orange
D5R2orange
D6R3orange
D7R4orange
D8C1blue
D9C2blue
D10C3blue
D11C4blue

No supply — passive switch matrix part, 5 V board. The keypad has no electronics, so it works at 3.3 V or 5 V. It does need eight GPIOs; on ESP32 avoid the strapping pins (0, 2, 5, 12, 15) and the input-only pins (34–39) for the row outputs.

Board quirks

  • D4–D11 are used so that D0/D1 (Serial1) and D2/D3 (I2C) stay free — add an OLED on D2/D3 later to show what each key does.
  • The Leonardo appears as a standard USB keyboard. Shortcuts follow the PC’s layout: Ctrl+C on Windows/Linux; on a Mac change KEY_LEFT_CTRL to KEY_LEFT_GUI.
  • Keys are only sent when a key is pressed, so the sketch can’t run away. Avoid sketches that type in loop() without a button.
  • Install both the Keypad library and Arduino’s Keyboard library; the Keyboard library only compiles for native-USB boards like the Leonardo.

Code

keypad-leonardo.inoArduino C++Open in PlaygroundOpen in Playground
// 4x4 keypad as a USB macro pad / numpad on an Arduino Leonardo (native USB keyboard).
// R1-R4 → D4, D5, D6, D7; C1-C4 → D8, D9, D10, D11. D0/D1 (Serial1) and D2/D3 (I2C) stay free.
// Digits type themselves, A = Ctrl+C, B = Ctrl+V, C = Ctrl+Z, D = Ctrl+S, * = Alt+Tab, # = Enter.
// Keys are only sent when you press one, so this sketch can't lock you out of the PC.
#include <Keypad.h>
#include <Keyboard.h>

const byte ROWS = 4, COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'},
};
byte rowPins[ROWS] = {4, 5, 6, 7};
byte colPins[COLS] = {8, 9, 10, 11};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void chord(uint8_t modifier, char key) {
  Keyboard.press(modifier);
  Keyboard.press(key);
  delay(20);
  Keyboard.releaseAll();
}

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 3000) {}  // native USB: wait up to 3 s, never forever
  Keyboard.begin();
  Serial.println(F("Macro pad ready"));
}

void loop() {
  char k = keypad.getKey();
  if (!k) return;
  Serial.print(F("Key "));
  Serial.println(k);
  switch (k) {
    case 'A': chord(KEY_LEFT_CTRL, 'c'); break;
    case 'B': chord(KEY_LEFT_CTRL, 'v'); break;
    case 'C': chord(KEY_LEFT_CTRL, 'z'); break;
    case 'D': chord(KEY_LEFT_CTRL, 's'); break;
    case '*': chord(KEY_LEFT_ALT, KEY_TAB); break;
    case '#': Keyboard.write(KEY_RETURN); break;
    default: Keyboard.write(k);  // digits
  }
}

Library: Keypad (Mark Stanley, Alexander Brevig) — compiled with Keypad 3.1.1.

Expected serial output

Serial Monitor · 115200Output
Macro pad ready
Key 4
Key A
Key #

Common errors

‘Keyboard’ not found. Does your sketch include the line #include <Keyboard.h>?

Board set to Uno instead of Leonardo, or the Keyboard library missing. Select Arduino Leonardo.

Wrong characters on a non-US keyboard layout

The Keyboard library sends US key codes. Set the PC’s input language to English (US). Recent Keyboard library versions also accept a layout in Keyboard.begin().

↑↓ to move · Enter to open