Who this is for
People who repeat keyboard-heavy work in editors, design tools, meeting apps or media software and want a tactile, programmable control surface.
Before you start
Have these basics ready before choosing parts or flashing firmware.
- An Adafruit MacroPad RP2040, compatible key switches and keycaps, and a USB data cable.
- The MacroPad-specific CircuitPython UF2 and the complete Adafruit Hotkeys project bundle, including code.py, lib and macros.
- A written list of target shortcuts for one application.
Choose the first control layer
Keyboard shortcuts: The app already exposes reliable shortcuts and portability matters.
Consumer control: You need volume, mute, play or other standard media actions.
Manual application profiles: You want the included Hotkeys program to change OLED labels and shortcuts when you turn the encoder.
Automatic app switching or MIDI: You are ready for a different or extended program; these are not features to assume in the stock Hotkeys setup.
Audit repetitive actions
For one week, note commands you repeat and how costly a wrong press would be. Select frequent, reversible actions for the first layer; leave delete, send, deploy and purchase actions on the normal keyboard.
- Prefer commands with visible feedback.
- Group related actions spatially.
Back up the board and choose the exact firmware
Use the Adafruit MacroPad RP2040 board, not a generic RP2040 keypad that happens to have twelve keys. The Hotkeys program uses the MacroPad library for this board's keys, encoder, OLED and RGB LEDs. Have installed switches and keycaps ready, connect through a known USB data cable, and copy any existing CIRCUITPY files to your computer before replacing the program.
Download the UF2 for Adafruit MacroPad RP2040 from the board selector linked in the Hotkeys setup page. Press and hold the rotary encoder: its push switch is BOOT/BOOTSEL. While holding it, press and release Reset and wait for RPI-RP2 to appear before releasing the encoder. Alternatively, hold the encoder while connecting USB. Copy the board-specific UF2 to RPI-RP2 and wait for that drive to disappear and CIRCUITPY to appear. These are two different USB modes, not two folders to copy the project into.
- Back up code.py, boot.py if present, libraries and custom profiles before updates.
- If RPI-RP2 never appears, repeat BOOT plus Reset with a known data cable and a direct computer USB port.
Install CircuitPython and libraries
Open Set up MacroPad hotkeys below and use Download Project Bundle in its Project Code section. Extract the ZIP on your computer and copy the contents of the relevant project directory onto CIRCUITPY. The root needs code.py and the lib and macros folders; nesting the whole downloaded directory one level below the drive root will not put the startup file where CircuitPython expects it.
Keep code.py and its supplied libraries together for the first run instead of combining files from unrelated examples. If choosing a versioned bundle directory, match its CircuitPython major version to the firmware installed on the board. Before replacing same-named files, move personal profiles to a separate backup and restore them only after the supplied program starts. Keep that working firmware filename and bundle together for recovery.
Inside CIRCUITPY/macros, move out samples for the wrong host: mac- and win-prefixed examples use different modifier keys. Keep at least one suitable .py profile. After the program restarts, the OLED should show a profile name and its key labels. Turn the encoder to cycle the available profiles; do not start by pressing every sample key against a real document or terminal.
- Pin the working CircuitPython major version.
- Keep keymap data separate from device loop code.
Build one application profile
Copy one working profile to a descriptive filename such as win-notes.py or mac-notes.py and edit it with a plain-text editor. A profile is executable Python, not JSON: it must define a dictionary named app with name and macros entries. Use a name of no more than 20 characters, then a list of macro tuples. Each tuple contains an RGB color, a short OLED label and an action sequence. Keep labels around six characters so neighboring labels remain readable.
The list runs left to right across the top row, then across each following row. Start with just three entries; the stock program blanks unused key labels and LEDs. For a harmless text test, set a tuple to (0x002000, 'Hello', 'hello'). Save the file, select its profile with the encoder, focus an empty disposable document and press the top-left key. Expect hello at the cursor, not an application launch. Add two more short strings and check that the physical positions match their OLED labels before adding shortcuts.
For special keys, import Keycode from adafruit_hid.keycode and use the constants inside an action list. A positive keycode presses a key; its negative value releases it partway through a longer sequence. Release a held modifier before typing ordinary text later in the same macro. Use a floating-point value such as 0.5 for a half-second pause: an integer is interpreted as a keycode, not a delay. The stock program processes releases when the physical key is released; test press and release, not just the initial output.
- Include an obvious “no action” slot.
- Use short LED feedback for accepted input.
Use the encoder safely
In this Hotkeys program, rotation selects the application profile and refreshes the OLED and LED colors. It is not a volume dial by default, and selecting a profile does not switch the computer's focused application. Choose the intended application on the computer separately, then check the profile name before sending a shortcut. Automatic foreground-app detection requires extra host-side software and is outside this setup.
The encoder push switch can have an optional thirteenth macro after the twelve key entries. With fewer than thirteen entries, pressing it has no configured action. Leave it unassigned during the three-key test, then choose a harmless command if you need it. Keep profile selection on rotation until the ordinary key mappings are dependable; changing rotation to volume requires editing the main program rather than merely renaming a profile.
- Test slow and fast turns.
- Do not map rotation directly to destructive commands.
Add layers and maintainability
Add another profile by copying a working .py file into /macros, changing its app name and editing its actions. Save and allow the program to reload before turning the encoder. The loader sorts filenames, so descriptive names also help make the selector order predictable. Keep only profiles you actually use: the program has no knowledge of which host app should receive a given shortcut.
For play, pause or volume buttons, use the guide's Consumer Control examples instead of treating media codes as ordinary keyboard keycodes. Mouse actions use dictionaries inside the sequence, while consumer-control actions use nested lists. Copy the complete example structure and change one action at a time. MIDI, extra keys over Stemma QT and automatic app switching need additional software work; they are not enabled by adding arbitrary names to a profile.
For each finished profile, test every key in the intended application, release it, switch to a second profile and return. Verify labels, LED colors and actions agree after a reconnect. Keep a computer-side copy of the known-good code.py, libraries and profiles before trying another bundle or CircuitPython major version.
- Show the active layer at all times.
- Version the keymap with a short change log.
Safety and workflow notes
A macro pad acts as a keyboard and can trigger anything the focused application accepts. Avoid credentials, irreversible actions and unattended automation; require a confirmation in the host application for sending, deleting, deploying or purchasing.
- Test macros in a disposable document.
- Do not store passwords or tokens in keymap files.
- Use accessibility features responsibly and avoid automating systems contrary to workplace policy.
Troubleshooting
A key repeats or sticks logically. Release the physical key and inspect held modifiers in its sequence. The stock program releases keyboard keys on key release and clears held inputs on profile changes. Disconnect the pad if unwanted input continues, then test one harmless text action before changing hardware.
Shortcuts work on one OS but not another. Create explicit OS profiles and translate modifier keys rather than detecting or guessing silently.
The board enters a crash loop. Read the serial console error before replacing files. For an inaccessible CIRCUITPY drive, enter CircuitPython safe mode by pressing Reset again during its startup window, then repair the files and reset normally. Safe mode skips saved user code; a flash-erasing nuke UF2 deletes files and is a last resort, not the first repair.
The screen says NO MACRO FILES FOUND or one profile is missing. Check that /macros contains at least one valid .py file defining app, name and macros. Read the serial console for the filename and Python error; the loader can skip a broken profile. Restore the working sample, then fix quotes, brackets and required imports in one file at a time.
The wrong app receives the shortcut. Select the host application manually and turn the encoder to its matching profile. OLED labels show the selected configuration, not detected foreground-app identity. Test again in a disposable document before restoring real work.
Next steps
Keep the next experiment small and reproducible.
- Ship a three-key profile.
- Add a second OS-specific profile and confirm that encoder rotation changes its name and labels.
- Run it for a week and replace unused mappings before adding layers.
