Skip to the content
PCBKitNew board
2 layers234.5 × 120.8 mmSmk kbd devkit, the printed circuit board in 3D

59-key hot-swap mechanical keyboard PCB carrier for a socketed ESP32-C6-DevKitC-1 dev board — KiCad project generated/routed by Python scripts

Size
234.5 × 120.8 mm
Copper layers
2
Thickness
1.6 mm
Parts
148
Nets
91
Pads
531
Tracks, vias
1,072, 136
Pours
3
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
14 Aug 2026
Updated
14 Aug 2026

Overview

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.

Main components

9

README

README.md

59-Key Matrix Carrier for ESP32-C6-DevKitC-1 — KiCad Project

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.

Layout

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.

Switch / socket footprint

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.

Electrical design

GPIO map (ESP32-C6-DevKitC-1, via J10/J11)

FunctionGPIOHeader pinvs. smk_kbd
ROW0–ROW3 (inputs)IO0–IO3J10.7, J10.8, J10.12, J10.13unchanged
ROW4 (input)IO5J10.4unchanged
COL0–COL2IO6, IO7, IO8J10.5, J10.6, J10.9unchanged
COL3IO10J10.10moved — see "Why not IO17" below
COL4–COL9IO15, IO18, IO19, IO20, IO21, IO22J11.4, J11.10, J11.9, J11.8, J11.7, J11.6unchanged
COL10IO23J11.5unchanged
COL11IO16J11.2was IO14 on smk_kbd; IO14 isn't exposed on this devkit — see "Why not IO17" below
VBAT sense (÷2 divider)IO4 / ADC1_CH4J10.3unchanged
Native USB D−/D+IO12 / IO13J11.14 / J11.13unchanged (same physical ESP32-C6 pins)
RESETENJ10.2unchanged — the devkit's own onboard RESET button drives this; no discrete button on this carrier
+3V3 in—J10.1fed directly from this board's own LDO, see below
GND—J10.15, J11.1, J11.12, J11.15
unusedGPIO11, 5V, NCJ10.11, J10.14, J10.16GPIO11 is the one genuine free spare left; 5V/NC intentionally not connected — this board's LDO feeds 3V3 directly (see Power, below)
no-connectGPIO17J11.3deliberately unconnected — see "Why not IO17" immediately below. Do not wire this pin.
unusedGPIO9, NCJ11.11, J11.16GPIO9 is reserved for the devkit's own onboard BOOT button — using it for the matrix too would conflict with that button

Why not IO17

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.

Power

Identical circuit to smk_kbd — Li-ion battery + USB-C charging + hard power switch:

  • USB-C (HRO TYPE-C-31-M-12) with 5.1kΩ CC pulldowns, ESD protection via USBLC6-2SC6.
  • Charger: MCP73831-2, 2.0kΩ PROG = 500mA charge current. Charge LED on STAT.
  • Power path: VBUS → B5819W Schottky → VSYS; battery → DMG3415U P-FET → VSYS. USB present = board runs from USB while battery charges; otherwise runs from battery.
  • ON/OFF: MSK-12C02 side-actuated slide switch, drives the AP2112K-3.3 LDO's EN pin between VSYS and GND.
  • Battery voltage divider (1MΩ/1MΩ + 100nF) into IO4 (via J10 pin 3).
  • Battery connector: JST-SH 2-pin, back side.

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.

