THE BLOG · BUILD GUIDE

Fab it yourself: ordering OpenMicroKbd from JLCPCB

The full ordering flow, start to finish — watch on YouTube.

The launch post ended on a claim: the manufacturing files are in the repository, so you can make one of these without me. This post is the part that comes after that sentence. It walks the actual order — the same one in the video above — from hw/v1/fab/ to a padded box of assembled boards.

None of it is hard. But a handful of choices on the quote page are the difference between a board that works and a board with a dead touch pad, and a handful of warnings look alarming and mean nothing. Those are what this post is really about.

Watch it first

The video is the primary source here: it is the click-by-click version, screen recorded, with the real quote page rather than a description of it. Everything below is the written companion — the numbers to type in, the files to reach for, and the reasoning behind the settings, in a form you can keep open in a second tab while you order.

If you only read one section, read the four options that matter.

The whole order is three files

Everything the fab needs is checked into hw/v1/fab/. You upload three of them.

FileWhere it goes
openmicro-gerbers.zipThe PCB quote page — upload the zip as-is, do not unpack it
openmicro-bom.csvThe assembly step — what to place
openmicro-smt.csvThe assembly step — where to place it

Two more files sit alongside them and are not part of the order. gerbers/ is the same package unzipped, there for review and diffing. openmicro-pos-all.csv is a position file for every footprint including the through-hole parts and mounting holes — it is for building fixtures and for inspection, not for the pick-and-place. Uploading it instead of openmicro-smt.csv will ask the machine to place parts that are not machine-placeable.

The BOM and the CPL come from different places, which is worth knowing when you go to regenerate them. The BOM is emitted by the CoHDL compiler from the schematic source in hw/v1/src/. The Gerbers and the CPL come from the routed board, hw/v1/pcb/openmicro.kicad_pcb, via KiCad 10. The design is the source of truth for what; the layout is the source of truth for where.

Part one: the board

Upload the Gerbers

Drop openmicro-gerbers.zip onto the quote page. It contains the four copper layers, both paste and solder-mask layers, both silkscreens, the edge cuts, and separate Excellon drill files for plated and non-plated holes.

One thing to actually look at rather than skip: the layer names in this package follow an Altium-style convention rather than KiCad's, so the inner planes arrive as Ground Layer 1.g1 and Ground Layer 2.g2 rather than *.g2l/*.g3l. JLC's parser handles this, but open the Gerber viewer after upload and confirm the assignment anyway: F.Cu signal, In1 ground plane, In2 ground plane, B.Cu signal, top to bottom. Thirty seconds there is cheap insurance against a board with its planes in the wrong order.

While you are in the viewer, the board should read as 95 × 95 mm with a square 4×4 key grid, thirteen keyswitch cutouts, and — this is the one that startles people — thirteen small rectangular openings in the board itself under the key positions. Those are supposed to be there. The key LEDs are reverse-mount and fire up through the board toward the keycaps.

The four options that matter

Most of the quote page can stay on its defaults. Four fields cannot.

OptionSet it to
Layers4
Impedance control / stackupJLC04161H-7628
Surface finishENIG
Thickness1.6 mm, 1 oz copper, FR-4

Four layers is not negotiable — the routing depends on two solid internal ground planes, and the file set will not make sense as a 2-layer order.

The stackup matters because of USB. The differential pair to the USB-C receptacle is routed to a target of 100 Ω differential and 50 Ω single-ended, against a dielectric of 0.2 / 1.03 / 0.2 mm. JLC04161H-7628 is the standard 4-layer stackup that matches those numbers. Pick a different stackup and the geometry that was routed for you stops meaning what it meant; the pair targets live in hw/v1/out/differential_pairs.csv if you want to check the arithmetic yourself.

ENIG is the one that silently ruins a board. TP1 is not a component — it is a nine-millimetre disc of bare copper acting as a capacitive touch electrode. On HASL it comes back as an uneven, oxidising blob of solder, and the sensor's baseline drifts with it. ENIG gives you a flat, stable, gold-over-nickel surface that a finger reads consistently. It costs a few dollars more on a prototype run. Pay it.

