
PSLab
fossasia/pslab-hardware
PSLab Hardware Design and Schematics https://pslab.io

A custom wireless low-profile mechanical keyboard powered by ZMK.
Keyboard is an open-source printed circuit board by ItzBuble, from the ItzBuble/Bubleboard repository on GitHub. It is a 2-layer board of 404 × 118.3 mm with 226 parts on 165 nets, routed with 697 tracks and 46 vias.
It is built around the 1N4148W. Its parts include 105 diodes, 4 resistors, 3 capacitors and 2 ICs. It belongs with the keyboards and input boards of this library.

A custom full-size (104-key) mechanical keyboard with a hand-designed PCB, a rotary encoder, an e-ink display and ZMK firmware. It was built as my graduation project (maturitní práce) at SPŠ a OA Uherský Brod, field Mechanik elektrotechnik, 2026.
| Part | Component |
|---|---|
| Microcontroller | Seeed XIAO BLE nRF52840 |
| I/O expanders | 2× MCP23017 (SSOP), I²C with 10 kΩ pull-ups |
| Switch sockets | 104× Kailh Choc V1 hot-swap |
| Matrix diodes | 105× 1N4148W |
| Rotary encoder | Bourns PEC11L, push button wired into the matrix (Column 16 / Row 0) |
| Display | nice!view, connected over SPI |
| Decoupling | 2× 100 nF and 1× 10 µF |
The PCB was made by JLCPCB and assembled by hand. The schematic and PCB use the marbastlib library, and the key positions come from a Keyboard Layout Editor file imported into KiCad.
The firmware is built on ZMK (Zephyr). The matrix is defined in the keyboard's devicetree: columns 0–15 and 16–20 are spread over the two MCP23017 expanders, rows 0–5 are on the second expander, and all pins use GPIO_ACTIVE_LOW with pull-ups.
Encoder functions:
Firmware is built with GitHub Actions using the standard ZMK user-config workflow. Push a commit (or run the workflow manually from the Actions tab) and download the firmware from the build artifacts.
The build uses:
seeeduino_xiao_blebuble_board plus nice_view_adapter and nice_viewThe ZMK build needs config/, boards/ and zephyr/module.yml to stay in the repository root.
The case was modelled in Autodesk Fusion and 3D printed. It is printed in two halves that are glued together in the middle, with alignment holes and pegs. The PCB is mounted on six screw posts, and a separate display holder (adapted from a freely available model) slides into V-shaped tabs in the top right corner. The keycaps are also 3D printed, adapted from existing models.
Power consumption while connected to a PC (no backlight, no heavy load):
| Meter | V | A | W |
|---|---|---|---|
| Custom USB meter | 5.092 | 0.005 | 0.027 |
| Commercial USB meter | 5.1080 | 0.0050 | 0.0252 |
Input latency, measured with a 240 fps phone camera (1 frame ≈ 4.167 ms):
| Keyboard | Frames | Latency |
|---|---|---|
| SteelSeries Apex Pro (magnetic) | 8 | 33.3 ms |
| Bubleboard | 11 | 45.8 ms |
| Razer Ornata Chroma (membrane) | 17 | 70.8 ms |
| School membrane keyboard | 19 | 79.2 ms |
Martin Janíček. Střední průmyslová škola a obchodní akademie Uherský Brod, 2026.
| Qty | Value | Footprint | References |
|---|---|---|---|
| 105 | 1N4148W | Diode_SMD:D_SOD-123 | D1, D10, D11, D12, D13, D14, D15, D16, D17, D18, D100, D101, D102, D103, D104, D105 +89 |
| 6 | MountingHole_3.2mm_M3_ISO14580 | MountingHole:MountingHole_3.2mm_M3_ISO14580 | MH1, MH1, MH1, MH1, MH1, MH1 |
| 4 | 10k | Resistor_SMD:R_1206_3216Metric_Pad1.30x1.75mm_HandSolder | R1, R2, R3, R4 |
| 2 | 50V 100nF | Capacitor_SMD:C_1206_3216Metric_Pad1.33x1.80mm_HandSolder | C1, C2 |
| 2 | MCP23017_SO | Package_SO:SOIC-28W_7.5x17.9mm_P1.27mm | U1, U2 |
| 1 | '" | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH68 |
| 1 | * | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH35 |
| 1 | ,< | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH83 |
| 1 | - | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH36 |
| 1 | -_ | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH28 |
| 1 | .> | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH84 |
| 1 | .Del | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH104 |
| 1 | / | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH34 |
| 1 | /? | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH85 |
| 1 | 0) | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH27 |
| 1 | 0Ins | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH103 |
| 1 | 1 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH88 |
| 1 | 1! | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH18 |
| 1 | 2 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH89 |
| 1 | 2@ | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH19 |
| 1 | 3 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH90 |
| 1 | 3# | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH20 |
| 1 | 4 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH72 |
| 1 | 4$ | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH21 |
| 1 | 5 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH73 |
| 1 | 5% | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH22 |
| 1 | 50V 10uF | Capacitor_SMD:C_1206_3216Metric_Pad1.33x1.80mm_HandSolder | C3 |
| 1 | 6 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH74 |
| 1 | 6^ | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH23 |
| 1 | 7 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH53 |
| 1 | 7& | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH24 |
| 1 | 8 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH54 |
| 1 | 8* | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH25 |
| 1 | 9 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH55 |
| 1 | 9( | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH26 |
| 1 | ;: | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH67 |
| 1 | =+ | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH29 |
| 1 | A | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH58 |
| 1 | Arrow Down | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH101 |
| 1 | Arrow Left | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH100 |
| 1 | Arrow Right | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH102 |
| 1 | Arrow Up | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH87 |
| 1 | B | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH80 |
| 1 | Backspace | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH30 |
| 1 | C | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH78 |
| 1 | Caps Lock | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH57 |
| 1 | D | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH60 |
| 1 | Delete | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH51 |
| 1 | E | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH40 |
| 1 | ESC | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH1 |
| 1 | End | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH52 |
| 1 | Enter | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH69 |
| 1 | F | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH61 |
| 1 | F1 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH2 |
| 1 | F10 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH11 |
| 1 | F11 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH12 |
| 1 | F12 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH13 |
| 1 | F2 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH3 |
| 1 | F3 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH4 |
| 1 | F4 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH5 |
| 1 | F5 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH6 |
| 1 | F6 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH7 |
| 1 | F7 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH8 |
| 1 | F8 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH9 |
| 1 | F9 | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH10 |
| 1 | Function | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH97 |
| 1 | G | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH62 |
| 1 | H | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH63 |
| 1 | Home | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH32 |
| 1 | I | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH45 |
| 1 | Insert | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH31 |
| 1 | J | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH64 |
| 1 | K | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH65 |
| 1 | L | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH66 |
| 1 | Left Alt | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH94 |
| 1 | Left Control | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH92 |
| 1 | Left Shift | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH75 |
| 1 | Long + | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH56 |
| 1 | Long Enter | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH91 |
| 1 | M | PCM_marbastlib-choc:SW_choc_v1_HS_CPG135001S30_1u | CH82 |
The 194 most common of 226 parts.
It measures 404 × 118.3 mm and has 2 copper layers, 1.6 mm thick.
226 parts, built around the 1N4148W. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the ItzBuble/Bubleboard repository on GitHub, at the commit this page was made from: the board is Hardware/Keyboard.kicad_pcb and its schematic Hardware/Keyboard.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Its repository states no licence, so its author keeps all rights: ask ItzBuble before reusing the design.

fossasia/pslab-hardware
PSLab Hardware Design and Schematics https://pslab.io

pierrechevalier83/ferris
A low profile split keyboard designed to satisfy one single use case elegantly



MickiusMousius/Rolio46Keyboard
A split wireless keyboard based on the Corne and Sofle


sadekbaroudi/bgkeeb
This is a reversible 100mm x 100mm pcb with support for mx or choc switches (no hotswap), per key led, rotary encoders, and exposes the SDA/SCL/GND/VCC lines in the form of the pimoroni trackball pins (meaning you can…
