pixalynx/esp32-usb-ducky

This code defines digital and analog ICs, connectors, and components with specified pinouts, footprints, and 3D models for PCB rendering, including voltage regulators, USB connectors, microSD sockets, and push-button switches.

Version
1.2.6
License
unset
Stars
0

index.circuit.tsx

PCBPCB preview for index.circuit.tsx
SchematicSchematic preview for index.circuit.tsx
import { Fragment } from "react"
import { ESP32_S3_WROOM_1_N16R8 } from "./imports/ESP32_S3_WROOM_1_N16R8"
import { TLV75733PDRVR } from "./imports/TLV75733PDRVR"
import { USBLC6_2SC6 } from "./imports/USBLC6_2SC6"
import { TF_01A } from "./imports/TF_01A"
import { TS_1187A_B_A_B } from "./imports/TS_1187A_B_A_B"
import { U261_121N_4BS2S } from "./imports/U261_121N_4BS2S"
import { XL_1608UBC_04 } from "./imports/XL_1608UBC_04"

// Schematic sections on a compact grid.
const SEC = {
  Usb: { x: 0, y: 16 },
  Mcu: { x: 0, y: 2 },
  BootReset: { x: 10, y: 2 },
  Power: { x: -10, y: -8 },
  Indicator: { x: 20, y: -8 },
  MicroSd: { x: 0, y: -20 },
  Test: { x: 22, y: 8 },
}

// Board outline is a plain 26 x 82.25mm stick. The USB-C male plug (J1) is a
// board-edge part: its SMD contacts and shell pegs sit on the PCB and the
// 11.25mm plug body overhangs the TOP edge to insert straight into a host.
// XKB's recommended PCB layout puts the board edge at the outer end of the
// shell-peg pads, which is also where JLCPCB's EasyEDA footprint starts its
// board cutout: y = 33.25 here, 0.22mm past the slot copper (JLCPCB's routed
// edge minimum is 0.2mm). v1.2.5 ran the board 2.25mm further under the plug.
// The ESP32 module lives at the BOTTOM with its printed antenna right at the
// bottom edge, as far from the USB metal as the board allows.
//   top edge (USB-C plug)   y = +33.25
//   bottom edge (antenna)   y = -49
const EDGE_TOP = 33.25
const EDGE_BOTTOM = -49
const BOARD_OUTLINE = [
  { x: -13, y: EDGE_BOTTOM },
  { x: 13, y: EDGE_BOTTOM },
  { x: 13, y: EDGE_TOP },
  { x: -13, y: EDGE_TOP },
]
const EDGE_FENCE = { pcbY: (EDGE_TOP + EDGE_BOTTOM) / 2, height: EDGE_TOP - EDGE_BOTTOM }
// The board's centre is pcbY + outlineOffsetY and every outline point is
// shifted by the same offset, so the outline is written relative to its own
// centre. That keeps the edges where they are while the board box DRC and
// the placement checker derive (centre +/- height/2) matches the outline.
const BOARD_CENTER_Y = (EDGE_TOP + EDGE_BOTTOM) / 2
const BOARD_OUTLINE_REL = BOARD_OUTLINE.map((p) => ({ x: p.x, y: p.y - BOARD_CENTER_Y }))

// U1 is placed antenna-down (pcbRotation=180) at the bottom. Its printed
// antenna (the module's own meander) sits flush at the bottom board edge
// with no copper around or under it — the proper spot for RF, well clear of
// the USB shell up top. The keepout spans the FULL board width (26mm) so the
// GND pours can't fill the strips beside the antenna down to the board edge
// (Espressif's module guidelines want the sides of the antenna clear, not just
// the area under it).
const ANTENNA_KEEPOUT = { pcbX: 0, pcbY: -45.65, width: 26.2, height: 6.6 }

// JLCPCB part for every passive, pinned so the BOM cannot drift when the
// parts engine picks differently in a later tscircuit release.
const JLC = {
  R_0_0402: { jlcpcb: ["C17168"] },
  R_22_0402: { jlcpcb: ["C25092"] },
  R_330_0402: { jlcpcb: ["C25104"] },
  R_5K1_0402: { jlcpcb: ["C25905"] },
  R_10K_0402: { jlcpcb: ["C25744"] },
  C_100N_0402: { jlcpcb: ["C1525"] },
  C_1U_0402: { jlcpcb: ["C52923"] },
  C_10U_0805: { jlcpcb: ["C15850"] },
  C_22U_0805: { jlcpcb: ["C45783"] },
}

type Pt = { x: number; y: number }
type Rot = 0 | 90 | 180 | 270
type Layer = "top" | "bottom"

// A <trace pcbPath> point is an offset from the from-pad component's pcbX/pcbY
// (not its land-pattern centre), rotated with that component. frame()
// converts board coordinates. Only the few non-critical links left on pcbPath
// use it; the router may still adjust those.
const frame =
  (c: Pt, rot: Rot = 0) =>
  (p: Pt): Pt => {
    const dx = p.x - c.x
    const dy = p.y - c.y
    if (rot === 90) return { x: dy, y: -dx }
    if (rot === 180) return { x: -dx, y: -dy }
    if (rot === 270) return { x: -dy, y: dx }
    return { x: dx, y: dy }
  }

// Board position of pin1/pin2 of a 2-pad passive (0402 offset 0.51mm, 0805
// 0.9125mm) placed at c with the given pcbRotation.
const pin = (c: Pt, rot: Rot, offset: number, which: 1 | 2): Pt => {
  const d = which === 1 ? -offset : offset
  const f = rot === 0 ? { x: d, y: 0 } : rot === 90 ? { x: 0, y: d } : rot === 180 ? { x: -d, y: 0 } : { x: 0, y: -d }
  return { x: c.x + f.x, y: c.y + f.y }
}

// ------------------------------------------------------------------------
// LOCKED ROUTES. tscircuit 0.0.2561's autorouter treats plain pcbPath traces
// as suggestions: it rewrote a third of them and routed other nets through
// them. Every critical connection is therefore a SAVED route in routing
// phase 0 (<autoroutingphase pcbTracePaths>): core hands the saved copper to
// the router as fixed obstacles, so it is never moved and nothing is routed
// through it. Rules: a saved route is written in board mm, must start and end
// exactly on PCB port positions, and phase 0 must cover every pad of every
// connection assigned to it (routingPhaseIndex={0} on those traces). Pads of
// a multi-pad net are chained: each route starts at a pad not yet joined.
// ------------------------------------------------------------------------
type SavedPoint =
  | { route_type: "wire"; x: number; y: number; width: number; layer: Layer }
  | { route_type: "via"; x: number; y: number; from_layer: Layer; to_layer: Layer }
