rp2350-macropad
PublicLoading…
RP2350 Macropad
A 3x3 mechanical macropad built around the Raspberry Pi RP2350A. It enumerates as a standard USB HID keyboard plus a USB CDC serial port, so each of the nine keys sends a configurable HID keycode and modifier combination. The key map is programmed over the serial port from a small web app, with no need to recompile or reflash the firmware to change it.
The board also drives a 1.14 inch ST7789 LCD that shows live temperature and humidity from an AHT20 sensor, basic system info (CPU clock, static RAM, uptime) and the current 3x3 key map. Three SK6805 RGB LEDs report status: power, caps lock, and an ambient temperature gradient.
Repository layout
.
├── Kicad/ KiCad 10 schematic, PCB and project files
├── assets/ Renders, board photos, exported schematic PDF
├── datasheets/ Datasheets for the main parts on the board
├── ibom/ Interactive HTML BOM
├── production/ Gerbers and fabrication outputs
└── code/
├── firmware/ RP2350 firmware (C, Pico SDK 2.2)
├── web-programmer/ Next.js web app for editing the key map
└── host-actions/ Optional Python daemon for host-side macros
Hardware
- MCU: RP2350A with 4 MB (32 Mbit) external QSPI flash (W25Q32)
- Display: 1.14 inch ST7789 240x135 SPI LCD
- Sensor: AHT20 I2C temperature and humidity
- LEDs: three SK6805-EC20 (WS2812 compatible) on a single PIO chain
- Switches: nine Cherry MX switches in a 3x3 matrix (rows GP5-7, columns GP2-4). The footprint takes the 5-pin PCB mount version, not the 3-pin plate mount one
- USB: native USB on the RP2350, exposing an HID keyboard plus a CDC serial port
- Debug: 3-pin JST-SH SWD connector (Raspberry Pi Debug Probe) and a 2.54mm UART header
The schematic and PCB are in Kicad/. Rendered images and a PDF schematic are
in assets/, and an interactive BOM is in ibom/.
Firmware (code/firmware)
Built with the Raspberry Pi Pico SDK 2.2. The recommended setup is the official
"Raspberry Pi Pico" VS Code extension, which downloads the SDK, toolchain and
CMake into ~/.pico-sdk/ and provides the build, debug and flash actions used
by .vscode/launch.json and .vscode/tasks.json.
Building from the command line
cd code/firmware
export PICO_SDK_PATH="$HOME/.pico-sdk/sdk/2.2.0"
export PICO_TOOLCHAIN_PATH="$HOME/.pico-sdk/toolchain/14_2_Rel1"
cmake -S . -B build -G Ninja
cmake --build build
The output is build/rp2350-macropad.uf2. To flash, hold BOOTSEL while plugging
the board in and copy the UF2 onto the mounted RPI-RP2 drive, or run
picotool load build/rp2350-macropad.uf2 -fx. With a Debug Probe on the SWD
connector, the VS Code extension can flash and debug over SWD directly.
Source files
main.c: main loop (USB tasks, matrix scan, sensor read, LCD draw, LED animation, the CDC command protocol, and key map persistence in the last flash sector).usb_descriptors.c: composite USB descriptor (HID keyboard plus CDC).st7789.c,st7789.h: minimal SPI driver and bitmap text routines for the ST7789 panel.font5x7.h: 5x7 bitmap font used by the LCD text routines.icons.h,tools/gen_icons.py: color macro icons, and the script that generates them.ws2812.pio: PIO program driving the SK6805 LED chain.tusb_config.h: TinyUSB configuration (HID and CDC enabled).CMakeLists.txt,pico_sdk_import.cmake: build configuration.
Web programmer (code/web-programmer)
A Next.js app that talks to the firmware over the USB CDC serial port using the Web Serial API. It reads the current key map from the device, lets you assign a keycode and modifier combination (or a host macro) to each of the nine keys, and writes the new map back to the last sector of flash so it survives a reboot. A live tester highlights each key on an on-screen copy of the LCD as it is pressed.
Web Serial runs in Chromium browsers (Chrome, Edge and other Chromium based browsers) and in Firefox 151 and newer. Safari does not support it.
Running locally
cd code/web-programmer
npm install
npm run dev
Open http://localhost:3000, click Connect via USB, choose the "Macropad Programmer" port from the browser picker, and edit the key map.
Host actions daemon (code/host-actions)
An optional Python daemon for macros that a single keystroke cannot express,
such as running a script or toggling a system setting. A cell marked as a macro
in the web programmer types nothing on its own; instead the firmware emits a
serial event that the daemon turns into an action on the host. See
code/host-actions/README.md for details.
USB CDC command protocol
The firmware exposes a line-oriented ASCII protocol on the CDC interface
(115200 baud, although the rate is irrelevant on USB CDC). Each command is a
single line ending in \n; responses are also single lines.
| Command | Response |
|---|---|
INFO | INFO:{"temp":..,"hum":..,"cpu_hz":..,"heap_used":..,"heap_free":..,"static_used":..,"uptime_ms":..,"flash_used":..,"flash_total":..} |
GET | CONFIG:k0,k1,k2,k3,k4,k5,k6,k7,k8,m0,m1,m2,m3,m4,m5,m6,m7,m8, nine HID keycodes followed by nine modifier bytes in row-major order |
SET:k0,k1,...,k8,m0,m1,...,m8 | OK on success, ERR on a malformed payload. The new key map is written to the last 4 KB sector of flash and takes effect immediately |
NAME | NAME:<current device name>, empty if the device has never been renamed |
SETNAME:<name> | OK on success, ERR if <name> is longer than 31 bytes or contains a non-printable-ASCII byte. The name is persisted alongside the key map |
In addition to the request/response commands above, the firmware emits two asynchronous push notifications while a host is connected. These are not replies to a request; they arrive whenever a key is touched:
| Event | When |
|---|---|
PRESS:<idx> | A physical key has just gone down (idx 0-8, row-major) |
RELEASE:<idx> | A physical key has just gone up |
Hosts that do not care about these events can ignore any line whose first token
is PRESS or RELEASE. The web programmer uses them to drive the live tester,
and the host actions daemon uses them to trigger macros.
Modifier bits follow the standard USB HID convention: bit 0 = Ctrl, bit 1 = Shift, bit 2 = Alt, bit 3 = GUI/Meta, with the right-side modifiers in the upper nibble.
The key map is stored in the last flash sector with the following layout:
| Offset | Bytes | Field |
|---|---|---|
| 0..3 | 4 | magic = 0xCA1A1002 (little-endian) |
| 4..12 | 9 | keycodes (row-major) |
| 13..21 | 9 | modifiers (row-major) |
| 22 | 1 | name length (0..31; 0xFF is treated as "unset") |
| 23..54 | 32 | name bytes (printable ASCII, padded with 0xFF) |
If the magic is missing or wrong (a fresh chip, or a firmware update that erased
the sector) the firmware falls back to digits 1-9 with no modifiers and an empty
name. Configs written by older firmware that did not yet have the name field
still load: byte 22 reads as 0xFF and the name is treated as unset.
License
Released under the MIT License. See LICENSE.
No comments yet. Be the first to ask about this board.