DevKitC-1 mounting — read before assembling

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:

  1. Which devkit header is which is an electrical fact, not a guess. 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.
  2. The antenna direction is fixed, and it fixes the in-plane rotation. Both devkit headers are numbered from the antenna end, and on this carrier both J10 and J11 put pin 1 at 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.)
  3. One degree of freedom is left, and it is a real ambiguity. Having pinned the antenna east, the only remaining choice is a 180° flip about that same east–west long axis. That flip keeps the antenna pointing east and keeps pin 1 at the east end of both headers — so neither the antenna nor the square pin-1 pads distinguish the two candidate mounts. Rotating the headers 90° did not remove this ambiguity, it moved it onto a different axis: what the flip now swaps is which header is north vs south.
  4. What makes it decidable is that the flip swaps two things at once. The devkit is a rigid board, so flipping it about the east–west axis simultaneously (a) exchanges which of J1/J3 lands on the northern header, and (b) exchanges which of its two faces points at the carrier. Pick either and the other follows.
  5. Working it through. The devkit hangs off the back of the carrier. Viewed from its own component side with the antenna up, the devkit has J1 on the left and J3 on the right (Espressif's dimension drawing). Hold it in the mounting position instead — antenna east, on the back side of the carrier — and requiring J1 to land on J10, the northern header, leaves exactly one of the two flip states: the one with the devkit's component side pointing back up at the carrier. The other flip state puts J3 north and J1 south, which mis-seats every net on the board.

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:

  • Flashing normally should not need either button. The carrier's USB-C goes to GPIO12/GPIO13, the ESP32-C6's native USB, and the chip's built-in USB-Serial-JTAG peripheral lets 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.
  • If you do want physical buttons, both nets are already on the headers: EN is on J10 pin 2 and GPIO9 (BOOT) on J11 pin 11 (currently a no-connect), each within a pin or two of a GND pin. Wiring your own momentary switches there is the recommended fallback.
  • Otherwise the devkit has to be unplugged from J10/J11 to reach its own buttons.

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.

USB routing

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:

  1. The MCP73831 charger circuit, for battery charging.
  2. The DevKitC-1's native-USB GPIO12/GPIO13 (via J11 pins 14/13) — for flashing and USB serial through this one connector.

The devkit's own onboard USB-C ports are not used in the assembled product (see "DevKitC-1 mounting" above).

Antenna keepout

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:

  • a named 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.

Routing — DONE, DRC-clean

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):

  • The header ROW/COL fanout is a single generic A* leg per net**, no hand-drawn corridors. A track leaving any header pin straight along the header's own short axis (its pins differ only in X within a row) never crosses a neighbouring pin, so every net just needs to clear its own header's row before it's free to route anywhere. The only real ordering constraint found by testing: routing the header's farthest targets first, and routing the power/USB cluster's own tightly-packed local hops (LED1/R4, in particular) before the header fanout, so those short local hops claim their space before a much longer, more flexible header leg has a chance to run through it.
  • The USB data pair from J11 to the ESD chip needs no hand-tied geometry at all, unlike the pre-rotation file (which hand-drew this entire leg to thread J10's own 0.84mm inter-pad gaps) and unlike the reference board's own early attempt (two independent A* legs that ended up on opposite layers, ~20mm apart for ~100mm). 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.
  • The GND zone's fill parameters here (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).
  • GND stitching runs FIRST in the router pipeline, before any net is A*-routed, and that ordering is load-bearing rather than incidental. Each SOT-23-5/6 or 0603 GND pin's only escape is the narrow corridor straight out from between its own flanking pins; run late, that corridor has already been walled off by whichever power or signal trunk A* happened to lay 0.9–1.1mm away, and five pads (U2.2, U3.2, U4.2, C3.2, R2.2) had no legal via spot left at all. Stitching first reserves those corridors as real obstacles and A* routes the trunks around them.

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.)

Running the generator and router

Three scripts, and they do not all run on the same Python:

ScriptInterpreterWhy
generate_kbd_devkit.pyany Python 3 (stdlib only)hand-emits the KiCad S-expressions
route_kbd_devkit.pya venv with numpy, sexpdata, shapelyA* router + shapely connectivity checks
smk_kbd_devkit/export_fab.pyKiCad's bundled Pythonneeds 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:

bash
python3 -m venv .routing_venv
.routing_venv/bin/pip install numpy sexpdata shapely

Full pipeline from scratch:

bash
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.py

The 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.