// "via" changes layer at the previous point.
const savedRoute = (width: number, pts: (Pt | "via")[], startLayer: Layer = "top"): SavedPoint[] => {
  let layer = startLayer
  let last = pts[0] as Pt
  const out: SavedPoint[] = []
  for (const p of pts) {
    if (p === "via") {
      const to: Layer = layer === "top" ? "bottom" : "top"
      out.push({ route_type: "via", x: last.x, y: last.y, from_layer: layer, to_layer: to })
      layer = to
      continue
    }
    out.push({ route_type: "wire", x: p.x, y: p.y, width, layer })
    last = p
  }
  return out
}
const mid = (a: Pt, b: Pt): Pt => ({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 })

// Exact PCB port positions of imported footprints (from circuit.json).
const PORT = {
  SD1_DAT2: { x: -5.45108765, y: -4.759986699999999 },
  SD1_CS: { x: -5.45108765, y: -5.8600353 },
  SD1_MOSI: { x: -5.45108765, y: -6.9600839 },
  SD1_VDD: { x: -5.45108765, y: -8.0601325 },
  SD1_SCK: { x: -5.45108765, y: -9.1599525 },
  SD1_MISO: { x: -5.45108765, y: -11.360049700000001 },
  SD1_DAT1: { x: -5.45108765, y: -12.459869699999999 },
  J1_A5: { x: 1.2501879999999992, y: 31.47500025 },
  J1_A6: { x: 1.750059999999999, y: 31.47500025 },
  J1_A7: { x: 2.250185999999999, y: 31.47500025 },
  U3_IO1: { x: 3.55004, y: 26.650904 },
  U3_IO2: { x: 5.44996, y: 26.650904 },
  U3_IO1_B: { x: 3.55004, y: 28.949096 },
  U3_IO2_B: { x: 5.44996, y: 28.949096 },
  U1_IO0: { x: -8.750046, y: -24.5349527 },
  U1_IO4: { x: 8.750046, y: -37.2349527 },
  U1_3V3: { x: 8.750046, y: -39.7749527 },
  U1_IO19: { x: 8.750046, y: -25.8049527 },
  U1_IO20: { x: 8.750046, y: -24.5349527 },
  U1_TOP_ROW_Y: -23.2550467,
  SW1_A: { x: 4, y: 8.85 },
  SW1_B: { x: 10, y: 8.85 },
  TP5: { x: -11, y: -24.54 },
}

// ---------------------------------------------------------------- USB ----
const u2 = frame({ x: 0, y: 15.5 }, 90)
const R3_C = { x: 10.85, y: -24.5 }
const R4_C = { x: 11.85, y: -24.5 }
const R11_C = { x: 1.4, y: 29 }
const C9_C = { x: 1.3, y: 27.8 }
const R21_C = { x: 1.3, y: 26.6 }
const USB_PATHS = [
  // plug -> ESD top row, both on top, 0.5mm apart
  { connection: ".U3 > .IO1_B", route: savedRoute(0.2, [PORT.U3_IO1_B, { x: PORT.U3_IO1_B.x, y: 29.8 }, { x: PORT.J1_A6.x, y: 29.8 }, PORT.J1_A6]) },
  { connection: ".U3 > .IO2_B", route: savedRoute(0.2, [PORT.U3_IO2_B, { x: PORT.U3_IO2_B.x, y: 30.3 }, { x: PORT.J1_A7.x, y: 30.3 }, PORT.J1_A7]) },
  // ESD bottom row -> right-edge lane (x=11.4 / 11.8) -> R3 / R4
  {
    connection: ".U3 > .IO1",
    route: savedRoute(0.2, [PORT.U3_IO1, { x: PORT.U3_IO1.x, y: 25.6 }, { x: 11.4, y: 20.8 }, { x: 11.4, y: -23 }, { x: 10.85, y: -23.55 }, pin(R3_C, 270, 0.51, 1)]),
  },
  {
    connection: ".U3 > .IO2",
    route: savedRoute(0.2, [PORT.U3_IO2, { x: PORT.U3_IO2.x, y: 24.907 }, { x: 11.8, y: 21.024 }, { x: 11.8, y: -23 }, { x: 11.85, y: -23.55 }, pin(R4_C, 270, 0.51, 1)]),
  },
  // R3 / R4 -> module, without crossing over each other
  { connection: ".R3 > .pin2", route: savedRoute(0.2, [pin(R3_C, 270, 0.51, 2), PORT.U1_IO20]) },
  { connection: ".R4 > .pin2", route: savedRoute(0.2, [pin(R4_C, 270, 0.51, 2), { x: 11.35, y: PORT.U1_IO19.y }, PORT.U1_IO19]) },
  // CC -> R11
  { connection: ".R11 > .pin1", route: savedRoute(0.2, [pin(R11_C, 0, 0.51, 1), { x: 0.89, y: 29.6 }, { x: PORT.J1_A5.x, y: 30 }, PORT.J1_A5]) },
  // VBUS_ESD (the USBLC6 VBUS pin, its decoupler C9 and the 0R link R21): the
  // VBUS pin sits between the two channels, so it leaves on top under the
  // package, between the two pin rows.
  { connection: ".R21 > .pin2", route: savedRoute(0.3, [pin(R21_C, 0, 0.51, 2), pin(C9_C, 180, 0.51, 1)]) },
  { connection: ".U3 > .VBUS", route: savedRoute(0.3, [{ x: 4.5, y: 28.949096 }, { x: 4.5, y: 27.8 }, pin(C9_C, 180, 0.51, 1)]) },
]

// ------------------------------------------------------------ microSD ----
// The socket's contacts face WEST (x = -5.451) and the module's SDMMC pins
// sit in a row along its top edge. Every line leaves its contact westward,
// runs south in its own lane, turns east and drops into its series resistor
// right above the module pin. Lane order (west -> east) follows the contact
// order, and the GPIO map follows the lane order, so nothing crosses and no
// line changes layer; the bottom GND pour stays unbroken underneath. CLK has
// 1mm to its neighbours (0.5mm elsewhere) so ground pour runs beside it.
const SD_RES_Y = -20.8
const SD_LANES = [
  { name: "SD_D2", cardNet: "SDC_D2", pad: "SD1.DAT2", sel: ".SD1 > .DAT2", at: PORT.SD1_DAT2, laneX: -11.9, turnY: -19.9, res: "R18", resX: -3.17, mcu: "U1.SD_D2", mcuX: -3.175 },
  { name: "SD_D3", cardNet: "SDC_D3", pad: "SD1.CS", sel: ".SD1 > .CS", at: PORT.SD1_CS, laneX: -11.4, turnY: -19.4, res: "R14", resX: -1.9, mcu: "U1.SD_D3", mcuX: -1.905 },
  { name: "SD_CMD", cardNet: "SDC_CMD", pad: "SD1.MOSI", sel: ".SD1 > .MOSI", at: PORT.SD1_MOSI, laneX: -10.9, turnY: -18.9, res: "R15", resX: -0.63, mcu: "U1.SD_CMD", mcuX: -0.635 },
  { name: "SD_CLK", cardNet: "", pad: "SD1.SCK", sel: ".SD1 > .SCK", at: PORT.SD1_SCK, laneX: -9.9, turnY: -17.9, res: "R13", resX: 0.64, mcu: "U1.SD_CLK", mcuX: 0.635 },
  { name: "SD_D0", cardNet: "SDC_D0", pad: "SD1.MISO", sel: ".SD1 > .MISO", at: PORT.SD1_MISO, laneX: -8.9, turnY: -16.9, res: "R16", resX: 1.91, mcu: "U1.SD_D0", mcuX: 1.905 },
  { name: "SD_D1", cardNet: "SDC_D1", pad: "SD1.DAT1", sel: ".SD1 > .DAT1", at: PORT.SD1_DAT1, laneX: -8.4, turnY: -16.4, res: "R17", resX: 3.18, mcu: "U1.SD_D1", mcuX: 3.175 },
]

