Skip to content

andornaut/splinter-keyboard

Repository files navigation

Splinter keyboard

A 62-key split columnar ergonomic keyboard.

This repo holds the hardware design files. The design pipeline is: Keyboard Layout Editor -> Ergogen -> KiCad -> Fabrication -> Onshape -> OrcaSlicer -> QMK firmware (see Developing).

Versions

Version MCU Changes from previous Firmware Photo
v4 splitkb Liatris (RP2040) Added USB VBUS detection and TRRS data-line protection splinter v4
v3 Adafruit KB2040 (RP2040) Switched from AVR to RP2040 splinter-v3.0 v3
v2 SparkFun Pro Micro (ATmega32U4) Symmetrical enclosures; added a key (62 keys) splinter-v2.0 v2
v1 SparkFun Pro Micro (ATmega32U4) Initial version: 61 keys, columnar layout, asymmetrical enclosures splinter-v1.0 v1

Installation

Install the following tools:

# Include submodules when cloning
git clone --recursive git@github.com:andornaut/splinter-keyboard.git
cd splinter-keyboard

# Install the Node version from .nvmrc, then the deps (including Ergogen)
nvm install
npm install

# Install KiCad 10 (provides kicad-cli, used for fab file generation)
# Or use the Ansible task: https://github.com/andornaut/ansible-ctrl/blob/main/roles/hobbies/tasks/electronics.yml
sudo add-apt-repository ppa:kicad/kicad-10.0-releases
sudo apt install kicad

# Fallback only if you cloned without --recursive: fetch the submodules
git submodule update --init --recursive

Alternatively, you can install OrcaSlicer, KiCad, and Freerouting using these Ansible tasks (tags orcaslicer, kicad, freerouting).

Updating footprint submodules

npm run ergogen uses the vendored footprint submodules (ceoloide, infused-kim) at their pinned revision and does not advance them, so builds stay reproducible. To pull the latest upstream footprints and re-pin them:

git submodule update --remote ergogen/footprints/ceoloide ergogen/footprints/infused-kim
git add ergogen/footprints/ceoloide ergogen/footprints/infused-kim
git commit -m "Bump footprint submodules"

Developing

Step 1. Configure the environment

Set the active version in package.json under config.VERSION to one of v1, v2, v3, or v4, either by editing the file or with npm pkg set config.VERSION=v3.

Keyboard Layout preview

  1. Prototype a layout in Keyboard Layout Editor.
  2. Export it to keyboard-layout-editor/keyboard-layout-editor.json so you can re-import and iterate later.
  3. Use it as the basis for the production Ergogen design.

Step 3. Ergogen

Ergogen preview

  1. Run docker compose up to start the Ergogen GUI (it builds automatically), then open http://ergogen.internal (needs docker_etc_hosts for the /etc/hosts entry).
  2. Paste in, edit, then download ergogen/config.yaml.
  3. Run npm run ergogen to generate outlines and PCBs into dist/v4/ergogen/, then npm run copy:dist-to-unrouted. Or use npm run watch / npm run watch:sync-unrouted.

Notes:

  • The GUI prototypes key placement, layout, and outlines but does not render PCBs. It runs client-side only (no filesystem access, no sync), so edit there and copy back to config.yaml, the source of truth. Use npm run ergogen for full builds and PCB generation.
  • Custom footprints are baked into the GUI image via the Dockerfile since the browser can't load them from disk. It registers this repo's custom footprints on top of the upstream ceoloide and infused-kim libraries; any unregistered what: shows up as unknown. After adding one, rebuild with docker compose build --no-cache.

Step 4. KiCad

