Skip to the content
PCBKitNew board
2 layers40 × 30 mmPlaced, the printed circuit board in 3D

Zener-style compiler for PCB placement and routing: AI writes Place/NetReq, geometry is compiled, KiCad is the backend.

Size
40 × 30 mm
Copper layers
2
Thickness
1.62 mm
Parts
19
Nets
27
Pads
124
Tracks, vias
0, 0
Pours
1
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
9 Sep 2026
Updated
15 Sep 2026

Overview

Placed is an open-source printed circuit board by RileyMcCarthy, from the RileyMcCarthy/pcb-space repository on GitHub, published under the MIT licence. It is a 2-layer board of 40 × 30 mm with 19 parts on 27 nets.

It is built around the ESP32-C3-MINI-1-N4, B3U-1000P and AP2112K-3.3TRG1, with the USBLC6-2SC6, KT-0603R and TYPE-C-31-M-12 among its other parts. Its parts include 7 capacitors, 5 resistors, 5 ICs, 1 diode and 1 connector. It belongs with the ESP32 and RISC-V boards of this library.

Main components

6

README

README.md

pcb-space

Zener (pcb) is the schematic language. pcb-space is the spatial compiler and fab packager in front of KiCad.

This is not a Zener fork. You write a .zen board and a .place.py. pcb-space build is the compiler: schematic (pcb build + lint) → seed (pcb layout --no-open) → place → route → fab. The agent never has to click Pcbnew.

text
pcb-space source   MPN / LCSC → Zener package + SOURCE.json
      ↓
.zen + .place.py   intent (Zener nets, CSS locks, NetReq)
      ↓
pcb-space build    schematic → seed → place → route → fab

Already-committed placed/ / routed/ / fab/ are left alone. --force or --from place is an opt-in rebuild (a new PCBA). pcb layout on those directories duplicates footprints; seed / place / build refuse them.

Getting started

You need four tools on PATH:

ToolRole
pcb (Zener)Schematic, pcb build, pcb layout --no-open
pcb-spacePlace, route, silk, fab, source, status
KiCad 10 kicad-cliDRC, Gerbers, review SVGs
KiCadRoutingTools (KRT_HOME, default ~/Downloads/KiCadRoutingTools)Place legalize + route engine
bash
pip install -e ".[dev]"          # this repo
# pcb: https://github.com/diodeinc/pcb  → ~/.local/bin/pcb
# KiCad 10: macOS app, or Ubuntu ppa:kicad/kicad-10.0-releases
export KRT_HOME=~/Downloads/KiCadRoutingTools

pcb-space init blinky -C ./blinky
# source parts, write the .zen and Place() locks, then:
pcb-space build blinky.place.py
pcb-space status blinky.place.py

--upto schematic|seed|place|route|fab stops early. --dry-run prints the plan. --force rebuilds from schematic (new copper).

Worked PCBA (already fabbed in git — build is a no-op until --force):

bash
pcb-space build   examples/c3_usb --dry-run
pcb-space status  examples/c3_usb
pcb-space lint    examples/c3_usb/c3_usb.zen

Why this exists

KiCad 10 already uses 0.20 mm traces and checks clearance. It will not:

  • derive 0.20/0.15 mm from “USB 2.0, 90 Ω” plus a JLCPCB 4-layer stackup
  • keep “USB-C at this end, this offset, for the enclosure” in git
  • autoroute from a CLI an agent can call

Zener (Net, Module, pcb build) is required for the netlist. pcb-space sources parts (LCSC first; DigiKey/Mouser when API keys exist), gates the land against the datasheet body, and compiles placement/routing. pcb-space lint checks USB-C both-orientations / CC Rd / ESP32 D+/D− on the .zen.

Analog EMG and switching nodes stay autoroute=False until you say otherwise. A maze that “meets 0.2 mm width” can still ruin a boost SW loop.

Install

bash
pip install -e ".[dev]"

Python 3.11+. Compile / apply / check need no extra Python packages. Zener (pcb), KiCad 10, and KRT are required for seed / place / route / fab — see Getting started.

Place file

