Skip to content

Ordering a 4-layer keyboard PCBA and plate at JLCPCB

This guide orders rev 1 of the auspex main board at JLCPCB: a 230.60 x 97.25 mm, 4-layer, 1.6 mm PCB with every JLC-placed part on the bottom side, assembled by JLC, plus an FR4 switch plate as a second, copper-free PCB, plus a resin fit print of the case at JLC3DP. The project, its gates and the generators that produce the board are described in auspex: an open 5x12 Hall-effect keyboard; why the gates below exist and what each one has caught is in Verifying a PCB as code.

Prerequisites: a routed KiCad 10 project whose footprints carry an LCSC field, kicad-cli on the path, and a JLCPCB account. The order described here was placed on 2026-11-04 through JLCONE, JLC’s desktop app. Board field names are from that day’s form, except Layer sequence and Inner Copper Weight, whose exact labels were not recorded; of the assembly fields, only Confirm Parts Placement and Confirm Production file are exact labels, the rest are reconstructed from memory. The project’s files carry its working name, gridlock, which is why that word appears in file names below.

FactValueWhere it comes from
Board outline230.60 x 97.25 mmthe board file; the pack step prints it
Copper layers4: F.Cu signal + GND fill, In1.Cu +3V3A plane, In2.Cu GND plane, B.Cu parts + routing + GND fillboard stackup
Stackup in the board file0.035 Cu / 0.2 mm FR4 prepreg / 0.035 Cu / 1.1 mm core / 0.035 Cu / 0.2 mm prepreg / 0.035 Cu, er 4.5(stackup ...) block of the .kicad_pcb
Vias1017, all 0.3 mm drill / 0.6 mm padthe committed board file
Assembly sidebottom only (every JLC-placed part on B.Cu)Economic PCBA places one side
JLC BOM20 linespack output, rev 1
JLC CPL225 placementspack output, rev 1
Not placed by JLCthe 60 switches, mounting holesswitches are pressed in by hand; both are excluded from BOM and CPL
Plate2-layer FR4, no copper, 14.00 mm switch cutouts, 2.2 mm mount holes drawn on Edge.Cutsplate project
routed board+ plate projectstrict routed DRCBOM check(live JLC parts API)CPL rotation check(JLC footprints)order packgerbers zip, BOM, CPL,plate zipJLC quote formPCB + PCBA + plateJLC reviewplacement, production file,plate supplement
  1. Three gates run against the board: strict routed DRC, a live check of every LCSC part, and a check of every CPL rotation against JLC’s footprint for that part.
  2. Only when all three pass is the order pack written: board gerbers zip, BOM, CPL and plate gerbers zip.
  3. The pack goes into one PCB quote with assembly and a second PCB quote for the plate.
  4. JLC reviews the files, sends the placement and production file for approval, and bills any supplement after review.
