diff --git a/index.tsx b/index.tsx index a34bf53..a6e23ad 100644 --- a/index.tsx +++ b/index.tsx @@ -6,3 +6,4 @@ export * from "./lib/XiaoBoard/XiaoReceiver.circuit" export * from "./lib/ProMicroBoard/ProMicroBoard.circuit" // export * from "./Common Boards/RaspberryPiHatBoard/RaspberryPiHatBoard.circuit" export * from "./lib/ViaGridBoard/ViaGridBoard.circuit" +export * from "./lib/Esp32C3SuperMini/Esp32C3SuperMini.circuit" diff --git a/lib/Esp32C3SuperMini/Esp32C3SuperMini.circuit.tsx b/lib/Esp32C3SuperMini/Esp32C3SuperMini.circuit.tsx new file mode 100644 index 0000000..8058072 --- /dev/null +++ b/lib/Esp32C3SuperMini/Esp32C3SuperMini.circuit.tsx @@ -0,0 +1,84 @@ +import type { BoardProps, ChipProps } from "@tscircuit/props" +import { outlineBuilder } from "../../util/outlineBuilder" +import { Esp32C3SuperMiniFootprint } from "./Esp32C3SuperMiniFootprint" +import { + BOARD_LENGTH_MM, + BOARD_WIDTH_MM, + LEFT_PINS, + PINS_IN_FOOTPRINT_ORDER, + RIGHT_PINS, +} from "./pinout" + +export type Esp32C3SuperMiniProps = { + boardProps?: BoardProps + chipProps?: Partial + children?: any +} + +const CORNER_RADIUS_MM = 1.5 +const halfWidth = BOARD_WIDTH_MM / 2 +const halfLength = BOARD_LENGTH_MM / 2 + +const pinLabels = Object.fromEntries( + PINS_IN_FOOTPRINT_ORDER.map((label, index) => [`pin${index + 1}`, label]), +) + +const schPinArrangement = { + leftSide: { direction: "top-to-bottom" as const, pins: [...LEFT_PINS] }, + rightSide: { direction: "top-to-bottom" as const, pins: [...RIGHT_PINS] }, +} + +/** + * The corner arcs go through Math.cos/sin, which are not required to be + * correctly rounded and differ by ~1 ULP between libm implementations. Snapping + * to a 1nm grid keeps the outline, and therefore the rendered snapshot, + * identical on every platform. + */ +const snapToNanometerGrid = ({ x, y }: { x: number; y: number }) => ({ + x: Math.round(x * 1e6) / 1e6, + y: Math.round(y * 1e6) / 1e6, +}) + +/** 18 x 22.5mm outline with rounded corners; USB-C sits on the bottom edge. */ +const outline = outlineBuilder(0, halfLength) + .lineTo(halfWidth, halfLength) + .corner({ radius: CORNER_RADIUS_MM, turn: "ccw" }) + .lineTo(halfWidth, -halfLength) + .corner({ radius: CORNER_RADIUS_MM, turn: "ccw" }) + .lineTo(-halfWidth, -halfLength) + .corner({ radius: CORNER_RADIUS_MM, turn: "ccw" }) + .lineTo(-halfWidth, halfLength) + .corner({ radius: CORNER_RADIUS_MM, turn: "ccw" }) + .lineTo(0, halfLength) + .toArray() + .map(snapToNanometerGrid) + +/** + * ESP32-C3 SuperMini development board (18 x 22.5mm) as a reusable common board. + * Two rows of eight 2.54mm-pitch header pins carrying 5V/GND/3V3 and + * GPIO0-GPIO10, GPIO20 and GPIO21. + */ +export const Esp32C3SuperMini = ({ + boardProps = {}, + chipProps = {}, + children, +}: Esp32C3SuperMiniProps) => { + const { name: resolvedName = "Esp32C3SuperMini", ...chipRest } = chipProps + + return ( + + } + doNotPlace + pcbX={0} + pcbY={0} + pinLabels={pinLabels} + schWidth={1.4} + schPinArrangement={schPinArrangement} + /> + {children} + + ) +} diff --git a/lib/Esp32C3SuperMini/Esp32C3SuperMiniFootprint.tsx b/lib/Esp32C3SuperMini/Esp32C3SuperMiniFootprint.tsx new file mode 100644 index 0000000..d88bc88 --- /dev/null +++ b/lib/Esp32C3SuperMini/Esp32C3SuperMiniFootprint.tsx @@ -0,0 +1,40 @@ +import React from "react" +import type { PlatedHoleProps } from "@tscircuit/props" +import { PINS_PER_SIDE, PIN_PITCH_MM, ROW_SPACING_MM } from "./pinout" + +export interface Esp32C3SuperMiniFootprintProps { + holeDiameter?: number + outerDiameter?: number +} + +/** + * Through-hole footprint for the ESP32-C3 SuperMini: two columns of eight + * 2.54mm-pitch pins, 15.24mm apart, matching the module's header rows. + * pin1-pin8 are the left column (top to bottom) and pin9-pin16 the right. + */ +export const Esp32C3SuperMiniFootprint = ({ + holeDiameter = 1, + outerDiameter = 1.8, +}: Esp32C3SuperMiniFootprintProps = {}) => { + const topPinY = ((PINS_PER_SIDE - 1) / 2) * PIN_PITCH_MM + const columnXs = [-ROW_SPACING_MM / 2, ROW_SPACING_MM / 2] + + const holes = columnXs.flatMap((columnX, column) => + Array.from({ length: PINS_PER_SIDE }, (_, row) => { + const pin = `pin${column * PINS_PER_SIDE + row + 1}` + const holeProps: PlatedHoleProps = { + portHints: [pin], + shape: "circle", + holeDiameter, + outerDiameter, + pcbX: columnX, + pcbY: topPinY - row * PIN_PITCH_MM, + } + // tscircuit's JSX types don't accept React's `key`, so build the element + // directly rather than widening the props to `any`. + return React.createElement("platedhole", { key: pin, ...holeProps }) + }), + ) + + return {holes} +} diff --git a/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-3d.snap.png b/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-3d.snap.png new file mode 100644 index 0000000..92abea6 Binary files /dev/null and b/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-3d.snap.png differ diff --git a/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-pcb.snap.svg b/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-pcb.snap.svg new file mode 100644 index 0000000..e24abd5 --- /dev/null +++ b/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-pcb.snap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-schematic.snap.svg b/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-schematic.snap.svg new file mode 100644 index 0000000..edb96a6 --- /dev/null +++ b/lib/Esp32C3SuperMini/__snapshots__/Esp32C3SuperMini.circuit-schematic.snap.svg @@ -0,0 +1,25 @@ +Esp32C3SuperMini1V52GND3V3_34IO45IO36IO27IO18IO09IO510IO611IO712IO813IO914IO1015IO2016IO21 \ No newline at end of file diff --git a/lib/Esp32C3SuperMini/pinout.ts b/lib/Esp32C3SuperMini/pinout.ts new file mode 100644 index 0000000..b44bc29 --- /dev/null +++ b/lib/Esp32C3SuperMini/pinout.ts @@ -0,0 +1,36 @@ +/** + * ESP32-C3 SuperMini header pinout and mechanical constants. + * + * Pins are listed top to bottom exactly as they sit on the board, with the PCB + * antenna at the top and USB-C at the bottom. + */ +export const LEFT_PINS = [ + "V5", + "GND", + "V3_3", + "IO4", + "IO3", + "IO2", + "IO1", + "IO0", +] as const + +export const RIGHT_PINS = [ + "IO5", + "IO6", + "IO7", + "IO8", + "IO9", + "IO10", + "IO20", + "IO21", +] as const + +/** Footprint pin order: pin1-pin8 are the left column, pin9-pin16 the right. */ +export const PINS_IN_FOOTPRINT_ORDER = [...LEFT_PINS, ...RIGHT_PINS] + +export const PINS_PER_SIDE = LEFT_PINS.length +export const PIN_PITCH_MM = 2.54 +export const ROW_SPACING_MM = 15.24 +export const BOARD_WIDTH_MM = 18 +export const BOARD_LENGTH_MM = 22.5