Placement uses CSS names. Unitless numbers are millimetres. The board is the containing block. Locked parts are position: absolute. Footprints have intrinsic size (the courtyard), like an <img>. margin: auto centers on the free axis. padding lives on Board / Region, not on a part.

python
Board(width=52, height=30, layers=4, stackup="jlcpcb_4l_1oz")

# Flush to the east end, vertically centered. rotate is pre-layout (PCB, not CSS transform).
Place(
    "U26",
    position="absolute",
    right=0,
    top=0,
    bottom=0,
    margin_top="auto",
    margin_bottom="auto",
    rotate=90,
    locked=True,
    reason="USB-C at pod end",
)

# Same thing as a style string (AIs dump CSS this way).
Place(
    "U28",
    style="position:absolute; top:0; left:0; right:0; margin-left:auto; margin-right:auto",
    locked=True,
    reason="FFC to garment",
)

Place accepts the Zener instance name (J1) or the KiCad reference (U3). pcb-space maps them through the footprint Path property (J1.TYPE_C_… → U3).

The web mapping:

CSSpcb-spaceNotes
position: absolutelocked mechanical partEngine must not move it
position: staticunlockedEngine packs it later
top / right / bottom / left / insetsamemm or % of the containing block
margin / margin-top / … / autosameCourtyard-to-edge. auto + both edges = center
paddingBoard / Region onlyShrinks the containing block
width / heightKeepout / RegionFootprints: courtyard is intrinsic
transform: translateY(-50%)same, layout-affectingSo the web centering trick actually moves copper
rotate / transform: rotate(90deg)before layoutUnlike CSS, rotation changes the used box
at=(x, y)KiCad originCAD apertures. Do not mix with left/top/right/bottom
side='B'back copperNot z-index

Rejected on purpose (clear error, not a silent no-op): z-index, flex / grid / display:flex, px / em / rem, position: sticky|fixed. Unlocked packing is the engine, not flexbox.

NetReq kinds

kindCompiles toAutoroute
usb_hs90 Ω (or z_diff_ohm) pair geometry from the stackupdiff pair
powerIPC-2221 width from ampsyes
analogF.Cu, no via, length capno
clocklength-match groupyes
switch_nodeF.Cu, no viano
digitaldefault classyes

Source (parts)

Distributors are not CAD libraries. source search hits LCSC/JLC. source check fails a land whose Fab body is not the datasheet size (the SHT40 1.5 mm die on a 1.0 mm UDFN). Passives stay stdlib generics — do not download a unique 0402 symbol.

bash
pcb-space source search TPS61023DRLR --fab jlcpcb
pcb-space source import  "100nF 0402" --kind generic -o components
pcb-space source import  SHT40-AD1B --footprint path/to.kicad_mod --body 1.5x1.5
pcb-space source check   components/Sensirion/SHT40-AD1B --body 1.5x1.5

Worked example: examples/c3_usb/ (USB-C ESP32-C3 node). EasyEDA’s ESP32 land failed the 16.6×13.2 mm body gate; the KiCad MINI-1 land passed.

pcb-source is the same commands without the source prefix. DigiKey/Mouser rows appear once DIGIKEY_CLIENT_ID / MOUSER_API_KEY are set; without keys they are listed as skipped, not invented.

Commands

bash
pcb-space init    blinky -C ./blinky
pcb-space build   examples/c3_usb --dry-run
pcb-space build   blinky.place.py --upto place
pcb-space lint    examples/c3_usb/c3_usb.zen
pcb-space seed    examples/c3_usb/c3_usb.place.py   # pcb layout --no-open; seed only
pcb-space status  examples/c3_usb
pcb-space refs    examples/c3_usb/c3_usb.place.py
pcb-space nets    examples/c3_usb/c3_usb.place.py --stub
pcb-space compile examples/blinky.place.py
pcb-space apply   examples/blinky.place.py --pcb path/to/layout.kicad_pcb
pcb-space place   examples/c3_usb/c3_usb.place.py
pcb-space check   examples/c3_usb/c3_usb.place.py --pcb path/to/layout_placed.kicad_pcb
pcb-space route   examples/blinky.place.py --pcb path/to/layout.kicad_pcb
pcb-space silk    examples/c3_usb/c3_usb.place.py
pcb-space fab     examples/c3_usb/c3_usb.place.py
pcb-space review  examples/c3_usb/c3_usb.place.py
pcb-space source  search "100nF 0402"