KiCad preview

  1. Copy Ergogen's generated boards into kicad/unrouted/ with npm run copy:dist-to-unrouted (existing boards are first backed up to gitignored kicad/backups/<name>-<timestamp>.kicad_pcb), then open one in KiCad, e.g. left.kicad_pcb.

    • Board keepout/DRC features that constrain routing are version-specific; v4 carries copper keepout zones (added by npm run ergogen) that flag stray copper near the board edge or screw bosses, see Copper keepout zones.
  2. Route the boards in kicad/unrouted/. Once routing is done, clean each board up before saving:

    • Cleanup Tracks & Vias (Tools > Cleanup Tracks & Vias): enable all options to merge collinear segments, delete redundant/dangling tracks and vias, and remove tracks inside pads.
    • Add Teardrops (Edit > Edit Teardrops, with nothing selected to apply board-wide): smooths track-to-pad/via junctions for stronger joints and better DFM. Re-run after any reroute.
    • Run DRC (Inspect > Design Rules Checker) with "Refill all zones before performing DRC" checked: resolve every violation and confirm there are no unrouted nets. npm run fab re-runs this same check headlessly and refuses to emit gerbers if it fails (see Step 5), but fixing violations interactively in KiCad is far easier than reading the JSON report.
    • Check copper/silk visually: confirm the GND zone has no isolated islands or stranded pads, and that silkscreen text and reference designators clear pads and the board edge.

    Then npm run copy:unrouted-to-routed to save them to kicad/routed/.

    • After regenerating boards with Ergogen, npm run copy:traces-to-unrouted copies the traces and teardrops from kicad/routed/ back into the same-named boards in kicad/unrouted/ (then File > Revert in KiCad).

Autorouting (optional)

KiCad has no built-in autorouter. npm run route routes the kicad/unrouted/ boards in place via Freerouting, leaving kicad/routed/ untouched. Expect to hand-clean the result (the matrix routes nicer by hand), then File > Revert to load it. The defaults below aim for a fully-connected, DRC-clean board; raising via cost trades vias for unrouted nets, so it can't beat hand-routing on via count.

Env var Default
FREEROUTING_PASSES 100
FREEROUTING_STRATEGY greedy
FREEROUTING_SELECTION prioritized
FREEROUTING_VIA_COST 50
FREEROUTING_UNDESIRED_DIR_COST unset
FREEROUTING_LOG_LEVEL WARN

One-command pipeline

npm run pipeline re-syncs already-routed boards after a config change. It requires existing routed masters in kicad/routed/: the copy:traces-to-unrouted step copies their traces back onto the freshly generated boards, and it aborts if a master has no human routing. It does not route for you, so use it only for updates where the existing routing still applies; for a first route (or when geometry changes enough that the old traces no longer fit), route by hand in KiCad (Step 4) instead.

It runs the whole hardware path in order: ergogen, copy:dist-to-unrouted, copy:traces-to-unrouted, copy:unrouted-to-routed, validate:provenance, fab, validate:fab, then panelize. It prints a per-step banner and a closing summary of the artifacts produced. Each step is a hard gate except the final panelize, which is skipped with a note when KiKit is not installed (the per-half fab from fab is complete on its own).

Provenance stamp (keeping routed/ in sync with config.yaml)

The cp steps and manual routing let routed/ silently drift from config.yaml, so you could fab a stale board. To guard against this, npm run ergogen stamps each board with a hash of config.yaml, and npm run fab checks that stamp (scoped to routed/) before it fabs, refusing a drifted or unstamped master. Run npm run validate:provenance any time to check without fabbing.

Clear a mismatch by re-running the pipeline (ergogen, copy:dist-to-unrouted, re-route, copy:unrouted-to-routed); when the existing routing still applies, npm run pipeline does that whole chain in one step. Caveat: only config.yaml is hashed, so a footprint .js or Ergogen-version change can move geometry without tripping the check, while a comment-only config edit trips a false "stale".

Step 5. Fabrication (JLCPCB)

With the boards saved to routed/ (Step 4), npm run fab exports from kicad/routed/ into dist/v4/kicad/jlcpcb/<name>/:

  • Gerbers + drill (<name>-gerber.zip): the bare PCB.
  • BOM + CPL (<name>-BOM.csv, <name>-CPL.csv): JLCPCB PCBA assembly files, generated only when kicad/jlcpcb-parts.json is present.
  • DRC report (<name>-drc.json): a headless DRC gate runs first and aborts the whole fab (no gerbers written) on any error-level violation or unrouted net.

