Community project

Handwired Macro Pad

Max G

Published August 11, 2026 · Updated August 11, 2026

Arduino24 components5 assembly steps
Remix this project
Photo of Handwired Macro Pad

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
Wiring diagram for Handwired Macro Pad

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
Push ButtonR1 C1 — 11Momentary push button switch
Push ButtonR1 C2 — 21Momentary push button switch
Push ButtonR1 C3 — 31Momentary push button switch
Push ButtonR1 C4 — 41Momentary push button switch
Push ButtonR2 C1 — 51Momentary push button switch
Push ButtonR2 C2 — 61Momentary push button switch
Push ButtonR2 C3 — 71Momentary push button switch
Push ButtonR2 C4 — 81Momentary push button switch
Push ButtonR3 C1 — 91Momentary push button switch
Push ButtonR3 C2 — 01Momentary push button switch
Push ButtonR3 C3 — -1Momentary push button switch
Push ButtonR3 C4 — =1Momentary push button switch
1N4148 Signal Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 Diode1N41481Fast 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 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.

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

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

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

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

    • 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
PinConnectionType
GPIO 4switch_1 GNDdigital
EXTswitch_1 SIGNAL1N4148 Signal Diode ANODEdigital
GPIO 7diode_1 CATHODEdata
EXTswitch_2 GNDPush Button GNDground
EXTswitch_2 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_3 GNDPush Button GNDground
EXTswitch_3 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_4 GNDPush Button GNDground
EXTswitch_4 SIGNAL1N4148 Signal Diode ANODEdigital
GPIO 5switch_5 GNDdigital
EXTswitch_5 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_6 GNDPush Button GNDground
EXTswitch_6 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_7 GNDPush Button GNDground
EXTswitch_7 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_8 GNDPush Button GNDground
EXTswitch_8 SIGNAL1N4148 Signal Diode ANODEdigital
GPIO 6switch_9 GNDdigital
EXTswitch_9 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_10 GNDPush Button GNDground
EXTswitch_10 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_11 GNDPush Button GNDground
EXTswitch_11 SIGNAL1N4148 Signal Diode ANODEdigital
EXTswitch_12 GNDPush Button GNDground
EXTswitch_12 SIGNAL1N4148 Signal Diode ANODEdigital
GPIO 8diode_2 CATHODEdata
GPIO 9diode_3 CATHODEdata
GPIO 10diode_4 CATHODEdata
EXTdiode_5 CATHODE1N4148 Signal Diode CATHODEdata
EXTdiode_6 CATHODE1N4148 Signal Diode CATHODEdata
EXTdiode_7 CATHODE1N4148 Signal Diode CATHODEdata
EXTdiode_8 CATHODE1N4148 Signal Diode CATHODEdata
EXTdiode_9 CATHODE1N4148 Signal Diode CATHODEdata
EXTdiode_10 CATHODE1N4148 Signal Diode CATHODEdata
EXTdiode_11 CATHODE1N4148 Signal Diode CATHODEdata
EXTdiode_12 CATHODE1N4148 Signal Diode CATHODEdata

Firmware

Arduino
main.cppDeploy to device
#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 author

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