Community project
Handwired Macro Pad
Max G
Published August 11, 2026 · Updated August 11, 2026

Build a 12-key macro pad from scratch using an Arduino Uno, push buttons, and signal diodes in a hand-wired matrix configuration. This guide covers the full assembly process: placing switches, soldering diodes to prevent ghosting, wiring rows and columns, and uploading firmware that scans the keyboard matrix and sends custom key codes to a connected computer.
The project teaches keyboard matrix scanning, debouncing techniques, and the Arduino Keyboard library. Builders will receive a complete wiring diagram, detailed assembly steps with diode polarity guidance, a full parts list, and customizable firmware that maps each of the 12 keys to any character or macro command.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| Push ButtonR1 C1 — 1 | 1 | Momentary push button switch |
| Push ButtonR1 C2 — 2 | 1 | Momentary push button switch |
| Push ButtonR1 C3 — 3 | 1 | Momentary push button switch |
| Push ButtonR1 C4 — 4 | 1 | Momentary push button switch |
| Push ButtonR2 C1 — 5 | 1 | Momentary push button switch |
| Push ButtonR2 C2 — 6 | 1 | Momentary push button switch |
| Push ButtonR2 C3 — 7 | 1 | Momentary push button switch |
| Push ButtonR2 C4 — 8 | 1 | Momentary push button switch |
| Push ButtonR3 C1 — 9 | 1 | Momentary push button switch |
| Push ButtonR3 C2 — 0 | 1 | Momentary push button switch |
| Push ButtonR3 C3 — - | 1 | Momentary push button switch |
| Push ButtonR3 C4 — = | 1 | Momentary push button switch |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
| 1N4148 Signal Diode1N4148 | 1 | Fast small-signal switching diode in DO-35 glass package. Forward voltage ~0.6–0.7 V at low current, reverse breakdown 75 V, max forward current 300 mA, reverse recovery time 4 ns. Used here in an anti-parallel pair across the TL072 op-amp feedback loop (D1: anode→OPAMP_OUT, cathode→OPAMP_IN_NEG; D2: anode→OPAMP_IN_NEG, cathode→OPAMP_OUT) to provide soft audio clipping. |
Assembly
5 stepsPlace the 12 switches
Press the 12 mechanical switches into the 3-by-4 plate so the two metal solder tabs of every switch are accessible from the back. Number the keys left to right: keys 1–4 on the top row, 5–8 in the middle, and 9–12 on the bottom.
- Tip: Use a marker or a small paper label to keep track of the rows before soldering.
- ⚠ Do not heat a switch terminal for too long; excess heat can soften the plastic switch housing.
Make the three row wires
Solder one bare wire across one terminal of switches 1, 2, 3, and 4, then run that wire to Pro Micro D4 (row 1 signal). Repeat for switches 5–8 to D5 (row 2 signal), and switches 9–12 to D6 (row 3 signal).
- Tip: Keep each row wire separate and use three different wire colors if possible.
- Tip: The two switch terminals have no positive or negative side; simply use the same chosen terminal across each row.
- ⚠ Make sure row wires do not touch each other — a solder bridge would make keys trigger incorrectly.
Solder one diode to every key
Solder the unstriped end of one 1N4148 diode to the unused terminal of each switch. The diode's painted stripe must face away from the switch; it will join a column wire next.
- Tip: The stripe is the line printed near one end of the tiny glass diode.
- Tip: Bend the diode legs first so they lie flat and do not press against nearby switch terminals.
- ⚠ All 12 stripes must point away from their switches. A reversed diode makes that key fail or behave unpredictably.
Make the four column wires
Join the striped diode ends for keys 1, 5, and 9, then run that column wire to Pro Micro D7 (key signal). Join keys 2, 6, and 10 to D8 (key signal); keys 3, 7, and 11 to D9 (key signal); and keys 4, 8, and 12 to D10 (key signal).
- Tip: The four column wires run vertically through the key layout.
- Tip: Use heat-shrink tubing or electrical tape over exposed diode legs where they cross another wire.
- ⚠ Do not connect a column wire to the row wire at any key; each key must connect through its own diode only.
Inspect before plugging in
With the Pro Micro unplugged, look closely for loose strands and solder blobs. Check that D4, D5, and D6 only go to row wires, while D7, D8, D9, and D10 only go to striped diode ends.
- Tip: A phone camera zoom can make it much easier to spot solder bridges.
- Tip: Use a multimeter continuity test if you have one: a key should connect its row to its column only while pressed.
- ⚠ Do not plug in USB until you have checked for touching wires; a short can make the board hot or stop it working.
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| GPIO 4 | switch_1 GND | digital |
| EXT | switch_1 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| GPIO 7 | diode_1 CATHODE | data |
| EXT | switch_2 GND → Push Button GND | ground |
| EXT | switch_2 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_3 GND → Push Button GND | ground |
| EXT | switch_3 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_4 GND → Push Button GND | ground |
| EXT | switch_4 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| GPIO 5 | switch_5 GND | digital |
| EXT | switch_5 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_6 GND → Push Button GND | ground |
| EXT | switch_6 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_7 GND → Push Button GND | ground |
| EXT | switch_7 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_8 GND → Push Button GND | ground |
| EXT | switch_8 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| GPIO 6 | switch_9 GND | digital |
| EXT | switch_9 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_10 GND → Push Button GND | ground |
| EXT | switch_10 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_11 GND → Push Button GND | ground |
| EXT | switch_11 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| EXT | switch_12 GND → Push Button GND | ground |
| EXT | switch_12 SIGNAL → 1N4148 Signal Diode ANODE | digital |
| GPIO 8 | diode_2 CATHODE | data |
| GPIO 9 | diode_3 CATHODE | data |
| GPIO 10 | diode_4 CATHODE | data |
| EXT | diode_5 CATHODE → 1N4148 Signal Diode CATHODE | data |
| EXT | diode_6 CATHODE → 1N4148 Signal Diode CATHODE | data |
| EXT | diode_7 CATHODE → 1N4148 Signal Diode CATHODE | data |
| EXT | diode_8 CATHODE → 1N4148 Signal Diode CATHODE | data |
| EXT | diode_9 CATHODE → 1N4148 Signal Diode CATHODE | data |
| EXT | diode_10 CATHODE → 1N4148 Signal Diode CATHODE | data |
| EXT | diode_11 CATHODE → 1N4148 Signal Diode CATHODE | data |
| EXT | diode_12 CATHODE → 1N4148 Signal Diode CATHODE | data |
Firmware
Arduino#include <Arduino.h>
#include <Keyboard.h>
// Hand-wired 3 x 4 keyboard matrix.
// Diode direction is COL2ROW: switch -> diode anode, diode stripe/cathode -> column.
const uint8_t ROW_1_PIN = 4;
const uint8_t ROW_2_PIN = 5;
const uint8_t ROW_3_PIN = 6;
const uint8_t COL_1_PIN = 7;
const uint8_t COL_2_PIN = 8;
const uint8_t COL_3_PIN = 9;
const uint8_t COL_4_PIN = 10;
const uint8_t rowPins[3] = {ROW_1_PIN, ROW_2_PIN, ROW_3_PIN};
const uint8_t colPins[4] = {COL_1_PIN, COL_2_PIN, COL_3_PIN, COL_4_PIN};
// Change these entries later to choose what each physical key sends.
const char macroKeys[3][4] = {
{'1', '2', '3', '4'},
{'5', '6', '7', '8'},
{'9', '0', '-', '='}
};
bool previousPressed[3][4] = {};
unsigned long lastChangeMs[3][4] = {};
const unsigned long DEBOUNCE_MS = 20;
void setup() {
// Rows normally listen with pull-up resistors enabled.
for (uint8_t row = 0; row < 3; row++) {
pinMode(rowPins[row], INPUT_PULLUP);
}
// Columns are released except for the one currently scanned.
for (uint8_t col = 0; col < 4; col++) {
pinMode(colPins[col], INPUT);
}
Keyboard.begin();
}
void loop() {
const unsigned long now = millis();
// This is the same COL2ROW scan direction used by the QMK config discussed.
for (uint8_t col = 0; col < 4; col++) {
pinMode(colPins[col], OUTPUT);
digitalWrite(colPins[col], LOW);
delayMicroseconds(30);
for (uint8_t row = 0; row < 3; row++) {
const bool pressed = (digitalRead(rowPins[row]) == LOW);
if (pressed != previousPressed[row][col] &&
now - lastChangeMs[row][col] >= DEBOUNCE_MS) {
previousPressed[row][col] = pressed;
lastChangeMs[row][col] = now;
if (pressed) {
Keyboard.press(macroKeys[row][col]);
} else {
Keyboard.release(macroKeys[row][col]);
}
}
}
pinMode(colPins[col], INPUT);
}
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Project files
Shared by the authorRemix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.