ComponentVersion at time of writing
KiCad / kicad-cli10.0.8
Rotation correction tableFabrication Toolkit plugins/transformations.csv, read 2026-11-03, with project overrides
JLC order surfaceJLCONE desktop app (the same quote as https://cart.jlcpcb.com/quote)

Step 1: export gerbers and drill for every copper layer

Section titled “Step 1: export gerbers and drill for every copper layer”

Plot only the layers a fab uses, and on a 4-layer board that list must include the inner layers:

Terminal window
kicad-cli pcb export gerbers -o gerbers/ \
--layers F.Cu,In1.Cu,In2.Cu,B.Cu,F.Mask,B.Mask,F.Silkscreen,B.Silkscreen,F.Paste,B.Paste,Edge.Cuts \
--subtract-soldermask board.kicad_pcb
kicad-cli pcb export drill -o gerbers/ board.kicad_pcb

Without --layers, kicad-cli plots every enabled layer (fab, courtyard, user layers), which bloats the zip and gives the fab a drawing layer to misread as copper or outline. With a fixed list written for a 2-layer board, the inner planes disappear without any error, which is what happened on this order (see Gotchas). The project’s exporter now builds the list from the board’s copper stack, and the pack step counts copper gerbers in the zip and fails unless there is one per copper layer in the board file.

JLC wants two CSV files. The column names and conventions follow the Fabrication Toolkit KiCad plugin (https://github.com/bennymeg/Fabrication-Toolkit), which JLC’s own KiCad instructions point at:

FileColumnsNotes
BOMComment, Designator, Footprint, LCSCgrouped by (value, footprint, LCSC); parts flagged exclude-from-BOM and switches are left out
CPLDesignator, Mid X, Mid Y, Layer, Rotationparts flagged exclude-from-position-files, switches, mounting holes and test points are left out

CPL positions are relative to the board’s auxiliary origin, in mm, with Y negated (KiCad’s Y axis points down, JLC’s up). Layer is top or bottom. The rotation has two terms:

  1. Bottom-side mirror. JLC’s rotation is as seen from above the component, and a bottom part is seen from below, so a bottom part at KiCad angle a becomes 180 - a. Top-side parts keep a.
  2. Footprint correction. KiCad’s footprint and JLC’s library footprint for the same LCSC part are not always drawn at the same zero angle. A per-footprint delta is added, then the sum is wrapped into [0, 360).

The correction table started as the upstream transformations.csv (regex on footprint name -> delta, for example ^LQFP- 270, ^TSSOP- 270, ^SOT-23 180). It is a starting point only: JLC’s footprint for the exact LCSC part is the authority, and Step 3 checks every row against it.

The project’s make order refuses to write the pack unless all three of these pass.

Terminal window
kicad-cli pcb drc --format json --severity-all -o drc-routed.json board.kicad_pcb

The project gates the JSON report rather than the exit code: every violation class fails except missing global libraries (the dev box has no global KiCad libraries installed), the USB-C receptacle’s silkscreen overhanging the board edge, and rev 1’s known violations, which are accepted only when they match a counted baseline file exactly (the uncoupled USB pair, a via near an unplated hole, small test-pad labels). A sentinel rule at the end of the board’s custom rule file must also fire exactly once, which proves kicad-cli loaded that file.

That is the gate as it stands now; it was added on 2026-11-05, the day after this order. The order itself was gated by the earlier version: the text .rpt report with the same allow-list (missing libraries and the edge silkscreen), no sentinel and no baseline. Why the gate changed and what the baseline holds are in Verifying a PCB as code.

For each unique (value, LCSC number, footprint) in the schematic netlist (kicad-cli sch export netlist --format kicadsexpr), the checker queries JLC’s parts search and requires:

CheckRule
Existsthe LCSC number resolves to a part
Valuethe capacitance, resistance or MPN prefix appears in JLC’s description or model, with a digit boundary so 30pF cannot match inside 330pF
Packagean imperial chip size in the footprint name (0402, 0603, 0805, 1206) appears in JLC’s description
Stockat least 5 x the number of placements of that part

It also prints whether each part is Basic or Extended. On 2026-11-03 it caught a capacitor entered as 30 pF whose LCSC number is a 330 pF part. The value field said 30 pF and the footprint was right, so only a lookup of the number could find it.

For every placed footprint with an LCSC number, the checker fetches JLC’s footprint (EasyEDA’s component data; on the parts compared, its result agreed with JLC’s placement preview, which is why the gate trusts it), finds the quarter turn that maps KiCad’s pads onto JLC’s matched by pad number, and checks that the exporter’s correction undoes it. Parts whose pads fit at several rotations (2-pad passives) pass if any fitting rotation matches.

It then checks the final CPL angle per part a second way, from geometry alone: the part’s pads as they sit on the board, mirrored (x -> -x) for a bottom part, matched against JLC’s footprint. This catches a change to the mirror term. A refactor from 180 - a to a + 180 agrees at 0 and 180 degrees, so the passives still pass, but rotates every part at 90 or 270 degrees by 180, which on this board is all 60 sensors.

What it caught on 2026-11-04. The two SOT-23 rows came from JLC’s placement preview of the 2-layer test variant of the same design. The JST SH row was found by the rotation check itself, on the main board, when the 3-pin SWD connector went onto it later the same day:

PartPackageTable saidJLC’s footprint needs
USBLC6-2SC6 (ESD)SOT-23-6180 (^SOT-23)270
TLV75733 (LDO)SOT-23-5180 (^SOT-23)270
BM03B-SRSS-TB (JST SH, SWD)JST SH top entry0180: JLC’s drawing of this part has its pads mirrored in x against KiCad’s; JLC’s SM03B and SM04B drawings agree with KiCad

JLC’s placement preview of the test variant showed the two SOT-23 parts 90 degrees off. The single ^SOT-23 -> 180 rule is right for SOT-23-3 and wrong for these SOT-23-5/6 parts. The HE60 v2.1 reference design’s released position file carries the same 180 for these parts. Whether HE60 corrected them in JLC’s review screen is not recorded, so a published CPL still needs checking part by part.

FileUpload where
<project>-gerbers.zip“Add gerber file” on the PCB quote page
<project>-bom-jlc.csvPCB Assembly step, “Add BOM File”
<project>-cpl-jlc.csvPCB Assembly step, “Add CPL File”
<project>-plate-gerbers.zipa second PCB quote: the switch plate

The pack step also prints the board size, copper layer count, board thickness, BOM line count, placement count and assembly side, so the form is filled from the board file rather than from memory.

Upload the gerbers zip first and read what JLC detects before touching anything else. The form on 2026-11-04:

FieldSetNote
Base MaterialFR-4default
Layers4must match both the pack output and the layer count JLC detects from the gerbers
Layer sequenceF_Cu / In1_Cu / In2_Cu / B_Cu4-layer only; confirm the inner gerbers landed in the right order
Dimensions230.60 x 97.25 mmread from the gerber
PCB Qty5minimum
Different Design1
Delivery FormatSingle PCB
PCB Thickness1.6 mmthe board file’s stackup is 1.6 mm
PCB ColorGreen
Surface FinishLeadFree HASLOSP is the form default and disables Economic PCBA (“The PCB specs you chose are not supported for Economic PCBA”)
Outer Copper Weight1 ozthe design rules assume it
Inner Copper Weight0.5 ozform default; JLC’s capability page states 0.5 oz as the inner default1
Via CoveringPluggedform default on 4-layer; Tented is greyed out
Min via hole size0.3 mm (0.4/0.45 mm)default; matches the board’s 0.3 mm drills
Mark on PCBRemove Mark
Electrical TestFlying Probe Fully Testdefault
Everything elsedefaulta 4-layer quote adds an Engineering fee: USD 25.00 on this quote

The board file’s stackup has 1 oz inner copper; the order took the 0.5 oz default. The review of rev 1 judged the impedance effect of that difference negligible.

No impedance control was ordered (the record does not name the form field). For 4-layer 1.6 mm, JLC’s “No requirement” stackup is the same build as its JLC04161H-7628 option: 0.2104 mm of 7628 prepreg under each outer layer and a 1.065 mm core with 0.0152 mm inner copper, prepreg Er 4.4 and core Er 4.62. That is close to the 0.2 mm prepreg / 1.1 mm core, Er 4.5 throughout, in the board file: JLC’s prepreg is about 5 % thicker. Outer-layer impedance computed from the board file’s stackup should land within a few percent of the default build; this was not recomputed. Economic PCBA accepts the standard stack-up only3. Where impedance control is ordered, JLC’s standard tolerance is +/-10 %1.

For the USB 2.0 High-Speed pair, which 8 kHz polling needs, the stackup does not fix an uncoupled pair. On rev 1 the MCU-side leg of USB_D+/USB_D- came out of the autorouter as two separate traces rather than a coupled pair: sampled every 0.1 mm, 1.2 mm of the D+ run lies within 0.5 mm of D-, and 9.8 mm lies 5 mm or more away. Uncoupled 0.25 mm traces over 0.2 mm prepreg are roughly 50-58 ohm single-ended, which puts the differential impedance at about 100-116 ohm against the 90 ohm +/-15 % USB asks for. The run is about 30 mm, so it may still enumerate at High-Speed; bring-up has to prove it, and rev 2 routes the pair coupled. JLC’s impedance tolerance applies to the trace geometry supplied, so ordering impedance control would not have coupled the pair.

Turn on PCB Assembly on the same quote page:

FieldSetNote
PCBA TypeEconomicsingle-sided placement, 2/4/6 layers, 0.8-1.6 mm, 2-50 pieces, 10 x 10 to 470 x 500 mm per board3. Standard PCBA places both sides, takes any stack-up, and needs 70 x 70 mm minimum. If Economic is greyed out, a PCB spec is outside its table; check the surface finish first
Assembly sideBottomthe form defaults to top; click “Assemble bottom side”
PCBA Qty2assembly minimum; the other 3 boards ship bare
Tooling holesAdded by JLC
Confirm Parts PlacementYesJLC sends the placement for approval before building
Confirm Production fileYessame, for the production file
Product Descriptionfilled incustoms; on the Quote and Order step

Economic PCBA’s single-sided placement is the reason every part on this board sits on B.Cu.

Upload the BOM and CPL. In the parts list, every line should match the LCSC number in the BOM with no shortfall; rev 1 had 20 lines and 225 placements. Each Extended part adds a per-part fee. The main board’s count of Extended lines was not recorded; the 2-layer test variant had 9 of its 19 lines Extended (MCU, sensor, mux, USB-C, ESD, LDO, fuse, JST SH and one 2.2 uF capacitor).

In the placement preview, bottom-side parts show mirrored. Check rotation and pin 1 on:

RefPartWhat to look for
U4AT32F405RCT7 MCU, LQFP-64pin 1 marker against the footprint’s pin 1
AM1-AM8SN74LV4051A muxes, TSSOP-16all eight, pin 1 on each
U1, U2, U3USBLC6-2SC6 (SOT-23-6), TLV75733 (SOT-23-5), XC6206 (SOT-23-3)the SOT-23-5/6 parts are the ones a package-name table gets wrong
Y13225 4-pin crystalorientation of the 4 pads
J1USB-C receptaclethe opening faces the board edge
J2JST SH 3-pin SWDJLC’s drawing for this part is mirrored against KiCad’s
HE1 (one is enough)MT9102ET Hall sensor, SOT-23-3all 60 share one rotation, so one checks them all

On rev 1 the preview showed every part on the bottom with correct rotation and pin 1. If one is wrong, fix the correction table, regenerate the CPL, and re-upload. With the rotation gate in Step 3 in place, this step now confirms rotations the gate has already checked.

After payment, JLC’s file review runs (quoted as 10-60 minutes on this order) and the placement and production-file confirmations arrive as “Action Required” items in JLCONE. Approve them only after checking the screenshots they attach.

The switch plate is a PCB project with no copper: an Edge.Cuts outline with 60 cutouts at the key centres and the mount holes. Quote it as a second PCB in the same batch:

FieldSet
Gerber<project>-plate-gerbers.zip
Layers2 (JLC treats it as a normal FR-4 board)
PCB Thickness1.6 mm
PCB ColorBlack (cosmetic)
Surface Finishcheapest; there is no exposed copper
PCB Assemblyoff
PCB Remarka copper-free keyboard switch plate: keep the 14.00 mm cutouts; the 2.2 mm mount-hole circles are on Edge_Cuts and must be milled or drilled non-plated; the drill file is empty

Two things happen after the order that the instant quote does not show:

  1. JLC emailed asking whether a design with no traces is intentional. Reply that it is a switch plate and list what they must keep (cutout size, the non-plated holes on Edge.Cuts, the empty drill file). Putting the same text in PCB Remark at order time may avoid the email.
  2. JLC billed a keyboard-plate supplement after file review. The structure on 2026-11-04: “keyboard PCB: densely packed milled slots”, USD 16.41 + 49.25 per m2, plus a charge for too many slots. Taking the area as the whole order’s area fits the bill: 16.41 + 49.25 x (5 x 0.2306 x 0.09725 m2) = 21.93, with a USD 0.18 remainder consistent with the slot charge. One order is not enough to pin the formula down. The plate holds until the supplement is paid.

Budget a supplement on plate orders of this kind; it is not in the instant quote. Moving the plate to another fab has its own costs. PCBWay’s help page lists extra cost when milling and drilling counts exceed 10 under its tab-routing method (https://www.pcbway.com/helpcenter/paymentproblems/What_PCBs_will_be_charged_of_extra_cost_.html). A PCBWay-sponsored review of a roughly 320 x 140 mm 2-layer keyboard found PCBWay’s online quotes 4-5x those of an unnamed China supplier before shipping; the supplier is not identified, so this is not necessarily a JLC comparison (https://github.com/Giraffasax/SpUnLy58/blob/main/Reference/PCBWay_Review.md). A separate order also loses the consolidated shipping.

The resin fit print is a separate JLC3DP order. The one placed on 2026-11-04 was SLA in 9600 resin, white, general sanding, USD 8.88 on that date. It checks the planar fit (board, plate, USB cable entry, SWD plug, feet, boss positions). It is not the daily case: the resin’s heat deflection temperature is 59 C at 0.45 MPa (53 C at 1.82 MPa)4, and the printed bosses are thin, so use self-tapping screws for plastic and stop at snug.

JLC3DP ran an engineering review on this upload. Its published rules for SLA resin56:

RuleSLA value
Recommended wall thickness (SLA service page)0.8 mm
Minimum wall by part size0.5 mm at 5 x 5, 0.8 at 10 x 10, 1.0 at 50 x 50, 1.5 at 100 x 100, 2.0 mm at 200 x 200 mm
Protrusions, positioning features, snaps, fastenersmore than 1.5 mm
Hole depth for diameter1.0 mm -> 1.0-3.0 mm deep, 1.5 -> 1.5-4.5, 2.0 -> 2.0-6.0
Hole tolerance+/-0.3 mm; holes generally shrink

JLC does not say how a part maps onto its size columns. The case is 237.6 x 104.25 mm; read conservatively it falls in the 200 x 200 mm row, 2.0 mm. The replacement file’s floor is 2.0 mm; the file first reviewed was an older geometry with a 2.5 mm floor.

The review of this order flagged 0.51 mm walls at all five boss tops. The pilot chamfer cone in the CAD had its radii swapped, which cut an undercut under each boss face: the bore was 1.6 mm at the face and 2.8 mm at 0.6 mm below it, so a self-tapping screw would have bitten only a knife-edge lip. The review’s second note, warping of a long flat tray, was accepted for a fit check. The fix, and the mesh check that now fails a print file with walls under 0.8 mm or a bore that widens below its entry, are in Designing a keyboard case in build123d.

The order went on hold. To fix a held JLC3DP order without re-ordering, ask JLC support to enable Replace File on it, then upload the corrected STL through that option; the replacement here was uploaded on 2026-11-05.

CheckHow
Inner layers are in the zipthe pack step lists copper gerbers; there must be 4 (F_Cu, In1_Cu, In2_Cu, B_Cu)
JLC sees 4 layersthe quote page’s detected layer count after the gerber upload
SizeJLC’s detected dimensions equal the pack output (230.60 x 97.25 mm)
BOMparts list: 20 lines matched, no shortfall
CPLplacement preview: 225 placements, all on the bottom; U4, AM1-AM8, U1-U3, Y1, J1, J2 and one sensor rotated correctly with pin 1 right
Economic PCBA availablePCBA Type not greyed out (surface finish LeadFree HASL, not OSP)
Batchboard with PCBA and the plate checked out together
  • “Detected 2 layer board.” JLC’s upload said this for the 4-layer board. The gerber export used a fixed 2-layer layer list and dropped In1.Cu and In2.Cu, the +3V3A and GND planes: the board would have been built with no sensor supply. The exporter now adds every inner layer and the pack step fails on a copper-gerber count mismatch. Read JLC’s detected layer count against your own before anything else on the form.
  • OSP disables Economic PCBA. It is the form’s default finish. LeadFree HASL was accepted for Economic on this 4-layer order.
  • A rotation table keyed on package name misrotates some parts. SOT-23-5/6 needed 270 where SOT-23-3 needs 180, and one JST SH part needed 180 because JLC’s drawing of it is mirrored. Check rotations against JLC’s footprint for each LCSC number, not against the package name.
  • The bottom-side mirror is 180 - a. a + 180 passes every part at 0 or 180 degrees and fails every part at 90 or 270.
  • Look up every LCSC number. A 330 pF LCSC number sat under a 30 pF value (Step 3).
  • 4-layer form defaults differ. Via Covering is Plugged with Tented greyed out, inner copper is 0.5 oz, and an Engineering fee appears (USD 25.00 on this quote).
  • The plate costs more than its quote. Budget the post-review keyboard-plate supplement; expect the “no traces” email.
  • Splitting a batch to use more coupons would cost a second shipment. This was reasoned rather than tried: the board and the plate shipped in one batch.
Path (in the project repo)Role
tools/jlc_export.pyBOM + CPL writer, rotation correction table, layer list for gerbers and drill
tools/cpl_rotation.pyCPL rotation maths without pcbnew: the exporter’s formula and the geometry-only expected rotation
tools/jlc_rotation_check.pyper-part rotation gate against JLC’s footprint data
tools/bom_check.pylive LCSC value / package / stock gate
tools/order_summary.pyprints the form numbers and gates the copper-gerber count
docs/ordering.mdthe order checklist, corrected after every order
mechanical/case/README.mdcase print settings and the JLC3DP review record
  1. JLCPCB, “Rigid PCB Manufacturing Capabilities,” JLCPCB. https://jlcpcb.com/capabilities/pcb-capabilities ↩ ↩2

  2. JLCPCB, “Controlled Impedance PCB Layer Stackup,” JLCPCB. https://jlcpcb.com/impedance ↩

  3. JLCPCB, “PCB Assembly Capabilities,” JLCPCB. https://jlcpcb.com/capabilities/pcb-assembly-capabilities ↩ ↩2

  4. JLC3DP, “9600 Resin - Photosensitive Resin,” JLC3DP Help Center, read 2026-11-05. https://jlc3dp.com/help/article/photosensitive-9600-resin ↩

  5. JLC3DP, “3D Printing Design Guidelines: Size, Tolerance & Design Rules,” JLC3DP Help Center. https://jlc3dp.com/help/article/3d-printing-design-guideline ↩

  6. JLC3DP, “SLA (Resin) 3D Printing Service Online,” JLC3DP. https://jlc3dp.com/3d-printing/stereolithography ↩