The one setting that will quietly cost you a board is the surface finish. The touch pad is bare copper by design — order ENIG.

Note that PCB quantity and assembly quantity are separate choices, each with its own minimum, and the fab's minimums change over time — read them off the quote page rather than assuming. Ordering the board minimum and assembling fewer of them is a perfectly reasonable way to keep a spare bare PCB around.

Part two: the assembly

This board is assembled on both sides

Turn on assembly and you are immediately asked which side to populate. The honest answer for this board is both, and it is worth knowing why before you see the price change.

The pick-and-place file carries 68 placements. Fifty-three of them are on the bottom: the STM32, the USB-C receptacle, the ESD diode, the LDO, the crystal, all thirteen matrix diodes, the SWD socket, most of the passives, and the thirteen reverse-mount key LEDs that shine up through those board cutouts. The remaining fifteen are on the top: the eight perimeter underglow LEDs and five passives.

You can, in principle, order bottom-side assembly only and hand-solder the eight underglow LEDs yourself. They are SK6812MINI-E packages on a shared data chain, so a single bad joint takes out everything downstream of it. Unless you have a reason to, let the machine do all sixty-eight.

The BOM and the CPL

Upload openmicro-bom.csv and openmicro-smt.csv. The BOM's columns are Manufacturer,Comment,Designator,Footprint, where Comment holds the manufacturer part numberSTM32F072CBT6, AP2112K-3.3TRG1, 1N4148W-7-F, and so on. That is deliberate: the MPN is what the matching step searches on, and a real part number resolves to a real catalogue entry far more reliably than a value string does.

The CPL is in millimetres, origin at the board's lower-left corner, Y-up, with rotations following KiCad's orientation convention. Its own Comment column carries friendly values — 100nF, SK6812, Diode_1N4148 — rather than MPNs. That mismatch between the two files is fine and expected: the two files are joined on the designator, not on the comment.

The five rows that are not parts

The BOM describes the design, and the design contains things that are not components. Five rows will not match anything in the catalogue, and both are supposed to fail:

Neither appears in the CPL, so nothing will be placed for them regardless. Mark them as do-not-place, or strip them from your copy of the BOM before uploading if you would rather not look at the warnings.

A second group will match but must not be assembled, because they are through-hole parts the fab is not populating:

These are in the BOM for sourcing, and absent from the CPL on purpose. You buy them separately and solder them by hand once the boards arrive. The switches in particular are a keyboard-hobby part, not an electronics-distributor part — the BOM lists a low-profile 55 g switch, and any Choc V2-compatible switch in a weight you like will drop into the same footprint.

Macro view of the board's underside during hand assembly: a soldering iron and solder wire at one joint, surrounded by the teal reverse-mount LED packages silkscreened LED17 through LED29
The machine does the fine-pitch work. What is left for you is through-hole: thirteen switches, an encoder and a joystick.

The placement preview is the real review

After matching, JLC renders every part onto a picture of your board. This is the single highest-value minute in the whole order, because it is the last point at which a rotation error is free to fix.

Rotations in the CPL follow KiCad's convention; assembly houses then apply their own per-part corrections for how a component sits in its tape. Usually this is invisible and correct. When it is not, it is because a polarised part was rotated by the correction, so those are the ones to check:

A diode array installed backwards produces a board where no key registers and nothing looks wrong. Thirty seconds in the preview is worth the reflow you will otherwise be doing with tweezers.

What the DFM report will flag, and why it is fine

The board passes DRC in KiCad with zero unconnected items and no schematic-parity errors. It does not pass with zero warnings, and the fab's own analysis will surface some of the same things. Every one of them has been reviewed; hw/v1/fab/README.md documents them in full. The short version:

The useful discipline: do not "fix" these. If you open the board and start pushing tracks around to clear warnings that were already understood, you will introduce a real problem chasing a documented non-problem.

What it costs

