olivergrabinski/tscircuit-weather

A 60×44 mm ESP32-S3 Wi‑Fi/BLE weather-station PCB with USB‑C power, 3.3 V regulation and ESD protection, temperature/humidity and UV/light sensors, status LEDs, buttons, Qwiic I²C expansion, debug GPIO header, and a printable enclosure.

Version
1.2.0
License
unset
Stars
0

index.circuit.tsx

PCBPCB preview for index.circuit.tsx
SchematicSchematic preview for index.circuit.tsx
import { ESP32_S3_WROOM_1_N8R2 } from "./imports/ESP32_S3_WROOM_1_N8R2"
import { SHT40_AD1B_R3 } from "./imports/SHT40_AD1B_R3"
import { LTR_390UV_01 } from "./imports/LTR_390UV_01"
import { AMS1117_3_3 } from "./imports/AMS1117_3_3"
import { USBLC6_2SC6 } from "./imports/USBLC6_2SC6"
import { TS_1187A_B_A_B } from "./imports/TS_1187A_B_A_B"
import { KT_0603G } from "./imports/KT_0603G"
import { KT_0603B } from "./imports/KT_0603B"
import { JST_SH_SM04B_SRSS_TB } from "./imports/JST_SH_SM04B_SRSS_TB"
import { assembly, enclosure } from "tscircuit"

/**
 * Wi-Fi weather station
 *
 *  - ESP32-S3-WROOM-1 module: Wi-Fi + BLE, native USB (no UART bridge needed)
 *  - SHT40-AD1B: temperature + relative humidity, I2C 0x44
 *  - LTR-390UV-01: UV index + ambient light, I2C 0x53, INT on IO10
 *  - USB-C power/programming, AMS1117-3.3 LDO, USBLC6-2SC6 ESD protection
 *  - Qwiic (JST-SH 1.0 mm) I2C expansion + 8-pin debug/GPIO header
 *
 * Rev 1.1 (2026-09-24)
 *  - SHT40 sits on a thermal-isolation island: three 1.2 mm milled slots
 *    leave a 3.2 mm bridge at the bottom for the four traces, so heat from the
 *    LDO/module reaches the sensor only through that bridge.
 *  - <enclosure.fdm.box> models a printable case: window above the UV sensor,
 *    vent above the SHT40 island, holes for USB-C, Qwiic and both buttons.
 *
 * Fab rules: 0.25 mm signal traces (0.15 mm min), 0.5 mm 3V3/VBUS and 0.4 mm
 * GND traces, vias 0.3 mm drill / 0.6 mm pad,
 * i.e. within JLCPCB's standard 2-layer capability.
 *
 * ESP32-S3 pin plan
 *  IO8  SDA        IO9  SCL        IO10 UV_INT
 *  IO19 USB D-     IO20 USB D+     IO21 status LED
 *  IO0  BOOT       EN   RESET      IO4..IO7 spare ADC-capable GPIO on header
 *  TXD0/RXD0 UART0 on header
 */
/**
 * USB-C receptacle land pattern for HRO TYPE-C-31-M-12 (JLCPCB C165948),
 * copied from the exact EasyEDA footprint with its original pin numbering
 * (1-4 shell anchors, 5-12 signals, 13/14 GND, 15/16 VBUS) so the pick-and-
 * place rotation can be verified against the supplier part and the 3D model
 * lines up. Each shell anchor is its own pin, so every pad resolves to
 * exactly one port. The cable enters from -Y in this unrotated frame.
 */