Worked PCBA: examples/c3_usb/ (USB-C → AP2112 → ESP32-C3-MINI-1). CI job pytest runs every test except those marked kicad. Job c3-usb-fab installs KiCad 10, runs the full suite (kicad tests may not skip), then pcb-space fab on the committed routed board. Both jobs are required to merge to master. Full re-place / re-route needs KRT_HOME and is not run on GitHub Actions.

apply locks Place(..., locked=True) footprints, writes the Edge.Cuts outline from Board size when the seed has none, writes net classes into the sibling .kicad_pro, writes .kicad_dru, and inserts keepout zones. It copies the board to *.kicad_pcb.bak-pcbspace first.

build is the unified compiler. It skips stages that already have artifacts so a committed routed/ board is what you order. --force / --from rebuilds on purpose and writes layout/<name>/pcbspace.lock.json (pcb-space, pcb, kicad-cli, KRT).

seed runs pcb layout --no-open on the sibling .zen and refuses if the target is under placed/, routed/, or fab/, or already has copper.

place copies the seed, runs apply on the copy, compiles a KRT floorplan-intent (locks, edge bands, keepouts), and legalizes unlocked parts with place_seed --force --anchors-first. Locked CSS poses are file-locks; the engine must not move them. Output is placed/layout.kicad_pcb next to the seed (a second .kicad_pro in the seed directory would make pcb layout refuse the project). pcb layout is seed-only — never a placer. pcb-space place on a packed board is an error.

status reports the stage (schematic / seeded / placed / routed / fab), whether pcb build is green, lint failures, missing Place() names, and nets with no NetReq.

refs maps Zener instance names (R_CC1) to KiCad references (R2) via footprint Path.

check fails if a locked part moved or a named keepout disappeared.

route picks the placed/ board when it exists, refreshes net classes, routes USB pairs (route_diff), then signals, then on 2-layer pours GND last and finalizes. Analog / switch-node nets stay in skip_autoroute. Output is routed/layout.kicad_pcb. --script-only writes the plan without running it. Set KRT_HOME if the router is not in ~/Downloads/KiCadRoutingTools. True 90 Ω USB needs 4-layer; 1.6 mm 2-layer is a tightly-coupled fab-floor pair, not 90 Ω.

fab picks routed/ when it exists, inserts three F.Cu fiducials, writes JLCPCB bom.csv / cpl.csv (LCSC from SOURCE.json; CPL from footprint positions, Y negated like KiCad POS), Gerbers, drill, and FAB_NOTES.md (via-in-pad). It does not upload. Copper kicad-cli DRC errors fail the command. KiCad 10 is required for DRC/Gerbers.

What this is not

  • Not a fork of Zener / pcb and not a replacement for it. The schematic is Zener. This tool consumes the KiCad board Zener seeded.
  • Not Pcbnew. Interactive routing stays in KiCad for leftover analog you want by hand.
  • Not an LLM dumping (segment …) into the s-expression.
  • Not a CSS layout engine. No flex, no grid, no px. Locked parts get a CSS containing block. Unlocked parts get a placer.

Status

v0.4: pcb-space build is the compiler (schematic → seed → place → route → fab). Zener is required for the netlist. init, status, seed, refs, lint, nets. CSS locks; place / route / silk / fab / review. CI: pytest (no KiCad) + c3-usb-fab (full suite + JLC package); both required to merge.

License

MIT

Parts