These are the same figures from the launch post's prototype quote — real quoted prices, per assembled board, for a one-off run.

LineCost
PCB — 4-layer, 95 × 95 mm, ENIG$6.93
SMT assembly — turnkey, parts + placement$18.14
13 × low-profile keyswitches$2.97
Keycap set$8.85
Total, one assembled board$36.90

The fab handles the first two lines — about $25. The turnkey figure includes the components and the machine time; roughly $7.67 of it is parts. The switches and keycaps you buy yourself.

What is not in that number: shipping, customs, spare boards, and the fact that minimum quantities mean your first order is not literally one board. My labour is priced at zero throughout, as it always is in numbers like these.

When the boxes arrive

Three things stand between a delivered board and a working macropad.

Solder the through-hole parts. Thirteen switches, the encoder, the joystick. hw/v1/mechanical/ includes a printable solder-assembly jig that holds the switches square to the board while you tack them — print it before the boards land, not after. It ships in the same MakerWorld profile as the case, the knob and the joystick cap (MakerWorld 中国 for mainland printers), so you can queue the whole set on the slicer while the order is in transit: 0.4 mm nozzle, 0.2 mm layers, PLA or PETG, no supports.

Program the firmware. Every GitHub release ships openmicro-fw-<version>.hex, Intel HEX with the 0x08000000 load address embedded, so any STM32-capable programmer places it correctly with no address entry. Verify it against the release's SHA256SUMS, then full-chip erase, program, and verify over SWD at J2.

Two details there are easy to get wrong. J2 carries no 3.3 V pin — power the board over USB-C while programming and configure the programmer accordingly if it expects a target-voltage sense line. And leave the last 2 KiB flash page erased. 0x0801F800 holds user settings; the firmware detects a blank page and boots with factory defaults. Do not program anything there.

Check enumeration. A programmed board appears over USB as 1209:0001. If it does, the rest is keycaps and lighting.

Close-up of the finished board showing per-key lighting, perimeter underglow, the knob, the joystick and the capacitive touch pad
Two LED chains, the knob and joystick along the top, and the capacitive touch pad at lower left — the reason the finish is ENIG.

One standing rule for anyone who builds one: keep the SWD socket reachable. BOOT0 is held low on this board, which makes J2 the recovery path if power is lost during a firmware update.

If you change the board

The interesting case is not ordering this board — it is ordering a modified one. Change the CoHDL source in hw/v1/src/ and the BOM regenerates from the design; change the layout and the Gerbers and CPL regenerate from the board:

# from hw/v1/
cohdl build                       # -> out/openmicro-bom.csv

kicad-cli pcb export gerbers -o fab/gerbers/ \
  --layers F.Cu,In1.Cu,In2.Cu,B.Cu,F.Paste,B.Paste,F.Silkscreen,B.Silkscreen,F.Mask,B.Mask,Edge.Cuts \
  pcb/openmicro.kicad_pcb
kicad-cli pcb export drill -o fab/gerbers/ --excellon-separate-th pcb/openmicro.kicad_pcb

smt_pos.py pcb/openmicro.kicad_pcb out/openmicro-smt.csv

smt_pos.py lives in the open-source CoHDL repository. Re-zip fab/gerbers/ and you have a fresh order package.

The reason this is worth pointing out: the schematic is source code, so a change to the design is a diff you can read, review, and argue about — and the fab package that comes out the other side is regenerated rather than hand-maintained. That property is the whole point of the project.

Tony at a workbench with a magnifier lamp and soldering iron, hand-soldering a board held in a helping-hands clamp
The last mile is still a soldering iron. That part has not been automated away.

Build one

Everything referenced above is in github.com/conol-ai/openmicrokbd:

Set it to 4 layers and ENIG, upload three files, check the placement preview. That is the whole secret.

If you order a set, or if something in the flow has changed since the video, say so in the Discord — fab interfaces move, and this guide should move with them. And if you modify the design before you order it, that is the outcome I was actually hoping for.

★ Get the Fab Package Join the Discord ← All Posts