imrishabh18/desk-led-matrix
This code models a hardware setup comprising a WLED-compatible LED cascade array connected to a XIAO ESP32-C3 microcontroller, including power regulation components and detailed PCB layout with traces, pads, and silkscreen markings.
- Version
- 1.0.4
- License
- unset
- Stars
- 0
index.circuit.tsx
PCB
Schematic
import { Fragment } from "react"
import { WLED } from "./imports/WLED"
const ROWS = 8
const COLS = 8
const LED_COUNT = ROWS * COLS
const LED_PITCH_MM = 8
const MATRIX_WIDTH_MM = (COLS - 1) * LED_PITCH_MM
const MATRIX_HEIGHT_MM = (ROWS - 1) * LED_PITCH_MM
const BOARD_SIZE_MM = 78
const MATRIX_CENTER_Y_MM = 0
const XIAO_CENTER_X_MM = 30
const XIAO_CENTER_Y_MM = 0
const xiaoBottomFootprint = (
<footprint>
{Array.from({ length: 7 }, (_, i) => (
<Fragment key={`xiao-left-${i + 1}`}>
<smtpad
portHints={[`pin${i + 1}`]}
layer="bottom"
shape="rect"
pcbX="-7.55mm"
pcbY={`${7.62 - i * 2.54}mm`}
width="2.4mm"
height="1.6mm"
/>
</Fragment>
))}
{Array.from({ length: 7 }, (_, i) => (
<Fragment key={`xiao-right-${i + 8}`}>
<smtpad
portHints={[`pin${i + 8}`]}
layer="bottom"
shape="rect"
pcbX="7.55mm"
pcbY={`${-7.62 + i * 2.54}mm`}
width="2.4mm"
height="1.6mm"
/>
</Fragment>
))}
</footprint>
)
const dataOrder = Array.from({ length: LED_COUNT }, (_, i) => i + 1)
const pixelPlacements = Array.from({ length: LED_COUNT }, (_, physicalIndex) => {
const row = Math.floor(physicalIndex / COLS)
const col = physicalIndex % COLS
const serpentineIndex =
row % 2 === 0 ? row * COLS + col : row * COLS + (COLS - 1 - col)
return {
name: `D${serpentineIndex + 1}`,
col,
row,
x: (col - (COLS - 1) / 2) * LED_PITCH_MM,
y: (row - (ROWS - 1) / 2) * LED_PITCH_MM + MATRIX_CENTER_Y_MM,
rotation: row % 2 === 0 ? 0 : 180,
}
})
export default function DeskLedMatrix() {
return (
<board
title="Desk WLED Pixel Art Matrix"
width={`${BOARD_SIZE_MM}mm`}
height={`${BOARD_SIZE_MM}mm`}
borderRadius="2mm"
solderMaskColor="black"
silkscreenColor="white"
>
<net
name="V5"
isPowerNet
nominalTraceWidth="0.45mm"
highlightColor="#ff4a36"
/>
<net
name="GND"
isGroundNet
nominalTraceWidth="0.45mm"
highlightColor="#4aa3ff"
/>
<net
name="DATA_IN"
nominalTraceWidth="0.2mm"
highlightColor="#ffd166"
/>
<chip
name="U1"
manufacturerPartNumber="Seeed Studio XIAO ESP32-C3"
footprint={xiaoBottomFootprint}
pcbX={XIAO_CENTER_X_MM}
pcbY={XIAO_CENTER_Y_MM}
pinLabels={{
1: "D0_GPIO2",
2: "D1_GPIO3",
3: "D2_GPIO4",
4: "D3_GPIO5",
5: "D4_GPIO6",
6: "D5_GPIO7",
7: "D6_GPIO21",
8: "D7_GPIO20",
9: "D8_GPIO8",
10: "D9_GPIO9",
11: "D10_GPIO10",
12: "3V3",
13: "GND",
14: "5V",
}}
pcbPinLabels={{
pin1: "D0",
pin2: "D1",
pin3: "D2",
pin4: "D3",
pin5: "D4",
pin6: "D5",
pin7: "D6",
pin8: "D7",
pin9: "D8",
pin10: "D9",
pin11: "D10",
pin12: "3V3",
pin13: "GND",
pin14: "5V",
}}
pcbRotation={90}
pinAttributes={{
D4_GPIO6: { isGpio: true },
GND: { requiresGround: true },
"5V": { requiresPower: true, requiresVoltage: "5V" },
"3V3": { providesPower: true, providesVoltage: "3.3V" },
}}
/>
<resistor
name="RDATA"
resistance="330"
footprint="0603"
layer="bottom"
pcbX={-18}
pcbY={-BOARD_SIZE_MM / 2 + 12}
schOrientation="horizontal"
/>
<capacitor
name="CBULK"
capacitance="1000uF"
polarized
footprint="1206"
layer="bottom"
pcbX={18}
pcbY={-BOARD_SIZE_MM / 2 + 12}
schOrientation="vertical"
/>
<trace from=".U1 > .pin14" to="net.V5" width="0.5mm" />
<trace from=".U1 > .pin13" to="net.GND" width="0.5mm" />
<trace from=".U1 > .pin5" to=".RDATA > .pin1" width="0.2mm" />
<trace from=".RDATA > .pin2" to=".D1 > .DI" width="0.2mm" />
<trace from=".CBULK > .pos" to="net.V5" width="0.7mm" />
<trace from=".CBULK > .neg" to="net.GND" width="0.7mm" />
{pixelPlacements.map((pixel) => (
<Fragment key={pixel.name}>
<WLED
name={pixel.name}
pcbX={pixel.x}
pcbY={pixel.y}
pcbRotation={pixel.rotation}
pcbPinLabels={{
pin1: "VDD",
pin2: "DO",
pin3: "GND",
pin4: "DI",
}}
pinAttributes={{
VDD: { requiresPower: true, requiresVoltage: "5V" },
pin1: { requiresPower: true, requiresVoltage: "5V" },
pin2: { isGpio: true },
GND: { requiresGround: true },
pin3: { requiresGround: true },
pin4: { isGpio: true },
DI: { isGpio: true },
DO: { isGpio: true },
}}
/>
</Fragment>
))}
{dataOrder.slice(0, -1).map((ledNumber) => (
<Fragment key={`data-${ledNumber}`}>
<trace
from={`.D${ledNumber} > .DO`}
to={`.D${ledNumber + 1} > .DI`}
width="0.18mm"
/>
</Fragment>
))}
{dataOrder.map((ledNumber) => (
<Fragment key={`v5-${ledNumber}`}>
<trace
from={`.D${ledNumber} > .VDD`}
to="net.V5"
width="0.35mm"
/>
</Fragment>
))}
{dataOrder.map((ledNumber) => (
<Fragment key={`gnd-${ledNumber}`}>
<trace
from={`.D${ledNumber} > .GND`}
to="net.GND"
width="0.35mm"
/>
</Fragment>
))}
{Array.from({ length: ROWS }, (_, row) => (
<Fragment key={`crow-${row + 1}`}>
<capacitor
name={`CROW${row + 1}`}
capacitance="100nF"
footprint="0402"
layer="bottom"
pcbX={-BOARD_SIZE_MM / 2 + 4}
pcbY={(row - (ROWS - 1) / 2) * LED_PITCH_MM + MATRIX_CENTER_Y_MM}
connections={{
pin1: "net.V5",
pin2: "net.GND",
}}
/>
</Fragment>
))}
<silkscreenrect
pcbX={0}
pcbY={MATRIX_CENTER_Y_MM}
width={`${MATRIX_WIDTH_MM + 5}mm`}
height={`${MATRIX_HEIGHT_MM + 5}mm`}
filled={false}
stroke="solid"
strokeWidth="0.18mm"
/>
{pixelPlacements.map((pixel) => (
<Fragment key={`pixel-frame-${pixel.name}`}>
<silkscreenrect
pcbX={pixel.x}
pcbY={pixel.y}
width="5.4mm"
height="5.4mm"
filled={false}
stroke="solid"
strokeWidth="0.08mm"
/>
</Fragment>
))}
<silkscreentext
text="XIAO WLED 8x8 SERPENTINE"
pcbX={0}
pcbY={BOARD_SIZE_MM / 2 - 2.2}
fontSize="1.2mm"
/>
<silkscreenrect
layer="bottom"
pcbX={XIAO_CENTER_X_MM}
pcbY={XIAO_CENTER_Y_MM}
width="17.5mm"
height="21mm"
filled={false}
stroke="dashed"
strokeWidth="0.12mm"
/>
<silkscreentext
layer="bottom"
text="XIAO ESP32-C3"
pcbX={XIAO_CENTER_X_MM}
pcbY={XIAO_CENTER_Y_MM + 11.5}
fontSize="1mm"
/>
<silkscreentext
layer="bottom"
text="USB"
pcbX={XIAO_CENTER_X_MM}
pcbY={XIAO_CENTER_Y_MM - 11.5}
fontSize="0.9mm"
/>
<silkscreentext
text="D1"
pcbX={-MATRIX_WIDTH_MM / 2}
pcbY={MATRIX_CENTER_Y_MM - MATRIX_HEIGHT_MM / 2 - 1}
fontSize="0.9mm"
/>
</board>
)
}