A custom 4x4 handwired macropad with rotary encoder and dynamic runtime macro updates.
This project is a handwired 4x4 macropad built around a Raspberry Pi Pico and QMK firmware. The firmware maps each physical key to a macro slot, and the host/ scripts can update macros at runtime over raw HID.
The encoder has layer-aware behavior, and the firmware supports 16 logical layers with 16 macros per layer.
- 1x Raspberry Pi Pico
- 16x Cherry MX compatible switches (or 15 switches plus encoder push if available)
- 1x Rotary encoder (EC11)
- 16x 1N4148 diodes
- 4x 3M 10mm DIN 965 screws (or similar)
- Hookup wire for handwiring
- Keycaps for 16 keys
- MX encoder adapter: Thingiverse Thing:3770166
- QMK Firmware for firmware compilation/customization
- Python 3 for the UI scripts
hidPython package for raw HID communication- Computer with USB port
- Soldering iron and solder
- Wire strippers
- 3D printer or printing service
- Basic hand tools
- Hold the top-left key while plugging in USB.
- Copy
jacco_4x4_macropad_default.uf2to theRPI-RP2drive. - The board will reboot with the loaded firmware.
The firmware maps the 16 keys to macro indices 0..15 on every layer. The actual key behavior is defined by macros, so the board is designed to be configured at runtime via the host scripts in host/.
Encoder: Layer-dependent actions, configured in firmware.
This repo includes a simple Python host-side tool for sending macro updates to the board over raw HID.
host/main.py- sends fragmented raw HID reports to update macro definitions on the device.host/keycode_converter.py- converts human-readable macro expressions likeLCTL(KC_C)andSWITCH_LAYER_5into QMK keycodes.host/qmk_keycodes.py- contains the QMK keycode constants used by the converter.
The firmware expects raw HID packets to update:
- encoder actions per layer
- macro contents per layer and slot
- repeating macro intervals
The sample script in host/main.py demonstrates:
- setting layer 0 key 4 to switch to layer 5
- setting layer 5 key 5 to send
Ctrl+Cand erpeating every 100ms until pressed again - setting layer 5 rotary encoder actions to send
aandb - setting layer 5 key 4 to return to layer 0
Install dependencies:
python3 -m pip install hidThen run:
python3 host/main.pyEdit host/main.py to send the macro updates you want.
qmk-config/- QMK keyboard definition and keymap sourcesqmk-config/jacco_4x4_macropad/keyboard.jsondeclares keyboard featuresqmk-config/jacco_4x4_macropad/keymaps/default/keymap.cimplements the dynamic macro engine and encoder behavior
hardware/- Handwiring guide, BOM, and assembly instructionscase/- 3D printable case filescase.3mf- main case designcase.f3d- Fusion 360 design filestl/- STL files for printing
images/- Assembly and wiring reference photoshost/- Python scripts for sending macro updates to the device
- Print
case.3mf - Print the MX encoder adapter from Thingiverse Thing:3770166
- Recommended settings: 0.2mm layer height, 20% infill, PLA or PETG
See hardware/README.md for the full Bill of Materials.
Follow hardware/assembly.md to:
- wire the switch matrix
- connect the Raspberry Pi Pico
- wire the encoder to GP26/GPIO28
- install the case and finish assembly
Use the Quick Start guide above or compile from QMK after copying the keyboard folder.
Copy qmk-config/jacco_4x4_macropad/ into your local QMK firmware tree under keyboards/.
Example:
cp -r qmk-config/jacco_4x4_macropad ~/qmk_firmware/keyboards/Then build and flash:
qmk compile -kb jacco_4x4_macropad -km default
qmk flash -kb jacco_4x4_macropad -km default- The current firmware stores macro definitions in RAM only. Custom macros are lost after reboot unless resent by the host tool.
- Layer switching is handled by macro keycodes such as
SWITCH_LAYER_0..SWITCH_LAYER_15.
Open source hardware and software. See individual files for specific licenses.

