A human- and LLM-readable DSL for PCB schematics, with a Rust compiler that validates constraints and emits KiCad netlists.
# ZAP Bracelet — NFC Payment Wearable
net VCC_3V3
net GND
net NFC_IRQ
component U1 ESP32-C3-MINI-1 {
power { vcc: VCC_3V3 gnd: GND }
gpio { 4: NFC_IRQ }
}
component U2 PN532 {
power { vcc: VCC_3V3 gnd: GND }
irq: NFC_IRQ
}
constraint trace_width >= 0.2mm
constraint max_voltage <= 3.3V
$ schem build examples/zap_bracelet.schem
✓ examples/zap_bracelet.schem → examples/zap_bracelet.net
8 nets • 5 components • 6 constraints
Writing schematics in KiCad or Altium is slow. The file formats are opaque — KiCad's S-expression .kicad_sch is machine-readable but not something a human (or an LLM) can write from scratch. schem is a thin, explicit DSL that:
- A human can write in a text editor without a GUI
- An LLM can generate and modify without hallucinating syntax
- A Rust compiler can validate before any layout work begins
- A CI pipeline can run as a pre-flight check
The reference design is the ZAP Bracelet — an NFC payment wearable targeting Malawi's mobile money ecosystem (Airtel Money / TNM Mpamba), built with an ESP32-C3 and PN532.
| Feature | Status |
|---|---|
.schem parser (pest PEG) |
✅ v0.1 |
| Constraint validation | ✅ v0.1 |
KiCad netlist emit (.net) |
✅ v0.1 |
KiCad schematic emit (.kicad_sch) |
🔜 v0.2 |
| Gerber-ready JSON emit | 🔜 v0.2 |
| Component library integration | 🔜 v0.2 |
| LSP server (editor autocomplete) | 🔜 v0.3 |
cargo install --path .# Validate a schematic
schem check design.schem
# Compile to KiCad netlist
schem build design.schem
# Compile with explicit output path
schem build design.schem --output build/design.net
# Dump the parsed AST (useful when authoring the DSL)
schem dump design.schemnet NET_NAME
All nets must be declared before being referenced in components.
component REF_PART {
group_name {
pin_name: NET_NAME
}
pin_name: NET_NAME
}
REF— reference designator, e.g.U1,C3,D1PART— part value or footprint name, e.g.ESP32-C3-MINI-1- Groups (
power {},gpio {},i2c {}) are purely organisational — they have no semantic meaning in v0.1 but will map to KiCad pin groups in v0.2 - Pins can also be declared flat (outside a group) with
pin: NET
constraint PROPERTY OP VALUE UNIT
| Property | Units | Notes |
|---|---|---|
trace_width |
mm, mil |
min trace width |
clearance |
mm, mil |
min copper clearance |
via_drill |
mm, mil |
min via drill diameter |
via_annular |
mm, mil |
min via annular ring |
max_voltage |
V, mV |
|
max_current |
A, mA |
|
max_impedance |
ohm |
Operators: >=, <=, ==, >, <
PRs welcome. The immediate priorities are listed in the status table above. If you're adding a new emitter, add a module under src/emitter/ and wire it into main.rs as a new subcommand.
MIT