@protoboardai/ext-tscircuit 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +59 -0
  3. package/README.md +493 -2
  4. package/evals/announce-pcb-fallback/case.ts +67 -0
  5. package/evals/catch-swapped-nets/case.ts +69 -0
  6. package/evals/generate-imu-board/case.ts +60 -0
  7. package/evals/locked-board-fix-upstream/case.ts +70 -0
  8. package/lib/checks.ts +464 -0
  9. package/lib/config.ts +384 -0
  10. package/lib/fingerprint.ts +144 -0
  11. package/lib/footprints.ts +370 -0
  12. package/lib/kicad/cli.ts +93 -0
  13. package/lib/kicad/drc.ts +95 -0
  14. package/lib/kicad/fields.ts +65 -0
  15. package/lib/kicad/handoff.ts +112 -0
  16. package/lib/kicad/netlists.ts +311 -0
  17. package/lib/kicad/probe.cjs +21 -0
  18. package/lib/kicad/project.ts +594 -0
  19. package/lib/kicad/refdes.ts +130 -0
  20. package/lib/kicad/schematic.ts +624 -0
  21. package/lib/kicad/sexpr.ts +117 -0
  22. package/lib/land-geometry.ts +177 -0
  23. package/lib/land-patterns.ts +706 -0
  24. package/lib/layout.ts +236 -0
  25. package/lib/models.ts +284 -0
  26. package/lib/native.ts +199 -0
  27. package/lib/outputs.ts +383 -0
  28. package/lib/paste.ts +69 -0
  29. package/lib/probe.cjs +31 -0
  30. package/lib/runtime.ts +185 -0
  31. package/lib/silk.ts +141 -0
  32. package/lib/source.ts +472 -0
  33. package/lib/stepfile.ts +210 -0
  34. package/lib/suppliers.ts +39 -0
  35. package/ops/circuit.ts +270 -0
  36. package/ops/kicad.ts +636 -0
  37. package/ops/package.ts +156 -0
  38. package/package.json +70 -4
  39. package/protoboard-extension.json +313 -0
  40. package/skills/circuit-board/SKILL.md +494 -0
  41. package/skills/circuit-board/references/pcb-workflow.md +87 -0
  42. package/skills/kicad-cli/SKILL.md +210 -0
  43. package/skills/kicad-layout-review/SKILL.md +275 -0
