From 83ca5bb3c03d0bd159ca1235dca974eda1ecd04a Mon Sep 17 00:00:00 2001 From: Joel Rubio <283646368+joel777rubio-web@users.noreply.github.com> Date: Tue, 12 May 2026 11:04:06 +0200 Subject: [PATCH] Add UTDFN-4-EP footprint --- src/fn/index.ts | 1 + src/fn/utdfn.ts | 62 +++++++++++++++++++++++++++++++++++++++++++++ src/footprinter.ts | 7 +++++ tests/utdfn.test.ts | 17 +++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 src/fn/utdfn.ts create mode 100644 tests/utdfn.test.ts diff --git a/src/fn/index.ts b/src/fn/index.ts index 94c0370e..ddc2e7cb 100644 --- a/src/fn/index.ts +++ b/src/fn/index.ts @@ -67,6 +67,7 @@ export { sod323w } from "./sod323w" export { sod323fl } from "./sod323fl" export { son } from "./son" export { vson } from "./vson" +export { utdfn } from "./utdfn" export { solderjumper } from "./solderjumper" export { sot457 } from "./sot457" export { sot963 } from "./sot963" diff --git a/src/fn/utdfn.ts b/src/fn/utdfn.ts new file mode 100644 index 00000000..dc25daa8 --- /dev/null +++ b/src/fn/utdfn.ts @@ -0,0 +1,62 @@ +import type { AnyCircuitElement, PcbCourtyardRect } from "circuit-json" +import { length, distance } from "circuit-json" +import { rectpad } from "../helpers/rectpad" +import { z } from "zod" +import { base_def } from "../helpers/zod/base_def" +import { dim2d } from "src/helpers/zod/dim-2d" +import { type SilkscreenRef, silkscreenRef } from "src/helpers/silkscreenRef" + +export const utdfn_def = base_def.extend({ + fn: z.string(), + num_pins: z.number().default(4), + p: distance.default("0.35mm"), + w: length.default("0.65mm"), + grid: dim2d.default("1x1mm"), + pinw: length.default("0.2mm"), + pinh: length.default("0.35mm"), + ep: dim2d.default("0.35x0.35mm"), +}) + +export const utdfn = ( + raw_params: z.input, +): { circuitJson: AnyCircuitElement[]; parameters: any } => { + const parameters = utdfn_def.parse(raw_params) + const pads: AnyCircuitElement[] = [] + + if (parameters.num_pins !== 4) { + throw new Error("UTDFN currently supports 4 pins") + } + + for (let i = 0; i < parameters.num_pins; i++) { + const leftSide = i < 2 + const pinIndexOnSide = i % 2 + const y = (pinIndexOnSide === 0 ? 1 : -1) * (parameters.p / 2) + const x = (leftSide ? -1 : 1) * (parameters.w / 2) + pads.push(rectpad(i + 1, x, y, parameters.pinw, parameters.pinh)) + } + + if (parameters.ep.x > 0 && parameters.ep.y > 0) { + pads.push(rectpad(parameters.num_pins + 1, 0, 0, parameters.ep.x, parameters.ep.y)) + } + + const courtyard: PcbCourtyardRect = { + type: "pcb_courtyard_rect", + pcb_courtyard_rect_id: "", + pcb_component_id: "", + center: { x: 0, y: 0 }, + width: parameters.grid.x + 0.5, + height: parameters.grid.y + 0.5, + layer: "top", + } + + const silkscreenRefText: SilkscreenRef = silkscreenRef( + 0, + parameters.grid.y / 2 + 0.25, + 0.15, + ) + + return { + circuitJson: [...pads, silkscreenRefText, courtyard], + parameters, + } +} diff --git a/src/footprinter.ts b/src/footprinter.ts index fa51a3b4..f792d036 100644 --- a/src/footprinter.ts +++ b/src/footprinter.ts @@ -182,6 +182,9 @@ export type Footprinter = { ) => FootprinterParamsBuilder< "p" | "w" | "grid" | "ep" | "epx" | "pinw" | "pinh" > + utdfn: ( + num_pins?: number, + ) => FootprinterParamsBuilder<"p" | "w" | "grid" | "ep" | "pinw" | "pinh"> vssop: ( num_pins?: number, ) => FootprinterParamsBuilder<"w" | "h" | "p" | "pl" | "pw"> @@ -271,6 +274,10 @@ const normalizeDefinition = (def: string): string => { return def .trim() .replace(/^pinheader(?=[\d_]|$)/i, "pinrow") + .replace( + /^utdfn-?4-?ep\(?1x1\)?(?=_|$)/i, + "utdfn_4_p0.35_w0.65_grid1x1mm_pinw0.2_pinh0.35_ep0.35x0.35mm", + ) .replace(/^sot23-(\d+)(?=_|$)/i, "sot23_$1") .replace(/^sot-223-(\d+)(?=_|$)/i, "sot223_$1") .replace(/^to-220f-(\d+)(?=_|$)/i, "to220f_$1") diff --git a/tests/utdfn.test.ts b/tests/utdfn.test.ts new file mode 100644 index 00000000..75f0e78d --- /dev/null +++ b/tests/utdfn.test.ts @@ -0,0 +1,17 @@ +import { test, expect } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" + +test("UTDFN-4-EP(1x1) aliases to configured VSON footprint", () => { + const utdfnCircuitJson = fp.string("UTDFN-4-EP(1x1)").circuitJson() + const canonicalCircuitJson = fp + .string("utdfn_4_p0.35_w0.65_grid1x1mm_pinw0.2_pinh0.35_ep0.35x0.35mm") + .circuitJson() + + expect(convertCircuitJsonToPcbSvg(utdfnCircuitJson)).toEqual( + convertCircuitJsonToPcbSvg(canonicalCircuitJson), + ) + expect( + utdfnCircuitJson.filter((elm) => elm.type === "pcb_smtpad"), + ).toHaveLength(5) +})