Sending to PCBWay

  1. 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.)

  2. Upload smk_kbd_devkit_gerbers.zip at pcbway.com → Quick Order PCB.

  3. Order parameters (close to smk_kbd's own outline):

    SettingValue
    Board size234.5 × 120.82 mm
    Layers2
    Thickness1.6 mm
    Min track/spacing6/6 mil (design uses ≥ 0.2mm signal, ≥ 0.1mm GND-pour minimum — relaxed)
    Min hole0.3 mm
    Surface finishHASL lead-free (or ENIG for nicer hot-swap pads)
    Copper1 oz
    Solder mask / silkyour choice
    Panelizationsingle piece
    Via tenting/pluggingnot needed — the board has zero via-in-pad instances (see the DFM note above); default/your choice
  4. 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.

BOM

Same components as smk_kbd for the matrix and power/USB/charging circuit, plus J10/J11 in place of the MINI-1 module:

RefPartPackage
J10, J111×16 female header, 2.54mm pitchTHT (back side)
U2MCP73831-2ACI/OTSOT-23-5
U3AP2112K-3.3TRG1SOT-23-5
U4USBLC6-2SC6SOT-23-6
Q1DMG3415U (P-MOSFET)SOT-23
D1–D591N4148WSOD-123
D60B5819W / SS14 SchottkySOD-123
LED1LED (charge)0603
J1HRO TYPE-C-31-M-12SMD
J2JST SH SM02B-SRSS-TB (1.0mm, low-profile side entry, back side)SMD
SW62MSK-12C02 side-actuated slide SPDT, lever exits rear edgeSMD
R1, R25.1 kΩ0603
R32.0 kΩ0603
R41 kΩ0603
R6, R71 MΩ0603
C1, C24.7 µF0603
C31 µF0603
C4, C710 µF0603
C5, C8100 nF0603
—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).

Verification status

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.

Fixed in the final-review pass

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:

    beforenow
    J1 → U4 (ESD)~150mm11.3mm
    C1 → U2 pin 4 (VBUS)20–29mm3.6mm
    C2 → U2 pin 3 (BAT+)20–29mm4.9mm
    C3 → U3 pin 1 (VSYS in)20–29mm4.0mm
    C4 → U3 pin 5 (+3V3 out)20–29mm2.7mm
    C8 → J10 pin 1 (+3V3)—3.2mm
    C5 → J10 pin 3 (VBAT ADC)~90mm3.0mm
    USB_DP total copper189mm90.7mm
    USB_DM total copper223mm99.7mm
    DP/DM run as a same-layer pairnone80.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".

Current check results

  • 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):
    • Zero copper clearance, track-width, annular-ring, via, hole, shorting, track-crossing, or keepout violations.
    • 0 unconnected items.
  • 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.
    • 1 GND cluster covering all 94 nodes (81 copper items + 13 zone-fill islands) — no orphans on either layer.
    • All five formerly-problematic pins (U2.2, U3.2, U4.2, C3.2, R2.2) confirmed in that one cluster.
    • 0 via-in-pad instances board-wide, checked by testing every via centre against every SMD pad's own copper.
    • --suggest proposes bridge points for GND_BRIDGE_VIAS if a future re-route ever strands a fragment again.
    • 72 remaining findings, all cosmetic/structural (down from 74 in an earlier revision — the re-placed VBAT-sense/decoupling cluster, see "Layout" above, resolved a silk_overlap/silk_edge_clearance finding on the way):
      • 59 courtyards_overlap — every SWn/Dn matched pair (each key's diode tucked under its own switch footprint by design).
      • 4 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.
      • 5 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.
    • 133 schematic-parity findings, none of them Value mismatches: 127 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).
  • Geometry independently measured on the generated board, not just eyeballed: J1's connector body face lands at y = -6.95 against a board edge at y = -7.0; SW62's actuator silkscreen extends to y = -7.7, i.e. out through the edge; zero tracks, vias or GND-pour fill inside the antenna keepout rectangle; the Edge.Cuts outline is a closed 4-segment rectangle (the antenna notch an earlier revision cut is gone — see "Antenna keepout" above; the keepout is fully interior now).
  • Gerbers exported and spot-checked (layer count, drill files present, zones filled) — not yet opened in a third-party Gerber viewer or sent to a fab.