@@ -0,0 +1,706 @@
1
+ /**
2
+ * The provider's land-pattern table (tool-integrations.md §4): package → copper,
3
+ * authored from the manufacturers' recommended land patterns. Each authored
4
+ * entry carries its verification record (land-geometry.ts, LandVerification):
5
+ * the drawing it was read from (document, page, figure) and the values the
6
+ * drawing gives, which footprints.ts `verifyEntry` compares with the copper.
7
+ *
8
+ * An entry's frame is the part's mounting frame (top view, mm): where the
9
+ * part's body origin goes on the board, so the copper and the 3D body agree.
10
+ * The geometry helpers are in land-geometry.ts; lookup and verification in
11
+ * footprints.ts.
12
+ */
13
+ import { dualPads, quadPads, type FootprintEntry, type LandHole, type LandPad, type LandVerification } from "./land-geometry.ts";
14
+
15
+ const CHECKED = "2026-10-03";
16
+
17
+ /** Courtyard basis when a drawing gives none (IPC-7351B, nominal density). */
18
+ const IPC_COURTYARD = "IPC-7351B nominal density: 0.25 mm beyond the pads, holes and body (the drawing gives no courtyard)";
19
+ /** Mask basis when a drawing gives none. */
20
+ const MASK_005 = "non-solder-mask-defined, 0.05 mm all round (the drawing states no mask; the usual fab clearance)";
21
+ const PASTE_1TO1 = "1:1 on every pad (the drawing gives no stencil)";
22
+
23
+ /**
24
+ * The courtyard round the pads, holes and the body (a box in the entry's frame), 0.25 mm beyond,
25
+ * rounded up to 0.05 mm; and its centre when that is not the origin.
26
+ */
27
+ function courtyard(pads: LandPad[], holes: LandHole[] = [], body?: { x: [number, number]; y: [number, number] }): { courtyard_mm: [number, number]; courtyard_at?: [number, number] } {
28
+ const xs = [...pads.flatMap((p) => [p.x - p.w / 2, p.x + p.w / 2]), ...holes.flatMap((h) => [h.x - h.diameter_mm / 2, h.x + h.diameter_mm / 2]), ...(body?.x ?? [])];
29
+ const ys = [...pads.flatMap((p) => [p.y - p.h / 2, p.y + p.h / 2]), ...holes.flatMap((h) => [h.y - h.diameter_mm / 2, h.y + h.diameter_mm / 2]), ...(body?.y ?? [])];
30
+ const up = (v: number) => Math.ceil(v / 0.05 - 1e-6) * 0.05;
31
+ const r = (v: number) => Math.round(v * 1000) / 1000;
32
+ const [x0, x1, y0, y1] = [Math.min(...xs) - 0.25, Math.max(...xs) + 0.25, Math.min(...ys) - 0.25, Math.max(...ys) + 0.25];
33
+ const size: [number, number] = [r(up(x1 - x0)), r(up(y1 - y0))];
34
+ const at: [number, number] = [r((x0 + x1) / 2), r((y0 + y1) / 2)];
35
+ return { courtyard_mm: size, ...(Math.abs(at[0]) > 1e-6 || Math.abs(at[1]) > 1e-6 ? { courtyard_at: at } : {}) };
36
+ }
37
+
38
+ /** Body box centred on the origin. */
39
+ const box = (l: number, w: number) => ({ x: [-l / 2, l / 2] as [number, number], y: [-w / 2, w / 2] as [number, number] });
40
+
41
+ /** An authored entry: courtyard from the pads, holes and body; the record's courtyard is the same. */
42
+ function authored(e: Omit<FootprintEntry, "courtyard_mm" | "courtyard_at" | "verification"> & { body?: { x: [number, number]; y: [number, number] }; record: Omit<LandVerification, "courtyard" | "checked"> & { courtyard?: LandVerification["courtyard"] } }): FootprintEntry {
43
+ const { body, record, ...entry } = e;
44
+ const c = courtyard(entry.pads ?? [], entry.holes ?? [], body);
45
+ return { ...entry, ...c, verification: { ...record, checked: CHECKED, courtyard: record.courtyard ?? { size_mm: c.courtyard_mm, basis: IPC_COURTYARD } } };
46
+ }
47
+
48
+ const r4 = (v: number) => Math.round(v * 1e4) / 1e4;
49
+ const pin = (p: string | number, x: number, y: number, w: number, h: number, extra: Partial<LandPad> = {}): LandPad => ({ pin: String(p), x: r4(x), y: r4(y), w, h, ...extra });
50
+ const range = (n: number, from = 1) => Array.from({ length: n }, (_, i) => String(i + from));
51
+
52
+ const RP2040_DS = "https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf";
53
+ const BMI270_DS = "https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf";
54
+ const TPS63020_DS = "https://www.ti.com/lit/ds/symlink/tps63020.pdf";
55
+
56
+ const builtin = (pkg: string, footprinter: string, extra: Partial<FootprintEntry> = {}): FootprintEntry => ({
57
+ id: `builtin-${footprinter}`,
58
+ match: { package: pkg },
59
+ footprinter,
60
+ source: `tscircuit footprinter "${footprinter}" (@tscircuit/footprinter, MIT, built in)`,
61
+ ...extra,
62
+ });
63
+
64
+ // ---- two-terminal chips
65
+
66
+ /** Yageo "Chip resistors mounting" Table 1, reflow: A overall, B gap, C land length, D land width. */
67
+ const YAGEO_MOUNT = { document: "Yageo data sheet Chip Resistors Mounting (referenced by the RC_L series data sheet, \"Footprint and soldering profiles\")", url: "https://sigma.octopart.com/138820526/technical_drawing/Yageo-RC0402FR-1310KL.pdf", page: 3, figure: "Table 1 \"Reflow soldering footprint dimensions\" with Fig. 4" };
68
+ /** Murata GRM specification sheets, "Land Dimensions", Table 2 (reflow). */
69
+ const MURATA_LAND = { document: "Murata GRM specification sheets, Caution \"2. Land Dimensions\" (e.g. GRM155R71C104KA88-01A, GRM188R61A106ME69)", url: "https://www.murata.com/en-us/products/productdetail?partno=GRM155R71C104KA88%23", page: "26 (GRM155R71C104KA88-01A)", figure: "Table 2 \"Reflow Soldering Method\": a (gap), b (land length), c (land width)" };
70
+
71
+ const chip = (o: { id: string; pkg: string; manufacturer: string; gap: number; length: number; width: number; body: [number, number]; record: Omit<LandVerification, "courtyard" | "checked" | "pads" | "paste" | "mask"> & { size: [[number, number], [number, number]]; row: [number, number] }; source: string }): FootprintEntry => {
72
+ const c = o.gap / 2 + o.length / 2;
73
+ return authored({
74
+ id: o.id,
75
+ match: { package: o.pkg, pin_count: 2, manufacturer: o.manufacturer },
76
+ pads: [pin(1, -c, 0, o.length, o.width), pin(2, c, 0, o.length, o.width)],
77
+ paste: { margin_mm: 0 },
78
+ mask: { margin_mm: 0.05 },
79
+ body: box(...o.body),
80
+ source: o.source,
81
+ record: {
82
+ document: o.record.document,
83
+ url: o.record.url,
84
+ page: o.record.page,
85
+ figure: o.record.figure,
86
+ pads: [{ pins: ["1", "2"], size: o.record.size, row_mm: o.record.row }],
87
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
88
+ mask: { margin_mm: 0.05, basis: MASK_005 },
89
+ notes: o.record.notes,
90
+ },
91
+ });
92
+ };
93
+
94
+ // ---- SOT-23 family: pins in two columns along Y (the parts' CAD frame), pin 1 at the top left
95
+
96
+ const sot = (pins: { pin: string; col: -1 | 1; y: number }[], row: number, w: number, h: number) => pins.map((p) => pin(p.pin, (p.col * row) / 2, p.y, w, h));
97
+ const SOT5 = [
98
+ { pin: "1", col: -1, y: 0.95 },
99
+ { pin: "2", col: -1, y: 0 },
100
+ { pin: "3", col: -1, y: -0.95 },
101
+ { pin: "4", col: 1, y: -0.95 },
102
+ { pin: "5", col: 1, y: 0.95 },
103
+ ] as const;
104
+ const SOT6 = [...SOT5.slice(0, 4), { pin: "5", col: 1, y: 0 }, { pin: "6", col: 1, y: 0.95 }] as const;
105
+
106
+ // ---- GCT USB-C receptacles: paired contacts are one land, drawn as two touching halves so each designator has a pad
107
+
108
+ const tails = (y: number, h: number, singles: { x: number; w: number }, pairs: { inner: number; outer: number; w: number }) => {
109
+ const half = pairs.w / 2;
110
+ const at = (c: number, side: -1 | 1, outer: boolean) => side * (c + (outer ? half / 2 : -half / 2));
111
+ const single = ["B8", "A5", "B7", "A6", "A7", "B6", "A8", "B5"].map((p, i) => pin(p, -1.75 + i * singles.x, y, singles.w, h));
112
+ const paired = [
113
+ pin("A1", at(pairs.outer, -1, true), y, half, h),
114
+ pin("B12", at(pairs.outer, -1, false), y, half, h),
115
+ pin("A4", at(pairs.inner, -1, true), y, half, h),
116
+ pin("B9", at(pairs.inner, -1, false), y, half, h),
117
+ pin("B4", at(pairs.inner, 1, false), y, half, h),
118
+ pin("A9", at(pairs.inner, 1, true), y, half, h),
119
+ pin("B1", at(pairs.outer, 1, false), y, half, h),
120
+ pin("A12", at(pairs.outer, 1, true), y, half, h),
121
+ ];
122
+ return [...paired, ...single];
123
+ };
124
+ const PAIRS = [["A1", "B12"], ["A4", "B9"], ["B4", "A9"], ["B1", "A12"]];
125
+ const SINGLES = ["B8", "A5", "B7", "A6", "A7", "B6", "A8", "B5"];
126
+
127
+ const USB4720 = "https://gct.co/files/drawings/usb4720.pdf";
128
+ const USB4720_TAIL_Y = 2.3; // tail-row and grounding-tag centre line, 0.30 mm behind the rear stake slots' centres (stakes at y = ±2.0)
129
+ const usb4720Pads: LandPad[] = [
130
+ ...tails(USB4720_TAIL_Y, 0.8, { x: 0.5, w: 0.3 }, { inner: 2.43, outer: 3.28, w: 0.65 }),
131
+ ...[-1, 1].flatMap((sx) => [-1, 1].map((sy) => pin("SHELL", sx * 5.65, sy * 2.0, 1.0, 2.15, { drill: { w: 0.5, h: 1.65 } }))),
132
+ pin("TAG", -4.1, USB4720_TAIL_Y, 0.8, 0.8, { drill: { w: 0.5, h: 0.5 } }),
133
+ pin("TAG", 4.1, USB4720_TAIL_Y, 0.8, 0.8, { drill: { w: 0.5, h: 0.5 } }),
134
+ ];
135
+
136
+ const USB4105 = "https://gct.co/files/drawings/usb4105.pdf";
137
+ const usb4105Pads: LandPad[] = [
138
+ // tail inner edges 4.18 mm behind the front stake slots' centres (y = -1.075), pads 1.15 long
139
+ ...tails(3.68, 1.15, { x: 0.5, w: 0.3 }, { inner: 2.4, outer: 3.2, w: 0.6 }),
140
+ pin("SHELL", -4.32, -1.075, 1.0, 1.8, { drill: { w: 0.6, h: 1.4 } }),
141
+ pin("SHELL", 4.32, -1.075, 1.0, 1.8, { drill: { w: 0.6, h: 1.4 } }),
142
+ pin("SHELL", -4.32, 3.105, 1.0, 2.1, { drill: { w: 0.6, h: 1.7 } }),
143
+ pin("SHELL", 4.32, 3.105, 1.0, 2.1, { drill: { w: 0.6, h: 1.7 } }),
144
+ ];
145
+
146
+ // ---- Raytac MDBT50Q: pad centres from §2.2 (origin at the module's lower-left corner), the module centre at (5.25, 7.75)
147
+ const RAYTAC_POINTS: [string, number, number][] = [
148
+ ["1", 0.6, 11.5], ["2", 0.6, 10.4], ["3", 0.6, 9.6], ["4", 0.6, 8], ["5", 1.5, 7.6], ["6", 0.6, 7.2], ["7", 1.5, 6.8], ["8", 0.6, 6.4], ["9", 1.5, 6], ["10", 0.6, 5.6],
149
+ ["11", 1.5, 5.2], ["12", 0.6, 4.8], ["13", 1.5, 4.4], ["14", 0.6, 4], ["15", 0.45, 0.6], ["16", 1.25, 0.6], ["17", 2.05, 0.6], ["18", 2.85, 0.6], ["19", 3.25, 1.5], ["20", 3.65, 0.6],
150
+ ["21", 4.05, 1.5], ["22", 4.45, 0.6], ["23", 4.85, 1.5], ["24", 5.25, 0.6], ["25", 5.65, 1.5], ["26", 6.05, 0.6], ["27", 6.45, 1.5], ["28", 6.85, 0.6], ["29", 7.25, 1.5], ["30", 7.65, 0.6],
151
+ ["31", 8.45, 0.6], ["32", 9.25, 0.6], ["33", 10.05, 0.6], ["34", 9.9, 1.6], ["35", 9.9, 2.4], ["36", 9, 2.8], ["37", 9.9, 3.2], ["38", 9, 3.6], ["39", 9.9, 4], ["40", 9, 4.4],
152
+ ["41", 9.9, 4.8], ["42", 9, 5.2], ["43", 9, 6], ["44", 9.9, 6.4], ["45", 9, 6.8], ["46", 9.9, 7.2], ["47", 9, 7.6], ["48", 9.9, 8], ["49", 9, 8.4], ["50", 9, 9.2],
153
+ ["51", 9.9, 9.6], ["52", 9, 10], ["53", 9.9, 10.4], ["54", 9, 10.8], ["55", 9.9, 11.5], ["56", 3.25, 7.2], ["57", 4.05, 7.2], ["58", 4.85, 7.2], ["59", 5.65, 7.2], ["60", 6.45, 7.2], ["61", 7.25, 7.2],
154
+ ];
155
+ // the drawing's dimension labels, transcribed independently of the pin table: left columns, right columns, rows
156
+ const RAYTAC_DRAWING: [number, number][] = [
157
+ ...[11.5, 10.4, 9.6, 8, 7.2, 6.4, 5.6, 4.8, 4].map((y) => [0.6, y] as [number, number]),
158
+ ...[7.6, 6.8, 6, 5.2, 4.4].map((y) => [1.5, y] as [number, number]),
159
+ ...[10.8, 10, 9.2, 8.4, 7.6, 6.8, 6, 5.2, 4.4, 3.6, 2.8].map((y) => [9, y] as [number, number]),
160
+ ...[11.5, 10.4, 9.6, 8, 7.2, 6.4, 4.8, 4, 3.2, 2.4, 1.6].map((y) => [9.9, y] as [number, number]),
161
+ ...[3.25, 4.05, 4.85, 5.65, 6.45, 7.25].flatMap((x) => [[x, 7.2], [x, 1.5]] as [number, number][]),
162
+ ...[0.45, 1.25, 2.05, 2.85, 3.65, 4.45, 5.25, 6.05, 6.85, 7.65, 8.45, 9.25, 10.05].map((x) => [x, 0.6] as [number, number]),
163
+ ];
164
+ // pads in the side columns lie along X (0.6 × 0.4); pads in the rows (y = 0.6, 1.5, 7.2) are turned (0.4 × 0.6)
165
+ const raytacPads = RAYTAC_POINTS.map(([p, x, y]) => ([0.6, 1.5, 7.2].includes(y) && ![0.6, 1.5, 9, 9.9].includes(x) ? pin(p, x - 5.25, y - 7.75, 0.4, 0.6) : pin(p, x - 5.25, y - 7.75, 0.6, 0.4)));
166
+
167
+ /** The table shipped with the provider. */
168
+ export const FOOTPRINTS: FootprintEntry[] = [
169
+ // ---- chip passives and SOT-23: tscircuit's generated patterns, the fallback for a maker with no entry below (unverified)
170
+ builtin("0201", "0201"),
171
+ builtin("0402", "0402"),
172
+ builtin("0603", "0603"),
173
+ builtin("0805", "0805"),
174
+ builtin("1206", "1206"),
175
+ builtin("SOT-23-5", "sot23_5", { match: { package: "SOT-23-5", pin_count: 5 } }),
176
+ builtin("SOT-23", "sot23", { match: { package: "SOT-23", pin_count: 3 } }),
177
+ // mating direction left to tscircuit's own inference from the pattern (+Y, the mechanical-tab side); not checked against JST's drawing
178
+ builtin("JST SH 4-pin side entry", "jst4_p1mm_sh", { match: { package: "JST SH 4-pin side entry", pin_count: 4, pitch_mm: 1 } }),
179
+
180
+ // ---- chip passives by maker
181
+ chip({
182
+ id: "yageo-rc0402-reflow",
183
+ pkg: "0402",
184
+ manufacturer: "Yageo",
185
+ gap: 0.5,
186
+ length: 0.5,
187
+ width: 0.6,
188
+ body: [1.0, 0.5],
189
+ source: `${YAGEO_MOUNT.url} Table 1, 0402: A 1.5, B 0.5, C 0.5, D 0.6 (reflow)`,
190
+ record: { ...YAGEO_MOUNT, size: [[0.5, 0.5], [0.6, 0.6]], row: [1.0, 1.0], notes: "A (1.5 overall) = B + 2C: the pad centres are 1.0 mm apart" },
191
+ }),
192
+ chip({
193
+ id: "murata-grm15-reflow",
194
+ pkg: "0402",
195
+ manufacturer: "Murata",
196
+ gap: 0.5,
197
+ length: 0.4,
198
+ width: 0.5,
199
+ body: [1.0, 0.5],
200
+ source: `Murata GRM15 (1.0 × 0.5) reflow land, Table 2: a 0.3-0.5 / 0.4-0.6, b 0.35-0.45 / 0.4-0.5, c 0.4-0.6 / 0.5-0.7 (dimension tolerance within ±0.10 / ±0.15-0.20); a 0.5, b 0.4, c 0.5 lies in both rows`,
201
+ record: { ...MURATA_LAND, size: [[0.4, 0.45], [0.5, 0.6]], row: [0.8, 0.95], notes: "One land for both tolerance rows (every GRM15 part): b and c within both rows' ranges; row = a + b with a in 0.4-0.5" },
202
+ }),
203
+ chip({
204
+ id: "murata-grm18-reflow",
205
+ pkg: "0603",
206
+ manufacturer: "Murata",
207
+ gap: 0.8,
208
+ length: 0.7,
209
+ width: 0.8,
210
+ body: [1.6, 0.8],
211
+ source: `Murata GRM18 (1.6 × 0.8) reflow land, Table 2: a 0.6-0.8 / 0.7-0.9, b 0.6-0.7 / 0.7-0.8, c 0.6-0.8 / 0.8-1.0 (within ±0.10 / ±0.15-0.20); a 0.8, b 0.7, c 0.8 lies in both rows`,
212
+ record: { ...MURATA_LAND, size: [[0.7, 0.7], [0.8, 0.8]], row: [1.4, 1.5], notes: "One land for both tolerance rows (every GRM18 part); row = a + b with a in 0.7-0.8" },
213
+ }),
214
+
215
+ // ---- RP2040 (test fixture part): pads only, no record yet
216
+ {
217
+ id: "qfn-56-7x7-p0.4-ep3.2",
218
+ match: { package: "QFN-56", pin_count: 56, pitch_mm: 0.4, size_mm: [7, 7] },
219
+ pads: [...quadPads({ perSide: 14, pitch: 0.4, row: 6.875, padLength: 0.875, padWidth: 0.2 }), { pin: "57", x: 0, y: 0, w: 3.2, h: 3.2 }],
220
+ source: `${RP2040_DS} §5.1.2 Figure 167 "Recommended PCB Footprint for the RP2040 QFN-56 package": pads 0.20 × 0.875 at 0.40 pitch, rows 6.00 mm inside / 7.75 mm outside, centre pad 3.20 × 3.20`,
221
+ },
222
+ authored({
223
+ id: "lga-14-3x2.5-p0.5-bosch",
224
+ match: { package: "LGA-14", pin_count: 14, pitch_mm: 0.5, size_mm: [3, 2.5] },
225
+ pads: [
226
+ ...[1, 2, 3, 4].map((p, i) => ({ pin: String(p), x: -1.1625, y: 0.75 - i * 0.5, w: 0.475, h: 0.25 })),
227
+ ...[5, 6, 7].map((p, i) => ({ pin: String(p), x: -0.5 + i * 0.5, y: -0.9125, w: 0.25, h: 0.475 })),
228
+ ...[8, 9, 10, 11].map((p, i) => ({ pin: String(p), x: 1.1625, y: -0.75 + i * 0.5, w: 0.475, h: 0.25 })),
229
+ ...[12, 13, 14].map((p, i) => ({ pin: String(p), x: 0.5 - i * 0.5, y: 0.9125, w: 0.25, h: 0.475 })),
230
+ ],
231
+ paste: { margin_mm: 0 },
232
+ mask: { margin_mm: 0.05 },
233
+ body: box(3.0, 2.5),
234
+ source: `${BMI270_DS} §8.3 "Landing pattern recommendation" (BST-BMI270-DS000-08): pads 0.475 × 0.25 at 0.5 pitch, side pads' inner edges 0.925 from the centre, end pads' inner edges 0.675 from the centre, body 3.0 × 2.5`,
235
+ record: {
236
+ document: "Bosch Sensortec BMI270 data sheet BST-BMI270-DS000-08",
237
+ url: BMI270_DS,
238
+ page: 145,
239
+ figure: "§8.3 \"Landing pattern recommendation\"",
240
+ pads: [
241
+ { pins: ["1", "2", "3", "4", "8", "9", "10", "11"], size: [0.475, 0.25], pitch_mm: 0.5, row_mm: 2.325 },
242
+ { pins: ["5", "6", "7", "12", "13", "14"], size: [0.475, 0.25], pitch_mm: 0.5, row_mm: 1.825 },
243
+ ],
244
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
245
+ mask: { margin_mm: 0.05, basis: MASK_005 },
246
+ notes: "side pads' inner edges 0.925 and end pads' 0.675 from the centre: rows 2.325 (side) and 1.825 (end) apart",
247
+ },
248
+ }),
249
+ authored({
250
+ id: "vson-14-dsj-4x3-p0.5",
251
+ match: { package: "VSON-14", pin_count: 14, pitch_mm: 0.5, size_mm: [4, 3] },
252
+ pads: [...dualPads({ perSide: 7, pitch: 0.5, row: 2.8, padLength: 0.6, padWidth: 0.24, shape: "pill" }), { pin: "EP", x: 0, y: 0, w: 2.85, h: 1.58 }],
253
+ paste: { margin_mm: 0, pins: { EP: { windows: [-1, 1].flatMap((sx) => [-1, 1].map((sy) => ({ x: sx * 0.725, y: sy * 0.43, w: 1.25, h: 0.66 }))) } } },
254
+ mask: { margin_mm: 0.07 },
255
+ body: box(4.0, 3.0),
256
+ source: `${TPS63020_DS} (SLVS916I) "LAND PATTERN DATA" DSJ (R-PVSON-N14), 4210895-2/E: pads 0.24 × 0.6 (R0.12) at 0.5 pitch on rows 2.8 apart; thermal pad 2.85 × 1.58 from "THERMAL PAD MECHANICAL DATA" 4208549-3/G; stencil: four 1.25 × 0.66 windows 0.2 apart`,
257
+ record: {
258
+ document: "TI TPS63020 data sheet SLVS916I, land pattern data 4210895-2/E and thermal pad data 4208549-3/G",
259
+ url: TPS63020_DS,
260
+ page: "32-33",
261
+ figure: "DSJ (R-PVSON-N14) Example Board Layout and Example Stencil Design (0.125 mm stencil)",
262
+ pads: [
263
+ { pins: range(14), size: [0.24, 0.6], pitch_mm: 0.5, row_mm: 2.8 },
264
+ { pins: ["EP"], size: [2.85, 1.58] },
265
+ ],
266
+ paste: { margin_mm: 0, windows: { EP: { count: 4, size: [1.25, 0.66] } }, basis: "Example Stencil Design: pin apertures 0.24 × 0.6 (1:1); the thermal pad as four 1.25 × 0.66 windows 0.2 mm apart (TI: 81 % of its centre pad)" },
267
+ mask: { margin_mm: 0.07, basis: "Non solder mask defined pad, 0.07 all round (Example Via Layout Design)" },
268
+ notes: "The board layout's thermal-pad side tabs (8 × 0.2) are not reproduced: the pad is the 2.85 × 1.58 exposed pad.",
269
+ },
270
+ }),
271
+
272
+ // ---- TI packages
273
+ authored({
274
+ id: "ti-dbv0005a",
275
+ match: { package: "SOT-23-5", pin_count: 5, manufacturer: "Texas Instruments" },
276
+ pads: sot([...SOT5], 2.6, 1.1, 0.6),
277
+ paste: { margin_mm: 0 },
278
+ mask: { margin_mm: 0.07 },
279
+ body: box(1.6, 2.9),
280
+ source: "TI DBV0005A land pattern example 4214839/K (TPS7A02 data sheet): 5 × 1.1 × 0.6 pads, 0.95 pitch, rows 2.6 apart",
281
+ record: {
282
+ document: "TI DBV0005A SOT-23 package drawing 4214839/K (in the TPS7A02 data sheet SBVS277)",
283
+ url: "https://www.ti.com/lit/ds/symlink/tps7a02.pdf",
284
+ page: "46-47",
285
+ figure: "Example Board Layout, Land Pattern Example (15X), and Example Stencil Design",
286
+ pads: [{ pins: range(5), size: [1.1, 0.6], pitch_mm: 0.95, row_mm: 2.6 }],
287
+ paste: { margin_mm: 0, basis: "Example Stencil Design: apertures the size of the pads" },
288
+ mask: { margin_mm: 0.07, basis: "Solder mask details: non solder mask defined, 0.07 max all around (preferred)" },
289
+ },
290
+ }),
291
+ authored({
292
+ id: "ti-dgs0010a",
293
+ match: { package: "VSSOP-10", pin_count: 10, pitch_mm: 0.5, manufacturer: "Texas Instruments" },
294
+ pads: [...range(5).map((p, i) => pin(p, -2.2, 1.0 - i * 0.5, 1.45, 0.3)), ...range(5, 6).map((p, i) => pin(p, 2.2, -1.0 + i * 0.5, 1.45, 0.3))],
295
+ paste: { margin_mm: 0 },
296
+ mask: { margin_mm: 0.05 },
297
+ body: box(3.0, 3.0),
298
+ source: "TI DGS0010A land pattern example 4221984/A (DRV2605L data sheet): 10 × 1.45 × 0.3 pads, 0.5 pitch, rows 4.4 apart",
299
+ record: {
300
+ document: "TI DGS0010A VSSOP package drawing 4221984/A (in the DRV2605L data sheet SLOS854)",
301
+ url: "https://www.ti.com/lit/ds/symlink/drv2605l.pdf",
302
+ page: "71-72",
303
+ figure: "Example Board Layout, Land Pattern Example (10X), and Example Stencil Design",
304
+ pads: [{ pins: range(10), size: [1.45, 0.3], pitch_mm: 0.5, row_mm: 4.4 }],
305
+ paste: { margin_mm: 0, basis: "Example Stencil Design: apertures the size of the pads" },
306
+ mask: { margin_mm: 0.05, basis: "Solder mask details: non solder mask defined, 0.05 max all around" },
307
+ },
308
+ }),
309
+ authored({
310
+ id: "ti-rsm0032b",
311
+ match: { package: "VQFN-32", pin_count: 32, pitch_mm: 0.4, manufacturer: "Texas Instruments" },
312
+ pads: [...quadPads({ perSide: 8, pitch: 0.4, row: 3.85, padLength: 0.55, padWidth: 0.2 }), { pin: "33", x: 0, y: 0, w: 2.8, h: 2.8 }],
313
+ paste: { margin_mm: 0, pins: { "33": { windows: [-1, 1].flatMap((sx) => [-1, 1].map((sy) => ({ x: sx * 0.715, y: sy * 0.715, w: 1.23, h: 1.23 }))) } } },
314
+ mask: { margin_mm: 0.05 },
315
+ body: box(4.0, 4.0),
316
+ source: "TI RSM0032B land pattern 4219108/B (LP5024 data sheet): 32 × 0.2 × 0.55 pads at 0.4 pitch, rows 3.85 apart, thermal pad 2.8 × 2.8; stencil: four 1.23 mm squares at ±0.715 (77 %)",
317
+ record: {
318
+ document: "TI RSM0032B VQFN package drawing 4219108/B (in the LP5024 data sheet SNVSB82)",
319
+ url: "https://www.ti.com/lit/ds/symlink/lp5024.pdf",
320
+ page: "54-55",
321
+ figure: "Example Board Layout (Land Pattern Example, 20X) and Example Stencil Design (0.1 mm stencil)",
322
+ pads: [
323
+ { pins: range(32), size: [0.55, 0.2], pitch_mm: 0.4, row_mm: 3.85 },
324
+ { pins: ["33"], size: [2.8, 2.8] },
325
+ ],
326
+ paste: { margin_mm: 0, windows: { "33": { count: 4, size: [1.23, 1.23], coverage: 0.77 } }, basis: "Solder paste example: pin apertures 1:1; exposed pad 33 as 4 × 1.23 mm squares, 77 % printed coverage" },
327
+ mask: { margin_mm: 0.05, basis: "Non solder mask defined (preferred), 0.05 max all around" },
328
+ },
329
+ }),
330
+
331
+ // ---- Microchip
332
+ authored({
333
+ id: "microchip-c04-2091-ot",
334
+ match: { package: "SOT-23-5", pin_count: 5, manufacturer: "Microchip" },
335
+ pads: sot([...SOT5], 2.8, 1.1, 0.6),
336
+ paste: { margin_mm: 0 },
337
+ mask: { margin_mm: 0.05 },
338
+ body: box(1.6, 2.9),
339
+ source: "Microchip C04-2091-OT Rev F recommended land pattern (MCP73831 data sheet DS20001984H p. 24): X 0.60, Y 1.10, C 2.80, E 0.95, Z 3.90",
340
+ record: {
341
+ document: "Microchip MCP73831/2 data sheet DS20001984H, drawing C04-2091-OT Rev F",
342
+ url: "https://ww1.microchip.com/downloads/en/DeviceDoc/MCP73831-Family-Data-Sheet-DS20001984H.pdf",
343
+ page: 24,
344
+ figure: "5-Lead SOT-23 (OT) Recommended Land Pattern",
345
+ pads: [{ pins: range(5), size: [1.1, 0.6], pitch_mm: 0.95, row_mm: 2.8 }],
346
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
347
+ mask: { margin_mm: 0.05, basis: MASK_005 },
348
+ notes: "Z 3.90 overall = C + Y; G 1.70 and GX 0.35 (minimum gaps) hold",
349
+ },
350
+ }),
351
+ authored({
352
+ id: "microchip-c04-2123b-mc",
353
+ match: { package: "DFN-8", pin_count: 8, pitch_mm: 0.5, manufacturer: "Microchip" },
354
+ pads: [...dualPads({ perSide: 4, pitch: 0.5, row: 2.9, padLength: 0.75, padWidth: 0.3 }), { pin: "9", x: 0, y: 0, w: 1.45, h: 1.75 }],
355
+ paste: { margin_mm: 0 },
356
+ mask: { margin_mm: 0.05 },
357
+ body: box(2.0, 3.0),
358
+ source: "Microchip C04-2123B recommended land pattern (MCP73831 data sheet p. 21): X1 0.30, Y1 0.75, C1 2.90, E 0.50, centre pad W2 1.45 × T2 1.75 (maximum), G 0.20",
359
+ record: {
360
+ document: "Microchip MCP73831/2 data sheet DS20001984H, drawing C04-2123B",
361
+ url: "https://ww1.microchip.com/downloads/en/DeviceDoc/MCP73831-Family-Data-Sheet-DS20001984H.pdf",
362
+ page: 21,
363
+ figure: "8-Lead DFN (MC) 2×3 Recommended Land Pattern",
364
+ pads: [
365
+ { pins: range(8), size: [0.3, 0.75], pitch_mm: 0.5, row_mm: 2.9 },
366
+ { pins: ["9"], size: [[1.3, 1.45], [1.6, 1.75]] },
367
+ ],
368
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
369
+ mask: { margin_mm: 0.05, basis: MASK_005 },
370
+ notes: "W2 and T2 are maxima; the centre pad is drawn at the maxima, which leaves G = 0.20 to the pin lands",
371
+ },
372
+ }),
373
+
374
+ // ---- Analog Devices (Maxim)
375
+ authored({
376
+ id: "maxim-90-0065-tdfn8",
377
+ match: { package: "TDFN-8", pin_count: 8, pitch_mm: 0.5, manufacturer: "Analog Devices" },
378
+ pads: [...dualPads({ perSide: 4, pitch: 0.5, row: 1.98, padLength: 0.7, padWidth: 0.3 }), { pin: "EP", x: 0, y: 0, w: 1.38, h: 0.8 }],
379
+ paste: { margin_mm: 0 },
380
+ mask: { margin_mm: 0.05 },
381
+ body: box(2.0, 2.0),
382
+ source: "Maxim land pattern 90-0065 rev E (8L TDFN 2×2, package codes T822-2/-3C/-5): 0.30 × 0.70 pads at 0.50 pitch, rows 1.98 apart, exposed pad 1.38 × 0.80",
383
+ record: {
384
+ document: "Maxim Integrated package land pattern 90-0065 rev E, 8L TDFN 2×2 mm (MAX17048 data sheet: package T822+3, land pattern 90-0065)",
385
+ url: "https://www.analog.com/media/en/package-pcb-resources/land-pattern/tdfn-ep/90-0065.pdf",
386
+ page: 1,
387
+ figure: "Package land pattern drawing",
388
+ pads: [
389
+ { pins: range(8), size: [0.3, 0.7], pitch_mm: 0.5, row_mm: 1.98 },
390
+ { pins: ["EP"], size: [1.38, 0.8] },
391
+ ],
392
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
393
+ mask: { margin_mm: 0.05, basis: MASK_005 },
394
+ notes: "Drawn with the pin rows vertical; here the rows run along X as in the part's body (pins 1-4 at -Y). The list of package codes names T822-3C; the MAX17048G+T10's T822+3 has the same outline (21-0168).",
395
+ },
396
+ }),
397
+ authored({
398
+ id: "maxim-90-0602-olga14",
399
+ match: { package: "OESIP-14", pin_count: 14, pitch_mm: 0.8, manufacturer: "Analog Devices" },
400
+ pads: [...range(7).map((p, i) => pin(p, -1.2, 2.4 - i * 0.8, 0.75, 0.35)), ...range(7, 8).map((p, i) => pin(p, 1.2, -2.4 + i * 0.8, 0.75, 0.35))],
401
+ paste: { margin_mm: 0 },
402
+ mask: { margin_mm: 0.05 },
403
+ body: box(3.3, 5.6),
404
+ source: "Maxim land pattern 90-0602 rev F (14L OLGA 3.3 × 5.6, MAX30102 package F143A5MK+1): 14 × 0.75 × 0.35 pads at 0.80 pitch, columns 2.40 apart",
405
+ record: {
406
+ document: "Maxim Integrated package land pattern 90-0602 rev F, 14L OLGA 3.3 × 5.6 mm (MAX30102 data sheet: land pattern 90-0602)",
407
+ url: "https://www.analog.com/media/en/package-pcb-resources/land-pattern/olga/90-0602.pdf",
408
+ page: 1,
409
+ figure: "Package land pattern drawing",
410
+ pads: [{ pins: range(14), size: [0.75, 0.35], pitch_mm: 0.8, row_mm: 2.4 }],
411
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
412
+ mask: { margin_mm: 0.05, basis: MASK_005 },
413
+ notes: "Span 4.80 (7 pads at 0.80) holds",
414
+ },
415
+ }),
416
+
417
+ // ---- discretes
418
+ authored({
419
+ id: "st-sot23-6l",
420
+ match: { package: "SOT23-6L", pin_count: 6, manufacturer: "STMicroelectronics" },
421
+ pads: sot([...SOT6], 2.3, 1.2, 0.6),
422
+ paste: { margin_mm: 0 },
423
+ mask: { margin_mm: 0.05 },
424
+ body: box(1.6, 2.9),
425
+ source: "ST USBLC6-2 data sheet DS4260 rev 6 Figure 19 \"Footprint recommendations\" (SOT23-6L): 0.60 × 1.20 pads, 0.95 pitch, rows 2.30 apart, 3.50 overall",
426
+ record: {
427
+ document: "ST USBLC6-2 data sheet DS4260 rev 6",
428
+ url: "https://www.st.com/resource/en/datasheet/usblc6-2.pdf",
429
+ page: 13,
430
+ figure: "Figure 19. Footprint recommendations (SOT23-6L)",
431
+ pads: [{ pins: range(6), size: [1.2, 0.6], pitch_mm: 0.95, row_mm: 2.3 }],
432
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
433
+ mask: { margin_mm: 0.05, basis: MASK_005 },
434
+ notes: "3.50 overall = 2.30 + 1.20",
435
+ },
436
+ }),
437
+ authored({
438
+ id: "aos-sot23-po00001",
439
+ match: { package: "SOT23", pin_count: 3, manufacturer: "Alpha & Omega" },
440
+ pads: [pin(1, -1.2, 0.95, 0.8, 0.8), pin(2, -1.2, -0.95, 0.8, 0.8), pin(3, 1.2, 0, 0.8, 0.8)],
441
+ paste: { margin_mm: 0 },
442
+ mask: { margin_mm: 0.05 },
443
+ body: box(1.6, 2.9),
444
+ source: "AOS SOT23 package outline PO-00001 rev N, recommended land pattern: 0.80 × 0.80 pads, 0.95 from the centre line, rows 2.40 apart",
445
+ record: {
446
+ document: "Alpha & Omega Semiconductor SOT23 package outline PO-00001 version N",
447
+ url: "https://www.aosmd.com/res/packaging_information/SOT23.pdf",
448
+ page: 1,
449
+ figure: "Recommended land pattern",
450
+ pads: [{ pins: ["1", "2", "3"], size: [0.8, 0.8], pitch_mm: 1.9, row_mm: 2.4 }],
451
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
452
+ mask: { margin_mm: 0.05, basis: MASK_005 },
453
+ notes: "pins 1 and 2 are 0.95 either side of the centre line (e1 1.90)",
454
+ },
455
+ }),
456
+ authored({
457
+ id: "coilcraft-xfl4020",
458
+ match: { package: "XFL4020 4.0 x 4.0 mm SMD", pin_count: 2, manufacturer: "Coilcraft" },
459
+ pads: [pin(1, -1.185, 0, 0.98, 3.4), pin(2, 1.185, 0, 0.98, 3.4)],
460
+ paste: { margin_mm: 0 },
461
+ mask: { margin_mm: 0.05 },
462
+ body: box(4.0, 4.0),
463
+ source: "Coilcraft XFL4020 document 745-3 (rev. 03/10/26) Recommended Land Pattern: 0.98 × 3.4 pads, centres 2.37 apart",
464
+ record: {
465
+ document: "Coilcraft XFL4020 Series, Document 745-3 (revised 03/10/26)",
466
+ url: "https://www.coilcraft.com/getmedia/xfl4020.pdf",
467
+ page: 3,
468
+ figure: "Recommended Land Pattern",
469
+ pads: [{ pins: ["1", "2"], size: [0.98, 3.4], row_mm: 2.37 }],
470
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
471
+ mask: { margin_mm: 0.05, basis: MASK_005 },
472
+ },
473
+ }),
474
+ authored({
475
+ id: "epson-fc-135",
476
+ match: { package: "3.2 x 1.5 mm SMD", pin_count: 2, manufacturer: "Seiko Epson" },
477
+ pads: [pin(1, -1.25, 0, 1.0, 1.8), pin(2, 1.25, 0, 1.0, 1.8)],
478
+ paste: { margin_mm: 0 },
479
+ mask: { margin_mm: 0.05 },
480
+ body: box(3.2, 1.5),
481
+ source: "Epson FC-135 application manual FC-135_E_Ver2.0 [7] Recommended Footprint: 1.0 × 1.8 pads, centres 2.5 apart; no circuit patterns between the pads",
482
+ record: {
483
+ document: "Seiko Epson FC-135 data sheet, Document No FC-135_E_Ver2.0",
484
+ url: "https://download.epsondevice.com/td/pdf/app/FC-135_en.pdf",
485
+ page: 4,
486
+ figure: "[7] Outline Drawing and Recommended Footprint",
487
+ pads: [{ pins: ["1", "2"], size: [1.0, 1.8], row_mm: 2.5 }],
488
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
489
+ mask: { margin_mm: 0.05, basis: MASK_005 },
490
+ notes: "The drawing asks for no circuit patterns in the shaded area between the pads; the provider does not enforce it (route review).",
491
+ },
492
+ }),
493
+ authored({
494
+ id: "kingbright-aptf1616",
495
+ match: { package: "1.6 x 1.6 mm SMD LED, 4 castellated pads", pin_count: 4, manufacturer: "Kingbright" },
496
+ pads: [pin(1, 0.85, -0.5, 0.9, 0.5), pin(2, -0.85, -0.5, 0.9, 0.5), pin(3, -0.85, 0.5, 0.9, 0.5), pin(4, 0.85, 0.5, 0.9, 0.5)],
497
+ paste: { margin_mm: 0 },
498
+ mask: { margin_mm: 0.05 },
499
+ body: box(1.6, 1.6),
500
+ source: "Kingbright APTF1616LSEEZGKQBKC spec DSAO4453 rev V.9B, Recommended Soldering Pattern: 4 × 0.9 × 0.5 pads, 2.6 overall in X, 1.5 in Y",
501
+ record: {
502
+ document: "Kingbright APTF1616LSEEZGKQBKC data sheet, Spec No DSAO4453 Rev V.9B (03/19/2025)",
503
+ url: "https://www.kingbrightusa.com/images/catalog/SPEC/APTF1616LSEEZGKQBKC.pdf",
504
+ page: 1,
505
+ figure: "Recommended Soldering Pattern",
506
+ pads: [{ pins: ["1", "2", "3", "4"], size: [0.9, 0.5], row_mm: 1.7 }],
507
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
508
+ mask: { margin_mm: 0.05, basis: MASK_005 },
509
+ notes: "Pad centres ±0.85 (2.6 overall less a 0.9 pad) and ±0.5 (1.5 overall less a 0.5 pad); pins as the package's top view: 3 and 4 at +Y, 2 and 1 at -Y, 1 at +X",
510
+ },
511
+ }),
512
+ authored({
513
+ id: "st-vl53l4cd-lga12",
514
+ match: { package: "Optical LGA12", pin_count: 12, pitch_mm: 0.8, manufacturer: "STMicroelectronics" },
515
+ pads: [
516
+ ...[1, 2, 3, 4, 5].map((p, i) => pin(p, 1.6 - i * 0.8, -0.8, 0.5, 0.5)),
517
+ pin(6, -1.6, 0, 0.5, 0.5),
518
+ ...[7, 8, 9, 10, 11].map((p, i) => pin(p, -1.6 + i * 0.8, 0.8, 0.5, 0.5)),
519
+ pin(12, 1.6, 0, 0.5, 0.5),
520
+ ],
521
+ paste: { margin_mm: 0 },
522
+ mask: { margin_mm: 0.05 },
523
+ body: box(4.4, 2.4),
524
+ source: "ST VL53L4CD data sheet DS13812 rev 8 Figure 19 (outline drawing 3/5): 12 pads 0.50 square on a 0.80 grid; ST gives no separate land pattern, so the lands are the package pads 1:1 (the figure is the bottom view, mirrored here to the top view: pin 1 at +X, -Y)",
525
+ record: {
526
+ document: "ST VL53L4CD data sheet DS13812 rev 8 (module drawing DM00726192 rev 4.0)",
527
+ url: "https://www.st.com/resource/en/datasheet/vl53l4cd.pdf",
528
+ page: 23,
529
+ figure: "Figure 19. Outline drawing (3/5): pad positions 0 / 0.80 / 1.60 / 2.40 / 3.20 and 0 / 0.80 / 1.60, □0.50 in 12 positions",
530
+ pads: [{ pins: range(12), size: [0.5, 0.5], pitch_mm: 0.8, row_mm: 1.6 }],
531
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
532
+ mask: { margin_mm: 0.05, basis: MASK_005 },
533
+ notes: "The data sheet gives the module's pads, not a separate land pattern: lands equal to the pads (1:1), as ST's LGA modules are usually laid out",
534
+ },
535
+ }),
536
+
537
+ // ---- switches and connectors
538
+ authored({
539
+ id: "alps-skrt-guide-bosses",
540
+ match: { package: "SMD side-push TACT switch, 4.5 × 3.4 mm, with guide bosses", pin_count: 3, manufacturer: "Alps" },
541
+ pads: [pin(1, -1.225, 0.9, 0.75, 1.8), pin(2, 0, 0.9, 0.6, 1.8), pin(3, 1.225, 0.9, 0.75, 1.8), pin("", -1.85, -1.05, 1.3, 0.9), pin("", 1.85, -1.05, 1.3, 0.9)],
542
+ holes: [
543
+ { x: -2.125, y: 0, diameter_mm: 0.9 },
544
+ { x: 2.125, y: 0, diameter_mm: 0.9 },
545
+ ],
546
+ paste: { margin_mm: 0 },
547
+ mask: { margin_mm: 0.05 },
548
+ // the body (4.5 wide, 3.4 deep with the stem 0.84 beyond its front) from the part's CAD, in this frame
549
+ body: { x: [-2.25, 2.25], y: [-2.05, 1.35] },
550
+ source: "Alps Alpine SKRT series catalog (update 2510), Drawing No.1 Land Dimensions (viewed from the switch mounting face): terminal lands 0.75 / 0.6 / 0.75 × 1.8 (3.2 over pins 1 and 3), fixing-leg lands 1.3 × 0.9 (5 overall, 2.4 between), 2 × Ø0.9 holes 4.25 apart for the guide bosses on the terminal lands' lower edge; no copper tracks in the 2 × 1.2 area between the legs. Origin: the bosses' centre line, as the part's CAD mounts",
551
+ record: {
552
+ document: "Alps Alpine TACT Switch SKRT series catalog (update 2510)",
553
+ url: "https://tech.alpsalpine.com/cms.media/product_catalog_ta_02_skrt_en_01ec237785.pdf",
554
+ page: 1,
555
+ figure: "Drawing No.1, Land Dimensions (viewed from switch mounting face)",
556
+ pads: [
557
+ { pins: ["1", "3"], size: [0.75, 1.8], row_mm: 2.45 },
558
+ { pins: ["2"], size: [0.6, 1.8] },
559
+ { pins: [""], size: [1.3, 0.9], row_mm: 3.7 },
560
+ ],
561
+ // 4.25 apart on the origin's line (the bosses' centre line)
562
+ holes: [{ diameter_mm: 0.9, count: 2, plated: false, at: [[-2.125, 0], [2.125, 0]] }],
563
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
564
+ mask: { margin_mm: 0.05, basis: MASK_005 },
565
+ notes: "3.2 over the outer terminal lands' outer edges, so their centres are 2.45 apart; 5 over the leg lands with 2.4 between them, so their centres are 3.7 apart; the terminal lands run 1.8 up from the holes' centre line, the leg lands from 0.6 to 1.5 below it (1.5 + 1.8 = 3.3 overall). The prohibited copper area (2 × 1.2 between the legs) is not enforced by the provider (route review).",
566
+ },
567
+ }),
568
+ authored({
569
+ id: "gct-usb4720-midmount",
570
+ match: { package: "USB4720 (USB Type-C receptacle, USB 2.0, SMT mid-mount, IP67)", pin_count: 18, manufacturer: "GCT" },
571
+ pads: usb4720Pads,
572
+ paste: { margin_mm: 0, plated: true },
573
+ mask: { margin_mm: 0.05 },
574
+ insertion: "from_bottom",
575
+ // the body: 11.30 wide over the stake legs, from the tail lands' rear edge to the tongue tip 0.80 in front of the gasket face (y = -4.15)
576
+ body: { x: [-5.65, 5.65], y: [-4.95, 2.7] },
577
+ source: `${USB4720} (customer drawing USB4720 rev B, sheet 1/3) Recommended PCB Layout, 0.6 ± 0.1 mm board: 12 tail lands 0.80 long, 8 × 0.30 at 0.50 pitch and 4 × 0.65 pair lands (centres 2.43 and 3.28 from the centre line), 2 × Ø0.50 plated holes (Ø0.80 solder area) 8.20 apart on the tail line, 4 plated stake slots 0.50 × 1.65 in 1.00 × 2.15 solder areas on 11.30 mm, the rear pair 0.30 and the front pair 4.30 in front of the tail line, the board edge 1.28 in front of the front pair, the 9.05 mm cut-out's rear edge 0.55 in front of the tail line`,
578
+ record: {
579
+ document: "GCT USB4720 customer drawing, revision B (B PCN, 02/01/25), sheet 1/3",
580
+ url: USB4720,
581
+ page: 1,
582
+ figure: "Recommended PCB Layout (Thickness 0.6 ± 0.10 mm; tolerance ±0.05)",
583
+ pads: [
584
+ { pins: SINGLES, size: [0.3, 0.8], pitch_mm: 0.5 },
585
+ ...PAIRS.map((pins) => ({ pins, size: [0.65, 0.8] as [number, number], merged: true })),
586
+ ],
587
+ // the drawing's tail line at y = 2.30: the rear slots 0.30 and the front ones 4.30 in front of it (toward -Y), on 11.30;
588
+ // the tag holes 8.20 apart on the tail line
589
+ slots: [{ drill: [0.5, 1.65], pad: [1.0, 2.15], count: 4, at: [[-5.65, 2.0], [5.65, 2.0], [-5.65, -2.0], [5.65, -2.0]] }],
590
+ holes: [{ diameter_mm: 0.5, count: 2, plated: true, pad_mm: 0.8, at: [[-4.1, 2.3], [4.1, 2.3]] }],
591
+ paste: { margin_mm: 0, plated: true, basis: "Solder Area hatched on the tail lands, the stake slots and the tag holes: 1:1 on the lands, pin-in-paste on the slots and holes (top side)" },
592
+ mask: { margin_mm: 0.05, basis: MASK_005 },
593
+ notes: "Board frame here: the stake slots' centre at the origin (the part's mount), mouth toward -Y. The 9.05 mm cut-out (rear edge at y = 1.75) and the board edge 1.28 in front of the front slots (y = -3.28) belong to the board outline (circuit.config.ts), not to the land pattern.",
594
+ },
595
+ }),
596
+ authored({
597
+ id: "gct-usb4105-topmount",
598
+ match: { package: "USB4105 (USB Type-C receptacle, USB 2.0, SMT top mount)", pin_count: 17, manufacturer: "GCT" },
599
+ pads: usb4105Pads,
600
+ holes: [
601
+ { x: -2.89, y: 2.605, diameter_mm: 0.65 },
602
+ { x: 2.89, y: 2.605, diameter_mm: 0.65 },
603
+ ],
604
+ paste: { margin_mm: 0, plated: true },
605
+ mask: { margin_mm: 0.05 },
606
+ insertion: "from_bottom",
607
+ body: { x: [-4.47, 4.47], y: [-3.675, 4.255] },
608
+ source: `${USB4105} (drawing USB4105 rev B4, 18/12/23) Recommended PCB Layout, viewed from the component side: 12 tail lands 1.15 long, 8 × 0.30 at 0.50 pitch and 4 × 0.60 pair lands (centres 4.80 and 6.40 apart), 4 plated stake slots 0.60 wide on 8.64 mm (front pair 0.60 × 1.40 in 1.00 × 1.80, rear pair 0.60 × 1.70 in 1.00 × 2.10, 4.18 apart), 2 × Ø0.65 holes 5.78 apart 3.68 behind the front slots, the board edge 2.60 in front of them`,
609
+ record: {
610
+ document: "GCT USB4105 drawing, revision B4 (18/12/23), sheet 1/2",
611
+ url: USB4105,
612
+ page: 1,
613
+ figure: "Recommended PCB Layout (as viewed from component side; tolerance ±0.05)",
614
+ pads: [
615
+ { pins: SINGLES, size: [0.3, 1.15], pitch_mm: 0.5 },
616
+ ...PAIRS.map((pins) => ({ pins, size: [0.6, 1.15] as [number, number], merged: true })),
617
+ ],
618
+ // on 8.64: the front pair at y = -1.075 (2.60 behind the board edge at -3.675), the rear pair 4.18 behind it;
619
+ // the peg holes 5.78 apart, 3.68 behind the front slots
620
+ slots: [
621
+ { drill: [0.6, 1.4], pad: [1.0, 1.8], count: 2, at: [[-4.32, -1.075], [4.32, -1.075]] },
622
+ { drill: [0.6, 1.7], pad: [1.0, 2.1], count: 2, at: [[-4.32, 3.105], [4.32, 3.105]] },
623
+ ],
624
+ holes: [{ diameter_mm: 0.65, count: 2, plated: false, at: [[-2.89, 2.605], [2.89, 2.605]] }],
625
+ paste: { margin_mm: 0, plated: true, basis: "Solder Area hatched on the tail lands and the stake slots: 1:1 on the lands, pin-in-paste on the slots (top side); the peg holes are bare" },
626
+ mask: { margin_mm: 0.05, basis: MASK_005 },
627
+ notes: "Frame: the part's mount (front slots at y = -1.075, the board edge at y = -3.675). The tail lands' inner edge is on the rear slots' centre line (4.18 behind the front pair).",
628
+ },
629
+ }),
630
+
631
+ // ---- modules
632
+ authored({
633
+ id: "espressif-esp32-c3-mini-1",
634
+ match: { package: "ESP32-C3-MINI-1 module, 53 pins", pin_count: 53, manufacturer: "Espressif" },
635
+ pads: [
636
+ ...range(11).map((p, i) => pin(p, -5.9, 1.3 - i * 0.8, 0.8, 0.4)),
637
+ ...range(13, 12).map((p, i) => pin(p, -4.8 + i * 0.8, -7.6, 0.4, 0.8)),
638
+ ...range(11, 25).map((p, i) => pin(p, 5.9, -6.7 + i * 0.8, 0.8, 0.4)),
639
+ ...range(13, 36).map((p, i) => pin(p, 4.8 - i * 0.8, 2.2, 0.4, 0.8)),
640
+ pin(50, 5.95, 2.25, 0.7, 0.7),
641
+ pin(51, 5.95, -7.65, 0.7, 0.7),
642
+ pin(52, -5.95, -7.65, 0.7, 0.7),
643
+ pin(53, -5.95, 2.25, 0.7, 0.7),
644
+ // the ground pad as Espressif draws it: a 3 × 3 grid of 1.45 mm squares over 5.4 mm
645
+ ...[-1, 0, 1].flatMap((i) => [-1, 0, 1].map((j) => pin(49, i * 1.975, -2.7 + j * 1.975, 1.45, 1.45))),
646
+ ],
647
+ paste: { margin_mm: 0 },
648
+ mask: { margin_mm: 0.05 },
649
+ // the module: 13.2 wide, 16.6 tall, its bottom edge 0.8 below the bottom pad row
650
+ body: { x: [-6.6, 6.6], y: [-8.4, 8.2] },
651
+ source: "Espressif ESP32-C3-MINI-1 & MINI-1U data sheet v2.2 Figure 11-1 Recommended PCB Land Pattern: 48 pads 0.4 × 0.8 at 0.8 pitch (columns 11.8 apart, rows 9.8 apart), 4 corner pads 0.7 × 0.7 (11.9 and 9.9 apart), 9 ground pads 1.45 × 1.45 in a 5.4 mm square; module 13.2 × 16.6 with its antenna area beyond the pads",
652
+ record: {
653
+ document: "Espressif ESP32-C3-MINI-1 & MINI-1U Datasheet v2.2",
654
+ url: "https://www.espressif.com/sites/default/files/documentation/esp32-c3-mini-1_datasheet_en.pdf",
655
+ page: 38,
656
+ figure: "Figure 11-1. ESP32-C3-MINI-1 Recommended PCB Land Pattern",
657
+ pads: [
658
+ { pins: [...range(11), ...range(11, 25)], size: [0.8, 0.4], pitch_mm: 0.8, row_mm: 11.8 },
659
+ { pins: [...range(13, 12), ...range(13, 36)], size: [0.4, 0.8], pitch_mm: 0.8, row_mm: 9.8 },
660
+ { pins: ["50", "51", "52", "53"], size: [0.7, 0.7], row_mm: 11.9 },
661
+ { pins: ["49"], size: [1.45, 1.45], pitch_mm: 1.975 },
662
+ ],
663
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
664
+ mask: { margin_mm: 0.05, basis: MASK_005 },
665
+ notes: "The 9 ground pads: 1.45 squares over 5.4 (pitch (5.4 - 1.45) / 2 = 1.975); the pin-1 pad's 0.6 chamfer is drawn square here. Espressif's thermal vias sit between the ground pads, not in them. Corner pads 9.9 apart in Y.",
666
+ },
667
+ }),
668
+ authored({
669
+ id: "raytac-mdbt50q",
670
+ match: { package: "MDBT50Q module, 61 pads", pin_count: 61, manufacturer: "Raytac" },
671
+ pads: raytacPads,
672
+ paste: { margin_mm: 0 },
673
+ mask: { margin_mm: 0.05 },
674
+ body: box(10.5, 15.5),
675
+ source: "Raytac MDBT50Q-1MV2 specification Ver. L §2.2 Recommended Layout of Solder Pad (top view): 61 pads 0.6 × 0.4 mm, centres dimensioned from the module's lower-left corner; module 10.5 × 15.5 (origin here: the module centre, (5.25, 7.75) in the drawing)",
676
+ record: {
677
+ document: "Raytac MDBT50Q-1MV2 module specification, Ver. L",
678
+ url: "https://www.raytac.com/download/index.php?index_id=43",
679
+ page: 10,
680
+ figure: "§2.2 Recommended Layout of Solder Pad, Top View (Unit: mm), x61 pad 0.6 × 0.4",
681
+ pads: [{ pins: range(61), size: [0.6, 0.4], pitch_mm: 0.8 }],
682
+ centres: { origin: [5.25, 7.75], points: RAYTAC_DRAWING },
683
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
684
+ mask: { margin_mm: 0.05, basis: MASK_005 },
685
+ notes: "Pads in the side columns lie along X; pads in the two inner rows and the bottom row are turned 90°. The antenna keep-out (3.8 mm, page 9) is a board keep-out in circuit.config.ts.",
686
+ },
687
+ }),
688
+ authored({
689
+ id: "eastrising-er-tft128-1-fpc",
690
+ match: { package: "Hollow out FPC, 12 pins", pin_count: 12, pitch_mm: 0.7, manufacturer: "EastRising" },
691
+ pads: range(12).map((p, i) => pin(p, 0, 3.85 - i * 0.7, 2.0, 0.35)),
692
+ paste: { margin_mm: 0 },
693
+ mask: { margin_mm: 0.05 },
694
+ source: "EastRising ER-TFT1.28-1 data sheet §3.1 outline drawing, FPC tail: 12 contacts 0.35 wide at P0.7 (7.7 over 12), exposed in a 2.00 mm hollow-out window; EastRising gives the contacts, not a board land, so the lands equal the contacts (hot-bar soldering)",
695
+ record: {
696
+ document: "EastRising ER-TFT1.28-1 TFT LCD Display Datasheet (outline drawing dated Dec-02-19)",
697
+ url: "https://www.buydisplay.com/download/manual/ER-TFT1.28-1_Datasheet.pdf",
698
+ page: 6,
699
+ figure: "3.1 ER-TFT1.28-1 with No Touch Panel Outline Drawing, FPC tail detail: 0.35, P0.7*(12-1)=7.7, 2.00",
700
+ pads: [{ pins: range(12), size: [2.0, 0.35], pitch_mm: 0.7 }],
701
+ paste: { margin_mm: 0, basis: PASTE_1TO1 },
702
+ mask: { margin_mm: 0.05, basis: MASK_005 },
703
+ notes: "No board land in the data sheet: the lands are the FPC's contacts (0.35 × 2.00 window) 1:1, pin 1 at +Y as the contacts number. The tail's two Ø1.0 alignment holes (10.00 apart) are not drilled in the board.",
704
+ },
705
+ }),
706
+ ];