diff --git a/docs/tutorials/draw-any-letter-with-leds.mdx b/docs/tutorials/draw-any-letter-with-leds.mdx new file mode 100644 index 0000000..ea5cadc --- /dev/null +++ b/docs/tutorials/draw-any-letter-with-leds.mdx @@ -0,0 +1,396 @@ +--- +title: Draw Any Letter with LEDs +description: >- + Learn how to use tscircuit and the @tscircuit/alphabet package to + automatically place 0402 LEDs along the outline of any capital letter, + producing a reusable LedLetter component. +--- + +## Overview + +In this tutorial you will build a reusable `` component that draws +any capital letter (A–Z) by placing 0402 LEDs along the letter's outline path. +The layout is fully automatic — you supply a letter and a size, and math does +the rest. + +import TscircuitIframe from "@site/src/components/TscircuitIframe" + +## Step 1: Sample points along an SVG path + +The `@tscircuit/alphabet` package exports `svgAlphabet`, a map from each +character to a normalized SVG path string (coordinates in `[0, 1]`). We need +to convert that path into a list of evenly-spaced `{x, y}` points so we know +where to place each LED. + + ( + + {pts.map((p, i) => ( + + ))} + +) +`} /> + +## Step 2: Add current-limiting resistors + +Each LED string needs a resistor (typically 68–100 Ω for a red 0402 LED at +5 V). We add one resistor per LED wired in series. + + { + const pts = samplePath(LETTER_A, 10) + const W = 20 // letter width in mm + const H = 25 // letter height in mm + + return ( + + {pts.map((p, i) => { + const px = (p.x - 0.5) * W + const py = (0.5 - p.y) * H + return ( + <> + + + + ) + })} + + ) +} +`} /> + +## Step 3: Build the reusable LedLetter component + +Now we wrap everything in a component that accepts any capital letter and a +scale factor. All 26 letters (A–Z) are supported using the path data from +`@tscircuit/alphabet`. + + { + const path = svgAlphabet[letter.toUpperCase()] + if (!path) return null + const pts = samplePath(path, nLeds) + + return ( + <> + {pts.map((p, i) => { + const px = offsetX + (p.x - 0.3) * width + const py = offsetY + (0.6 - p.y) * height + const ledName = namePrefix + letter + \`_LED\${i + 1}\` + const resName = namePrefix + letter + \`_R\${i + 1}\` + const midNet = \`net.\${ledName}_A\` + return ( + <> + + + + ) + })} + + ) +} + +export default () => ( + + + + + + + +) +`} /> + +## Step 4: Complete usage example + +Here is the complete `` component with all 26 letters supported, +ready to copy into your own project. + +```tsx +import { svgAlphabet } from "@tscircuit/alphabet" + +function samplePath(pathStr: string, n: number) { + const points: { x: number; y: number }[] = [] + const tokens = pathStr.trim().split(/\s+/) + let i = 0 + while (i < tokens.length) { + const cmd = tokens[i++] + if (cmd === "M" || cmd === "L") { + points.push({ x: parseFloat(tokens[i]), y: parseFloat(tokens[i + 1]) }) + i += 2 + } + } + if (points.length < 2) return points + const arcLen = [0] + for (let j = 1; j < points.length; j++) { + const dx = points[j].x - points[j - 1].x + const dy = points[j].y - points[j - 1].y + arcLen.push(arcLen[j - 1] + Math.sqrt(dx * dx + dy * dy)) + } + const total = arcLen.at(-1)! + const result: { x: number; y: number }[] = [] + for (let k = 0; k < n; k++) { + const target = (k / (n - 1)) * total + let seg = 0 + while (seg < arcLen.length - 1 && arcLen[seg + 1] < target) seg++ + const segLen = arcLen[seg + 1] - arcLen[seg] + const t = segLen === 0 ? 0 : (target - arcLen[seg]) / segLen + result.push({ + x: points[seg].x + t * (points[seg + 1].x - points[seg].x), + y: points[seg].y + t * (points[seg + 1].y - points[seg].y), + }) + } + return result +} + +interface LedLetterProps { + letter: string // Any capital A–Z letter + power: string // Net name for VCC (e.g. "net.PWR") + gnd: string // Net name for GND (e.g. "net.GND") + offsetX?: number // PCB X offset in mm + offsetY?: number // PCB Y offset in mm + width?: number // Letter width in mm (default 18) + height?: number // Letter height in mm (default 24) + nLeds?: number // Number of LEDs (default 14) + namePrefix?: string // Prefix to avoid name collisions +} + +export const LedLetter = ({ + letter, + power, + gnd, + offsetX = 0, + offsetY = 0, + width = 18, + height = 24, + nLeds = 14, + namePrefix = "", +}: LedLetterProps) => { + const path = svgAlphabet[letter.toUpperCase() as keyof typeof svgAlphabet] + if (!path) return null + const pts = samplePath(path, nLeds) + + return ( + <> + {pts.map((p, i) => { + const px = offsetX + (p.x - 0.3) * width + const py = offsetY + (0.6 - p.y) * height + const ledName = `${namePrefix}${letter}_LED${i + 1}` + const resName = `${namePrefix}${letter}_R${i + 1}` + const midNet = `net.${ledName}_A` + return ( + <> + + + + ) + })} + + ) +} +``` + +Usage: + +```tsx +export default () => ( + + + + +) +``` diff --git a/docs/tutorials/esp32-pcb-layout-and-routing.mdx b/docs/tutorials/esp32-pcb-layout-and-routing.mdx new file mode 100644 index 0000000..c578479 --- /dev/null +++ b/docs/tutorials/esp32-pcb-layout-and-routing.mdx @@ -0,0 +1,545 @@ +--- +title: ESP32 PCB Layout and Routing +description: Learn how to place and route an ESP32 module board in tscircuit — module placement, antenna keepout, decoupling capacitors, USB power, and autorouting. +--- + +## Overview + +This tutorial builds a complete ESP32 development board PCB step by step. You will +place the ESP32-WROOM-32 module, add power circuitry, wire up a USB-to-UART +bridge for programming, and let the autorouter connect everything. + +Good PCB layout for an ESP32 follows three placement rules: +1. The module antenna must hang over the board edge with no copper beneath it. +2. The 3.3 V supply path (USB connector → regulator → module) should be short. +3. Programming controls (EN reset, IO0 boot) live next to the module with their + pull-up resistors and pushbuttons nearby. + +import TscircuitIframe from "@site/src/components/TscircuitIframe" + +## Components + +| Ref | Part | Description | Footprint | +|-----|------|-------------|-----------| +| U1 | ESP32-WROOM-32 | Wi-Fi + BT module | stamp receiver 18 mm | +| U2 | AMS1117-3.3 | 3.3 V LDO regulator | SOT-223 | +| U3 | CH340C | USB-to-UART bridge | SOP-16 | +| J1 | USB-C receptacle | 5 V power + programming | SMD USB-C | +| SW1 | Pushbutton | EN (reset) | THT tactile | +| SW2 | Pushbutton | IO0 (boot mode) | THT tactile | +| R1 | 10 kΩ | EN pull-up | 0402 | +| R2 | 10 kΩ | IO0 pull-up | 0402 | +| R3, R4 | 10 kΩ | Auto-reset (EN + IO0 RC) | 0402 | +| C1 | 10 µF | VBUS bulk decoupling | 0805 | +| C2 | 10 µF | 3V3 bulk decoupling | 0805 | +| C3, C6 | 100 nF | Module / CH340C bypass | 0402 | + +## Step 1: Place the ESP32 Module + +The ESP32-WROOM-32 is a stamp-style module. In tscircuit it is modelled as a +`` with the `stampreceiver` footprint family. The footprint string encodes +the pad count and spacing: + +``` +stampreceiver_left14_right14_bottom10_top0_w18mm_p1.27mm +``` + +Place the module near the **top edge** of the board so the antenna side (the +rounded end of the WROOM module) points outward away from the rest of the +circuitry. + + ( + +) + +export default () => ( + + + +) +`} /> + +The `pcbY={8}` shifts the module toward the top of the 40 mm board, leaving +room below for power and programming components. + +## Step 2: Add USB Power and 3.3 V Regulation + +The ESP32 runs on 3.3 V but USB supplies 5 V. We need: +- A **USB-C connector** for power input +- An **AMS1117-3.3** LDO regulator (SOT-223 package) to step 5 V down to 3.3 V +- **Decoupling capacitors** on both sides of the regulator + +Place the USB connector at the left board edge and the regulator between it and +the module to keep the high-current 5 V path short. + + ( + +) + +export default () => ( + + + + {/* USB-C power input at the left board edge */} + + + {/* AMS1117-3.3 LDO: VIN=VBUS, VOUT=3V3 */} + + + {/* 10 uF input bulk cap (close to regulator VIN) */} + + + {/* 10 uF output bulk cap (close to regulator VOUT) */} + + + {/* 100 nF bypass cap right at the ESP32 3V3 pin */} + + + {/* Power rails to ESP32 */} + + + + + + +) +`} /> + +The regulator at `pcbX={-10}` sits between the USB connector (`pcbX={-22}`) and +the module (`pcbX={0}`), creating a straight left-to-right power flow that is +easy to review. + +## Step 3: Add the USB-to-UART Bridge + +To program the ESP32 over USB you need a serial bridge chip. The **CH340G** +(SOP-16) converts USB signals to the UART RX/TX lines the ESP32 bootloader uses. + +The bridge also drives the **DTR** and **RTS** hardware flow-control lines. +Those connect to the ESP32's EN and IO0 pins through 100 nF capacitors to +implement the automatic reset-into-bootloader sequence that the Arduino IDE and +`esptool` rely on. + + ( + +) + +export default () => ( + + + + + + + + {/* CH340G USB-UART bridge */} + + + {/* UART cross-connect: CH340 TX→ESP RX, CH340 RX←ESP TX */} + + + + {/* Auto-reset: DTR/RTS → 100nF caps → EN/IO0 */} + + + + + + {/* USB D+/D- to CH340 */} + + + + {/* Decoupling on CH340 VCC */} + + + + + + + + + + + + +) +`} /> + +The auto-reset capacitors on C4 and C5 implement the standard ESP32 reset +circuit: when a host asserts DTR and then RTS (or vice versa), the resulting +pulse drives EN low followed by IO0 low, putting the chip into download mode +without you pressing any buttons. + +## Step 4: EN Reset and BOOT Buttons + +Even with automatic reset, physical buttons are important for manual recovery. +Place the **EN** (reset) button and the **IO0** (boot mode) button near the +right edge of the board with their 10 kΩ pull-up resistors right beside them. + + ( + +) + +export default () => ( + + + + + + + + + + {/* 10k pull-up on EN */} + + + {/* EN reset button */} + + + {/* 10k pull-up on IO0 */} + + + {/* BOOT button */} + + + + + + + + + + + + + + + + + + + + + + +) +`} /> + +Pressing SW1 (EN) resets the ESP32. Pressing SW2 (IO0) while holding SW1 puts +it into download mode so you can flash firmware manually without needing the +auto-reset circuit. + +## Step 5: Autorouting + +Once component placement is done, tscircuit can route all the traces automatically. +Add `autorouter="auto-cloud"` to the `` element to use the cloud autorouter, +or `autorouter="sequential-trace"` for the local solver: + +```tsx + + ... + +``` + +The autorouter reads every `connections` prop and every `` you added, +so there is nothing extra to configure. After routing you can switch to the PCB +tab in the preview to inspect trace widths and clearances. + +### Placement tips that help the autorouter + +- **Short nets first**: the regulator-to-module 3V3 path and the UART TX/RX + crossover are the nets most affected by placement order. +- **Avoid crossing power nets**: keep VBUS traces on the left side of the board + and 3V3 traces in the middle so they do not create unnecessary crossings that + require vias. +- **Put bypass caps as close as possible**: C3 (module bypass) and C6 (CH340 + bypass) should be within 2–3 mm of their respective IC's power pin to minimize + the inductance the router must deal with. + +## Cost Estimate + +| Component | Unit Cost | Qty | Total | +|-----------|-----------|-----|-------| +| ESP32-WROOM-32 | $2.20 | 1 | $2.20 | +| AMS1117-3.3 (SOT-223) | $0.10 | 1 | $0.10 | +| CH340C (SOP-16) | $0.25 | 1 | $0.25 | +| USB-C receptacle | $0.15 | 1 | $0.15 | +| Tactile pushbuttons | $0.05 | 2 | $0.10 | +| 10 kΩ resistors (0402) | $0.01 | 4 | $0.04 | +| 10 µF capacitors (0805) | $0.05 | 2 | $0.10 | +| 100 nF capacitors (0402) | $0.02 | 2 | $0.04 | +| PCB (2-layer, 5 pcs) | $0.50 | 1 | $0.50 | +| **Total** | | | **~$3.48** | + +*Prices based on LCSC/JLCPCB quantities of 10.* + +## Pre-fabrication checklist + +Before exporting Gerbers: + +- [ ] The antenna end of the ESP32 module hangs over the board edge, or at least + no copper, vias, or components appear within 3 mm of the antenna window. +- [ ] C1 is within 5 mm of the USB connector VBUS pin. +- [ ] C3 is within 3 mm of U1 pin 2 (3V3). +- [ ] C6 is within 3 mm of U3 pin 16 (VCC). +- [ ] All GND pins on U1 (pins 1, 15, 37, 38) connect to the same GND net. +- [ ] The UART crossover is correct: U3 TXD → U1 RXD0, U3 RXD ← U1 TXD0. +- [ ] Both pushbuttons are reachable from the board edge.