Skip to the content
PCBKitNew board
2 layers150.5 × 114.6 mmHojicha44-Left, the printed circuit board in 3D

Hojicha44-Left

BitPeppr/Hojicha44

Open Source Split wireless columnar 44-key keyboard with a focus on sound!

Size
150.5 × 114.6 mm
Copper layers
2
Thickness
1.6 mm
Parts
54
Nets
47
Pads
245
Tracks, vias
384, 71
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
11 Oct 2026
Updated
30 Oct 2026

Overview

Hojicha44-Left is an open-source printed circuit board by BitPeppr, from the BitPeppr/Hojicha44 repository on GitHub. It is a 2-layer board of 150.5 × 114.6 mm with 54 parts on 47 nets, routed with 384 tracks and 71 vias.

It is built around the K08, 1N4148W and K12, with the K13, K14 and K15 among its other parts. It belongs with the keyboards and input boards and the nRF52 and nRF5x boards of this library.

Main components

10

README

README.md

Hojicha44 — split columnar keyboard (case + plate phase)

Custom split columnar mechanical keyboard (44 keys + 2 encoders, nice!nano x2). Roadmap: KEYBOARD_PROJECT_ROADMAP.md. Current phase: PCB release v0.5.0 cut + fab-pointable; plate rev-8 gated (+ new z-stack gate); case/cover v4.1 generated + gated (thin top plate, key-field ledge, floor −6.35) with committed STEP solids; ZMK CI green. Board release evidence: generated/release/v0.5.0; order-time print geometry: generated/print/v0.5.0/MANIFEST.txt (STEP files verified in CI); renders: generated/renders/v0.5.0. Remaining before any batch: the first-article checklist (spec/first-article.md) — coupon-first, per its order policy. Layout geometry lives in layout/ergogen/config.yaml (frozen snapshot: layout/- FINAL/keyboard_layout_v7); each half has a placed+routed board file — see Schematic.

Repository layout (what every top-level thing is)

PathTracked?What it is
boards/shields/hojicha44/yesThe ZMK shield definition (devicetree hojicha44.dtsi, left/right overlays, Kconfig) — standard ZMK module layout; consumed by the firmware build via zephyr/module.yml
config/yesZMK build inputs — hojicha44.keymap (+ 4 variant keymaps), *.conf Kconfig, west.yml. The keymaps are generated by config/gen_keymap.py from the spec (byte-compared by CI every run)
spec/yesEngineering source of truth (human-authored): layout.yaml, electronics.yaml, case.md, bom.md, first-article.md, open items. Generators read this; config/ is its firmware-facing output — same pipeline, different roles: spec = source, config = consumed by west/Zephyr
zephyr/module.ymlyesDeclares this repo as a Zephyr module (makes boards/ + config/ visible to west)
zmk/gpio-map.mdyesMachine-generated pin-map doc (cross-checked against the dtsi)
build.yamlyesCI firmware matrix consumed by .github/workflows/build.yml (one ZMK build per shield, pinned ZMK v0.3)
pcb/yesKiCad project + generators (gen_schematic.py, gen_pcb.py, gen_route.py) + the committed .net netlists CI parses (no KiCad in CI) + vendored stock
plate/, case/, layout/yesGenerators + tracked outputs (plate DXF/SVG/lands; case scripts; layout points). case/.venv/ and case/out/ are local-only
generated/yes, selectiveDerived artifacts worth versioning: release/v0.5.0 (the shipped gerbers/reports bundle), renders/v0.5.0 (final PNGs), layout points, print manifest. The regenerable mesh tree generated/renders/assets/ is ignored
tests/yesspec_checks.cjs — the static gate suite (npm test)
renders/yesPhase-10 render pipeline scripts (see renders/README.md)
node_modules/nonpm deps for the gate suite — recreated with npm ci; pinned by package-lock.json
out/noLocal build outputs: out/firmware (west/Zephyr build destination — the build result stays out of git; the CI rebuilds it), out/fit (fit prints)
opencode/noAgent-harness scratch: run logs, probe scripts, and gen_pcb's pre-regen board archive (rejected/pre-regen-* snapshots cited from notes and hand-copper.txt)

