I designed the circuit board shown above to make a USB controller for a laptop keyboard with up to 34 FPC pins using the RP2350 Stamp XL from Solder Party. The Stamp, USB Breakout, and FPC Breakout boards come with all the surface mount components already attached, leaving you to do the easy soldering of header pins. All associated files for this project are at my USB_Laptop_Keyboard_Controller GitHub repository in the StampXL_34Pin_Keyboard folder.
The Stamp XL uses a Raspberry Pi 2350B microcontroller chip with 48 GPIO’s, compared with the 26 GPIO’s available on a Raspberry Pi Pico. I've chosen to only use 34 of the Stamp's GPIO's since this is more than enough for most keyboards and it reduces the board size.
The carrier board was designed in KiCad and measures 2.82 x 2.68 inches (71.7 x 68.0mm). The KiCad board file “2350B_Keybd_Cntrl.kicad_pcb” can be sent to fab houses like OSHPark.com($38/3 boards) and Eurocircuits.com(€47/1 board). The zipped Gerber file “2350B_Keybd_Cntrl.zip” can be sent to fab houses like JLCPCB.com and PCBWay.com. With slow economy shipping to the US, the cost is $10 for 10 boards. The above files are available for download at my repo. The 3 components that attach to my carrier board are shown below prior to soldering.
FPC breakout boards are very common and can be found at Amazon,EBay, and AliExpress among other sellers. Pick the FPC breakout board pitch and pin count that matches your keyboard FPC cable. These boards will have 2 rows of 2.54 mm pitch through-hole pads. The 32 pin FPC breakout board shown above was needed for a Lenovo E550 keyboard. The E550 FPC cable had locking notches that had to be trimmed off with scissors in order to use the generic connector on the FPC adapter board. Avoid boards that come with the header pins already soldered because this limits how the board can be mounted. The typical breakout board uses FPC connectors with bottom contacts. Determine if your FPC cable will end up with its contacts facing up or down when routed to the carrier board. It’s common for the FPC cable to be flipped back 180 degrees when the FPC connector is underneath the keyboard. The pictures below show when to solder the FPC breakout board facing up or facing down so it can handle bottom contacts or top contacts on the cable. The left picture shows the cable will be flipped over so the carrier board ends up mounted underneath the keyboard. The right picture shows the FPC cable is not flipped and the carrier board will be mounted under the palm rest.
The Type C USB connector board from Amazon,Sparkfun, and AliExpress can be installed directly on the carrier board with 6 header pins or with 4 wires that allow the USB connector to be at the back or side of your keyboard enclosure. Only 4 wires (VBUS, GND, D+, D-) are needed because CC1 and CC2 are connected to 5.1K resistors on the USB C board and are not used by the Stamp XL. You can run wires to a USB Type A,Mini-B, or microB breakout connector if you don’t want to use USB C.
GPIO to FPCYou must attach the Stamp XL to the board with 2 mm pitch header pins (not the standard 2.54 mm pitch). The picture below shows how GPIO’s 0 through 33 are connected to the 2 rows of pads for the FPC breakout board. If your FPC connector has less than 34 pins, solder it any way you want. The software will figure out which GPIO signals are connected to key switches and which are no-connects. My code only deals with GPIO numbers, never pin numbers. This eliminates the problem of defining which end of the connector is pin 1.
I used Circuit Python instead of Arduino C code to program the Stamp XL board. A good starting point for learning Circuit Python is at circuitpython.org. Select the “Get Started” box. The following is a synopsis of the steps that they provide.
To enter into loader mode, hold the boot push button down while plugging in the USB cable. A new folder will show as a USB drive, named RP2350.
If things are acting weird or you want to start with a clean slate, copy the flash_nuke.uf2 file (downloaded here) over to the USB drive folder. The drive will blink out and then return all clean. I did this because the Stamp XL arrived with an old version of Python installed.
Start with the circuitpython.org/downloads page, then select the Stamp XL. Download and copy the latest Adafruit Circuit Python uf2 over to the drive folder. The drive will blink out and return as CIRCUITPY with an empty lib folder. The code.py file is a one-line “Hello world” program. You will be replacing this file later.
Go to circuitpython.org/libraries and download/unzip the library bundle that matches the version of Python you are using. The bundle includes lots of libraries but you only need to copy the Adafruit_HID folder into the lib folder on the CIRCUITPY drive.
As you write and debug your Python code, you will need an editor. I prefer Thonny and it can be downloaded at thonny.org/.
Keyboard Matrix DecoderDownload my Matrix_Decoder_RP2350B.py code. In Thonny, select File, Open, This Computer and navigate to the Matrix Decoder code you downloaded. This will bring it up in the editor and you can read through the comments to get an idea of how it works. In Thonny, select the “Stop” icon to halt the "Hello World" program. Next select File, Save As, CircuitPython Device. Overwrite the "Hello World" code.py file with your Thonny code (renamed to code.py). With the keyboard not connected, hit the "Stop" icon and then "Run" icon. To prove the program will send numbers, use a wire to short two GPIO's together. Disconnect the USB cable to power down and hook up the keyboard FPC cable. When you reconnect the USB cable, the code will start running again but it's best to hit the "Stop" icon and then the “Run” icon so you can watch the Thonny console window. If the program sends two numbers before any keys are pressed, the GPIO pins are probably connected to grounds or LEDs in the keyboard. These GPIO's must be skipped by removing them from the I_O array starting on line 33 in the code. Once you have no numbers reported when you hit "Run", you're ready to make a key matrix.
Bring up another editor like Notepad++ and load the blank keyboard_pin_list text file that has all the possible keyboard keys. Place the cursor to the far right of the first key in the list. This is where the Python program will send the GPIO numbers when you push a key. After each key press and release, the program will send a down arrow to position the cursor for the next key. Once you have GPIO numbers for all keys, you can use the manual procedure described in step 12 of my Keyboard Instructable to determine the row and column GPIO's. This procedure is given in the following example for a Lenovo E550.
Determine the Columns - Lenovo E550 Example
To create the key matrix, you must determine which GPIO's connect to the columns and the remainder are the rows. The modifier keys, (Control, Alt, Shift, GUI, and Fn) are a good place to start. Note that GUI is the Windows key on a PC or the Clover key on a Mac. The Lenovo E550 key list is at my repo and the GPIO connections were determined using the Matrix_Decoder_RP2350B.py routine. The left and right control keys show 2 and 8 are columns and 17 is a row. The Shift keys show 0 and 2 are columns (we already knew 2) and 3 is a row. The Alt keys show 1 and 11 are columns and 18 is a row. GUI shows 0 is a column (which we already knew) and 19 is a row. Lenovo keyboards typically give the Fn "Hot Key" its own two signals so 28 and 29 will be treated separately. So far, the columns we know are 0, 1, 2, 8, 11 and the rows we know are 17, 3, 18, and 19. Scan down the list and find when these rows are used and if they reveal a new column. 18 is used with 5 for the Calc key so 5 is a column. Next start with “A”, looking for numbers not yet known to be rows or columns. “A” uses 6 and 4 so look at other keys that use either 6 or 4. “Z” uses 6 and 2 but we already know 2 is a column so 6 must be a row. Going back to “A”, if 6 is a row, 4 is a column. Now the known columns are 0, 1, 2, 4, 5, 8, 11. Typically there are 8 columns so there’s 1 more to find. Going down the list, “E” uses 10 and 7 but “C” tells us that 10 is a row so 7 must be the last column. To confirm all columns are identified, put all the keys into a matrix like the one shown belowe. If you find a key can't be placed, then you have another column to figure out. I picked 29 as a column for Fn but 28 would also have worked. This Lenovo E550 Key matrix places each key name at the intersection of the row and column GPIO's given in the keyboard_pin_list text file.
I used KMK to turn the matrix table into a USB keyboard routine. It's open-source firmware normally used for mechanical keyboards but it also works for laptop keyboards (once you have the matrix). KMK uses CircuitPython which is why I also wrote the matrix decoder code in CircuitPython. All of the hard work of scanning key switches and communicating over USB is embedded in KMK. All you need to do is edit my KMK example keyboard code with your key matrix information. Getting started with KMK is described at their KMK GitHub repo which I will detail below.
For step 1, you already have CircuitPython installed.
For step 2, click on “get an up-to-date copy of KMK”.
For step 3, unzip the kmk_firmware-main folder. It has many folders and files. Copy the KMK folder and boot.py to the CIRCUITPY drive on the Stamp.
For step 4, use code_LenovoE550.py as your starting point. Load the code into Thonny and edit as described below:
Change keyboard.col_pins to list the GPIO column pins left to right across the top of your matrix.
Change keyboard.row_pins to list the GPIO row pins top to bottom along the side of your matrix.
Change the keyboard.keymap matrix for the base layer per the table you created. The basic keycode names (and aliases) that KMK uses are given here plus you can see the names I used in the E550 code example. If you have a keypad, these keys must start with KC.P followed by the name or number from the keycode basic list. Each keycode name (except FN) must be preceded with KC. Put KC.NO in the matrix locations that have no key assigned.
Copy the layer 0: Base Layer over to the layer 1: Fn Media Layer. Now look at your keyboard’s media keys to see which (if any) you want to make work. The media keycode names are given here. For the Lenovo E550, I wanted the Mute, Volume Down, Volume Up, Brightness Down, and Brightness Up keys to work when Fn is pressed. These media keycode names replaced the corresponding Function key names at F1, F2, F3, F5, and F6 in the matrix.
Click the “Stop” icon in Thonny just to make sure it's ready for new code. Save your code to your computer and then save it to the Stamp’s CIRCUITPY drive with the name "code.py" (this will overwrite your previous code.py that was the matrix decoder).
Now click the “Run” icon in Thonny and see if you have any typos reported in the console window. Once all typos are cleared, you should be able to type on the keyboard to test if all the keys work.
For normal USB keyboard operation (without Thonny), you can just plug in the USB cable. This will bring up a CIRCUITPY drive folder which you can close. The Stamp will start running the code as soon as USB power is applied.
Example Keyboard EnclosureYou can install the keyboard in the original laptop base or make a new base with a 3D printer but I chose to use plywood to make a keyboard bridge. The bridge sits over my new Lenovo IdeaPad 5 laptop (which has a terrible keyboard). I don’t need the trackpoint or touchpad to work since I use a wireless mouse but this would be an interesting future project. This picture shows the finished laptop keyboard bridge.
The top board for the bridge is 3/16” 3 ply finish grade plywood. This board was sanded and may be stained and/or varnished in the future. I placed the keyboard on it and traced the outline, then cut it out with a jig saw. The board underneath is ½” 4 ply standard plywood. The keyboard sits on this board and could be screwed down with the threaded fasteners on the back of the keyboard. I chose to just make the keyboard opening in the top board a tight fit so it can’t move. The 2350 Stamp XL controller board sits in a cut out. The bottom board is glued to the middle board and provides a way to screw down the controller board. The controller board cutout could have been made with a router, eliminating the need for the bottom board. The FPC cable and USB cable each need chiseled openings cut in the wood. These pictures show the 3 plywood boards and the cable connections.
The side boards for the bridge are cut from ¾” solid pine and glued/nailed to the 1/2" plywood. They provide just enough clearance so the bridge doesn’t touch the laptop underneath. The controller boards’ USB cable is plugged into the laptops connector which is accessed through a cutout on the right side. The left side has a larger cutout for the other USB laptop connectors. Screws hold the top plywood piece in place should I ever need to get to the control board underneath. I cut rubber shelving material and glued it to the bottom of the side boards so they can't slide on the desk.
Raspberry Pi PicoIf you have a laptop keyboard with 26 or fewer pins on it's FPC cable, then you can use the Raspberry Pi Pico instead of the RP2350 Stamp XL. The circuit board shown below has 26 surface mount pads for a 1mm or 0.5mm pitch FPC connector. If you're handy with a soldering iron and want to use the Pico, this provides a smaller and cheaper USB keyboard controller. The Pico 26 pin keyboard controller PDF, board files, and code are at my repo.
If you have any questions about the RP2350 Stamp XL or Pico USB keyboard controllers, let me know in the comment section below.




_3u05Tpwasz.png?auto=compress%2Cformat&w=40&h=40&fit=fillmax&bg=fff&dpr=2)
Comments