const UsbC16Footprint = (
  <footprint insertionDirection="from_bottom">
    <platedhole portHints={["pin1"]} shape="pill" pcbX={4.325} pcbY={1.405} outerWidth={1.2} outerHeight={2.0} holeWidth={0.8} holeHeight={1.6} />
    <platedhole portHints={["pin2"]} shape="pill" pcbX={4.325} pcbY={-2.774} outerWidth={1.2} outerHeight={1.8} holeWidth={0.8} holeHeight={1.4} />
    <platedhole portHints={["pin3"]} shape="pill" pcbX={-4.325} pcbY={-2.774} outerWidth={1.2} outerHeight={1.8} holeWidth={0.8} holeHeight={1.4} />
    <platedhole portHints={["pin4"]} shape="pill" pcbX={-4.325} pcbY={1.405} outerWidth={1.2} outerHeight={2.0} holeWidth={0.8} holeHeight={1.6} />
    <smtpad portHints={["pin5"]} shape="rect" pcbX={-1.75} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin6"]} shape="rect" pcbX={-1.25} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin7"]} shape="rect" pcbX={-0.75} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin8"]} shape="rect" pcbX={-0.25} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin9"]} shape="rect" pcbX={0.25} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin10"]} shape="rect" pcbX={0.75} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin11"]} shape="rect" pcbX={1.25} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin12"]} shape="rect" pcbX={1.75} pcbY={2.174} width={0.3} height={1.3} />
    <smtpad portHints={["pin13"]} shape="rect" pcbX={-3.2} pcbY={2.174} width={0.6} height={1.3} />
    <smtpad portHints={["pin14"]} shape="rect" pcbX={3.2} pcbY={2.174} width={0.6} height={1.3} />
    <smtpad portHints={["pin15"]} shape="rect" pcbX={2.4} pcbY={2.174} width={0.6} height={1.3} />
    <smtpad portHints={["pin16"]} shape="rect" pcbX={-2.4} pcbY={2.174} width={0.6} height={1.3} />
    {/* two locating pegs of the receptacle */}
    <hole pcbX={-2.9} pcbY={0.905} diameter={0.6} />
    <hole pcbX={2.9} pcbY={0.905} diameter={0.6} />
    <courtyardrect pcbX={0} pcbY={-1.29} width={10.35} height={8.72} />
    <silkscreenpath route={[{ x: -4.46, y: -1.67 }, { x: -4.46, y: 0.18 }]} />
    <silkscreenpath route={[{ x: 4.47, y: -1.67 }, { x: 4.47, y: 0.18 }]} />
    <silkscreenpath route={[{ x: -4.46, y: -3.91 }, { x: -4.46, y: -5.39 }, { x: 4.47, y: -5.39 }, { x: 4.47, y: -3.91 }]} />
  </footprint>
)

/** 3D model of the receptacle, as published for JLCPCB C165948 */
const usbCCadModel = {
  objUrl: "https://modelcdn.tscircuit.com/easyeda_models/assets/C165948.obj?uuid=617b05f9bba7410b96c001093d8189e4",
  stepUrl: "https://modelcdn.tscircuit.com/easyeda_models/assets/C165948.step?uuid=617b05f9bba7410b96c001093d8189e4",
  pcbRotationOffset: 180,
  modelOriginPosition: { x: 0, y: -2.75, z: 0 },
}

const usbCPinLabels = {
  pin1: ["SHELL1", "EH2"],
  pin2: ["SHELL2", "EH1"],
  pin3: ["SHELL3", "EH4"],
  pin4: ["SHELL4", "EH3"],
  pin5: ["SBU2", "B8"],
  pin6: ["CC1", "A5"],
  pin7: ["DM2", "DN2", "B7"],
  pin8: ["DP1", "A6"],
  pin9: ["DM1", "DN1", "A7"],
  pin10: ["DP2", "B6"],
  pin11: ["SBU1", "A8"],
  pin12: ["CC2", "B5"],
  pin13: ["GND1", "A1B12"],
  pin14: ["GND2", "B1A12"],
  pin15: ["VBUS1", "B4A9"],
  pin16: ["VBUS2", "A4B9"],
} as const