Layout

WhatWhereNotes
Parametric source of truthlayout/ergogen/config.yamlErgogen 4.2.1, 46 pts: 6×3 + 4 thumbs + encoder/side
KLE sketch (derived)layout/- FINAL/keyboard_layout_v2.jsonDo not hand-edit; regenerate
KLE sketch (archive)layout/- FINAL/keyboard_layout_v1.jsonOriginal row-staggered sketch
Layout decisionsspec/layout.yamlKeymap, thumb roles, relocated keys
Board outlinelayout/generated/v2/outlines/board.dxfPCB edge, no encoder hole — the EC11E needs copper under it
Plate outlineplate/Hojicha44-{Left,Right}-plate.dxfSame contour + tabs, 16.2 × 12.4mm chamfered encoder window
Generatorslayout/generators/export_kle.py (points → KLE), render_preview.mjs (KLE → labeled PNG)
Ergogen outputslayout/generated/v2/Points, outlines, demo SVGs, keymap render
Snapshotslayout/- FINAL/v1–v4 KLE, v5–v6 Ergogen, v7 config, v8 layout (frozen)

Schematic

WhatWhereNotes
Electronics specspec/electronics.yamlSource of truth: matrix, pin map, parts
Generatorpcb/generators/gen_schematic.pySpec → .kicad_sch + ERC + net audit; never hand-edit the schematic
Left projectpcb/kicad/Hojicha44-Left.kicad_schOne project + one board file per half (a split is two PCBs)
Right projectpcb/kicad/Hojicha44-Right.kicad_schSame; both share the vendored libs below
Board filespcb/kicad/Hojicha44-{Left,Right}.kicad_pcbPlaced+routed: 54 footprints / 48 nets per half, outline from board.dxf. Hand copper is preserved via hand-copper.txt; router-owned copper regenerates on every run
Hand-copper manifestpcb/kicad/hand-copper.txtThe authoritative preserve set, scoped per half ([Left]/[Right]). A regen keeps only items matching manifest signatures; a manifest line the committed board lacks is a hard error
Placement generatorpcb/generators/gen_pcb.pyPoints + outline + netlist → boards. Extraction reads the committed baseline (git HEAD), gates run before the write, and a DRC-rejected board is reverted so it can never leak into the next build
ZMK firmwareboards/shields/hojicha44/ + config/Shield dtsi/overlays/Kconfig + keymap/conf; compiled by CI (.github/workflows/build.yml), no local west build
ZMK evidencezmk/gpio-map.mdMachine-generated GPIO continuity map
Project symbolspcb/kicad/Hojicha44.kicad_symLocal nice!nano (stock libs cover the rest)
Project footprintspcb/kicad/Hojicha44.pretty/Choc hotswap, PJ-320A, 2x12 nice!nano socket (vendored, see spec sources)

Why two projects: a single flat sheet produces one netlist, so "Update PCB from schematic" would drop both halves into one board file. One project per half keeps each outline, netlist and DRC separate; the half-to-half link (TRRS_T/TRRS_R1 crossover) lives in the spec, not the netlist.

Each half differs from the other only in the TRRS TX/RX swap: D1→TRRS_T on the left, D1→TRRS_R1 on the right, for a straight cable. The crossover is copper, so both halves share identical UART pinctrl (TX=P0.06 RX=P0.08) — no firmware swap; see zmk/.

sh
python3 pcb/generators/gen_schematic.py   # spec check + both sheets + audit + ERC
sh
python3 pcb/generators/gen_pcb.py         # place both boards + containment + DRC gate