Still not verified

  • Not physically prototyped or assembled. No real DevKitC-1 has been plugged into a fabricated version of this board. The GPIO map, power path and J10/J11 pinout are cross-checked against Espressif's own dimension drawing, user guide and schematic v1.4, and the female-header footprint geometry against KiCad's own stock 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.
  • No firmware support yet. A new 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).
  • Ready to order? The electrical and geometric defects the final review found are fixed and re-verified, and DRC/ERC/parity are at the baselines above. It has still never been fabricated, so treat the first run as a prototype rather than a production order.

Parts

148
QtyValueFootprintReferences
591N4148Wkbd:D_SOD-123_BackD1, D2, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D20, D21, D22, D23 +43
58KS-33kbd:SW_Gateron_KS33_HSSW1, SW2, SW10, SW11, SW12, SW13, SW14, SW15, SW16, SW17, SW18, SW19, SW20, SW21, SW22, SW23 +42
6M2kbd:MountingHole_M2H1, H2, H3, H4, H5, H6
2100nkbd:RC_0603C5, C8
210ukbd:RC_0603C4, C7
21Mkbd:RC_0603R6, R7
21x16kbd:PinSocket_1x16_P2.54mmJ10, J11
24.7ukbd:RC_0603C1, C2
25.1kkbd:RC_0603R1, R2
11kkbd:RC_0603R4
11ukbd:RC_0603C3
12.0kkbd:RC_0603R3
12Ukbd:SW_Gateron_KS33_HSSW54
1AP2112K-3.3kbd:SOT-23-5U3
1B5819Wkbd:D_SOD-123_BackD60
1CHGkbd:LED_0603LED1
1DMG3415Ukbd:SOT-23Q1
1JST-SH-2 SM02Bkbd:JST_SH_SM02B_2pin_BackJ2
1MCP73831-2ACI/OTkbd:SOT-23-5U2
1PWR MSK-12C02kbd:SW_Slide_MSK12C02SW62
1TYPE-C-31-M-12kbd:USB_C_Receptacle_HRO_TYPE-C-31-M-12J1
1USBLC6-2SC6kbd:SOT-23-6U4

Questions

How big is the Smk kbd devkit PCB?

It measures 234.5 × 120.8 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Smk kbd devkit use?

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.

Where can I download the Smk kbd devkit KiCad files?

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.

Can I use the Smk kbd devkit design in my own project?

Its repository states no licence, so its author keeps all rights: ask mburger89 before reusing the design.

Similar boards

  • 2 layers285 × 94.5 mmRmk ble keyboard, the printed circuit board in 3D

    Rmk ble keyboard

    HaoboGu/rmk-ble-keyboard

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

    160 parts42GPL-3.0
  • 4 layers120 × 89.3 mmSMK macro pad, the printed circuit board in 3D

    SMK macro pad

    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.

    89 partsMIT
  • 2 layers42 × 32 mmRAMS - PCB, the printed circuit board in 3D

    RAMS - PCB

    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.

    68 parts1
  • 2 layers301.2 × 94.1 mmRmk ble keyboard, the printed circuit board in 3D

    Rmk ble keyboard

    HaoboGu/rmk-ble-keyboard-65

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

    165 partsGPL-3.0
  • 4 layers54 × 54 mmSmartknob driver, the printed circuit board in 3D

    Smartknob driver

    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.

    69 parts
  • 2 layers87.9 × 87.5 mmCreative controller, the printed circuit board in 3D

    Creative controller

    ZMGID/grokbot-home

    Grok Bot account migration home (secrets encrypted)

    59 parts
  • 4 layers38.5 × 29 mmPl60c dmx mini, the printed circuit board in 3D

    Pl60c dmx mini

    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.

    37 parts