pixalynx/pixal-nfc-card
This code defines React components representing specific electronic hardware components—namely, a 100-ohm SMD resistor, a surface-mount LED, and a chip with labeled pins—each modeled with detailed footprints, 3D CAD models, and manufacturer part information for PCB design.
- Version
- 1.1.0
- License
- unset
- Stars
- 0
index.circuit.tsx
PCB
Schematic
import { Fragment } from "react"
import { ST25TN01K_AFH5 } from "./imports/ST25TN01K_AFH5"
import { arc, dashPolyline, roundedRectPolyline } from "./lib/artwork"
import { coilPath } from "./lib/coil"
import { estimateInductance } from "./lib/inductance"
import { CARD, COIL, LEAD_WIDTH, PARTS, TERMINAL } from "./lib/layout"
// Front print -- edit before ordering
const OWNER = "PIXALYNX"
const TAGLINE = "NFC CARD"
const coil = coilPath(COIL)
const outerEnd = coil[0]
const innerEnd = coil[coil.length - 1]
const coilMicrohenries = (estimateInductance(coil, COIL.width) * 1e6).toFixed(2)
// The coil is a DC short between its terminals. Core honours this for every
// component, but InductorProps does not declare it yet.
const coilTerminalsBonded = { internallyConnectedPins: [["pin1", "pin2"]] }
const coilCopperMargin = Math.max(COIL.width, TERMINAL.outerDiameter) / 2 + 0.25
const courtyard = [
{ x: COIL.left - coilCopperMargin, y: COIL.bottom - coilCopperMargin },
{ x: COIL.right + coilCopperMargin, y: COIL.bottom - coilCopperMargin },
{ x: COIL.right + coilCopperMargin, y: COIL.top + coilCopperMargin },
{ x: COIL.left - coilCopperMargin, y: COIL.top + coilCopperMargin },
{ x: COIL.left - coilCopperMargin, y: COIL.bottom - coilCopperMargin },
]
// Front artwork: dashed tap zone just outside the coil, contactless icon, owner block on the right
const zone = { cx: (COIL.left + COIL.right) / 2, w: COIL.right - COIL.left + 2, h: COIL.top - COIL.bottom + 2 }
const zoneDashes = dashPolyline(roundedRectPolyline(zone.cx, 0, zone.w, zone.h, 4), 1, 0.8)
const icon = { x: zone.cx - 3, y: 7 }
const iconArcs = [2.2, 3.9, 5.6].map((r) => arc(icon.x, icon.y, r, -48, 48))
const iconDot = arc(icon.x, icon.y, 0.3, 0, 360, 12)
const textX = 28
export default () => (
<board
title="pixal-nfc-card"
width={`${CARD.width}mm`}
height={`${CARD.height}mm`}
borderRadius={`${CARD.cornerRadius}mm`}
thickness={`${CARD.thickness}mm`}
layers={2}
solderMaskColor="black"
silkscreenColor="white"
>
<schematicsheet name="NFC" displayName="Passive NFC — revision B" sheetWidth={60} sheetHeight={40}>
<keepout shape="rect" pcbX={zone.cx} pcbY={0} width={49} height={47} layers={["top", "bottom"]} excludeRefs={[".L1"]} />
<ST25TN01K_AFH5 name="U1" pcbX={PARTS.U1.x} pcbY={PARTS.U1.y} pcbRotation={PARTS.U1.rot} schX={0} schY={0} />
{/*
The PCB antenna. Its copper lives in the footprint so the schematic shows a
plain inductor between the two antenna nets; the terminals are plated holes
that carry the coil (bottom) up to the leads (top).
*/}
<inductor
name="L1"
inductance={`${coilMicrohenries}uH`}
doNotPlace
{...coilTerminalsBonded}
pcbX={0}
pcbY={0}
schX={-3.2}
schY={-1.2}
schOrientation="horizontal"
footprint={
<footprint>
<platedhole
portHints={["pin1"]}
shape="circle"
outerDiameter={`${TERMINAL.outerDiameter}mm`}
holeDiameter={`${TERMINAL.holeDiameter}mm`}
pcbX={outerEnd.x}
pcbY={outerEnd.y}
/>
<platedhole
portHints={["pin2"]}
shape="circle"
outerDiameter={`${TERMINAL.outerDiameter}mm`}
holeDiameter={`${TERMINAL.holeDiameter}mm`}
pcbX={innerEnd.x}
pcbY={innerEnd.y}
/>
<pcbtrace
layer="bottom"
thickness={`${COIL.width}mm`}
route={coil.map((p) => ({ route_type: "wire", x: p.x, y: p.y, width: COIL.width, layer: "bottom" }))}
/>
<courtyardoutline outline={courtyard} />
</footprint>
}
/>
{/* Only the two differential feeds enter the antenna keepout. Release audits
enforce these exceptions geometrically, including turn-to-turn spacing. */}
<trace name="ANT_A" schDisplayLabel="ANT_A" from=".L1 > .pin2" to=".U1 > .AC0"
thickness={LEAD_WIDTH} pcbPath={[
{ x: 11.7, y: 3 }, { x: 11.7, y: 1.1 }, { x: 12.3, y: 0.5 },
]} />
<trace name="ANT_B" schDisplayLabel="ANT_B" from=".L1 > .pin1" to=".U1 > .AC1"
thickness={LEAD_WIDTH} pcbPath={[
{ x: 11.7, y: -3 }, { x: 11.7, y: -1.1 }, { x: 12.3, y: -0.5 },
]} />
{zoneDashes.map((route, i) => (
<Fragment key={`zone-${i}`}>
<silkscreenpath route={route} strokeWidth="0.3mm" />
</Fragment>
))}
{iconArcs.map((route, i) => (
<Fragment key={`icon-${i}`}>
<silkscreenpath route={route} strokeWidth="0.55mm" />
</Fragment>
))}
<silkscreenpath route={iconDot} strokeWidth="0.6mm" />
<silkscreentext text="TAP HERE" fontSize="3.2mm" anchorAlignment="center" pcbX={zone.cx} pcbY={-2.5} />
<silkscreentext text="HOLD PHONE HERE" fontSize="1.7mm" anchorAlignment="center" pcbX={zone.cx} pcbY={-6.5} />
<silkscreentext text={OWNER} fontSize="3.2mm" anchorAlignment="center" pcbX={textX} pcbY={15} />
<silkscreentext text={TAGLINE} fontSize="1.8mm" anchorAlignment="center" pcbX={textX} pcbY={10.5} />
<silkscreenpath route={[{ x: 17, y: 8 }, { x: 40, y: 8 }]} strokeWidth="0.2mm" />
<silkscreentext text="TAP TO OPEN" fontSize="1.7mm" anchorAlignment="center" pcbX={textX} pcbY={5} />
<silkscreentext text="MY PAGE" fontSize="1.7mm" anchorAlignment="center" pcbX={textX} pcbY={2} />
<silkscreentext text="U1" fontSize="1.7mm" anchorAlignment="center" pcbX={13.5} pcbY={-3.5} />
<silkscreentext text="13.56 MHz" fontSize="1.7mm" anchorAlignment="center" pcbX={textX} pcbY={-19} />
<silkscreentext text="REV B / NFC" fontSize="1.7mm" anchorAlignment="center" pcbX={textX} pcbY={-22} />
</schematicsheet>
</board>
)