
Rmk ble keyboard
HaoboGu/rmk-ble-keyboard
A fully open source BLE/USB dual-mode GH60 keyboard powered by rmk

59-key hot-swap mechanical keyboard PCB carrier for a socketed ESP32-C6-DevKitC-1 dev board — KiCad project generated/routed by Python scripts
Smk kbd devkit is an open-source printed circuit board by mburger89, from the mburger89/SMK_Keyboard_DevKit repository on GitHub. It is a 2-layer board of 234.5 × 120.8 mm with 148 parts on 91 nets, routed with 1,072 tracks and 136 vias.
It is built around the AP2112K-3.3TRG1, MCP73831-2ACI/OT and USBLC6-2SC6, with the 1N4148W, B5819W and DMG3415U among its other parts. Its parts include 61 diodes, 60 switches, 7 capacitors, 6 resistors and 4 connectors. It belongs with the keyboards and input and microcontroller boards boards and the ESP32 and RISC-V boards of this library.
59-key (5×12, bottom row 5 + 2U + 5) hot-swap low-profile keyboard carrier
board — the same physical key layout, switch/socket footprint, and power
system as the sibling ~/esp/SMK_Keyboard/smk_kbd project, but instead of a
soldered-down ESP32-C6-MINI-1 module, this board takes a removable
Espressif ESP32-C6-DevKitC-1 dev board plugged into two female headers on
the back.
Open smk_kbd_devkit.kicad_pro in KiCad 9/10. Files are written in KiCad 8
format, which KiCad 9/10 open natively. All symbols and footprints are
embedded and provided as project-local libraries (kbd.pretty,
kbd.kicad_sym) so nothing external is required.
Same 59-key 5×12 matrix as smk_kbd: 19.05mm pitch, rows 0–3 are full
12-key rows, row 4 is 5 keys (cols 0–4) + one 2U key (plate-mounted
stabilizer, electrically column 5) + 5 keys (cols 7–11), with no switch at
column 6 in row 4.
The one real difference from smk_kbd: the MCU section. Instead of a
soldered ESP32-C6-MINI-1 module with its own antenna keepout, this board has
two 1×16, 2.54mm-pitch female headers (J10, J11) on the back —
a real ESP32-C6-DevKitC-1 dev board plugs into them, antenna and all,
entirely off this board's own copper. See "DevKitC-1 mounting" below before
assembling.
Board outline 234.5 × 120.82mm — close to smk_kbd's own
234.5 × 116.5mm, and 24.68mm shorter than an earlier revision's
234.5 × 145.5mm. That earlier height came from the header footprints'
38.1mm pin span (pin 1 to pin 16) running parallel to the board's Y axis,
stacking directly above row 0's switches; rotating both header footprints
90° swaps which axis is long, so only the headers' ~3.5mm-wide short axis
(per header) plus 18.00mm of header-to-header spacing needs to fit above
row 0 now, and the antenna keepout moved into the same region instead of
adding its own separate band. See generate_kbd_devkit.py's BOARD dict
comment for the exact DRC-driven derivation. 6× M2 mounting holes, same
positions as smk_kbd, keyed off the same key-grid pitch.
Identical to smk_kbd — Gateron KS-33 low-profile switches, Gateron
KS-2P02B01-02 hot-swap sockets: ø5.2mm center hole, 2×ø3.0mm contact
barrels at (−4.4, 4.7) and (+2.6, 5.75) from switch center, 2.55mm square
solder pads on the back copper (sockets mount on the back, switches
insert from the front). One 1N4148W (SOD-123, back side) diode per key,
COL2ROW.
GPIO map (ESP32-C6-DevKitC-1, via J10/J11)
| Function | GPIO | Header pin | vs. smk_kbd |
|---|---|---|---|
| ROW0–ROW3 (inputs) | IO0–IO3 | J10.7, J10.8, J10.12, J10.13 | unchanged |
| ROW4 (input) | IO5 | J10.4 | unchanged |
| COL0–COL2 | IO6, IO7, IO8 | J10.5, J10.6, J10.9 | unchanged |
| COL3 | IO10 | J10.10 | moved — see "Why not IO17" below |
| COL4–COL9 | IO15, IO18, IO19, IO20, IO21, IO22 | J11.4, J11.10, J11.9, J11.8, J11.7, J11.6 | unchanged |
| COL10 | IO23 | J11.5 | unchanged |
| COL11 | IO16 | J11.2 | was IO14 on smk_kbd; IO14 isn't exposed on this devkit — see "Why not IO17" below |
| VBAT sense (÷2 divider) | IO4 / ADC1_CH4 | J10.3 | unchanged |
| Native USB D−/D+ | IO12 / IO13 | J11.14 / J11.13 | unchanged (same physical ESP32-C6 pins) |
| RESET | EN | J10.2 | unchanged — the devkit's own onboard RESET button drives this; no discrete button on this carrier |
| +3V3 in | — | J10.1 | fed directly from this board's own LDO, see below |
| GND | — | J10.15, J11.1, J11.12, J11.15 | |
| unused | GPIO11, 5V, NC | J10.11, J10.14, J10.16 | GPIO11 is the one genuine free spare left; 5V/NC intentionally not connected — this board's LDO feeds 3V3 directly (see Power, below) |
| no-connect | GPIO17 | J11.3 | deliberately unconnected — see "Why not IO17" immediately below. Do not wire this pin. |
| unused | GPIO9, NC | J11.11, J11.16 | GPIO9 is reserved for the devkit's own onboard BOOT button — using it for the matrix too would conflict with that button |
Verified against Espressif's own ESP32-C6-DevKitC-1 schematic v1.4
(dl.espressif.com/dl/schematics/esp32-c6-devkitc-1-schematics_v1.4.pdf).
The devkit's onboard USB-to-UART bridge is a CP2102N-A02-GQFN28, and its VDD sits on the same shared VCC_3V3 rail as the ESP32-C6-WROOM-1 module itself — the rail this carrier board drives from its own LDO. So the bridge is powered and running whenever this board is on, whether or not anything is plugged into the devkit's own USB-UART port. Its TXD pin (an output) connects directly to the module's RXD0 = GPIO17, with no series resistor and no isolation, and a powered UART transmitter holds its line at logic-idle HIGH rather than floating. A matrix column on GPIO17 would therefore be strobing against a live output driver every scan — real bus contention, not a theoretical one. Hence the no-connect.
GPIO16 is safe for the mirror-image reason: it is the bridge's RXD pin, an input. Inputs listen and never drive the net, so sharing it with a matrix column is harmless. GPIO10 and GPIO11 appear only in the devkit's pin-header pass-through section of that schematic — no bridge, no LDO, no other onboard circuitry — so both are genuine free spares.
COL3 and COL11 swapped pins rather than COL11 simply moving to GPIO10. Which column sits on which pin is free as long as the firmware matches, so the choice was made for routing: column trunks run west-to-east across the board (COL0 at x≈56mm through COL11 at x≈266mm), and (in an earlier revision, before the headers were rotated 90° to shrink the board's height — see "Layout" above) J10 was strictly the west header and J11 strictly the east one, so a column fed from the wrong-side header had to detour around a 16-pin through-hole pad column on a 2-layer board. Putting COL3 (a west column) on J10's GPIO10 and COL11 (the easternmost column) on J11's GPIO16 left only COL4 needing such a detour.
Post-rotation, J10 and J11 sit one above the other (north/south) sharing
one X range rather than sitting west/east of each other, so that specific
"wrong-side header" framing no longer applies as stated. Re-evaluated
under the new geometry: COL3's pin (J10 pin 10) and COL11's pin (J11 pin
2) both land inside the two headers' now-shared pin-column X range, so
swapping which of the two hosts which column doesn't change whether a
header-crossing detour is needed for either — that now depends only on
each net's own trunk position, not on which header it starts from. No
materially better assignment turned up, so it was kept as-is rather than
churned for its own sake — see generate_kbd_devkit.py's own comment
above J10_NETS/J11_NETS for the full re-derivation.
Rows are on IO0–IO3 + IO5 (LP/deep-sleep-wake-capable, ADC-capable),
matching smk_kbd's convention exactly.
Identical circuit to smk_kbd — Li-ion battery + USB-C charging + hard
power switch:
One deliberate deviation from smk_kbd: no R5 (EN_LDO pulldown).
smk_kbd's reference design uses R5 to guarantee EN_LDO never floats.
This board's SW62 is a make-before-break SPDT slide switch that always
drives EN_LDO to either VSYS or GND directly — it never leaves the pin
floating, so the pulldown is redundant here. Confirmed correct by Task 3's
review; noted here so it doesn't read as an oversight against smk_kbd.
LDO output feeds the devkit's 3V3 pin directly (J10 pin 1) — this carrier's own AP2112K-3.3 LDO powers the DevKitC-1 module directly, bypassing whatever onboard regulation the devkit itself has. The devkit's own 5V pin and both onboard USB-C ports are intentionally left unconnected.
The ESP32-C6-DevKitC-1 plugs into J10 and J11 on the back of
this board, so it hangs below the carrier, out of the way of the top-side
switches (same principle as smk_kbd keeping its BOOT/RESET buttons off
the key-side surface).
Orientation matters, and it is not the intuitive one. Per Espressif's own dimension drawing and user guide, the DevKitC-1 has two headers, J1 and J3 when you look at the devkit from its component side, each numbered top-to-bottom from the antenna end. This carrier's J10 mirrors the devkit's J1 and J11 mirrors its J3. J10/J11 were rotated 90° (see "Layout" above) so they now sit one above the other — J10 is the header nearer the board's top edge, J11 the one nearer the key matrix — instead of side by side as in an earlier revision.
For those to line up, the devkit must plug in component side facing this board's back copper — i.e. the two boards' component sides face each other, with the devkit's own parts sandwiched in the gap between them. Step by step, because a wrong-orientation mount is still physically possible and it is worth being able to re-check this rather than take it on trust:
J10_NETS/J11_NETS in generate_kbd_devkit.py are the devkit's own
published header pinouts, copied pin for pin: J10 pins 1–2 are
3V3, EN, which is J1's sequence; J11 pins 13–14 are USB_DP, USB_DM = GPIO13/GPIO12 and its pins 1/12/15 are GND, which is J3's
sequence. So the requirement is J1 → J10, J3 → J11.MCU_X = x 188.0
with pin 16 at x 149.9 — pin 1 east on both, with the antenna
keepout at x 189.5–195.5 just beyond it. So the devkit's antenna end
points east. (Confirmed by render, not assumed — see the rot=270
comment at the fp_female_header_1x16 calls.)So: plug the devkit in component-side facing this board's back copper — J1 lands on J10 (top), J3 lands on J11 (bottom) — and the two boards' component sides face each other.
Both header footprints put a square pad on pin 1 (round on all the
rest), and the board carries a mirrored B.Silkscreen note near the
headers reading DEVKIT COMPONENT SIDE FACES THIS BOARD /
ITS J1 -> J10 (top) ITS J3 -> J11 (bottom). Check both before
soldering the devkit's own headers on, or before final assembly if using
pre-soldered headers.
BOOT and RESET are not reachable in the assembled product. This is a real limitation, not an oversight, and it follows directly from the orientation above: the devkit's BOOT and RESET buttons sit on its component side, which faces this carrier's back copper across the ~8.5mm header mating gap, roughly 46mm in from the board's top edge — too deep to reach through any case cutout. (An earlier revision of this README said they were reachable "through a cutout". That was wrong.) In practice:
esptool reset the
chip and enter download mode over that link without touching a button.
That is the intended path — but it has not been verified on real
hardware for this board, because no prototype exists yet.The devkit's own two onboard USB-C ports (USB-UART bridge + native USB) go unused once installed in the assembled product — they're only reachable if the devkit is pulled back out for standalone debugging.
One USB-C connector lives on this carrier board (same HRO
TYPE-C-31-M-12 + USBLC6-2SC6 ESD protection as smk_kbd), wired to:
The devkit's own onboard USB-C ports are not used in the assembled product (see "DevKitC-1 mounting" above).
The DevKitC-1 plugs into the back of this board, which puts its ESP32-C6-WROOM-1 module's PCB antenna roughly 8.5mm above this carrier's own copper. The design doc originally said no keepout was needed because "the DevKitC-1 handles its own antenna" — true of the devkit's copper, false of this board's, which was an unrelieved GND pour on both layers directly under the antenna.
The board relieves that region two ways:
antenna_keepout zone (copperpour not_allowed, plus
tracks/vias/pads not allowed) on F.Cu and B.Cu, matching how the
sibling smk_kbd project handles its own soldered module;route_kbd_devkit.py's ANT variable, so the router will not lay
a track or via there in the first place.An earlier revision also cut an Edge.Cuts notch at the board edge,
because the keepout used to sit flush against the board's top edge. After
J10/J11 were rotated 90° (see "Layout" above), the antenna region moved
into the open area east of the headers instead — x = [189.5, 195.5],
y = [22.35, 37.85] mm, 76.5mm short of the board's own east edge and
clear of column 11's switch and the nearest mounting-hole column — so
there is no board edge nearby to notch. The keepout is now a plain
interior rectangle: zone + router exclusion only, no Edge.Cuts notch.
The rectangle covers 15.5mm of the antenna module's 18mm width (inset 1.25mm from each header's own centreline — the two headers now sit 18.00mm apart in Y, where the strip was previously in X — leaving room at each header's own pad row rather than butting the keepout against it) and the antenna's full ~6mm length past pin 1. Verified on the generated board: zero tracks, vias or GND-pour fill inside that rectangle.
The board is fully routed: matrix (columns on F.Cu, rows on B.Cu, one
via per key), header fanout for every ROW/COL net, the USB differential
pair, charger, power path, LDO, and battery-sense. 1072 track segments
and 136 vias in total — GND vias (29 of them: 20 stitching vias placed
beside SMD GND pads, 8 USB-pair ground-return vias, 1 pour-island
bridging via) are part of that total, placed automatically by
route_kbd_devkit.py.
GND is carried by full-board copper pours on both layers (F.Cu and B.Cu) — fill them before export (the export script, below, does this automatically).
Routing details worth knowing if you ever re-route this board (rewritten for the rotated J10/J11 layout — see "Layout" above; the header fanout no longer needs the hand-tuned corridor/threading tricks an earlier revision did, now that J10/J11 sit one above the other sharing one X range instead of side by side):
USB_DP/USB_DM
are two plain, independent route_chain() calls from J11 pins 13/14
to U4 (already tied to the J1-side connector traces) — post-rotation,
U4 sits well west of both headers' shared pin range, so there's no pad
column to thread, and since the two pins start 2.54mm apart on the
same header row heading toward the same nearby target, generic A*
naturally lands them close together for nearly the whole run: ~0.6–
0.8mm apart along most of the path, ~17.5mm of total length skew
between the two nets' full copper (USB_DP 72.1mm, USB_DM 89.6mm — the
gap grew from an earlier ~14mm once the number of USB ground-return
via transitions dropped from 10 to 8, following Espressif's own
siting guidance more closely; no electrical consequence at USB Full
Speed, where this skew is ~0.12ns). A first attempt did hand-draw this leg
(a fixed lane through the inter-header gap) and found it crossed
already-routed header-fanout copper the static lane didn't know
about — replaced with the two independent legs, which needed no further
hand-tying.min_thickness 0.1mm,
thermal_gap/thermal_bridge_width 0.25mm, connect_pads clearance 0.25mm) are tighter than smk_kbd's own (0.25/0.5/0.5), needed
to let the pour reach GND pads boxed in by neighbouring SOT-23-5 pins.
Every SMD GND pad additionally gets an explicit stub and stitching via
from the router (stitch_gnd()), and any pour fragment left stranded
by the fill gets a bridging via (GND_BRIDGE_VIAS).DFM note — no via-in-pad on this board. Every one of the 20 SMD GND pads is stitched with a via placed beside the pad, never drilled through it. Via-in-pad on a reflow-soldered SMD pad is a recognised DFM risk (solder can wick down the drilled hole during reflow instead of forming a proper fillet), and it is treated here as a hard rule to avoid rather than an acceptable fallback: the router's same-net pad-via keepout is unconditional, with no per-net exemption. No via tenting or plugging needs to be requested at order time on this board's account.
(History, since an earlier revision of this README said otherwise: the
board did carry via-in-pad stitches — four of them, at U3 pin 2, U4 pin
2, C3 pin 2 and R2 pin 2 — placed by a via_in_pad_fallback() that has
since been deleted. A fifth pad, U2 pin 2, could not even be given one,
and shipped with no connection to the ground network at all, which
would have left the MCP73831 charger non-functional. Both problems came
from the same root cause — GND stitching running last — and both are
fixed by the ordering change described above. Verify with
check_gnd_connectivity.py, below.)
Three scripts, and they do not all run on the same Python:
| Script | Interpreter | Why |
|---|---|---|
generate_kbd_devkit.py | any Python 3 (stdlib only) | hand-emits the KiCad S-expressions |
route_kbd_devkit.py | a venv with numpy, sexpdata, shapely | A* router + shapely connectivity checks |
smk_kbd_devkit/export_fab.py | KiCad's bundled Python | needs the pcbnew module |
Neither the system Python nor KiCad's bundled Python ships numpy/shapely,
so the router needs its own environment. This repo's convention (matching
the sibling SMK_Keyboard project) is a local, git-ignored venv:
python3 -m venv .routing_venv
.routing_venv/bin/pip install numpy sexpdata shapelyFull pipeline from scratch:
rm -f tracks.sexp # drop stale routing
.routing_venv/bin/python generate_kbd_devkit.py # board with no tracks
.routing_venv/bin/python route_kbd_devkit.py # writes tracks.sexp
.routing_venv/bin/python generate_kbd_devkit.py # re-inject tracks
cd smk_kbd_devkit && \
/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 export_fab.py
kicad-cli pcb drc --severity-all --schematic-parity smk_kbd_devkit/smk_kbd_devkit.kicad_pcb
kicad-cli sch erc --severity-all smk_kbd_devkit/smk_kbd_devkit.kicad_sch
# ground connectivity + via-in-pad audit (DRC is NOT a substitute -- see
# "Current check results"). Add --suggest to get GND_BRIDGE_VIAS hints.
/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 check_gnd_connectivity.pyThe two generate_kbd_devkit.py runs are not a typo: the generator
injects tracks.sexp into the board when it exists, and the router reads
pad geometry out of the generated board, so the order is
generate → route → generate.
Note that export_fab.py rewrites smk_kbd_devkit.kicad_pro, expanding
it from the generator's ~80-line form to ~390 lines of KiCad defaults.
The compact generated form is what's committed (same as the sibling
project); KiCad regenerates the defaults on open, so nothing is lost.
smk_kbd_devkit_gerbers.zip is a build artifact and is git-ignored.
cd smk_kbd_devkit and run the export script with KiCad's bundled
Python (it fills the zones, saves, and writes
smk_kbd_devkit_gerbers.zip one directory up):
macOS: /Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 export_fab.py
(Alternative: open the PCB in KiCad, press B to fill zones, run DRC,
then File → Fabrication Outputs → Gerbers + Drill Files and zip the
output.)
Upload smk_kbd_devkit_gerbers.zip at pcbway.com → Quick Order PCB.
Order parameters (close to smk_kbd's own outline):
| Setting | Value |
|---|---|
| Board size | 234.5 × 120.82 mm |
| Layers | 2 |
| Thickness | 1.6 mm |
| Min track/spacing | 6/6 mil (design uses ≥ 0.2mm signal, ≥ 0.1mm GND-pour minimum — relaxed) |
| Min hole | 0.3 mm |
| Surface finish | HASL lead-free (or ENIG for nicer hot-swap pads) |
| Copper | 1 oz |
| Solder mask / silk | your choice |
| Panelization | single piece |
| Via tenting/plugging | not needed — the board has zero via-in-pad instances (see the DFM note above); default/your choice |
Sanity checks before paying: view the Gerbers in PCBWay's online viewer
or gerbview — confirm the GND pours are present on both layers (if
missing, you exported without filling zones) and the board outline is
closed.
Same components as smk_kbd for the matrix and power/USB/charging
circuit, plus J10/J11 in place of the MINI-1 module:
| Ref | Part | Package |
|---|---|---|
| J10, J11 | 1×16 female header, 2.54mm pitch | THT (back side) |
| U2 | MCP73831-2ACI/OT | SOT-23-5 |
| U3 | AP2112K-3.3TRG1 | SOT-23-5 |
| U4 | USBLC6-2SC6 | SOT-23-6 |
| Q1 | DMG3415U (P-MOSFET) | SOT-23 |
| D1–D59 | 1N4148W | SOD-123 |
| D60 | B5819W / SS14 Schottky | SOD-123 |
| LED1 | LED (charge) | 0603 |
| J1 | HRO TYPE-C-31-M-12 | SMD |
| J2 | JST SH SM02B-SRSS-TB (1.0mm, low-profile side entry, back side) | SMD |
| SW62 | MSK-12C02 side-actuated slide SPDT, lever exits rear edge | SMD |
| R1, R2 | 5.1 kΩ | 0603 |
| R3 | 2.0 kΩ | 0603 |
| R4 | 1 kΩ | 0603 |
| R6, R7 | 1 MΩ | 0603 |
| C1, C2 | 4.7 µF | 0603 |
| C3 | 1 µF | 0603 |
| C4, C7 | 10 µF | 0603 |
| C5, C8 | 100 nF | 0603 |
| — | ESP32-C6-DevKitC-1 (not soldered — plugs into J10/J11) | dev board |
Not present on this board (vs. smk_kbd): R5 (see "Power" above), R8/C6
(ESP32 EN decoupling — the devkit handles its own EN externally), SW60/SW61
(RESET/BOOT buttons — the devkit's own onboard buttons are used instead).
What's actually been checked, and what hasn't. This board has been through a final whole-branch review that found real cross-task defects; everything below reflects the state after those fixes.
Four of these were "the pieces don't fit together as a product" bugs that each individual task's own review could not see, because each task was internally consistent and DRC-clean:
The USB-C connector was not at a board edge. J1 had been placed at smk_kbd's own coordinates, which are relative to that board's top edge (y1 = 22.0). This board's top edge moved to y1 = -7.0 in Task 2 to fit the DevKitC-1 header, and nothing was re-derived — leaving J1 stranded 28.5mm inside the board, surrounded by solid FR4 and GND pour. No cable could have been plugged in. J1 now sits flush with the real top edge (connector body face 0.05mm inside the outline, byte-for-byte the same relationship smk_kbd has).
The slide power switch's actuator was buried 19mm inside the board, for the same reason. SW62 now sits against the top edge with its lever pointing out through it.
There was no antenna keepout and the plugged-in module's antenna sat over solid GND pour on both layers. See "Antenna keepout" above.
The whole power/USB section was placed on a router-convenience grid, not with electrical intent — the USB ESD clamp 150mm from the connector it protects, bypass caps 20–29mm from the pins they decouple, and the two USB data lines on opposite layers ~20mm apart for ~100mm. The section was re-placed as one coherent cluster. Measured on the generated board:
| before | now | |
|---|---|---|
| J1 → U4 (ESD) | ~150mm | 11.3mm |
| C1 → U2 pin 4 (VBUS) | 20–29mm | 3.6mm |
| C2 → U2 pin 3 (BAT+) | 20–29mm | 4.9mm |
| C3 → U3 pin 1 (VSYS in) | 20–29mm | 4.0mm |
| C4 → U3 pin 5 (+3V3 out) | 20–29mm | 2.7mm |
| C8 → J10 pin 1 (+3V3) | — | 3.2mm |
| C5 → J10 pin 3 (VBAT ADC) | ~90mm | 3.0mm |
| USB_DP total copper | 189mm | 90.7mm |
| USB_DM total copper | 223mm | 99.7mm |
| DP/DM run as a same-layer pair | none | 80.7 of 90.7mm within 3mm |
GPIO17 had real bus contention with the devkit's onboard CP2102N USB-UART bridge and is now a no-connect; COL3/COL11 moved to GPIO10 and GPIO16. See "Why not IO17" above.
Three schematic↔PCB Value mismatches (J2, SW54, U2) — the two
builders each spelled component values out independently. Both now read
from one shared VALUES dict in the generator, the same way net names
already came from one NETS/NETI table.
The DevKitC-1 mounting orientation was ambiguous and the BOOT/RESET-access claim was wrong. Both corrected, with the reasoning written out and a silkscreen marker added. See "DevKitC-1 mounting".
kicad-cli pcb drc --severity-all --schematic-parity on the
zone-filled board (the state that matters — an unfilled-zone run shows
every GND pad as "unconnected" purely because the pour hasn't been
computed, which export_fab.py does as its first step):
check_gnd_connectivity.py (run with KiCad's bundled Python, on
the zone-filled board) — this is the check that actually matters for
ground, and DRC is not a substitute for it. DRC reports a stranded
ground pad only indirectly, as a generic Zone [GND] F.Cu ↔ Zone [GND] B.Cu missing connection, which reads like a stray copper sliver; an
earlier revision of this README recorded exactly that finding as a
benign, documented gap when in fact the orphaned cluster contained
U2 pin 2, the MCP73831 charger's ground pin, with no connection to
ground at all. This script asks KiCad's own connectivity engine
directly instead, clustering every GND pad, via, track and every
individual zone-fill island, and reporting which cluster each pad is
in. Current result:
GetUnconnectedCount() == 0.--suggest proposes bridge points for GND_BRIDGE_VIAS if a future
re-route ever strands a fragment again.silk_overlap/silk_edge_clearance
finding on the way):
courtyards_overlap — every SWn/Dn matched pair (each key's
diode tucked under its own switch footprint by design).lib_footprint_mismatch — inherent to this project's
hand-emitted-footprint codegen (absolute pad coordinates baked in
at instantiation vs. a zero-position library export); smk_kbd's
own board has the identical 4 warnings for the same reason.silk_edge_clearance, 1 silk_overlap, 3 silk_over_copper —
silkscreen text/outlines clipped by the board edge or by each
other. Several of these are intended: J1's and SW62's own
outlines deliberately cross the edge, because those parts sit on
it. The rest are the title text at the bottom of the board and one
reference-designator collision (U3/C4). Cosmetic only.net_conflict (KiCad auto-names each switch-to-diode net
Net-(Dn-Pad2) where the generator names it N_RrCc — a naming
difference, not a wiring one) and 6 extra_footprint (the M2
mounting holes, which have no schematic symbol). This is the same
benign baseline the final review characterised; the three real
mismatches it found are gone.kicad-cli sch erc --severity-all — 4 findings, unchanged from
the documented baseline: 3 power_pin_not_driven (expected — every
power port in this project's symbol convention is power_in with no
explicit power_out driver) and 1 isolated_pin_label on EN
(expected by design — the devkit's own onboard reset button drives it,
with no local driver on this sheet).PinSocket_1x16_P2.54mm_Vertical — but none of that substitutes for a
real bring-up. In particular the mounting orientation and the
flash-without-BOOT-button claim above are reasoned, not observed.SMK_BOARD_* variant for this GPIO
map (in the smk firmware repo's Main.swift) is separate follow-up
work. Note that its column map must match this board's new
assignment (COL3 = GPIO10, COL11 = GPIO16, GPIO17 unused).| Qty | Value | Footprint | References |
|---|---|---|---|
| 59 | 1N4148W | kbd:D_SOD-123_Back | D1, D2, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D20, D21, D22, D23 +43 |
| 58 | KS-33 | kbd:SW_Gateron_KS33_HS | SW1, SW2, SW10, SW11, SW12, SW13, SW14, SW15, SW16, SW17, SW18, SW19, SW20, SW21, SW22, SW23 +42 |
| 6 | M2 | kbd:MountingHole_M2 | H1, H2, H3, H4, H5, H6 |
| 2 | 100n | kbd:RC_0603 | C5, C8 |
| 2 | 10u | kbd:RC_0603 | C4, C7 |
| 2 | 1M | kbd:RC_0603 | R6, R7 |
| 2 | 1x16 | kbd:PinSocket_1x16_P2.54mm | J10, J11 |
| 2 | 4.7u | kbd:RC_0603 | C1, C2 |
| 2 | 5.1k | kbd:RC_0603 | R1, R2 |
| 1 | 1k | kbd:RC_0603 | R4 |
| 1 | 1u | kbd:RC_0603 | C3 |
| 1 | 2.0k | kbd:RC_0603 | R3 |
| 1 | 2U | kbd:SW_Gateron_KS33_HS | SW54 |
| 1 | AP2112K-3.3 | kbd:SOT-23-5 | U3 |
| 1 | B5819W | kbd:D_SOD-123_Back | D60 |
| 1 | CHG | kbd:LED_0603 | LED1 |
| 1 | DMG3415U | kbd:SOT-23 | Q1 |
| 1 | JST-SH-2 SM02B | kbd:JST_SH_SM02B_2pin_Back | J2 |
| 1 | MCP73831-2ACI/OT | kbd:SOT-23-5 | U2 |
| 1 | PWR MSK-12C02 | kbd:SW_Slide_MSK12C02 | SW62 |
| 1 | TYPE-C-31-M-12 | kbd:USB_C_Receptacle_HRO_TYPE-C-31-M-12 | J1 |
| 1 | USBLC6-2SC6 | kbd:SOT-23-6 | U4 |
It measures 234.5 × 120.8 mm and has 2 copper layers, 1.6 mm thick.
148 parts, built around the AP2112K-3.3TRG1, MCP73831-2ACI/OT, USBLC6-2SC6 and 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 mburger89/SMK_Keyboard_DevKit repository on GitHub, at the commit this page was made from: the board is smk_kbd_devkit/smk_kbd_devkit.kicad_pcb and its schematic smk_kbd_devkit/smk_kbd_devkit.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 mburger89 before reusing the design.

HaoboGu/rmk-ble-keyboard
A fully open source BLE/USB dual-mode GH60 keyboard powered by rmk

mburger89/SMK_macro_pad
4x4 wireless macro pad — RP2040 + Raspberry Pi RM2. KiCad project generated from Python; includes a 42%-smaller 'mini' variant with back-mounted electronics.


JanClyde05/Road-Accident-Monitoring-System
This directory contains the planning, specification, and initial KiCad project for RAMS Wearable PCB Revision C. Do not fabricate from this directory yet -- manual schematic wiring and DRC verification are still needed.

HaoboGu/rmk-ble-keyboard-65
An open source BLE/USB dual-mode %65 keyboard using Ebyte's E73 nRF52840 module, powered by rmk.

aryanthomare/SmartKnob
A programmable force-feedback knob for your computer. Custom motor-driver PCB, closed-loop haptic firmware, a native Windows companion app and a parametric 3D-printed enclosure, all designed in house.


tsmiltonAgents/pl60c-dmx-wakeup
A small ESP32-S3 board that drives the wired DMX512 input of a Neewer PL60C panel light so the light can be ramped up slowly in the morning (sunrise alarm). Rev A, 2026-09-02.