How to Reverse-Engineer Almost Any Keyboard Matrix With Raspberry Pi Pico
by thanishurs31 in Circuits > Raspberry Pi
690 Views, 9 Favorites, 0 Comments
How to Reverse-Engineer Almost Any Keyboard Matrix With Raspberry Pi Pico
So you have a keyboard. Maybe you yanked it out of a broken laptop, saved it from a bin, or bought a random membrane thing at a thrift store for 50 cents. And now you're staring at a ribbon cable with 20-something pins going absolutely nowhere, wondering what the heck you're supposed to do with it.
The old way to tackle this is the continuity-meter dance — you probe every possible pin pair, draw a grid, cross things out, wonder if that reading was real or just a ghost, and eventually end up with a hand-cramp and a half-correct diagram. It works. Eventually. But it's also the kind of experience that makes people give up and buy a new keyboard.
This guide does it differently. We use a Raspberry Pi Pico running CircuitPython to scan the entire matrix for us — it figures out which pins are rows, which are columns, handles diode-protected N-key rollover boards and old simple membrane boards, flags shared power lines, and spits out a clean JSON map at the end. Then we take that map and turn the keyboard into a proper USB HID device you can actually type on, with layers, Fn keys, and everything.
This isn't a one-keyboard tutorial — the method works on most salvaged boards, custom keypads, mystery matrices, and whatever else you've got. Once you've done it once, you'll do it again.
What You're Actually Getting Out Of This
- A full wiring map of your unknown keyboard matrix
- Detection of whether your board has diodes (N-key rollover) or not
- Ghost key filtering for no-diode boards
- A working CircuitPython USB keyboard firmware with shift, Fn layers, and media keys
- A QMK-compatible info.json stub if you want to go further with custom firmware
Supplies
Hardware
The essentials:
- Raspberry Pi Pico (the plain RP2040 one — not the W, you don't need WiFi for this)
- A micro USB cable — and please, use one you know transfers data. Charge-only cables will waste 20 minutes of your life
- Your keyboard with its ribbon cable
For connecting the ribbon cable:
This is where most people get confused. The flat cable coming out of your keyboard is an FPC (Flexible Printed Circuit) cable, and you need a breakout board that matches its pitch — the spacing between the pins. The two most common pitches are:
- 0.5mm pitch — the fine one, found on most laptop keyboards built after 2010ish
- 1.0mm pitch — a bit chunkier, older laptops, some standalone keyboards
Count the pins and measure the pitch (or just search your keyboard model). Breakout boards for both are dirt cheap on AliExpress, Amazon, or wherever you get your Pico. Search for "FPC breakout 0.5mm [pin count]" or "FPC to DIP adapter."
BTB connectors: Some keyboards — especially from tablets or unusual laptops — use board-to-board (BTB) connectors instead of a ribbon cable. These are the stubby little things that plug directly onto a PCB. Good luck finding a breakout for those. If you're in that situation, you're probably better off desoldering the connector pads directly. That's a different rabbit hole.
The rest of the list:
- Male header pins (for both the Pico and the breakout board)
- Breadboard
- Jumper wires (female-to-male)
- Optional: A perfboard if you want to make this permanent and not have a wiring nest
Software
- Thonny — free Python IDE, works on Windows, Mac, Linux. Download from thonny.org
- CircuitPython — we'll install this through Thonny. No separate download needed.
- The two scripts in this guide (scanning script + keyboard firmware)
Get CircuitPython Onto the Pico
Before anything else, the Pico needs CircuitPython on it. This takes about two minutes.
1. Grab your Pico and your data USB cable (seriously, data cable).
2. Hold down the BOOTSEL button on the Pico — it's the small white button on the board — and while holding it, plug the USB cable into your computer.
3. Release BOOTSEL. Your computer should see a new drive pop up called RPI-RP2. If nothing appears, you've probably got a charge-only cable. Swap it.
4. Open Thonny. In the bottom-right corner, you'll see the current interpreter — click it and select "Install CircuitPython".
5. In the dialog box that appears pick your board variant:
- Regular Pico → select Raspberry Pi Pico / Pico H
- Pico W → select Raspberry Pi Pico W / Pico WH
(It will automatically select the drive of the rpi if connected as well as the latest version)
6. Click Install. It'll take a few seconds. When it's done, the RPI-RP2 drive disappears and a new one called CIRCUITPY takes its place. That's your Pico, ready to go.
Confirm it worked: In Thonny's shell at the bottom, you should see a >>> prompt. Type print("hello") and hit enter. If it prints hello back at you, you're in business.
Wire Up the Keyboard
Now for the physical part — connecting that ribbon cable to the Pico.
1. Plug the FPC ribbon cable into your breakout board. Make sure the lock/actuator is lifted before inserting, then push it down to lock. The cable should sit snugly with the exposed contacts facing the right direction for your breakout (check which side the contacts face — top-contact vs bottom-contact matters).
2. Solder male header pins onto the breakout board's output pads, then do the same for the Pico's GPIO pins if you haven't already.
Don't place the breakout on the breadboard. The breakout's pads will short against the breadboard rails. Put the breakout somewhere off to the side and run jumper wires to the Pico.
3. Wire the breakout outputs to the Pico's GPIO pins. The order doesn't matter — really. The scanning script will figure out which pin is which, so you can just connect them sequentially.
On the Pico, use GPIOs 2 through 22, then 26, 27, 28. That gives you 24 pins, which covers most keyboards. If yours has fewer — say 16 pins — just wire those 16 and leave the rest floating. If yours has more than 24, you're probably looking at a keyboard that uses an I/O expander chip (MCP23017, 74HC595, etc.), which is a different situation entirely.
4. That's it for wiring. Once you've confirmed everything works, you can optionally transfer the whole setup to a perfboard for a permanent, rigid result. But breadboard-and-jumpers is fine for getting started.
Scan the Matrix
This is where the magic happens. The script below will:
- Find all the keyboard's matrix pins and separate them from power/ground pins
- Detect whether your board uses diodes (N-key rollover boards do; old membranes don't)
- Walk you through pressing each key and labeling it
- Print a finished matrix map and QMK JSON stub at the end
How to run it:
- Open Thonny
- Make sure the bottom-right shows your Pico / CircuitPython — not your desktop Python
- Paste the code below into a new file
- Make sure no keys are pressed on the keyboard you're scanning
- Hit the green Run button
What happens after you run it:
The script runs in three phases automatically:
Phase 1 — Baseline scan. With no keys pressed, it drives each pin low and checks what else goes low. Pins that are always connected (GND, VCC, backlight lines) get filtered out automatically. You'll see a list of "matrix candidates" — these are the actual row and column pins of your keyboard.
Phase 2 — Key mapping. This is the interactive part. Hold down a key — just hold it, don't tap — and within a second or two the serial terminal will print something like:
Type whatever name makes sense for that key and hit enter. Then release the key. Repeat for every key you want to map.
A few tips that'll save you time:
- For Shift/Alt/Ctrl: These are just keys like any other — press and label them.
- For special characters on shifted keys: When labeling, you can include the shifted character too. So if the key has 5 and %, just label it 5 — you can add the shifted mapping later in the firmware. Or, if you want to be thorough while you're at it, label it 5_PERCENT or whatever makes sense to you.
- For function keys activated by Fn combos: Label the key by its base function (e.g. F5). The Fn layer gets defined separately in the firmware anyway.
- Type done and press Enter when you've finished all the keys.
Phase 3 — Live scan. After mapping, it'll ask if you want to do a live test. Say yes. Press keys and you'll see ↓ keyname and ↑ keyname printed in real-time. This confirms everything got mapped correctly before you move on. Press Ctrl-C when done.
Before moving to the next step: Copy the entire serial terminal output. All of it. You'll need it.
Turn It Into a USB Keyboard
Now you have a map. Time to make it do something.
The firmware below takes that pin map and makes the Pico show up as a real USB HID keyboard to whatever computer it's plugged into — including layers for Shift, Fn, and consumer control keys like brightness and volume.
The quick approach: Copy your terminal output from Step 3 alongside the example BASE_MAP and FN_MAP below, and paste both to an AI (Claude, ChatGPT, whatever you prefer). Ask it to generate the BASE_MAP dictionary for your specific keyboard, matching your labeled keys to their GPIO pairs. It'll take two minutes and the result will be correct. There's no shame in it — the interesting part was the matrix scan, not retyping pin numbers.
The manual approach: Find each key in your terminal output, note the two pin numbers, and fill in the BASE_MAP accordingly. The p(a, b) function just creates a canonical sorted tuple so direction doesn't matter.
The example below is my actual 24-pin laptop keyboard — use it as a reference for structure, then replace the BASE_MAP and FN_MAP with your own data:
How the layers work:
- Shift works naturally — the host OS handles it. Just map LSHIFT and RSHIFT to their pins and the firmware holds the shift modifier while they're pressed.
- Fn layer — any key in FN_MAP gets its alternate action when FN is held. Fn isn't sent to the host as a keypress, it just switches which map the firmware reads from.
- Consumer control (brightness, volume, media) — these use a separate HID descriptor from the keyboard, which is why there's a ConsumerControl object. The MEDIA dict maps action names to their codes.
- Keys not in the map — they're silently ignored. No crashes, no weirdness.
To deploy: Save this as code.py on the Pico's CIRCUITPY drive. Unplug from your computer and plug into a different USB port (or the same one after a moment). The Pico will enumerate as a keyboard and you're done.
Troubleshooting
"Too few matrix candidates" error in Phase 1 The most common cause is a charge-only USB cable that's also being used for Thonny communication — unlikely but check. More often: a wiring issue, or your keyboard has a GND pin that's pulling down more pins than the script expects. Try narrowing PROBE_PINS at the top of the scanner to only the pins you actually wired up.
Some keys aren't detected at all Check the jumper wire for that pin. Also worth verifying with a multimeter that pressing the key produces continuity between those two ribbon cable pins.
Keys type wrong characters The keyboard layout on the host OS matters here. If your OS is set to a different layout than what you mapped (e.g., you mapped for QWERTY but your OS is AZERTY), things will be off. The firmware sends raw keycodes, not characters. Match your BASE_MAP labels to the physical positions, not the characters printed on the keycaps.
Ghost keys when pressing multiple keys If your board has no diodes (the scanner will tell you — it'll say "simple bidirectional matrix"), ghost keys are a physical limitation of the matrix design. The ghost_filter in the live scan handles basic cases, but the real solution is to not rely on N-key rollover for gaming or anything where you need many simultaneous keypresses.
Pico shows up as CIRCUITPY but doesn't appear as a keyboard The firmware uses usb_hid — make sure this module is available in your CircuitPython version. It's included by default in recent versions. Also double-check you saved the file as code.py, not code.py.txt or anything else.
Where to Go From Here
Once this is working, you've got a fully editable firmware sitting in a text file on a flash drive. A few directions you could take it:
- Macropad — drop half the keys, remap the Fn layer to macros, make a dedicated coding shortcuts board
- QMK — the JSON output from the scanner is a valid stub for a QMK info.json. If you want more advanced features (tap-dance, per-key RGB, rotary encoders), QMK on a compatible microcontroller is the next step
- Multiple keyboards — the Pico is cheap. One for each salvaged keyboard you want to experiment with
- Permanent build — solder everything to a perfboard, add a nice enclosure, use it as your daily driver
The method scales. That's the point.