Skip to content

Week 06 — PCB Design Documentation

Fab Academy | Dorian Fritze Date: March 24, 2026


Project Overview

Designing a custom artistic PCB in KiCad featuring: - A thought-cloud board outline (Edge.Cuts) - A lightbulb as a decorative copper element - Artistic hand-routed traces as part of the visual design - XIAO RP2040 microcontroller - Radiating slash marks as milled/engraved decorative elements

Original Design Sketch

Original PCB Design Sketch


Problem: PCB Was Too Large

The original PCB design was too large to mill on the Carvera. The goal was to scale it down to 50% (0.5x) while preserving the artistic traces and decorative elements.


Step 1: Attempting to Scale via KiCad Scripting Console

Tools → Scripting Console (or backtick ` key)

First Attempt — Multi-line Paste Fails

Pasting a multi-line Python script directly into the KiCad shell causes:

SyntaxError: multiple statements found while compiling a single statement

First scripting error

Fix: The KiCad scripting console is line-by-line. Use exec() with \n to run multi-line blocks, or save a .py file and run it with:

exec(open('/path/to/script.py').read())


Step 2: Successfully Scaling Footprints

Running each line separately, then using exec() for the loop:

import pcbnew
board = pcbnew.GetBoard()
scale = 0.5
origin = pcbnew.VECTOR2I(pcbnew.FromMM(100), pcbnew.FromMM(100))
exec("for item in board.GetFootprints():\n    pos = item.GetPosition()\n    new_x = origin.x + int((pos.x - origin.x) * scale)\n    new_y = origin.y + int((pos.y - origin.y) * scale)\n    item.SetPosition(pcbnew.VECTOR2I(new_x, new_y))")
pcbnew.Refresh()

Footprints successfully scaled

Result: Footprints moved. Namespace confirms scale: 0.5, new_x and new_y values changed correctly.


Step 3: Diagnosing Edge.Cuts — What Shape Type Is It?

Running a diagnostic to check the Edge.Cuts layer:

exec("for item in board.GetDrawings():\n    print(item.GetLayer(), item.GetClass(), item.GetStart(), item.GetEnd())")

The Edge.Cuts outline showed layer 25 (KiCad 9 numbering) with start and end at the same point — meaning it's a polygon, not individual line segments.

Edge.Cuts diagnostic

Key finding: GetStart()/GetEnd() won't work on polygons — need GetPolyShape().


Step 4: Board State After Initial Scaling

After scaling footprints but before scaling traces and drawings:

Board state showing misalignment

Problems visible: - Flower/cloud Edge.Cuts outline (top left) — old choppy outline from manual tracing - Actual PCB components (bottom right) — already scaled but offset - Red copper flood fill covering both areas


Step 5: Original Design Intent

The PCB is designed to look like the hand-drawn sketch — the traces ARE the art:

Element KiCad Layer Carvera Operation
Cloud outline Edge.Cuts Mill/cut board shape
Radiating slash marks Edge.Cuts or Eco1.User Drill/mill slots
Lightbulb outline F.Cu Copper engraving
Circuit traces F.Cu Mill isolation

Board with cloud and circuit visible


Step 6: Curved Traces Didn't Scale — Arc Diagnosis

After scaling straight tracks, the curved artistic traces (thought bubble, lightbulb coil) remained at original size.

Curved traces not scaled

Diagnostic showed all tracks were PCB_TRACK (straight segments) — the curves were actually PCB_SHAPE arcs on layer 0 (F.Cu), not tracks.


Step 7: Bad Script — Circles Went Wrong

Attempting to scale F.Cu shapes with SetCenter() produced incorrect giant circles:

Bad arc scaling result

Lesson learned: SetCenter() does not correctly scale arcs in KiCad 9. Need to use SetMid() for arcs. Also, SetStart/SetEnd without SetMid distorts arc shapes.


Step 8: Undo Failed — Scripts Don't Enter Undo History

Python scripting changes in KiCad do not go into the undo buffer. Ctrl+Z could not reverse the bad script.

Undo attempt and revert dialog

⚠️ Critical lesson: Always save a backup before running any script:

board.Save('/Users/dorianfritze/Desktop/week06-BACKUP.kicad_pcb')


Step 9: Recovering from Autosave

KiCad's autosave (_autosave-week06-assignment.kicad_pcb) was found with the first scaling partially done — footprints scaled but traces not yet scaled.

Autosave recovery

Recovery steps: 1. File → Save As → week06-assignment-v2.kicad_pcb 2. Run backup script before any further changes


Step 10: Fresh Start — Scripting Console Import Error

Opening a new scripting console requires re-importing pcbnew every time:

pcbnew not defined error

import pcbnew  # Always run this first in a new session!
board = pcbnew.GetBoard()

Step 11: Traces Successfully Scaled

Working carefully with backups saved between each step:

import pcbnew
board = pcbnew.GetBoard()
board.Save('/Users/dorianfritze/Desktop/week06-SAFE-BACKUP.kicad_pcb')  # BACKUP FIRST!
scale = 0.5
origin = pcbnew.VECTOR2I(pcbnew.FromMM(100), pcbnew.FromMM(100))
exec("for track in board.GetTracks():\n    s = track.GetStart()\n    e = track.GetEnd()\n    track.SetStart(pcbnew.VECTOR2I(origin.x + int((s.x - origin.x) * scale), origin.y + int((s.y - origin.y) * scale)))\n    track.SetEnd(pcbnew.VECTOR2I(origin.x + int((e.x - origin.x) * scale), origin.y + int((e.y - origin.y) * scale)))")
pcbnew.Refresh()

Traces scaled successfully

Status: Straight traces now aligned with scaled footprints. Curved F.Cu shapes still need attention.


Step 12: Straight Line Drawings Scaled, Curves Still Wrong

After scaling straight tracks successfully, ran script to scale straight F.Cu drawings (shape type 0):

exec("for item in board.GetDrawings():\n    if item.GetLayer() == 0 and item.GetShape() == 0:\n        ...")

Curved shapes still didn't scale. Zoomed in view showed the problem clearly:

Curved traces still unscaled


Step 13: Discovering Curves Are Functional Traces with Nets

Right-clicking a stray curve revealed it was on F.Cu with Net: D3 assigned — meaning it's a functional copper trace, not just a decorative drawing. Line width was 0.2mm (too thin for Carvera).

Properties dialog showing F.Cu and Net D3

Attempted script using GetNet() to find these shapes — but it made things worse again.


Step 14: Layer Diagnostic Returned Nothing

Ran diagnostic to find F.Cu.user layer number:

exec("for item in board.GetDrawings():\n    if 'user' in str(item.GetLayer()).lower() or item.GetLayer() > 30:\n        print(item.GetLayer(), item.GetClass(), item.GetShape(), item.GetStart())")

Diagnostic returned no output

No output — all curved shapes were on layer 0 (F.Cu proper), not a user layer.


Step 15: Recovering to Backup2 — Good State Found

Restored from week06-SAFE-BACKUP2.kicad_pcb. Board looked nearly correct with cloud, lightbulb, and components visible:

Good state from backup2

However, on closer inspection the cloud and lightbulb art were still at original large scale — they hadn't been scaled at all. The components appeared small relative to the large artistic elements.


Step 16: Double-Scaling Problem Discovered

After further scripting attempts, some components appeared to have been scaled twice — the XIAO and J8 connector were far apart from the rest of the cluster:

Double scaling problem


Step 17: Key Insight — Footprints Must NOT Resize

The fundamental challenge clarified: - ✅ Footprint positions → must scale (move closer together) - ❌ Footprint pad/component sizes → must NOT scale (must match real parts) - ✅ Trace positions and routing → must scale - ✅ Artistic drawing positions → must scale - ❌ Trace widths → keep or increase for Carvera

SetPosition() correctly moves without resizing. The scripts were right — the problem was repeated execution causing double-scaling.


Step 18: Back to Original — Clean Restart

Opened the original unscaled week06-assignment.kicad_pcb. All components, traces, and artistic elements intact:

Original clean file


New Strategy: Delete Art First, Then Scale

Lesson learned the hard way: Scripting arc/curve scaling in KiCad is unreliable. The new approach:

  1. Save protected original immediately
  2. Manually delete all curved artistic elements before any scripting:
  3. Cloud interior curves
  4. Lightbulb outline
  5. Thought bubble circles
  6. Coil/spiral
  7. Radiating slashes
  8. Edge.Cuts outline
  9. Run proven scaling scripts on simple straight geometry only
  10. Rebuild all art manually at correct scale after scaling
  11. Import Inkscape SVG for cloud Edge.Cuts

This avoids all arc scripting problems entirely.


Step 19: Fresh Start — Delete Art First, Then Scale

Opened original file. Manually deleted all curved artistic elements first, leaving only straight traces and footprints.

Curved art deleted


Step 20: Footprints Scaled Successfully

scale = 0.5
origin = pcbnew.VECTOR2I(pcbnew.FromMM(100), pcbnew.FromMM(100))
exec("for item in board.GetFootprints():\n    pos = item.GetPosition()\n    item.SetPosition(pcbnew.VECTOR2I(origin.x + int((pos.x - origin.x) * scale), origin.y + int((pos.y - origin.y) * scale)))")
pcbnew.Refresh()

Footprints scaled


Step 21: Traces Scaled Successfully

exec("for track in board.GetTracks():\n    s = track.GetStart()\n    e = track.GetEnd()\n    track.SetStart(pcbnew.VECTOR2I(origin.x + int((s.x - origin.x) * scale), origin.y + int((s.y - origin.y) * scale)))\n    track.SetEnd(pcbnew.VECTOR2I(origin.x + int((e.x - origin.x) * scale), origin.y + int((e.y - origin.y) * scale)))")
pcbnew.Refresh()

Traces scaled


Step 22: Trace Widths Fixed for Carvera

exec("for track in board.GetTracks():\n    if track.GetWidth() < pcbnew.FromMM(0.4):\n        track.SetWidth(pcbnew.FromMM(0.4))")
pcbnew.Refresh()

Trace widths fixed

Saved as week06-SCALED-CLEAN.kicad_pcb.


Current Status

  • Footprints scaled to 50%
  • Straight traces scaled to 50%
  • Trace widths fixed to 0.4mm minimum
  • Delete stray vertical line at top
  • Fix remaining ratsnest connections manually
  • Trace cloud in Inkscape → import as Edge.Cuts
  • Redraw lightbulb and artistic elements at new scale
  • Run DRC check
  • Export Gerbers for Carvera/MakerCAM

Key Lessons Learned

  1. KiCad scripting console is line-by-line — use exec() with \n for loops
  2. Python scripts bypass the undo buffer — always save before scripting
  3. Arc scaling requires SetMid() — not just SetStart/SetEnd, and even then unreliable
  4. PCB_SHAPE arcs vs PCB_ARC tracks are different object types
  5. Layer 25 = Edge.Cuts in KiCad 9 (not layer 44)
  6. Polygon outlines need GetPolyShape() not GetStart()/GetEnd()
  7. Inkscape → Trace Bitmap → SVG → KiCad is the best workflow for organic board outlines
  8. Artistic curves with nets assigned are functional traces — behave differently from drawings
  9. Running scaling scripts twice causes double-scaling — always check state before running
  10. Delete curved art BEFORE scripting — rebuild by hand after scaling is safer and faster
  11. Always save a protected backup of the original before any modifications

Carvera Milling Specifications (Reference)

Parameter Value
Min trace width 0.4mm (1/64" bit)
Min clearance 0.4mm
Min drill size 0.8mm
Edge clearance 1.0mm

Documentation continues as work progresses...