19
QtyValueFootprintReferences
3CL05B104KO5NNNCstdlib_kicad-footprints_Capacitor_SMD:C_0402_1005MetricC2, C5, C7
20402WGF1002TCEstdlib_kicad-footprints_Resistor_SMD:R_0402_1005MetricR1, R4
20402WGF5101TCEstdlib_kicad-footprints_Resistor_SMD:R_0402_1005MetricR2, R3
2B3U-1000Pworkspace_components_Omron_B3U-1000P:SW_SPST_B3U-1000PU4, U5
2CL10A106KP8NNNCstdlib_kicad-footprints_Capacitor_SMD:C_0603_1608MetricC1, C4
10402WGF1001TCEstdlib_kicad-footprints_Resistor_SMD:R_0402_1005MetricR5
1AP2112K-3.3TRG1workspace_components_Diodes_Inc_AP2112K-3.3TRG1:SOT-23-5U2
1CL05A105KA5NQNCstdlib_kicad-footprints_Capacitor_SMD:C_0402_1005MetricC3
1CL10A106KO8NQNCstdlib_kicad-footprints_Capacitor_SMD:C_0603_1608MetricC6
1ESP32-C3-MINI-1-N4workspace_components_Espressif_ESP32-C3-MINI-1-N4:ESP32-C6-MINI-1U1
1KT-0603Rstdlib_kicad-footprints_LED_SMD:LED_0603_1608MetricD1
1TYPE-C-31-M-12workspace_components_HRO_TYPE-C-31-M-12:USB_C_Receptacle_HRO_TYPE-C-31-M-12J1
1USBLC6-2SC6workspace_components_ST_USBLC6-2SC6:SOT-23-6U3

Questions

How big is the Placed PCB?

It measures 40 × 30 mm and has 2 copper layers, 1.62 mm thick.

What parts does the Placed use?

19 parts, built around the ESP32-C3-MINI-1-N4, B3U-1000P, AP2112K-3.3TRG1 and USBLC6-2SC6. 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 Placed KiCad files?

From the RileyMcCarthy/pcb-space repository on GitHub, at the commit this page was made from: the board is examples/c3_usb/layout/c3_usb/placed/layout.kicad_pcb. PCBKit opens the board in the browser, with nothing to install.

Can I use the Placed design in my own project?

Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

Similar boards

  • 2 layers45.5 × 46 mmChipsat v1, the printed circuit board in 3D

    Chipsat v1

    noellayang/chipsat-v1

    Custom ESP32-C3 power-management testbed for small satellite firmware experiments

    69 parts
  • 4 layers45 × 34 mmTriangle flywheel balancer, the printed circuit board in 3D

    Triangle flywheel balancer

    RobbyV2/smart_latch_lock

    A self-closing bike lock built as a clamshell ring. A fixed arc bolts to the housing, a hinged arc swings 180 degrees to meet it, and a worm gearmotor closes the loop around the frame and rim on a button press or a BLE…

    70 parts
  • 2 layers95 × 55 mmWifi geiger counter, the printed circuit board in 3D

    Wifi geiger counter

    atomicra1n/wifi-geiger-counter

    A DIY Geiger counter PCB which can detect ionizing radiation and log readings over Wi-Fi. Built with an ESP32, and a HV multiplier circuit that steps up 3.7V to 400V for the GM tube.

    64 parts
  • 6 layers40 × 40 mmKestrel fc, the printed circuit board in 3D

    Kestrel fc

    HeyPCB/kestrel-fc

    Kestrel H7: STM32H743 FPV flight controller with ICM-20602 + BMI088, MS5611, 16 MB blackbox, 5 V and switchable 10 V BECs.

    114 partsCERN-OHL-S-2.0
  • 4 layers54 × 63.4 mmRaceTracker v3, the printed circuit board in 3D

    RaceTracker v3

    jonaschramm/race-tracker

    Custom PCB data logger for motorcycle racing: GPS position + IMU motion data, logged to a microSD card, with an e-Paper status display. Built around an ESP32-S3 (WiFi/BT built in) so it can also stream/sync over WiFi.

    49 parts
  • 4 layers100 × 100 mmEc30-tee, the printed circuit board in 3D

    Ec30-tee

    justinpirie/ec30

    Onan EC30 Tee Board with ESP32 Control

    147 partsApache-2.0
  • 4 layers60 × 31 mmGarageDoor pro, the printed circuit board in 3D

    GarageDoor pro

    nikolaus018/garageDoor-pro

    ESP32-C3 + GPS board that opens the garage door when you leave and arrive: firmware, KiCad PCB and perfboard build

    46 parts1MIT