The schematic generator also asserts the two things ERC and DRC cannot catch:

  • Socket rows are 15.24mm apart, pitch 2.54mm. Socket_Strips_2x12_P2.54mm_NiceNano numbers 1–12 down the TX/D1 edge and 13–24 down the RAW edge in the opposite direction, so pads 1 (D1) and 24 (RAW) share the USB end. The stock PinSocket_2x12 is row-major with 2.54mm row spacing and would land every MCU net on the wrong pad while still passing both checks — and the module would not fit.
  • Reference designators. KiCad treats a refdes not ending in a digit as un-annotated, which makes every netlist/PCB export warn even though ERC is clean.

kicad-cli lives inside the KiCad bundle (/Applications/KiCad/KiCad.app/Contents/MacOS/kicad-cli, v10.0.6 here) — it is not on PATH by default. The generator finds it automatically.

Build notes (things that will bite you at assembly)

TRRS cable — buy a 90° right-angle one. The two jack mouths face each other across 22.40mm of mouth-to-mouth clearance (plastic faces; 20.42mm board-edge to board-edge, each mouth 0.99mm recessed behind its edge — silk tops at 21.40mm, still recessed). A molded straight plug protrudes ~12mm past the jack face, so the pair needs ~24mm and is a tight fit at best. A 90° plug protrudes only ~2–4mm axially because the boot exits perpendicular, so the current gap is ample. 90° is the community standard for splits anyway (less port torque). Size the cable port-to-port +2–5cm minimum — both cables exit upward and arch over the gap.

Always power-cycle in this order: plug/unplug the TRRS with both halves switched OFF and USB unplugged from both, then switch on. Switch OFF alone is insufficient — USB still powers the controller. Sleeve-GND mates at an angle during insertion, so connecting live can glitch TX/RX.

Don't treat TRRS as a daily link. splitkb's own nice!nano FAQ is blunt: "Do not use a cable between the halves… shorting connections when unplugging while the battery is still on… unbalanced charging when two batteries are connected." This design already isolates ring 2, which blocks the charging half; the shorting half is only mitigated by the power-off ordering above. TRRS here is a bring-up/debug fallback — move to 4-pin JST if you want it in normal use.

Battery: the 301230 goes directly under the module. This is correct. The cell's south 5mm sits over the antenna keepout circle, which looks alarming on the preview but is exactly what Lily58, Corne, Sofle, the whole splitkb Aurora series, and nicekeyboards' own docs do — none apply a cell keepout. The r=5 copper keepout is on our PCB and is honoured independently; the cell is a different layer. A PCB trace antenna has a null perpendicular to its plane, so a cell 3mm below the antenna end is in the null, not shadowing the main lobe. If BLE range ever disappoints, the documented upgrades are a 301228 (90mAh) or a 306080 between PCB and bottom plate with 9mm standoffs.

Regenerate

sh
npx ergogen layout/ergogen -o layout/generated/v2 --svg --clean
node layout/generators/dump_points.cjs            # restores the JSON/YAML sidecars (4.2.1 no longer writes them)
python3 layout/generators/export_kle.py --check
node layout/generators/render_preview.mjs

PCB (place + route; generator copper + preserved GUI copper):

sh
python3 pcb/generators/gen_schematic.py   # spec check + both sheets + guards + audit + ERC
python3 pcb/generators/gen_pcb.py         # place + route + containment + copper checks + DRC

gen_pcb.py owns the outline, footprints, nets, placement and generator copper (deterministic router gen_route.py, revision-tagged so stale copper is dropped, never silently kept). GUI copper is the other way round: (segment …), (via …) and (zone …) are preserved verbatim from the board file (net codes remapped by name, since codes are not stable across netlist exports). GUI routes survive regeneration (generator copper is revision-tagged, so a router rev drops only stale generator copper, never GUI copper). The build fails, rather than quietly producing a broken board, if preserved copper no longer lands on a pad/via/track — the usual result of moving a footprint without re-routing — or if it enters the r=5mm antenna keepout.