After npm run fab, run npm run validate:fab to audit the outputs against design intent the DRC and provenance gates miss: that the routed master and the exported gerbers both carry a board-spanning GND ground plane, that the gerber zip has every expected layer plus drill, and that every assembled part made it into the BOM/CPL. It is step 7 of npm run pipeline. (This guards the failure where a board with no ground plane passed every other gate and still shipped.)

LCSC part numbers live in kicad/jlcpcb-parts.json, kept out of the .kicad_pcb so they survive Ergogen regen. Which parts JLCPCB places vs. which you hand-solder is version-specific; see the version README.

Ordering from JLCPCB: upload each <name>-gerber.zip for bare boards, plus the matching <name>-BOM.csv / <name>-CPL.csv for assembly. Check placement in JLCPCB's DFM viewer; fix a mis-oriented part via its rotation in the JSON and re-run.

Panelization (optional, for PCBA cost)

npm run panelize combines left + right into one panel so JLCPCB's per-order assembly setup and stencil fees are paid once instead of twice. Worthwhile only for PCBA orders; skip it for bare boards. It exports gerbers + BOM/CPL into dist/v4/kicad/jlcpcb/panel/, with the per-half fab staying the strict DRC gate. Requires KiKit (git-master build for KiCad 10 support); point panelize.sh at its interpreter with KIKIT_PYTHON.

Step 6. Onshape

Onshape preview

  1. In Onshape, create a document and start a sketch.
  2. Select "Insert a DXF or DWG file" > "Import ..." (bottom of the dialog) > dist/v4/ergogen/outlines/full.dxf.
  3. Design the case, then export *.step files to onshape/.

Step 7. OrcaSlicer

  1. Open or create an OrcaSlicer project.
  2. Import the *.step files from onshape/.
  3. Slice and print the case.
  4. Install an M2.5 heat-set insert (CNC Kitchen) into each mounting boss with a soldering iron, then clamp the PCB with the M2.5 screws (this is the recommended approach; the machined-aluminium alternative below taps the holes directly instead).

Alternative: machined aluminium case (JLCCNC)

Instead of 3D printing, the case can be CNC-machined in aluminium via JLCCNC. Upload each half's *.step and set:

  • Material: 6061 aluminium (JLCCNC's standard alloy; 6063 is the equivalent keyboard-typical option, marginally more even anodize color).
  • Surface finish: Bead blasting + Anodizing (matte) is the recommended premium-keyboard finish: smooth, uniform, fingerprint resistant. For a glossier sheen, choose Anodizing without bead blasting. Pick a color (black is the safe default).
  • Tolerance: the default (ISO 2768 medium) is fine for a case.
  • Quantity: left and right are two separate mirrored parts, so set quantity per file.
  • Threaded holes: tap the M2.5 mounting holes directly (the heat-set inserts in the BOM are only for the printed case). A STEP can't carry threads, so model each hole at the ~2.05mm tap-drill diameter and also upload a 2D drawing (PDF) with an M2.5x0.45 thread callout (depth, through/blind) for JLCCNC to tap to.

To make the thread drawing in Onshape:

  1. From the case Part Studio, create a Drawing and place a top (or section) view of the half.
  2. Add a Hole/thread callout (right-click the hole edge > Callout) on a mounting hole; it reads the hole and emits M2.5x0.45. Add the thread depth and through/blind.
  3. Export the drawing to PDF and upload it with the .step.

Since every mounting hole shares one spec, a full dimensioned drawing is optional: a single PDF or screenshot noting "All mounting holes: M2.5x0.45 tapped, N mm deep" is accepted, and JLCCNC's order interface can also tag threaded holes in its 3D viewer. The STEP still needs the holes at ~2.05mm tap-drill either way.

Step 8. QMK Firmware

  1. Install the custom QMK firmware

About

A 62-key split columnar ergonomic keyboard

Topics

Resources

Stars

Watchers

Forks

Contributors