Community project

Handwired Macro Pad

Arduino
Photo of Handwired Macro Pad

Max G

Last 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

Wiring diagram for Handwired Macro Pad

Gather all the parts

QtyComponent
1

Push Button

R1 C1 — 1

Momentary push button switch

1

Push Button

R1 C2 — 2

Momentary push button switch

1

Push Button

R1 C3 — 3

Momentary push button switch

1

Push Button

R1 C4 — 4

Momentary push button switch

1

Push Button

R2 C1 — 5

Momentary push button switch

1

Push Button

R2 C2 — 6

Momentary push button switch

1

Push Button

R2 C3 — 7

Momentary push button switch

1

Push Button

R2 C4 — 8

Momentary push button switch

1

Push Button

R3 C1 — 9

Momentary push button switch

1

Push Button

R3 C2 — 0

Momentary push button switch

1

Push Button

R3 C3 — -

Momentary push button switch

1

Push Button

R3 C4 — =

Momentary push button switch

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

1

1N4148 Signal Diode

1N4148

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.

Assemble it in 5 steps

1. Place 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.

  • 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.

2. 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).

  • Keep each row wire separate and use three different wire colors if possible.
  • 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.

3. 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.

  • The stripe is the line printed near one end of the tiny glass diode.
  • 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.

4. 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).

  • The four column wires run vertically through the key layout.
  • 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.

5. 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.

  • A phone camera zoom can make it much easier to spot solder bridges.
  • 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.

Review all connections

1. Connections between "switch_1" and "Arduino"

Functionswitch_1Arduino
digitalGNDGPIO 4
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

2. Connections between "diode_1" and "Arduino"

Functiondiode_1Arduino
dataCATHODEGPIO 7

3. Connections between "switch_2" and "Arduino"

Functionswitch_2Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

4. Connections between "switch_3" and "Arduino"

Functionswitch_3Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

5. Connections between "switch_4" and "Arduino"

Functionswitch_4Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

6. Connections between "switch_5" and "Arduino"

Functionswitch_5Arduino
digitalGNDGPIO 5
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

7. Connections between "switch_6" and "Arduino"

Functionswitch_6Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

8. Connections between "switch_7" and "Arduino"

Functionswitch_7Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

9. Connections between "switch_8" and "Arduino"

Functionswitch_8Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

10. Connections between "switch_9" and "Arduino"

Functionswitch_9Arduino
digitalGNDGPIO 6
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

11. Connections between "switch_10" and "Arduino"

Functionswitch_10Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

12. Connections between "switch_11" and "Arduino"

Functionswitch_11Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

13. Connections between "switch_12" and "Arduino"

Functionswitch_12Arduino
groundGND → Push Button GNDEXT
digitalSIGNAL → 1N4148 Signal Diode ANODEEXT

14. Connections between "diode_2" and "Arduino"

Functiondiode_2Arduino
dataCATHODEGPIO 8

15. Connections between "diode_3" and "Arduino"

Functiondiode_3Arduino
dataCATHODEGPIO 9

16. Connections between "diode_4" and "Arduino"

Functiondiode_4Arduino
dataCATHODEGPIO 10

17. Connections between "diode_5" and "Arduino"

Functiondiode_5Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

18. Connections between "diode_6" and "Arduino"

Functiondiode_6Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

19. Connections between "diode_7" and "Arduino"

Functiondiode_7Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

20. Connections between "diode_8" and "Arduino"

Functiondiode_8Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

21. Connections between "diode_9" and "Arduino"

Functiondiode_9Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

22. Connections between "diode_10" and "Arduino"

Functiondiode_10Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

23. Connections between "diode_11" and "Arduino"

Functiondiode_11Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

24. Connections between "diode_12" and "Arduino"

Functiondiode_12Arduino
dataCATHODE → 1N4148 Signal Diode CATHODEEXT

Deploy the firmware

#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);
  }
}

Download project files

Remix 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.

Open in Schematik