
AquaPCB
schwabix-1311/aquaPCB
Open-hardware Raspberry Pi base board for aquaPi software. 12 V supply; six 5 A MOSFET outputs for LED lamps and fans (2 with hardware PWM); 4 SSR/relay drivers; protected inputs; 1-Wire; ADS1115 with a pH amplifier on…

Open-source, local-first bed climate system: water-cooled/heated mat, standard parts, DIY kits, Home Assistant. Design phase.
Carrier is an open-source printed circuit board by moinsen-dev, from the moinsen-dev/open-bed-climate repository on GitHub, published under the Apache-2.0 licence. It is a 2-layer board of 100 × 100 mm with 126 parts on 112 nets, routed with 1,135 tracks and 198 vias.
It is built around the ESP32-S3-DEVKITC-1-N8, ADS1115IDGSR and LM2596S-5.0, with the SP3485EN-Y, SS34 and AO3400A among its other parts. Its parts include 40 resistors, 26 connectors, 20 diodes, 15 capacitors and 9 transistors. It belongs with the home automation and IoT boards and the ESP32, RP2040 and RP2350 boards of this library.
⚠️ Rev A is UNTESTED: review it before you order. Nobody has built or measured it yet. Read SAFETY.md first. Please post a build report if you build one.
The carrier replaces the perfboard and loose breakout boards of step 2 with one 100 × 100 mm, 2-layer board, so a small series (~10 units) can be ordered assembled. It carries signals and the 12 V aux rail only. The TEC high-current path stays off-board, wired with ≥ 2.5 mm²: 24 V brick → DPS5020 → 20 A fuse → zone relay → polarity relays → TECs (step 3).
What plugs in:
Licence: CERN-OHL-S-2.0, like the rest of the hardware.
flowchart LR
IN["12 V aux brick<br/>barrel jack (Solo, <= 5 A) or 12V IN screw terminal"] --> F["mini blade fuse<br/>5 A (7.5 A on 2 oz)"] --> TVS["SMBJ15A TVS"] --> RP["P-FET reverse-polarity<br/>(AOD4185)"] --> V12(("+12V"))
V12 --> BUCK["LM2596S-5.0<br/>12 -> 5 V, 3 A"] --> V5(("+5V"))
V5 -- "SS34" --> PICO["Pico 2 (VSYS)"]
V5 -- "SS34" --> ESP["ESP32-S3-DevKitC-1 (5V)"]
PICO -- "3V3 OUT" --> V33(("+3V3"))
PICO <-- "OPL UART GP0/GP1 460800 Bd" --> ESP
ESP --- I2CH["I2C HUB header<br/>(room SHT45)"]
PICO -- "I2C0 GP8/GP9" --> ADS["ADS1115 @0x48"] --- NTC["4x NTC divider<br/>10k 0.1 % + RC"]
PICO -- "UART1 GP4/GP5, DE GP6" --> RS["SP3485"] --> DPS["RS-485 A/B/GND<br/>to the DPS module(s)"]
PICO -- "GP10..13 / GP16..19" --> DRV["8x AO3400A low-side<br/>220R gate, 4.7k pull-down, SS34 flyback"]
V12 --> LOOP["CUTOFF LOOP terminal<br/>(bimetal cutoffs in series)"] --> COIL["zone relay coil"] --> DRV
V12 --> LOADS["polarity coils A/B, pump, fan"] --> DRV
FLOW["flow GP14/GP20"] & LEAK["leak GP21"] & LEVEL["level GP22"] -- "1k + BAT54S clamp + 10k pull-up" --> PICOboard.rs)The pin map comes from firmware/obc-pico2/src/board.rs.
software/obc/tests/test_carrier_pcb.py parses
carrier.kicad_pcb and checks it against board.rs and
hub/esphome/obc-hub-common.yaml in CI. Pin 1 is the square pad.
Zone 0's terminals run along the top edge, zone 1's along the bottom edge (mirrored). Silk labels sit next to each
block, with the pin names written in left-to-right order.
| Terminal (zone 0 / zone 1) | Pins | Function | Pico 2 (board.rs) |
|---|---|---|---|
J13 / J20 CUTOFF LOOP | 1 12V out, 2 RET | +12 V out to the bimetal cutoffs in series (all 50 °C and 80 °C) and back. The return is the only feed of the zone relay coil. | none, pure hardware |
J14 / J21 ZONE RLY | 1 + coil (from RET), 2 − driver | zone (enable) relay coil, flyback across the coil | GP10 / GP16 |
J15 + J16 / J22 + J23 POL A, POL B | 1 + 12 V, 2 − driver | polarity relay coils: 2 × SPDT (shopping list) on A and B, both switched by one MOSFET, each with its own flyback. One DPDT relay: use POL A only. | GP11 / GP17 (low = cool) |
J17 / J24 PUMP | 1 + 12 V, 2 − | pump, ≥ 2 A, PWM-capable MOSFET | GP12 / GP18 |
J18 / J25 FAN | 1 + 12 V, 2 − | heatsink fans, ≥ 2 A | GP13 / GP19 |
J19 / J26 FLOW | 1 5V, 2 G, 3 S | hall flow sensor; 1 kΩ series, BAT54S clamp to 3.3 V/GND, 10 kΩ pull-up (5 V outputs are safe) | GP14 / GP20 |
J4 / J6 NTC WTR | 1 N node, 2 G | water NTC: 3.3 V → 10 kΩ 0.1 % → node → NTC → GND, 1 kΩ/100 nF to the ADS | ADS1115 A0 / A2 |
J5 / J7 NTC HOT | 1 N, 2 G | TEC hot-side NTC | ADS1115 A1 / A3 |
| Terminal (right edge) | Pins | Function | Pico 2 |
|---|---|---|---|
J10 RS-485 | 1 A, 2 B, 3 G | Modbus to the DPS module(s); JP1 TERM = 120 Ω across A/B (fit only if this end has no other termination) | UART1 GP4 (TX→DI) / GP5 (RO→RX), DE + /RE on GP6 (10 kΩ pull-down = receive) |
J11 LEAK | 1 12V, 2 G, 3 S | leak module (powered from 12 V); contact to GND while dry. LOW = dry, open = fault | GP21 (10 kΩ pull-up) |
J12 LEVEL | 1 5V, 2 G, 3 S | level sensor at 5 V; LOW = OK, open = fault. The clamp makes a 5 V output safe; the divider from the shopping list is not needed | GP22 (10 kΩ pull-up) |
J2 12V IN | 1 +12V, 2 GND | 12 V aux brick (use this one for Duo) | |
| J1 barrel 5.5 × 2.5 | centre + | in parallel with J2. Solo only, ≤ 5 A | |
J3 STAR GND | 1, 2 GND | one wire to the star point near the DPS (24 V −). Do not link 12 V − and 24 V − anywhere else |
| Header | Pins | Notes |
|---|---|---|
J9 I2C HUB | 1 ESP 3V3, 2 GND, 3 SDA = ESP32 GPIO8, 4 SCL = GPIO9 | room SHT45 for the dew-point guard. 4.7 kΩ pull-ups on board. The hub YAML only mentions the SHT45 in a comment; add i2c: {sda: GPIO8, scl: GPIO9} and an sht4x sensor, then set room_temperature / room_humidity |
J8 I2C PICO | 1 3V3, 2 GND, 3 SDA = GP8, 4 SCL = GP9 | expansion on the ADS1115 bus (avoid address 0x48) |
| TP1–TP4 | 12V, 5V, 3V3, GND | test points (2 mm pads) |
All eight driver gates have a 4.7 kΩ pull-down (RP2350-E9: ≤ 8.2 kΩ). An unflashed, crashed or reset Pico leaves every output off, and the test checks every gate for it. Each zone relay coil gets +12 V only through its CUTOFF LOOP terminal. An open cutoff or an unplugged loop drops the relay whatever the MCU does, and the test checks that too. The flyback diode sits directly across the coil, so an opening bimetal never has to break the coil current as an arc.
| Path | What |
|---|---|
scripts/design.py | source of truth: every part with symbol, footprint, value, LCSC number and the net on each pin |
scripts/gen_lib.py | project library: ESP32-S3-DevKitC-1 symbol, socket footprints for both modules |
scripts/gen_sch.py | carrier.kicad_sch: one sheet, grouped by function, net labels on every pin, no-connect flags, power flags |
scripts/gen_pcb.py | carrier.kicad_pcb: placement, nets, outline, silkscreen, net classes, JLC rules (carrier.kicad_pro) |
scripts/route.py | Freerouting autoroute, GND pours on both layers, GND stitching vias |
scripts/silk.py | post-route silkscreen pass: connector refs to F.Fab, test point / header / jumper labels, back-side labels for the NTC terminals hidden by the barrel jack |
scripts/fab.py | ERC/DRC, Gerbers + drill, JLC BOM/CPL, full BOM, PDFs, renders into fab/ |
build.sh | runs all of the above |
fab/ | carrier-rev-a-gerbers.zip, jlc-bom.csv, jlc-cpl.csv, bom-full.csv, carrier-schematic.pdf, carrier-assembly-top.pdf, carrier.net, reports/ (ERC, DRC), renders/ |
Rebuild (KiCad 10, and Freerouting 1.9.0 on Java 17: freerouting-1.9.0.jar from
https://github.com/freerouting/freerouting/releases/tag/v1.9.0):
FREEROUTING_JAR=~/Downloads/freerouting-1.9.0.jar JAVA=/opt/homebrew/opt/openjdk@17/bin/java ./build.shChange the design in scripts/design.py, never in the generated KiCad files. Freerouting isn't deterministic, so a
rebuild gives different traces; re-run DRC and look at the renders.
fab/reports/.fab/renders/render-top.png, render-bottom.png, render-3d.png, top-layers.svg,
bottom-layers.svg. The 12 × 12 mm inductor has no 3D model in KiCad's library and shows as a bare footprint.PCB: upload fab/carrier-rev-a-gerbers.zip, 2 layers, 100 × 100 mm, 1.6 mm FR-4, HASL lead-free, 1 oz
copper (5 A input). For Duo boards with all 12 V loads near their maximum, choose 2 oz outer copper (7.5 A, see
limitations). Quantity 10.
Assembly: PCBA, Economic, top side, "Confirm parts placement". Upload fab/jlc-bom.csv and fab/jlc-cpl.csv.
In the BOM step, check every match. Parts without an LCSC number need one picked from JLC's library first:
The numbers marked "check" in bom-full.csv are the usual JLC basic parts but weren't confirmed on a product
page while Rev A was drawn up.
In the placement preview, check the rotation of every polarized part: diodes, LED, electrolytics, SOT-23, ADS1115, SP3485, LM2596. KiCad and JLC rotation conventions differ for some packages; fix any wrong one in the preview.
Hand-soldered THT parts (not in the JLC files; buy them separately or add them as JLC THT assembly):
| Qty | Part |
|---|---|
| 14 | screw terminal 5.08 mm, 2-pin (Phoenix MKDS 1,5/2-5.08 or compatible, e.g. KF128/DG128-2P) |
| 4 | screw terminal 3.5 mm, 2-pin (NTC) |
| 5 | screw terminal 3.5 mm, 3-pin (flow ×2, RS-485, leak, level) |
| 2 + 2 | 1x20 and 1x22 female headers, 2.54 mm (Pico 2 / ESP32 sockets) |
| 1 | DC barrel jack 5.5 × 2.5 mm, 3-pin THT, horizontal, with the pin layout of KiCad's BarrelJack_Horizontal (e.g. CUI PJ-063AH-class): check the pins against the footprint, a mismatched jack leaves GND or +12 V open |
| 1 | Keystone 3568 mini blade fuse holder + ATM fuse: 5 A (1 oz board) or 7.5 A (2 oz board) |
| 2 + 1 | 1x4 and 1x2 pin headers, jumper |
| 4 | test points (2 mm pads): bare wire loops or nothing |
Check each terminal block's footprint against your part. The Phoenix footprints assume the wire entry faces the board edge (verify on the 3D render before ordering).
| Item | ~USD for 10 |
|---|---|
| PCB 2 layers 100 × 100 mm, 1 oz | 5–10 (2 oz: +15) |
| PCBA setup + stencil | 10 |
| Extended-part fees (~10 unique extended parts × $3) | 30 |
| SMD components (ADS1115 ≈ $3 is the biggest item) | 60–80 |
| Placement and soldering | 5–10 |
| THT parts (≈25 terminals, sockets, jack, fuse holder) | 60–90 |
| Shipping to the EU + import VAT | 40–60 |
| Total | ≈ 210–290, i.e. ≈ €20–27 per board |
That's without the Pico 2 and ESP32-S3-DevKitC-1 (about €6 + €15 each). Prices move: get a live quote.
AISLER (EU, PCB + assembly) or PCBWay take the same Gerbers. Their BOM formats differ: use bom-full.csv and
the KiCad position file fab/reports/pos-kicad.csv.
bom-full.csv). The rest need checking
in JLC's BOM matcher, and a few must be picked (see Ordering).| Qty | Value | Footprint | References |
|---|---|---|---|
| 12 | 4.7k | Resistor_SMD:R_0603_1608Metric | R3, R4, R13, R14, R22, R24, R26, R28, R32, R34, R36, R38 |
| 12 | SS34 | Diode_SMD:D_SMA | D5, D6, D9, D10, D11, D12, D13, D15, D16, D17, D18, D19 |
| 9 | 100n | Capacitor_SMD:C_0603_1608Metric | C1, C8, C9, C10, C11, C12, C13, C14, C15 |
| 9 | 1k | Resistor_SMD:R_0603_1608Metric | R2, R6, R8, R10, R12, R17, R19, R29, R39 |
| 8 | 220 | Resistor_SMD:R_0603_1608Metric | R21, R23, R25, R27, R31, R33, R35, R37 |
| 8 | AO3400A | Package_TO_SOT_SMD:SOT-23 | Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9 |
| 6 | 10k | Resistor_SMD:R_0603_1608Metric | R1, R15, R18, R20, R30, R40 |
| 4 | 10k 0.1% | Resistor_SMD:R_0603_1608Metric | R5, R7, R9, R11 |
| 4 | BAT54S | Package_TO_SOT_SMD:SOT-23 | D7, D8, D14, D20 |
| 4 | M3 | MountingHole:MountingHole_3.2mm_M3 | H1, H2, H3, H4 |
| 3 | 10u | Capacitor_SMD:C_0805_2012Metric | C3, C4, C7 |
| 1 | 100u 35V | Capacitor_SMD:CP_Elec_6.3x7.7 | C2 |
| 1 | 120 | Resistor_SMD:R_0603_1608Metric | R16 |
| 1 | 12V IN | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J2 |
| 1 | 12V IN 5.5x2.5 (Solo only, <=5 A) | Connector_BarrelJack:BarrelJack_Horizontal | J1 |
| 1 | 220u 16V | Capacitor_SMD:CP_Elec_6.3x7.7 | C5 |
| 1 | 22u | Capacitor_SMD:C_0805_2012Metric | C6 |
| 1 | 33uH 4A | Inductor_SMD:L_12x12mm_H8mm | L1 |
| 1 | 5A mini blade | Fuse:Fuseholder_Blade_Mini_Keystone_3568 | F1 |
| 1 | ADS1115IDGSR | Package_SO:MSOP-10_3x3mm_P0.5mm | U4 |
| 1 | AOD4185 | Package_TO_SOT_SMD:TO-252-2 | Q1 |
| 1 | BZT52C12 | Diode_SMD:D_SOD-123 | D2 |
| 1 | ESP32-S3-DevKitC-1 | carrier:ESP32-S3-DevKitC-1_Socket | U2 |
| 1 | I2C HUB SHT45 (3V3 GND SDA SCL) | Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical | J9 |
| 1 | I2C PICO (3V3 GND SDA SCL) | Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical | J8 |
| 1 | LEAK +12V GND SIG | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-3-3.5-H_1x03_P3.50mm_Horizontal | J11 |
| 1 | LEVEL +5V GND SIG | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-3-3.5-H_1x03_P3.50mm_Horizontal | J12 |
| 1 | LM2596S-5.0 | Package_TO_SOT_SMD:TO-263-5_TabPin3 | U3 |
| 1 | PWR 5V | LED_SMD:LED_0603_1608Metric | D4 |
| 1 | RS485 A B GND | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-3-3.5-H_1x03_P3.50mm_Horizontal | J10 |
| 1 | Raspberry Pi Pico 2 | carrier:RaspberryPi_Pico2_Socket | U1 |
| 1 | SMBJ15A | Diode_SMD:D_SMB | D1 |
| 1 | SP3485EN | Package_SO:SOIC-8_3.9x4.9mm_P1.27mm | U5 |
| 1 | SS54 | Diode_SMD:D_SMC | D3 |
| 1 | STAR GND | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J3 |
| 1 | TERM 120R | Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical | JP1 |
| 1 | TP 12V | TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mm | TP1 |
| 1 | TP 3V3 | TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mm | TP3 |
| 1 | TP 5V | TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mm | TP2 |
| 1 | TP GND | TestPoint:TestPoint_THTPad_D2.0mm_Drill1.0mm | TP4 |
| 1 | Z0 CUTOFF LOOP | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J13 |
| 1 | Z0 FAN | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J18 |
| 1 | Z0 FLOW +5V GND SIG | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-3-3.5-H_1x03_P3.50mm_Horizontal | J19 |
| 1 | Z0 NTC HOT | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-2-3.5-H_1x02_P3.50mm_Horizontal | J5 |
| 1 | Z0 NTC WATER | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-2-3.5-H_1x02_P3.50mm_Horizontal | J4 |
| 1 | Z0 POL A | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J15 |
| 1 | Z0 POL B | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J16 |
| 1 | Z0 PUMP | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J17 |
| 1 | Z0 ZONE RELAY COIL | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J14 |
| 1 | Z1 CUTOFF LOOP | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J20 |
| 1 | Z1 FAN | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J25 |
| 1 | Z1 FLOW +5V GND SIG | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-3-3.5-H_1x03_P3.50mm_Horizontal | J26 |
| 1 | Z1 NTC HOT | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-2-3.5-H_1x02_P3.50mm_Horizontal | J7 |
| 1 | Z1 NTC WATER | TerminalBlock_Phoenix:TerminalBlock_Phoenix_PT-1,5-2-3.5-H_1x02_P3.50mm_Horizontal | J6 |
| 1 | Z1 POL A | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J22 |
| 1 | Z1 POL B | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J23 |
| 1 | Z1 PUMP | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J24 |
| 1 | Z1 ZONE RELAY COIL | TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal | J21 |
It measures 100 × 100 mm and has 2 copper layers, 1.6 mm thick.
126 parts, built around the ESP32-S3-DEVKITC-1-N8, ADS1115IDGSR, LM2596S-5.0 and SP3485EN-Y. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the moinsen-dev/open-bed-climate repository on GitHub, at the commit this page was made from: the board is hardware/pcb/carrier/carrier.kicad_pcb and its schematic hardware/pcb/carrier/carrier.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Its repository publishes it under the Apache-2.0 licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

schwabix-1311/aquaPCB
Open-hardware Raspberry Pi base board for aquaPi software. 12 V supply; six 5 A MOSFET outputs for LED lamps and fans (2 with hardware PWM); 4 SSR/relay drivers; protected inputs; 1-Wire; ADS1115 with a pH amplifier on…


ratmuds/revo
a "cheap" 6 DoF robot arm intentionally pushing the limits of low quality hobby servos

Hidayatur-rahman/Solar-Water-Level-Monitor-ESP8266-ESPHome-
A solar-powered water tank level monitoring system built around the ESP8266 (ESP-12E), designed in KiCad and running on ESPHome

mrwillson2u/propmesh
Open-source wireless control for Halloween and holiday props.

comedy1982-spec/pcb-viewer
Dadu 기반 ELD 솔레노이드 ECU. 12 V 입력, TC364 · CAN-FD · PMIC, PCB 위 센서는 NTC 하나. A4911(3상 드라이버)의 A·B 레그로 H-브리지(하프브리지 IAUC60N04S6N050H ×2, 레그 션트 2 mΩ ×2), C 레그는 비워 두고 SPI로 그 고장(VC · CHO · CLO · OC3)을 마스크.

