Install
openclaw skills install @zhoushoujianwork/easyeda-pcbEasyEDA PCB automation skill. Use when working with EasyEDA PCB documents through the easyeda-agent CLI or daemon — switching to a PCB, reading components/layers/nets/board context, syncing components from the schematic (import changes), and laying out components (move, rotate, flip, align, distribute, grid-snap, and net-cluster auto-arrange). PCB design rules live in the sibling easyeda-conventions skill.
openclaw skills install @zhoushoujianwork/easyeda-pcbDrive easyeda-agent typed actions. Run easyeda actions for the live machine-readable
list. Prefer typed actions; only fall back to debug.exec_js when a typed action is
missing and the user explicitly accepts a debug path.
PCB design rules live in the sibling
easyeda-conventionsskill — especiallypcb-layout-conventions.md(placement priority P0–P7, stackup-conditioned decoupling, thermal/SI/DFM/grid rules, each with a data-detectable check). This operational skill links to it — single source, never copy the rules here.
1 mil (schematics are 10 mil / 0.01in — different). y-UP: +y renders upward.TOP / BOTTOM). No left/right mirror — only flip (change layer via pcb.component.modify).primitiveId right before mutating.pcb.component.delete returns a boolean meaning "operation completed", not "actually deleted something" — don't rely on it; verify with pcb.components.list.align / distribute / grid_snap / components.move / components.arrange) act on the current selection by default; pass primitiveIds to target a specific set. With nothing selected and no primitiveIds, they error (0 targets).easyeda daemon health → confirm a connected window (route by --project <name>; --window <windowId> only for fine control). Context is live — refreshed on every action AND, with connector ≥ v0.5.7, pushed by the heartbeat within ~3s of a UI tab-switch (so health follows the UI even with no command run). connectorVersionOk: false flags a stale connector loaded in an open window (fully quit + relaunch EasyEDA).easyeda doc ls --project <name> → see every openable doc (★=active). If the active doc isn't the target PCB, easyeda doc switch <PCB-name|uuid> --project <name> (cross-type PCB↔schematic works). With 2+ windows open, --project/--window is REQUIRED — without it the command only auto-targets when exactly one window is connected, else errors no EasyEDA connector is available (a momentary connector reconnect can also trigger this — just retry). (Low-level equivalent: document.current → pcb.documents.list → document.open <pcbUuid>.)pcb.components.list (includeBBox+includePads), pcb.layers.list (read copperLayerCount), pcb.nets.list, pcb.board.info.pcb.drc.check.delete, import_changes, bulk arrange) and before saving.pcb.documents.list — all PCB documents in the project (uuid + name); pair with document.open.document.open — open any document (schematic page or PCB) by uuid; the cross-type switch entry.pcb.board.info — current Board (schematic↔PCB linkage) + current PCB; the prerequisite context for import_changes.A Board groups exactly one schematic + one PCB — that is how the two are kept
together, and what import_changes follows. Boards are identified by name, not
uuid. CLI: easyeda board …. Maps to eda.dmt_Board.*.
board.list / board.current — all boards (name + bound schematic + pcb) / the current one.board.create — bind a schematic and/or PCB into a new board (--schematic / --pcb). The fix for a floating/unlinked PCB before import_changes.board.rename — rename a board (--name → --new).board.copy — duplicate a board (its schematic + PCB).board.delete — delete a board by name (confirm — no undo).Act on the focused canvas; the editor view shortcuts. CLI: easyeda view ….
view.fit — zoom to fit all primitives (适应全部, the K shortcut) → easyeda view fit.view.fit_selection — zoom to fit the current selection → easyeda view fit-selection.view.zoom — pan/zoom to a center coordinate and/or scale percent (--x/--y/--scale; omitted keeps current).view.region — zoom to a rectangular region (--left/--right/--top/--bottom, mil).pcb.components.list — placed footprints. includeBBox → per-component rendered extent (for overlap/spacing reasoning); includePads → pads + net (the net-by-name connectivity).pcb.layers.list — layers (id/name/type), currentLayer, and copperLayerCount (2-layer vs 4+-layer — gates the decoupling rules).pcb.nets.list — nets (net / length / color).pcb.report — read-only design report driven by per-net copper length: every net's routed length, each net class's aggregate length, differential-pair P/N lengths + skew (|lenP−lenN|), and equal-length-group per-net lengths + spread (max−min). No DRC run — the quantitative companion to pcb.drc.check for routing-quality gates (diff skew / length matching). Pure read.pcb.drc.rules — read the active PCB's DRC rule configuration (clearances, track widths, via sizes, …) without running a check. Use to feed real rule values into layout reasoning / gates, or to see what pcb.drc.check enforces.Real routing primitives — additive creates (no confirm), like the schematic
wire.create. Bind to a net by name (pull from pcb.nets.list); layer ids from
pcb.layers.list. EasyEDA's create() is lenient — it can return no primitive on a
bad layer/coords without throwing, so each action verifies a primitive came back and
fails honestly otherwise. PCB autosave is on (debounced) — still save explicitly
at checkpoints. There is no one-call autorouter on this build
(pcb_Document.autoRouting is undefined — see docs/ecosystem-survey.md §6/§7); route
segment-by-segment, or use the file-exchange autoroute flow.
pcb.line.create — a copper track (导线): line segment on a copper layer
(TOP=1, BOTTOM=2; inner-copper ids are higher — id 3 is silkscreen, not
copper, so read real ids from pcb.layers.list) between (startX,startY) and
(endX,endY) (mil, y-up), lineWidth (default 6 mil), optional net. Verify with
pcb.drc.check.pcb.via.create — a via (过孔) at (x,y) with holeDiameter (drill, default 12
mil) + diameter (outer pad, default 24 mil), optional net.pcb.line.list / pcb.via.list — read what's routed (filter by net/layer) before
rip-up or reroute.pcb.route.rip_up — reliable rip-up: delete tracks+arcs+vias, --net to scope
(string or list) or omit for ALL. Copper layers only — never deletes the board
outline, silkscreen/assembly/mechanical artwork, or locked primitives. The
iteration primitive: rip_up → re-route. (Reports {requested, ok} per type, since
delete() is a batch boolean.)pcb.clear_routing — native clearRouting (@alpha, may be undefined on this build,
and does NOT protect unlocked outline) — prefer pcb.route.rip_up.A pour is a net-bound copper region (usually GND/power plane). The agent passes raw
points — the connector builds the IPCB_Polygon (pcb_MathPolygon.createPolygon)
and re-pours; passing raw points to the bare eda.* create fails ("无法创建覆铜边框图元").
pcb.pour.create — pour from a closed polygon points ([[x,y],…], mil, y-up) on a
copper layer, bound to a net. fill = solid (default) | grid | grid45. Size it to
the board outline; verify poured:true + pcb.drc.check.pcb.pour.list / pcb.pour.delete — inspect / remove pours.pcb.pour.rebuild — re-pour all (or by net) after moving components/routing so the
copper reflows around new obstacles.Routing boundary (load-bearing — see
docs/ecosystem-survey.md§7): EasyEDA's interactive 布线 menu (single/multi/differential routing, stretch, optimize, length-tuning/serpentine, fanout, remove-loops) has NOeda.*API — the agent cannot do smart/avoiding/push-and-shove routing. Programmatic routing is limited to: create tracks/vias/pours by coordinate (above), rip-up, the@alphaautoRouting(undefined on 3.2.148), or read-primitives → external engine → write (the official kirouting pattern). So route segment-by-segment, pour planes, and leave smart routing to the human/UI. Shipped: copper pour + rip-up (R1/R2). Still pending: net-class/diff-pair/equal-length definitions (R3 — read side is inpcb.report).
pcb.import_changes — sync components/netlist from the schematic (从原理图导入变更). The primary way parts arrive on the board: ensures a Board links SCH+PCB, then importChanges, then recomputes ratlines. Mutates the board; confirm first. Returns imported:false (with a reason) for a floating/unlinked PCB.pcb.component.modify — move (x/y), rotate, flip layer (top/bottom), lock, designator/BOM flags.pcb.component.delete — delete component primitives. Confirm first (no undo).pcb.align — mode = left | right | top | bottom | centerX | centerY (y-up: top = larger y), aligned to the group extent.pcb.distribute — even center spacing, axis = x | y, extremes fixed.pcb.grid_snap — round component anchors to grid (mil; SMD 25, THT 50).pcb.components.move — translate a group by relative dx / dy.pcb.components.arrange — coarse auto-layout seed (priority P6): mode=cluster groups by shared local nets then grid-packs each cluster into a tidy non-overlapping block; mode=grid packs a flat grid. Skips locked parts.easyeda pcb auto-place — module-aware heuristic placement (daemon-side). Main chips (≥ --main-pins, default 8, distinct pins) are anchors that stay put; every satellite (cap/R/LED) is pulled to the chip edge nearest the pad it connects to, then packed along that edge with no overlap: decoupling caps land by their power pin (3V3/VCC), signal R's by their signal pin, an LED chains beside its series resistor. --dry-run prints the plan without moving. A SEED (v1 translates only, no rotation) — refine by hand + verify with pcb drc. Prefer this over arrange when there is a clear main chip; use arrange for chip-less or flat-grid cases.easyeda pcb route-short — short-trace self-router (daemon-side, the heuristic tier — NOT pcb autoroute/Freerouting). Per net: MST over pads, then a track per hop ≤ --max-len (Manhattan) on the pads' shared layer. Skips GND (poured; --route-gnd to include), already-routed nets, cross-layer hops (need a via), over-long hops (maze tier). Track width is by net class: power/GND nets get --width-power (default 20 mil), signals get --width-signal (default 10 mil); a single --width forces both. Corner style via --corner: 90 (Manhattan L, default), 45 (chamfer — avoids acid traps/reflections), round (chord-approximated fillet, --round-radius; native arcs don't commit on this build so it's segmented). No obstacle avoidance in v1 — run after auto-place so hops are short/clear, then pcb drc. --dry-run previews. Long/congested/any-distance routing → pcb autoroute (external Freerouting).v1 (route-short / pour) is mechanically correct but coarse. Planned quality upgrades:
pour: a
directly-drawn 填充区域 primitive (eda.pcb_PrimitiveRegion, 类型=填充区域) on a layer, bound
to a net (e.g. a 3V3 power-plane patch, RF ground, thermal copper), arbitrary/odd polygon. Plan:
pcb region create action (also covers keep-out — same primitive, different rule type). (task #11)The board outline is a prerequisite for layout (edge keep-out, connectors-to-edge, mounting holes are all relative to it). If the customer has an outline spec, build it first; otherwise draft a layout, then define an outline around it.
pcb.outline.set — set the outline from a closed polygon points ([[x,y],…], mil,
y-up). Replaces any existing outline; reports allInside/outside (components out of
the board). Confirm first (redraws the board edge).pcb.outline.get — current outline (segment/arc count + bbox).pcb.outline.clear — remove the outline.The agent generates the points for the wanted shape. Curves are line-segment
approximated (~48–120 segments) — native arcs do not commit on this build, so a true
circle/arc needs the EasyEDA UI (圆形/圆弧 tool) or an SVG import. Recipes (centre (cx,cy),
all mil):
| Shape | Points |
|---|---|
Rectangle w×h | the 4 corners |
| Rounded-rect | corners replaced by N-step quarter-circle fillets of radius r |
Circle Ød | N≈72: [cx+r·cosθ, cy+r·sinθ] for θ=2πi/N, r=d/2 |
| Instrument / dashboard (异形) | squircle `x=a·sign(cosθ)· |
Size the outline to enclose the component extent (pcb.components.list --includeBBox)
with margin, then verify allInside from the response.
Follow the priority hierarchy in
pcb-layout-conventions.md
(P0 mechanical/enclosure > P1 safety/isolation > P2 EMI hot-loop + critical decoupling >
P3 reference-plane/return > P4 thermal keep-out > P5 functional grouping > P6 DFM >
P7 grid/align/silkscreen — P7 is cosmetic and never overrides a function-driven position).
Operational order:
pcb.components.list (includeBBox+includePads) + pcb.layers.list (copperLayerCount) + pcb.nets.list; classify each part by net/designator (anchor / hot / sensitive / IC / passive).lock them; treat as immovable obstacles; edge connectors open outward.easyeda pcb auto-place (module-aware: satellites hug the chip pin they connect to); otherwise pcb.components.arrange mode=cluster for a net-clustered seed. Run --dry-run first to review the plan.{Cin + switch + catch-diode} bbox; spread hot parts ≥400 mil; keep heat-sensitive parts (electrolytics/crystals/sensors) ≥200 mil from heat.pcb.align / pcb.distribute / pcb.grid_snap, without breaking any function-driven position.pcb.drc.check (and the PCB linter once it lands); fix by rule number. Pull fresh primitiveIds before each mutation; confirm destructive ops; log before/after.Key corrections from review (see the conventions doc): decoupling effectiveness is governed by the cap's mounting-loop inductance (pad→via→plane), not raw distance; default a single solid ground plane partitioned by placement (do not split-ground by default); all hard thresholds are conditioned on stackup / fab / enclosure context.
pcb.component.delete, pcb.import_changes, or a bulk arrange/auto-layout plan.File/Blob outputs (gerber/pick-and-place/3D) as artifacts.