export default () => (
  <assembly.device name="weather_station">
  <board
    name="B1"
    width="60mm"
    height="44mm"
    layers={2}
    defaultTraceWidth="0.25mm"
    minTraceWidth="0.15mm"
    minViaHoleDiameter="0.3mm"
    minViaPadDiameter="0.6mm"
  >
    <schematicsection name="power" displayName="Power: USB-C in, 3.3 V LDO" />
    <schematicsection name="mcu" displayName="MCU: ESP32-S3 (Wi-Fi, native USB)" />
    <schematicsection name="sensors" displayName="Sensors: SHT40 (T/RH) + LTR-390UV (UV)" />
    <schematicsection name="io" displayName="Expansion: Qwiic + debug header" />

    {/* power nets get wider copper: Wi-Fi TX bursts pull ~350 mA peaks */}
    <net name="GND" isGroundNet nominalTraceWidth="0.4mm" />
    <net name="V3_3" isPowerNet nominalTraceWidth="0.5mm" />
    <net name="VBUS" isPowerNet nominalTraceWidth="0.5mm" />

    {/* ---------------- mechanical ---------------- */}
    <hole diameter="3.2mm" pcbX={-27} pcbY={19} />
    <hole diameter="3.2mm" pcbX={27} pcbY={19} />
    <hole diameter="3.2mm" pcbX={-27} pcbY={-19} />
    <hole diameter="3.2mm" pcbX={27} pcbY={-19} />
    {/* no copper under the module's PCB antenna (top 6 mm of the module) */}
    <keepout
      shape="rect"
      pcbX={-14}
      pcbY={19}
      width="18mm"
      height="6mm"
      excludeRefs={[".U1"]}
    />
    <silkscreentext text="WEATHER STATION v1.1" pcbX={12} pcbY={5} fontSize="1.2mm" />

    {/* keep vias/traces 0.3 mm away from the USB-C locating pegs (NPTH) */}
    <keepout shape="circle" pcbX={-16.9} pcbY={-15.795} radius="0.6mm" excludeRefs={[".J1"]} />
    <keepout shape="circle" pcbX={-11.1} pcbY={-15.795} radius="0.6mm" excludeRefs={[".J1"]} />

    {/* SHT40 thermal-isolation island: slots left/right run out through the
        top edge, the bottom slot is split to leave a 3.2 mm trace bridge */}
    <cutout shape="rect" pcbX={15.9} pcbY={18.5} width="1.2mm" height="8mm" />
    <cutout shape="rect" pcbX={22.6} pcbY={18.5} width="1.2mm" height="8mm" />
    <cutout shape="rect" pcbX={16.45} pcbY={13.9} width="2.3mm" height="1.2mm" />
    <cutout shape="rect" pcbX={22} pcbY={13.9} width="2.4mm" height="1.2mm" />
    <silkscreentext text="ANTENNA KEEP-OUT" pcbX={-14} pcbY={19} fontSize="0.8mm" />

    {/* ---------------- power ---------------- */}
    <connector
      name="J1"
      standard="usb_c"
      manufacturerPartNumber="TYPE-C-31-M-12"
      supplierPartNumbers={{ jlcpcb: ["C165948"] }}
      pinLabels={usbCPinLabels}
      footprint={UsbC16Footprint}
      cadModel={usbCCadModel}
      schSectionName="power"
      pcbX={-14}
      pcbY={-16.7}
      schX={-15}
      schY={0}
      connections={{
        VBUS1: "net.VBUS",
        VBUS2: "net.VBUS",
        GND1: "net.GND",
        GND2: "net.GND",
        SHELL1: "net.GND",
        SHELL2: "net.GND",
        SHELL3: "net.GND",
        SHELL4: "net.GND",
        CC1: "net.CC1",
        CC2: "net.CC2",
        DP1: "net.USB_DP",
        DP2: "net.USB_DP",
        DM1: "net.USB_DM",
        DM2: "net.USB_DM",
      }}
    >
      <enclosure.cutoutaperture shape="pill" width="10mm" height="4mm" margin="0.5mm" />
    </connector>
    {/* 5.1k CC pull-downs advertise a USB-C sink (UFP) */}
    <resistor
      name="R1"
      resistance="5.1k"
      footprint="0603"
      schSectionName="power"
      pcbX={-21}
      pcbY={-15}
      pcbRotation={90}
      schX={-13}
      schY={-2.5}
      schRotation={90}
      connections={{ pin1: "net.CC1", pin2: "net.GND" }}
    />
    <resistor
      name="R2"
      resistance="5.1k"
      footprint="0603"
      schSectionName="power"
      pcbX={-7}
      pcbY={-13}
      pcbRotation={90}
      schX={-11.5}
      schY={-2.5}
      schRotation={90}
      connections={{ pin1: "net.CC2", pin2: "net.GND" }}
    />
    {/* USB ESD protection: I/O1 = pins 1+6, I/O2 = pins 3+4 */}
    <USBLC6_2SC6
      name="U5"
      schSectionName="power"
      pcbX={-14}
      pcbY={-9.5}
      schX={-11}
      schY={1.5}
      connections={{
        pin1: "net.USB_DM",
        pin6: "net.USB_DM",
        pin3: "net.USB_DP",
        pin4: "net.USB_DP",
        GND: "net.GND",
        VBUS: "net.VBUS",
      }}
    />
    <AMS1117_3_3
      name="U2"
      schSectionName="power"
      pcbX={-1}
      pcbY={-15}
      schX={-8}
      schY={0}
      connections={{
        VIN: "net.VBUS",
        GND: "net.GND",
        VOUT1: "net.V3_3",
        VOUT2: "net.V3_3",
      }}
    />
    <capacitor
      name="C1"
      capacitance="10uF"
      footprint="0805"
      schSectionName="power"
      pcbX={-1}
      pcbY={-20}
      schX={-9.5}
      schY={-2}
      schRotation={90}
      connections={{ pin1: "net.VBUS", pin2: "net.GND" }}
    />
    <capacitor
      name="C2"
      capacitance="22uF"
      footprint="0805"
      schSectionName="power"
      pcbX={5.5}
      pcbY={-15}
      pcbRotation={90}
      schX={-7}
      schY={-2}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.GND" }}
    />
    <capacitor
      name="C3"
      capacitance="100nF"
      footprint="0603"
      schSectionName="power"
      pcbX={5.5}
      pcbY={-11.5}
      pcbRotation={90}
      schX={-5.8}
      schY={-2}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.GND" }}
    />
    {/* power indicator */}
    <resistor
      name="R9"
      resistance="1k"
      footprint="0603"
      schSectionName="power"
      pcbX={9.5}
      pcbY={-10.5}
      schX={-5.8}
      schY={1.2}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.PWR_LED" }}
    />
    <KT_0603G
      name="D1"
      color="green"
      schSectionName="power"
      pcbX={9.5}
      pcbY={-13}
      schX={-5.8}
      schY={-0.3}
      schRotation={-90}
      connections={{ anode: "net.PWR_LED", cathode: "net.GND" }}
    />

    {/* ---------------- MCU ---------------- */}
    <ESP32_S3_WROOM_1_N8R2
      name="U1"
      schSectionName="mcu"
      pcbX={-14}
      pcbY={5.5}
      schX={5.5}
      schY={0}
      schPinArrangement={{
        leftSide: {
          direction: "top-to-bottom",
          pins: [
            "3V3", "EN", "IO0", "IO19", "IO20", "IO8", "IO9", "IO10", "IO21",
            "TXD0", "RXD0", "IO4", "IO5", "IO6", "IO7",
          ],
        },
        rightSide: {
          direction: "top-to-bottom",
          pins: [
            "IO1", "IO2", "IO3", "IO11", "IO12", "IO13", "IO14", "IO15", "IO16",
            "IO17", "IO18", "IO35", "IO36", "IO37", "IO38", "IO39", "IO40",
            "IO41", "IO42", "IO45", "IO46", "IO47", "IO48",
          ],
        },
        bottomSide: {
          direction: "left-to-right",
          pins: ["GND1", "GND2", "GND3"],
        },
      }}
      connections={{
        GND1: "net.GND",
        GND2: "net.GND",
        GND3: "net.GND",
        "3V3": "net.V3_3",
        EN: "net.EN",
        IO0: "net.BOOT",
        IO19: "net.USB_DM",
        IO20: "net.USB_DP",
        IO8: "net.SDA",
        IO9: "net.SCL",
        IO10: "net.UV_INT",
        IO21: "net.LED",
        TXD0: "net.TXD0",
        RXD0: "net.RXD0",
        IO4: "net.IO4",
        IO5: "net.IO5",
        IO6: "net.IO6",
        IO7: "net.IO7",
      }}
    />
    {/* module decoupling, next to pin 2 (3V3): 22uF bulk + 10uF + 100nF */}
    <capacitor
      name="C9"
      capacitance="22uF"
      footprint="0805"
      schSectionName="mcu"
      pcbX={-26}
      pcbY={14.5}
      pcbRotation={90}
      schX={6}
      schY={4.2}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.GND" }}
    />
    <capacitor
      name="C4"
      capacitance="10uF"
      footprint="0805"
      schSectionName="mcu"
      pcbX={-26}
      pcbY={11}
      pcbRotation={90}
      schX={3.5}
      schY={4.2}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.GND" }}
    />
    <capacitor
      name="C5"
      capacitance="100nF"
      footprint="0603"
      schSectionName="mcu"
      pcbX={-26}
      pcbY={7.5}
      pcbRotation={90}
      schX={4.7}
      schY={4.2}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.GND" }}
    />
    {/* EN: 10k pull-up + 1uF gives a clean power-on reset delay */}
    <resistor
      name="R3"
      resistance="10k"
      footprint="0603"
      schSectionName="mcu"
      pcbX={-26}
      pcbY={4.0}
      pcbRotation={90}
      schX={1}
      schY={2.5}
      schRotation={90}
      connections={{ pin1: "net.EN", pin2: "net.V3_3" }}
    />
    <capacitor
      name="C6"
      capacitance="1uF"
      footprint="0603"
      schSectionName="mcu"
      pcbX={-26}
      pcbY={0.5}
      pcbRotation={90}
      schX={1}
      schY={0.8}
      schRotation={90}
      connections={{ pin1: "net.EN", pin2: "net.GND" }}
    />
    <TS_1187A_B_A_B
      name="SW1"
      schSectionName="mcu"
      pcbX={-25}
      pcbY={-9}
      pcbRotation={90}
      schX={-1}
      schY={1.7}
      connections={{ A: "net.EN", D: "net.GND" }}
    >
      <enclosure.cutoutaperture shape="circle" radius="2mm" />
    </TS_1187A_B_A_B>
    {/* BOOT: hold while resetting to enter the USB download mode */}
    <resistor
      name="R4"
      resistance="10k"
      footprint="0603"
      schSectionName="mcu"
      pcbX={4.5}
      pcbY={4.5}
      pcbRotation={90}
      schX={1}
      schY={-1.2}
      schRotation={90}
      connections={{ pin1: "net.BOOT", pin2: "net.V3_3" }}
    />
    <TS_1187A_B_A_B
      name="SW2"
      schSectionName="mcu"
      pcbX={2}
      pcbY={-3}
      pcbRotation={90}
      schX={-1}
      schY={-2.2}
      connections={{ A: "net.BOOT", D: "net.GND" }}
    >
      <enclosure.cutoutaperture shape="circle" radius="2mm" />
    </TS_1187A_B_A_B>
    {/* status LED on IO21 */}
    <resistor
      name="R8"
      resistance="1k"
      footprint="0603"
      schSectionName="mcu"
      pcbX={-9.5}
      pcbY={-7}
      schX={1}
      schY={-3.5}
      connections={{ pin1: "net.LED", pin2: "net.STATUS_LED" }}
    />
    <KT_0603B
      name="D2"
      color="blue"
      schSectionName="mcu"
      pcbX={-9.5}
      pcbY={-9}
      schX={2.7}
      schY={-3.5}
      connections={{ anode: "net.STATUS_LED", cathode: "net.GND" }}
    />

    {/* ---------------- sensors ---------------- */}
    <SHT40_AD1B_R3
      name="U3"
      schSectionName="sensors"
      pcbX={19.25}
      pcbY={19}
      schX={15.5}
      schY={3.5}
      noConnect={["EP"]}
      connections={{
        SDA: "net.SDA",
        SCL: "net.SCL",
        VDD: "net.V3_3",
        VSS: "net.GND",
      }}
    >
      {/* vent so outside air reaches the humidity sensor */}
      <enclosure.cutoutaperture shape="rect" width="5mm" height="7mm" />
    </SHT40_AD1B_R3>
    <capacitor
      name="C7"
      capacitance="100nF"
      footprint="0603"
      schSectionName="sensors"
      pcbX={19.25}
      pcbY={16}
      schX={18}
      schY={3.5}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.GND" }}
    />
    <LTR_390UV_01
      name="U4"
      schSectionName="sensors"
      pcbX={10}
      pcbY={18.5}
      schX={15.5}
      schY={0}
      noConnect={["NC"]}
      connections={{
        VDD: "net.V3_3",
        GND: "net.GND",
        SCL: "net.SCL",
        SDA: "net.SDA",
        INT: "net.UV_INT",
      }}
    >
      {/* UV window: cover with PTFE film or quartz, not acrylic */}
      <enclosure.cutoutaperture shape="circle" radius="3mm" />
    </LTR_390UV_01>
    <capacitor
      name="C8"
      capacitance="100nF"
      footprint="0603"
      schSectionName="sensors"
      pcbX={10}
      pcbY={15}
      schX={18}
      schY={0}
      schRotation={90}
      connections={{ pin1: "net.V3_3", pin2: "net.GND" }}
    />
    {/* I2C pull-ups (one pair for the whole bus incl. Qwiic) + INT pull-up */}
    <resistor
      name="R5"
      resistance="4.7k"
      footprint="0603"
      schSectionName="sensors"
      pcbX={4}
      pcbY={18}
      schX={11.5}
      schY={3.5}
      schRotation={90}
      connections={{ pin1: "net.SDA", pin2: "net.V3_3" }}
    />
    <resistor
      name="R6"
      resistance="4.7k"
      footprint="0603"
      schSectionName="sensors"
      pcbX={4}
      pcbY={15}
      schX={13}
      schY={3.5}
      schRotation={90}
      connections={{ pin1: "net.SCL", pin2: "net.V3_3" }}
    />
    <resistor
      name="R7"
      resistance="10k"
      footprint="0603"
      schSectionName="sensors"
      pcbX={4}
      pcbY={12}
      schX={13}
      schY={0}
      schRotation={90}
      connections={{ pin1: "net.UV_INT", pin2: "net.V3_3" }}
    />

    {/* ---------------- expansion ---------------- */}
    {/* Qwiic / STEMMA QT: GND, 3V3, SDA, SCL on a JST-SH 1.0 mm 4-pin */}
    <JST_SH_SM04B_SRSS_TB
      name="J2"
      schSectionName="io"
      pcbX={20}
      pcbY={-19}
      schX={13}
      schY={-4}
      pinLabels={{
        pin1: "GND",
        pin2: "V3_3",
        pin3: "SDA",
        pin4: "SCL",
        pin5: "SHIELD1",
        pin6: "SHIELD2",
      }}
      connections={{
        GND: "net.GND",
        V3_3: "net.V3_3",
        SDA: "net.SDA",
        SCL: "net.SCL",
        SHIELD1: "net.GND",
        SHIELD2: "net.GND",
      }}
    >
      <enclosure.cutoutaperture shape="rect" width="7mm" height="4mm" margin="0.5mm" />
    </JST_SH_SM04B_SRSS_TB>
    <pinheader
      name="J3"
      pinCount={8}
      pitch="2.54mm"
      gender="male"
      showSilkscreenPinLabels
      manufacturerPartNumber="PZ254V-11-08P"
      supplierPartNumbers={{ jlcpcb: ["C492407"] }}
      schSectionName="io"
      pcbX={26.5}
      pcbY={0}
      pcbRotation={90}
      schX={19.5}
      schY={-4}
      schFacingDirection="left"
      pinLabels={["V3_3", "GND", "TXD0", "RXD0", "IO4", "IO5", "IO6", "IO7"]}
      connections={{
        V3_3: "net.V3_3",
        GND: "net.GND",
        TXD0: "net.TXD0",
        RXD0: "net.RXD0",
        IO4: "net.IO4",
        IO5: "net.IO5",
        IO6: "net.IO6",
        IO7: "net.IO7",
      }}
    />

    {/* bottom-side ground pour (keeps clear of the antenna keepout) */}
    <copperpour connectsTo="net.GND" layer="bottom" />
  </board>

  {/* 3D-printable two-part case; openings come from the parts' apertures */}
  <enclosure.fdm.box boardRef=".B1" wallThickness="2mm" boardClearance="1mm" />
  </assembly.device>
)