Previews

  • Paste layout/- FINAL/keyboard_layout_v2.json into keyboard-layout-editor.
  • Paste layout/ergogen/config.yaml into ergogen.xyz.
  • layout/generated/v2/keymap.png (+.svg) is a labeled render with the keymap baked in — no Ergogen PNG export exists, so this script draws it from the KLE (ImageMagick required).
  • Never cross-paste: the KLE importer rejects mid-row rotation by design.

Parts

54
QtyValueFootprintReferences
221N4148WDiode_SMD:D_SOD-123D_L00, D_L01, D_L02, D_L03, D_L04, D_L05, D_L06, D_L07, D_L08, D_L09, D_L10, D_L11, D_L12, D_L13, D_L14, D_L15 +6
3FIDUCIALFiducial:Fiducial_1mm_Mask3mmFID_L1, FID_L2, FID_L3
2TOOLINGMountingHole:MountingHole_3.2mm_M3MT_L1, MT_L2
1BATT_LIPOConnector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_HorizontalJ_L2
1EC11E-SwitchRotary_Encoder:RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm_CircularMountingHolesENC_L1
1K00Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L00
1K01Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L01
1K02Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L02
1K03Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L03
1K04Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L04
1K05Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L05
1K06Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L06
1K07Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L07
1K08Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L08
1K09Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L09
1K10Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L10
1K11Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L11
1K12Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L12
1K13Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L13
1K14Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L14
1K15Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L15
1K16Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L16
1K17Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L17
1K18Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L18
1K19Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L19
1K20Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L20
1K21Hojicha44:SW_Hotswap_Kailh_Choc_V1_1.00uSW_L21
1PJ-320A TRRSHojicha44:TRRS-PJ-320AJ_L1
1PWR_SWButton_Switch_THT:SW_Slide-03_Wuerth-WS-SLTV_10x2.5x6.4_P2.54mmSW_LPWR1
1nice!nano v2Hojicha44:Socket_Strips_2x12_P2.54mm_NiceNanoU_L1

Questions

How big is the Hojicha44-Left PCB?

It measures 150.5 × 114.6 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Hojicha44-Left use?

54 parts, built around the K08, 1N4148W, K12 and K13. 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 Hojicha44-Left KiCad files?

From the BitPeppr/Hojicha44 repository on GitHub, at the commit this page was made from: the board is pcb/kicad/Hojicha44-Left.kicad_pcb and its schematic pcb/kicad/Hojicha44-Left.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Hojicha44-Left design in my own project?

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

Similar boards

  • 2 layers298.8 × 122 mmUrsaminor pcb, the printed circuit board in 3D

    Ursaminor pcb

    markstory/ursa-minor-keyboard

    Design and schematics for the ursaminor keyboard

    225 parts23
  • 2 layers315 × 121 mmSentry keyboard v1, the printed circuit board in 3D

    Sentry keyboard v1

    markstory/sentry-keyboard

    Replacement PCB for the gifted keyboards

    265 parts3MIT
  • 2 layers285 × 116 mmDK60TP, the printed circuit board in 3D

    DK60TP

    Dbroqua/DK60TP

    [MOVED] Another 60% keyboard with integrated trackpoint

    319 parts3GPL-3.0
  • 2 layers100 × 100 mmRelay brd, the printed circuit board in 3D

    Relay brd

    reivax-boucoi/PCB_Kicad

    Repo for all my home projects involving PCBs developped with Kicad

    146 parts1
  • 2 layers109 × 71 mmGamma split, the printed circuit board in 3D

    Gamma split

    ahtn/keyboard_pcb

    156 parts96
  • 2 layers123.4 × 91.9 mmScissorball right, the printed circuit board in 3D

    Scissorball right

    rotep/scissorball

    Split 36-key low-profile keyboard with Kailh PG1425 scissor switches and a 25mm PMW3360 trackball. KiCad / FreeCAD (scripted) / QMK.

    46 partsGPL-3.0
  • 2 layers76.2 × 76.2 mmFor idle hands, the printed circuit board in 3D

    For idle hands

    peej/for-idle-hands-keyboard

    A modular split keyboard PCB

    33 parts5MIT