// 10k pull-ups sit in the pockets between the contact escapes, 0.5mm west of
// the socket, with pin2 on a 1mm stub to their contact. Their 3V3 is its own
// small net (V3V3_SD, joined to the main 3V3 by the 0R link R19 beside the
// regulator's output cap) so it can be locked: each pull-up's pin1 drops
// through a via and runs on the BOTTOM layer east under the socket body
// (never under an SD lane) to a spine at x=-3 that ends at the bottom-side
// 3V3 test pad TP1, in the free area below the last contact. R9 sits right
// above TP1. C7 decouples the card's VDD contact and is fed through R7.
const SD_PULLUP_X = -7.35
const SD_V3_VIA_X = -8.65
const V3_SPINE_X = -3
const TP_3V3 = { x: -7.35, y: -15.2 }
const SD_PULLUPS = [
  { name: "R10", y: -5.31, net: "SDC_D2", pad: "SD1.DAT2", at: PORT.SD1_DAT2, sch: { x: -10, y: 4 } },
  { name: "R6", y: -6.41, net: "SDC_D3", pad: "SD1.CS", at: PORT.SD1_CS, sch: { x: 6, y: 3 } },
  { name: "R7", y: -7.51, net: "SDC_CMD", pad: "SD1.MOSI", at: PORT.SD1_MOSI, sch: { x: 10, y: 7 } },
  { name: "R8", y: -10.81, net: "SDC_D0", pad: "SD1.MISO", at: PORT.SD1_MISO, sch: { x: 10, y: 4 } },
  { name: "R9", y: -13, net: "SDC_D1", pad: "SD1.DAT1", at: PORT.SD1_DAT1, sch: { x: -10, y: 7 } },
]
const pullupPin = (p: { y: number }, which: 1 | 2) => pin({ x: SD_PULLUP_X, y: p.y }, 0, 0.51, which)
const C7_C = { x: SD_PULLUP_X, y: -8.61 }
const R19_C = { x: -3, y: 11.6 }
const SD_PATHS = [
  // pull-up stubs first (they start at a pad not yet joined), then the lanes
  ...SD_PULLUPS.map((p) => ({
    connection: `.${p.name} > .pin2`,
    route: savedRoute(0.2, [pullupPin(p, 2), mid(pullupPin(p, 2), p.at), p.at]),
  })),
  ...SD_LANES.map((l) => ({
    connection: l.sel,
    route: savedRoute(0.2, [
      l.at,
      { x: l.laneX, y: l.at.y },
      { x: l.laneX, y: l.turnY },
      { x: l.resX, y: l.turnY },
      pin({ x: l.resX, y: SD_RES_Y }, 270, 0.51, 1),
    ]),
  })),
  ...SD_LANES.map((l) => ({
    connection: `.${l.res} > .pin2`,
    route: savedRoute(0.2, [pin({ x: l.resX, y: SD_RES_Y }, 270, 0.51, 2), { x: l.mcuX, y: PORT.U1_TOP_ROW_Y }]),
  })),
  // V3V3_SD: card VDD -> C7 -> R7 -> bottom spine -> TP1, other pull-ups -> TP1, link R19 -> TP1
  { connection: ".SD1 > .VDD", route: savedRoute(0.3, [PORT.SD1_VDD, mid(PORT.SD1_VDD, pin(C7_C, 180, 0.51, 1)), pin(C7_C, 180, 0.51, 1)]) },
  { connection: ".C7 > .pin1", route: savedRoute(0.3, [pin(C7_C, 180, 0.51, 1), { x: SD_PULLUP_X, y: -8.06 }, pullupPin({ y: -7.51 }, 1)]) },
  ...SD_PULLUPS.filter((p) => p.name !== "R9").map((p) => ({
    connection: `.${p.name} > .pin1`,
    route: savedRoute(p.name === "R7" ? 0.4 : 0.3, [
      pullupPin(p, 1),
      { x: SD_V3_VIA_X, y: p.y },
      "via",
      { x: V3_SPINE_X, y: p.y },
      { x: V3_SPINE_X, y: TP_3V3.y },
      TP_3V3,
    ]),
  })),
  { connection: ".R9 > .pin1", route: savedRoute(0.3, [pullupPin({ y: -13 }, 1), { x: -7.86, y: -14 }, "via", TP_3V3]) },
  {
    connection: ".R19 > .pin2",
    route: savedRoute(0.5, [pin(R19_C, 180, 0.51, 2), { x: -4.25, y: 11.6 }, "via", { x: V3_SPINE_X, y: 10.35 }, { x: V3_SPINE_X, y: TP_3V3.y }, TP_3V3]),
  },
]

// -------------------------------------------- BOOT, LED and module 3V3 ----
// These run from the buttons/regulator end of the board to the module
// without passing under the SD lanes or the USB pair: BOOT on the bottom
// layer east of the SD lanes and in under the module to a via beside IO0;
// the module's 3V3 (its own net V3V3_MCU behind the 0R link R20) on the bottom
// at x=6.4 through the bulk cap C8 to a via beside the 3V3 pad. The status
// LED sits on the right edge beside IO4, so its line never crosses the board.
const R2_C = { x: 4, y: 10 }
const R20_C = { x: 5.2, y: 11.4 }
const C8_C = { x: 7, y: -18.5 }
const C3_C = { x: 11.3, y: -34.5 }
const C4_C = { x: 11.3, y: -39.5 }
const R5_C = { x: 10.3, y: -29 }
const LED1_C = { x: 11.6, y: -29.5 }
const LED1_ANODE = { x: LED1_C.x, y: LED1_C.y + 0.749 }
const BOOT_PATHS = [
  { connection: ".SW1 > .B", route: savedRoute(0.25, [PORT.SW1_B, PORT.SW1_A]) },
  { connection: ".R2 > .pin2", route: savedRoute(0.2, [pin(R2_C, 0, 0.51, 2), PORT.SW1_A]) },
  { connection: ".TP5 > .pin1", route: savedRoute(0.2, [PORT.TP5, PORT.U1_IO0]) },
  {
    connection: ".SW1 > .A",
    route: savedRoute(0.2, [PORT.SW1_A, { x: 2.9, y: 8.85 }, "via", { x: 2.9, y: -0.4 }, { x: 4, y: -1.5 }, { x: 4, y: -24.6 }, { x: -7.3, y: -24.6 }, "via", PORT.U1_IO0]),
  },
]
const LED_PATHS = [
  { connection: ".U1 > .IO4", route: savedRoute(0.2, [PORT.U1_IO4, { x: 9.9, y: PORT.U1_IO4.y }, { x: 9.9, y: pin(R5_C, 90, 0.51, 1).y }, pin(R5_C, 90, 0.51, 1)]) },
  { connection: ".R5 > .pin2", route: savedRoute(0.2, [pin(R5_C, 90, 0.51, 2), LED1_ANODE]) },
]
const V3_MCU_PATHS = [
  {
    connection: ".R20 > .pin2",
    route: savedRoute(0.5, [pin(R20_C, 0, 0.51, 2), { x: 6.5, y: 11.4 }, "via", { x: 6.4, y: 11 }, { x: 6.4, y: -17 }, { x: 7.9125, y: -17 }, "via", pin(C8_C, 180, 0.9125, 1)]),
  },
  {
    connection: ".C8 > .pin1",
    route: savedRoute(0.5, [pin(C8_C, 180, 0.9125, 1), { x: 7.9125, y: -19.7 }, "via", { x: 6.4, y: -19.7 }, { x: 6.4, y: -38.9 }, { x: 7.3, y: PORT.U1_3V3.y }, "via", PORT.U1_3V3]),
  },
  { connection: ".C3 > .pin1", route: savedRoute(0.3, [pin(C3_C, 90, 0.9125, 1), { x: 11.9, y: -36 }, { x: 11.9, y: -39.5 }, pin(C4_C, 90, 0.51, 1)]) },
  { connection: ".C4 > .pin1", route: savedRoute(0.3, [pin(C4_C, 90, 0.51, 1), PORT.U1_3V3]) },
]
const LOCKED_PATHS = [...USB_PATHS, ...SD_PATHS, ...BOOT_PATHS, ...LED_PATHS, ...V3_MCU_PATHS]

// GND stitching vias for the module's centre pad (between and around its nine
// paste squares) and the regulator's thermal pad (just past the package ends).
// Board vias, not footprint plated holes: the Gerber export puts paste on every
// plated hole, tented or not, which would print solder onto the mask.
const GND_STITCH_VIAS = [
  { x: 0.800124, y: -34.0248767, d: 0.5 },
  { x: 2.200124, y: -34.0248767, d: 0.5 },
  { x: 0.800124, y: -32.6248767, d: 0.5 },
  { x: 2.200124, y: -32.6248767, d: 0.5 },
  { x: 1.500124, y: -35.7248767, d: 0.6 },
  { x: 1.500124, y: -30.9248767, d: 0.6 },
  { x: -0.899876, y: -33.3248767, d: 0.6 },
  { x: 3.900124, y: -33.3248767, d: 0.6 },
  { x: -1.55, y: 15.5, d: 0.6 },
  { x: 1.55, y: 15.5, d: 0.6 },
]

// --------------------------------------------------------- test pads ----
const TEST_PADS = [
  { name: "TP2", label: "5V", at: { x: -2.6, y: 21 }, to: "net.VBUS", labelAt: { x: -2.6, y: 22.1 } },
  { name: "TP3", label: "GND", at: { x: -4.6, y: 21 }, to: "net.GND", labelAt: { x: -4.6, y: 22.1 } },
  { name: "TP4", label: "EN", at: { x: -1, y: 11.6 }, to: "U1.EN", labelAt: { x: 0.35, y: 11.6 } },
  { name: "TP5", label: "IO0", at: PORT.TP5, to: "net.BOOT", labelAt: { x: -11, y: -23.5 } },
  { name: "TP6", label: "RX", at: { x: -11, y: -35.97 }, to: "U1.RXD0", labelAt: { x: -12.35, y: -35.97 } },
  { name: "TP7", label: "TX", at: { x: -11, y: -37.24 }, to: "U1.TXD0", labelAt: { x: -12.35, y: -37.24 } },
  { name: "TP8", label: "GND", at: { x: -11, y: -38.51 }, to: "net.GND", labelAt: { x: -12.3, y: -38.51 } },
]

export default () => (
  <board
    width="26mm"
    height="82.25mm"
    layers={2}
    thickness="1.6mm"
    outline={BOARD_OUTLINE_REL}
    outlineOffsetY={BOARD_CENTER_Y}
    title="ESP32-S3 USB-C Stick"
    // Vias: 0.3mm hole / 0.6mm pad = 0.15mm annular ring (v1.2.5 used 0.5mm
    // pads, JLCPCB's bare minimum ring).
    pcbStyle={{ viaHoleDiameter: "0.3mm", viaPadDiameter: "0.6mm" }}
  >
    <net name="GND" isGroundNet />
    <net name="VBUS" isPowerNet />
    <net name="V3V3" isPowerNet />
    {/* Multi-pad nets locked in routing phase 0 are NAMED: the router then gets one
        connection holding all their pads, so the saved routes chain pad to pad.
        (Pin-to-pin traces become separate connections that share a pad, and a
        saved route cannot start on a shared pad.) */}
    <net name="V3V3_SD" isPowerNet routingPhaseIndex={0} />
    <net name="V3V3_MCU" isPowerNet routingPhaseIndex={0} />
    <net name="VBUS_ESD" isPowerNet routingPhaseIndex={0} />
    <net name="BOOT" routingPhaseIndex={0} />
    {SD_LANES.filter((l) => l.cardNet).map((l) => (
      <Fragment key={l.cardNet}>
        <net name={l.cardNet} routingPhaseIndex={0} />
      </Fragment>
    ))}

    <schematicsection name="Usb" displayName="USB-C Plug & ESD" />
    <schematicsection name="Mcu" displayName="ESP32-S3-WROOM-1" />
    <schematicsection name="BootReset" displayName="Boot / Reset" />
    <schematicsection name="Power" displayName="5V -> 3.3V Regulation" />
    <schematicsection name="Indicator" displayName="Status LED" />
    <schematicsection name="MicroSd" displayName="MicroSD (4-bit SDMMC)" />
    <schematicsection name="Test" displayName="Test Pads" />

    {/* Phase 0: every locked route (see LOCKED ROUTES above). Everything
        else is autorouted afterwards around this copper. */}
    <autoroutingphase phaseIndex={0} pcbTracePaths={LOCKED_PATHS} />

    {/* ===================== USB-C male plug (C2879827) ===================== */}
    {/* XKB U261-121N-4BS2S: a solder-on, right-angle, surface-mount USB-C MALE
        plug. It mounts flat on the board and the plug overhangs the top edge to
        insert straight into a host. USB 2.0 device wiring: VBUS/GND on their
        pins, one CC line with a 5.1k Rd to GND (correct for a plug), and D+/D-
        through the ESD array + 22ohm resistors. SuperSpeed pins are unused. */}
    {/* pcbY is 37.95, not 32: the tsci 0.0.2561 reimport moved the footprint
        origin +5.95mm (local y) relative to its pads, so the pads still land at
        y=31.475 (signals) / 31.825 (shell pegs) exactly as before. */}
    <U261_121N_4BS2S
      name="J1"
      pcbX={0}
      pcbY={37.95}
      pcbRotation={180}
      schX={SEC.Usb.x}
      schY={SEC.Usb.y}
      schSectionName="Usb"
    />

    {/* CC pull-down (5.1k Rd) presents the board as a USB device (sink). R11
        sits right under the plug, between the CC and D+ escapes, so CC reaches
        it without crossing anything. */}
    <resistor name="R11" resistance="5.1k" footprint="0402" supplierPartNumbers={JLC.R_5K1_0402}
      pcbX={R11_C.x} pcbY={R11_C.y}
      schSectionName="Usb" schX={SEC.Usb.x - 5} schY={SEC.Usb.y - 2} />

    {/* Low-capacitance ESD array 2.7mm behind the plug's D+/D- pads. Flow-
        through: the plug feeds the TOP row (IO1_B pin6 / IO2_B pin4), the chip
        clamps, and the BOTTOM row (IO1 pin1 / IO2 pin3) carries the pair on to
        the MCU — the two pins of each channel are bonded inside the USBLC6. */}
    <USBLC6_2SC6
      name="U3"
      pcbX={4.5}
      pcbY={27.8}
      schSectionName="Usb"
      schX={SEC.Usb.x + 6}
      schY={SEC.Usb.y - 3}
    />

    {/* USB series resistors on D+/D- between the ESD array and the MCU, at the
        bottom of the right-edge D+/D- lane. */}
    <resistor
      name="R3"
      resistance="22"
      footprint="0402"
      supplierPartNumbers={JLC.R_22_0402}
      pcbX={R3_C.x}
      pcbY={R3_C.y}
      pcbRotation={270}
      schSectionName="Usb"
      schX={SEC.Usb.x + 3}
      schY={SEC.Usb.y - 6}
    />
    <resistor
      name="R4"
      resistance="22"
      footprint="0402"
      supplierPartNumbers={JLC.R_22_0402}
      pcbX={R4_C.x}
      pcbY={R4_C.y}
      pcbRotation={270}
      schSectionName="Usb"
      schX={SEC.Usb.x - 3}
      schY={SEC.Usb.y - 6}
    />

    {/* VBUS + GND (both VBUS pins, all GND pins + shell tabs), autorouted around
        the locked CC/D+/D- escapes. A12 is boxed in by the D- escape and ties
        straight to the right shell tab. (A hand-drawn B9-A4 link over the pad
        row was rewritten by the router onto the bottom with vias on the pads.) */}
    <trace from="J1.VBUS1" to="net.VBUS" thickness="0.4mm" />
    <trace from="J1.VBUS2" to="net.VBUS" thickness="0.3mm" />
    <trace from="J1.GND1" to="net.GND" />
    <trace from="J1.GND2" to="net.GND" />
    <trace from="J1.GND3" to="J1.SHELL1" thickness="0.3mm" pcbPath={[]} />
    <trace from="J1.SHELL1" to="net.GND" />
    <trace from="J1.SHELL2" to="net.GND" />
    {/* locked: CC -> R11, plug -> ESD, ESD -> R3/R4 lane, R3/R4 -> module */}
    <trace from="R11.pin1" to="J1.CC1" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="R11.pin2" to="net.GND" />
    <trace name="USB_DP_conn" from="U3.IO1_B" to="J1.DP1" thickness="0.2mm" routingPhaseIndex={0} />
    <trace name="USB_DN_conn" from="U3.IO2_B" to="J1.DN1" thickness="0.2mm" routingPhaseIndex={0} />
    <trace name="USB_DP_pair" from="U3.IO1" to="R3.pin1" thickness="0.2mm" routingPhaseIndex={0} />
    <trace name="USB_DN_pair" from="U3.IO2" to="R4.pin1" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="R3.pin2" to="U1.USB_D_POS" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="R4.pin2" to="U1.USB_D_NEG" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="U3.GND" to="net.GND" />
    {/* The USBLC6 VBUS pin sits between the two channels, so every other way
        out crosses the pair: it is its own locked net (VBUS_ESD) that leaves on
        top under the package, between the two pin rows, to C9 (100nF, ST's
        recommended VBUS decoupler), fed from VBUS through the 0R link R21. */}
    <trace from="U3.VBUS" to="net.VBUS_ESD" thickness="0.3mm" routingPhaseIndex={0} />
    <resistor name="R21" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={R21_C.x} pcbY={R21_C.y}
      schSectionName="Usb" schX={SEC.Usb.x + 9} schY={SEC.Usb.y - 6} />
    <trace from="R21.pin1" to="net.VBUS" thickness="0.3mm" />
    <trace from="R21.pin2" to="net.VBUS_ESD" thickness="0.3mm" routingPhaseIndex={0} />
    <capacitor name="C9" capacitance="100nF" footprint="0402" supplierPartNumbers={JLC.C_100N_0402}
      // its link to U3.VBUS runs ~3.8mm (1mm default)
      maxDecouplingTraceLength="5mm"
      pcbX={C9_C.x} pcbY={C9_C.y} pcbRotation={180}
      schOrientation="vertical"
      schSectionName="Usb" schX={SEC.Usb.x + 6} schY={SEC.Usb.y - 6} />
    <trace from="C9.pin1" to="net.VBUS_ESD" thickness="0.3mm" routingPhaseIndex={0} />
    <trace from="C9.pin2" to="net.GND" />

    {/* ===================== 5V -> 3.3V regulation ===================== */}
    {/* TLV75733PDRVR (1A LDO, 2x2mm WSON with a GND thermal pad; about half
        the thermal resistance of the SOT-23 part used up to v1.2.5).
        Rotated 90 so IN/EN face the plug (north) and OUT faces the rest of
        the board: IN top-left, NC, EN top-right; OUT bottom-left, NC, GND
        bottom-right. C1 (VBUS in) sits beside IN and C2 (3V3 out) beside OUT,
        each with its rail pad facing the regulator. EN is tied to IN by a
        short link over the top of the NC pad, so the LDO is always on. */}
    <TLV75733PDRVR
      name="U2"
      pcbX={0}
      pcbY={15.5}
      pcbRotation={90}
      schSectionName="Power"
      schX={SEC.Power.x}
      schY={SEC.Power.y - 0.68}
    />
    <capacitor
      name="C1"
      capacitance="10uF"
      footprint="0805"
      supplierPartNumbers={JLC.C_10U_0805}
      schOrientation="vertical"
      pcbX={-3.5}
      pcbY={17.4}
      pcbRotation={180}
      schSectionName="Power"
      schX={SEC.Power.x - 4}
      schY={SEC.Power.y}
    />
    <capacitor
      name="C2"
      capacitance="22uF"
      footprint="0805"
      supplierPartNumbers={JLC.C_22U_0805}
      schOrientation="vertical"
      pcbX={-3.5}
      pcbY={13.6}
      pcbRotation={180}
      schSectionName="Power"
      schX={SEC.Power.x + 4}
      schY={SEC.Power.y}
    />
    <trace from="net.VBUS" to="U2.IN" thickness="0.4mm" />
    <trace from="U2.EN" to="U2.IN" thickness="0.3mm"
      pcbPath={[{ x: 0.65, y: 17.05 }, { x: -0.65, y: 17.05 }].map(u2)} />
    <trace from="U2.GND" to="net.GND" />
    <trace from="U2.OUT" to="net.V3V3" thickness="0.3mm" />
    <trace from="C1.pin1" to="net.VBUS" thickness="0.4mm" />
    <trace from="C1.pin2" to="net.GND" />
    <trace from="C2.pin1" to="net.V3V3" thickness="0.4mm" />
    <trace from="C2.pin2" to="net.GND" />

    {/* 0R links that split off the two locked 3V3 feeds: R19 (right under C2)
        feeds V3V3_SD, R20 (beside R2's 3V3 pad) feeds V3V3_MCU. */}
    <resistor name="R19" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={R19_C.x} pcbY={R19_C.y} pcbRotation={180}
      schSectionName="MicroSd" schX={SEC.MicroSd.x - 9} schY={SEC.MicroSd.y + 3} />
    <trace from="R19.pin1" to="net.V3V3" thickness="0.4mm" />
    <trace from="R19.pin2" to="net.V3V3_SD" thickness="0.5mm" routingPhaseIndex={0} />
    <resistor name="R20" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={R20_C.x} pcbY={R20_C.y}
      schSectionName="Mcu" schX={SEC.Mcu.x - 8} schY={SEC.Mcu.y - 1} />
    <trace from="R20.pin1" to="net.V3V3" thickness="0.4mm" />
    <trace from="R20.pin2" to="net.V3V3_MCU" thickness="0.5mm" routingPhaseIndex={0} />
    <trace from="C8.pin1" to="net.V3V3_MCU" thickness="0.5mm" routingPhaseIndex={0} />

    {/* ===================== Boot / Reset ===================== */}
    {/* EN: pull-up + power-on delay cap + reset button. */}
    <resistor
      name="R1"
      resistance="10k"
      footprint="0402"
      supplierPartNumbers={JLC.R_10K_0402}
      pcbX={-1}
      pcbY={10}
      pcbRotation={180}
      schSectionName="BootReset"
      schX={SEC.BootReset.x}
      schY={SEC.BootReset.y + 4.58}
    />
    <capacitor
      name="C5"
      capacitance="1uF"
      footprint="0402"
      supplierPartNumbers={JLC.C_1U_0402}
      schOrientation="vertical"
      pcbX={-4}
      pcbY={10}
      schSectionName="BootReset"
      schX={SEC.BootReset.x + 4}
      schY={SEC.BootReset.y + 4}
    />
    <TS_1187A_B_A_B
      name="SW2"
      pcbX={-7}
      pcbY={7}
      schSectionName="BootReset"
      schX={SEC.BootReset.x}
      schY={SEC.BootReset.y}
    />
    <trace from="U1.EN" to="R1.pin2" />
    <trace from="U1.EN" to="C5.pin1" />
    <trace from="U1.EN" to="SW2.pin1" />
    <trace from="R1.pin1" to="net.V3V3" />
    <trace from="C5.pin2" to="net.GND" />
    {/* TS-1187A internal wiring (XKB datasheet): A(pin1)-B(pin2) are one
        terminal, C(pin3)-D(pin4) the other; A/B is the TOP row and C/D the
        BOTTOM row of the footprint. The switch closes A/B <-> C/D, so the
        signal goes to the top row and GND to the bottom row. (Tying pin1 to
        pin3 as before shorted EN straight to GND through the A-B bond.) */}
    <trace from="SW2.pin3" to="net.GND" />
    {/* tie each terminal's two redundant pads together */}
    <trace from="SW2.pin2" to="SW2.pin1" />
    <trace from="SW2.pin4" to="SW2.pin3" />

    {/* IO0/BOOT: pull-up + button to force the ROM download-mode bootloader.
        The whole BOOT net (SW1 A/B, R2, TP5, IO0) is locked; see BOOT_PATHS. */}
    <resistor
      name="R2"
      resistance="10k"
      footprint="0402"
      supplierPartNumbers={JLC.R_10K_0402}
      pcbX={R2_C.x}
      pcbY={R2_C.y}
      schSectionName="BootReset"
      schX={SEC.BootReset.x}
      schY={SEC.BootReset.y - 8}
    />
    <TS_1187A_B_A_B
      name="SW1"
      pcbX={7}
      pcbY={7}
      schSectionName="BootReset"
      schX={SEC.BootReset.x}
      schY={SEC.BootReset.y - 12}
    />
    <trace from="SW1.pin1" to="net.BOOT" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="SW1.pin2" to="net.BOOT" thickness="0.25mm" routingPhaseIndex={0} />
    <trace from="R2.pin2" to="net.BOOT" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="U1.BOOT" to="net.BOOT" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="R2.pin1" to="net.V3V3" />
    {/* same A/B-top, C/D-bottom terminal pairing as SW2 */}
    <trace from="SW1.pin3" to="net.GND" />
    <trace from="SW1.pin4" to="SW1.pin3" />

    {/* ===================== Status LED ===================== */}
    {/* On the right edge beside IO4 (x 10-12, y -29), clear of the SD lanes. */}
    <resistor
      name="R5"
      resistance="330"
      footprint="0402"
      supplierPartNumbers={JLC.R_330_0402}
      schOrientation="vertical"
      pcbX={R5_C.x}
      pcbY={R5_C.y}
      pcbRotation={90}
      schSectionName="Indicator"
      schX={SEC.Indicator.x}
      schY={SEC.Indicator.y}
    />
    {/* XL-1608UBC-04 blue LED (C965807): pin1 is CATHODE, pin2 is ANODE, so
        wire by the named anode/cathode ports to guarantee correct polarity. */}
    <XL_1608UBC_04
      name="LED1"
      schRotation={90}
      pcbX={LED1_C.x}
      pcbY={LED1_C.y}
      pcbRotation={90}
      schSectionName="Indicator"
      schX={SEC.Indicator.x}
      schY={SEC.Indicator.y - 3}
    />
    <trace from="U1.LED_GPIO" to="R5.pin1" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="R5.pin2" to="LED1.anode" thickness="0.2mm" routingPhaseIndex={0} />
    <trace from="LED1.cathode" to="net.GND" />

    {/* ===================== MicroSD (4-bit SDMMC) ===================== */}
    {/* Native SDMMC host, 4-bit bus, via the S3 GPIO matrix (see the U1 import
        for the firmware pin map): D2 = GPIO21, D3 = GPIO14, CMD = GPIO13,
        CLK = GPIO12 (22R R13), D0 = GPIO11, D1 = GPIO10. The TF-01A import
        keeps its SPI-era pin aliases; physically SD1.CS = DAT3, SD1.MOSI =
        CMD, SD1.SCK = CLK, SD1.MISO = DAT0, and SD1.DAT1/DAT2 are DAT1/DAT2.
        Socket placement unchanged: middle of the board (card inserts from the
        side edge), well away from the antenna at the bottom. */}
    <TF_01A
      name="SD1"
      pcbX={0}
      pcbY={-7}
      pcbRotation={90}
      schHeight={1.4}
      schSectionName="MicroSd"
      schX={SEC.MicroSd.x}
      schY={SEC.MicroSd.y + 2.82}
    />
    {/* SDMMC pull-ups (10k to 3V3, one per line, next to the socket): R10 =
        DAT2, R6 = DAT3, R7 = CMD, R8 = DAT0, R9 = DAT1. CLK is push-pull driven
        and has NO pull-up. */}
    {SD_PULLUPS.map((p) => (
      <resistor
        key={p.name}
        name={p.name}
        resistance="10k"
        footprint="0402"
        supplierPartNumbers={JLC.R_10K_0402}
        pcbX={SD_PULLUP_X}
        pcbY={p.y}
        schSectionName="MicroSd"
        schX={SEC.MicroSd.x + p.sch.x}
        schY={SEC.MicroSd.y + p.sch.y}
      />
    ))}
    {/* Card VDD decoupling in the pocket between the CMD and CLK escapes,
        pin1 (3V3) facing the VDD contact; R7's 3V3 via feeds both. */}
    <capacitor
      name="C7"
      capacitance="100nF"
      footprint="0402"
      supplierPartNumbers={JLC.C_100N_0402}
      // locked 1.5mm links to the VDD contact and R7 (1mm default)
      maxDecouplingTraceLength="3mm"
      schOrientation="vertical"
      pcbX={C7_C.x}
      pcbY={C7_C.y}
      pcbRotation={180}
      schSectionName="MicroSd"
      schX={SEC.MicroSd.x - 3}
      schY={SEC.MicroSd.y + 3}
    />
    {/* 3V3 bulk on the module's feed (V3_MCU_PATHS) */}
    <capacitor
      name="C8"
      capacitance="10uF"
      footprint="0805"
      supplierPartNumbers={JLC.C_10U_0805}
      // it sits on the locked module 3V3 feed (R20 -> C8 -> U1.3V3, ~50mm)
      maxDecouplingTraceLength="60mm"
      schOrientation="vertical"
      pcbX={C8_C.x}
      pcbY={C8_C.y}
      pcbRotation={180}
      schSectionName="MicroSd"
      schX={SEC.MicroSd.x - 6}
      schY={SEC.MicroSd.y + 3}
    />
    {/* Series resistors, one per SDMMC line, in a row 2mm above the module's
        pin row with each resistor directly over its GPIO pad (pitch 1.27mm
        = the module's pin pitch), rotated 270 so pin1 faces the card and pin2
        faces the MCU. R13 keeps its 22R on CLK (Espressif SD guidance); the
        other five are 0R placeholders that give 40MHz tuning options without a
        re-spin. West -> east: D2, D3, CMD, CLK, D0, D1. */}
    <resistor name="R18" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={-3.17} pcbY={SD_RES_Y} pcbRotation={270}
      schSectionName="MicroSd" schX={SEC.MicroSd.x - 14} schY={SEC.MicroSd.y - 3} />
    <resistor name="R14" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={-1.9} pcbY={SD_RES_Y} pcbRotation={270}
      schSectionName="MicroSd" schX={SEC.MicroSd.x + 6} schY={SEC.MicroSd.y - 3} />
    <resistor name="R15" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={-0.63} pcbY={SD_RES_Y} pcbRotation={270}
      schSectionName="MicroSd" schX={SEC.MicroSd.x + 2} schY={SEC.MicroSd.y - 3} />
    <resistor name="R13" resistance="22" footprint="0402" supplierPartNumbers={JLC.R_22_0402}
      pcbX={0.64} pcbY={SD_RES_Y} pcbRotation={270}
      schSectionName="MicroSd" schX={SEC.MicroSd.x - 2} schY={SEC.MicroSd.y - 3} />
    <resistor name="R16" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={1.91} pcbY={SD_RES_Y} pcbRotation={270}
      schSectionName="MicroSd" schX={SEC.MicroSd.x - 6} schY={SEC.MicroSd.y - 3} />
    <resistor name="R17" resistance="0" footprint="0402" supplierPartNumbers={JLC.R_0_0402}
      pcbX={3.18} pcbY={SD_RES_Y} pcbRotation={270}
      schSectionName="MicroSd" schX={SEC.MicroSd.x - 10} schY={SEC.MicroSd.y - 3} />

    {/* Card supply (locked, V3V3_SD) and grounds */}
    <trace from="SD1.VDD" to="net.V3V3_SD" thickness="0.3mm" routingPhaseIndex={0} />
    <trace from="C7.pin1" to="net.V3V3_SD" thickness="0.3mm" routingPhaseIndex={0} />
    <trace from="TP1.pin1" to="net.V3V3_SD" thickness="0.3mm" routingPhaseIndex={0} />
    <trace from="C7.pin2" to="net.GND" />
    <trace from="C8.pin2" to="net.GND" />
    <trace from="SD1.VSS" to="net.GND" />
    <trace from="SD1.GND1" to="net.GND" />
    <trace from="SD1.GND2" to="net.GND" />
    <trace from="SD1.GND3" to="net.GND" />
    <trace from="SD1.GND4" to="net.GND" />

    {/* The six SDMMC lanes, the resistor -> GPIO drops, the pull-up stubs and
        the pull-ups' 3V3, all locked (SD_PATHS). */}
    {SD_LANES.map((l) =>
      l.cardNet ? (
        <Fragment key={l.name}>
          <trace from={l.pad} to={`net.${l.cardNet}`} thickness="0.2mm" routingPhaseIndex={0} />
          <trace from={`${l.res}.pin1`} to={`net.${l.cardNet}`} thickness="0.2mm" routingPhaseIndex={0} />
        </Fragment>
      ) : (
        <trace key={l.name} name={`${l.name}_lane`} from={l.pad} to={`${l.res}.pin1`} thickness="0.2mm" routingPhaseIndex={0} />
      ),
    )}
    {SD_LANES.map((l) => (
      <trace key={`${l.name}_mcu`} from={`${l.res}.pin2`} to={l.mcu} thickness="0.2mm" routingPhaseIndex={0} />
    ))}
    {SD_PULLUPS.map((p) => (
      <trace key={`${p.name}_stub`} from={`${p.name}.pin2`} to={`net.${p.net}`} thickness="0.2mm" routingPhaseIndex={0} />
    ))}
    {SD_PULLUPS.map((p) => (
      <trace key={`${p.name}_3v3`} from={`${p.name}.pin1`} to="net.V3V3_SD" thickness={p.name === "R7" ? "0.4mm" : "0.3mm"} routingPhaseIndex={0} />
    ))}

    {/* ===================== MCU (bottom, antenna at edge) ===================== */}
    <ESP32_S3_WROOM_1_N16R8
      name="U1"
      pcbX={0}
      pcbY={-32}
      pcbRotation={180}
      schHeight={4.2}
      schSectionName="Mcu"
      schX={SEC.Mcu.x}
      schY={SEC.Mcu.y}
    />

    {/* Module 3V3 (locked, V3V3_MCU): R20 -> bottom lane -> C8 -> bottom again
        under the module -> via beside the 3V3 pad; C3/C4 on top beside it. */}
    <trace from="U1.3V3" to="net.V3V3_MCU" thickness="0.5mm" routingPhaseIndex={0} />
    <trace from="C3.pin1" to="net.V3V3_MCU" thickness="0.3mm" routingPhaseIndex={0} />
    <trace from="C4.pin1" to="net.V3V3_MCU" thickness="0.3mm" routingPhaseIndex={0} />

    {/* The module's centre GND pad and the regulator's thermal pad are stitched
        to the bottom plane by tented GND vias (GND_STITCH_VIAS), clear of all
        paste. */}
    {GND_STITCH_VIAS.map((v) => (
      <Fragment key={`gnd_via_${v.x}_${v.y}`}>
        <via pcbX={v.x} pcbY={v.y} fromLayer="top" toLayer="bottom" holeDiameter="0.3mm" outerDiameter={`${v.d}mm`} connectsTo="net.GND" tented="both_sides" />
      </Fragment>
    ))}
    <trace from="U1.GND1" to="net.GND" />
    <trace from="U1.GND2" to="net.GND" />
    {/* U1.GND3 (centre pad) is internally bonded to GND1/GND2 (see import);
        the GND pours and the stitching vias tie it to both planes. */}

    {/* MCU decoupling in the strip beside the module's 3V3 pad (right edge,
        y=-39.8): C4 (100nF) right at the pin, C3 (10uF bulk) just above it. */}
    <capacitor
      name="C3"
      capacitance="10uF"
      footprint="0805"
      supplierPartNumbers={JLC.C_10U_0805}
      // locked ~5mm link to C4 (1mm default)
      maxDecouplingTraceLength="10mm"
      schOrientation="vertical"
      pcbX={C3_C.x}
      pcbY={C3_C.y}
      pcbRotation={90}
      schSectionName="Mcu"
      schX={SEC.Mcu.x - 5}
      schY={SEC.Mcu.y - 3}
    />
    <capacitor
      name="C4"
      capacitance="100nF"
      footprint="0402"
      supplierPartNumbers={JLC.C_100N_0402}
      // locked ~2.6mm link to the 3V3 pad (1mm default)
      maxDecouplingTraceLength="5mm"
      schOrientation="vertical"
      pcbX={C4_C.x}
      pcbY={C4_C.y}
      pcbRotation={90}
      schSectionName="Mcu"
      schX={SEC.Mcu.x - 5}
      schY={SEC.Mcu.y - 5}
    />
    <trace from="C3.pin2" to="net.GND" />
    <trace from="C4.pin2" to="net.GND" />

    {/* ===================== Test pads ===================== */}
    {/* Bare pads (not assembled) for bring-up and recovery when USB is not
        working: 5V/GND by the regulator, EN beside its RC, IO0 and UART0
        RX/TX/GND on the module's left edge, and 3V3 (TP1) on the BOTTOM side
        below the SD socket, where it terminates the locked V3V3_SD spine. */}
    <testpoint name="TP1" footprintVariant="pad" padShape="circle" padDiameter="1.5mm" layer="bottom"
      pcbX={TP_3V3.x} pcbY={TP_3V3.y} doNotPlace
      schSectionName="Test" schX={SEC.Test.x} schY={SEC.Test.y + 4} />
    <silkscreentext text="3V3" layer="bottom" pcbX={TP_3V3.x} pcbY={TP_3V3.y - 1.2} fontSize="0.5mm" anchorAlignment="center" />
    {TEST_PADS.map((tp, i) => (
      <testpoint
        key={tp.name}
        name={tp.name}
        footprintVariant="pad"
        padShape="circle"
        padDiameter="1mm"
        pcbX={tp.at.x}
        pcbY={tp.at.y}
        doNotPlace
        schSectionName="Test"
        schX={SEC.Test.x}
        schY={SEC.Test.y + 2.5 - i * 1.5}
      />
    ))}
    {TEST_PADS.map((tp) => (
      <trace
        key={`${tp.name}_net`}
        from={`${tp.name}.pin1`}
        to={tp.to}
        {...(tp.name === "TP5" ? { thickness: "0.2mm", routingPhaseIndex: 0 } : {})}
      />
    ))}
    {TEST_PADS.map((tp) => (
      <Fragment key={`${tp.name}_label`}>
        <silkscreentext text={tp.label} pcbX={tp.labelAt.x} pcbY={tp.labelAt.y}
          fontSize="0.5mm" anchorAlignment="center" />
      </Fragment>
    ))}

    {/* ===================== Keepouts ===================== */}
    {/* Edge fences: routing keepouts along both long edges (x 12.4..13 and
        -13..-12.5) so the autorouter can never hug the board edge — it
        otherwise pushes traces inside the edge clearance. */}
    <keepout shape="rect" layer="top" pcbX={12.7} pcbY={EDGE_FENCE.pcbY} width={0.6} height={EDGE_FENCE.height} />
    <keepout shape="rect" layer="bottom" pcbX={12.7} pcbY={EDGE_FENCE.pcbY} width={0.6} height={EDGE_FENCE.height} />
    <keepout shape="rect" layer="top" pcbX={-12.75} pcbY={EDGE_FENCE.pcbY} width={0.5} height={EDGE_FENCE.height} />
    <keepout shape="rect" layer="bottom" pcbX={-12.75} pcbY={EDGE_FENCE.pcbY} width={0.5} height={EDGE_FENCE.height} />

    {/* Antenna keepout — no copper (traces, vias, or pour) around the module's
        printed antenna at the bottom edge. */}
    <keepout shape="rect" layer="top" {...ANTENNA_KEEPOUT} />
    <keepout shape="rect" layer="bottom" {...ANTENNA_KEEPOUT} />

    {/* Ground pour on both layers; pulled back from the board edge so it never
        reaches the tongue-tip contact area. With the lanes locked (and no
        shadow keepouts any more) the top pour also runs beside the USB pair
        and the SD lanes. */}
    {/* LDO output copper: a bounded V3V3 pour joining U2.OUT (bottom-left pin
        at x=-0.65, y=14.5) to C2's V3V3 pad, kept below the thermal pad. */}
    <copperpour
      name="U2_out_pour"
      connectsTo="net.V3V3"
      layer="top"
      outline={[
        { x: -3.7, y: 12.4 },
        { x: -0.3, y: 12.4 },
        { x: -0.3, y: 14.85 },
        { x: -3.7, y: 14.85 },
      ]}
    />
    <copperpour connectsTo="net.GND" layer="top" clearance="0.2mm" boardEdgeMargin="1mm" />
    <copperpour connectsTo="net.GND" layer="bottom" clearance="0.2mm" boardEdgeMargin="1mm" />
  </board>
)