

Ezo pmp controller
Open schematics and design files for a computer controlled nutrient dosing system
- Size
- 195 × 58 mm
- Copper layers
- 4
- Thickness
- 1.6 mm
- Parts
- 83
- Nets
- 37
- Pads
- 248
- Tracks, vias
- 507, 73
- Pours
- 12
- Routed
- Yes
- KiCad
- KiCad 10.0 (file 20260206)
- Created
- 10 Aug 2026
- Updated
- 13 Aug 2026
Overview
Ezo pmp controller is an open-source printed circuit board by dephekt, from the dephekt/dosing-pump-hardware repository on GitHub, published under the CERN-OHL-S-2.0 licence. It is a 4-layer board of 195 × 58 mm with 83 parts on 37 nets, routed with 507 tracks and 73 vias.
It is built around the SRV05-4, AP2112K-3.3TRG1 and AO3401A, with the BSS138LT1G, BZT52C10 and SMBJ13A among its other parts. Its parts include 24 resistors, 18 capacitors, 9 connectors, 7 ICs and 4 diodes. It belongs with the ESP32 boards of this library.
Main components
8SRV05-4
SOT-23-6L-55℃~+150℃ 15V 1pF 4A 5V 5uA 6V 80W ESD Four channels IEC 61000-4-2、IEC 61000-4-5 Unidirectional SOT-23-6L ESD and Surge Protection (TVS/ESD) ROHShongjiachengESD And Surge Protection (TVS/ESD)AP2112K-3.3TRG1
SOT-25-5-40℃~+85℃@(Ta) 1 250mV@(600mA) 3.3V 50uVrms 55uA 600mA 65dB@(1kHz) 6V Fixed Over Current Protection、Thermal Shutdown、Enable control Positive SOT-25-5 Voltage Regulators - Linear, Low Drop Out (LDO) Regulators ROHSDiodes IncorporatedVoltage Regulators - Linear, Low Drop Out (LDO) RegulatorsAO3401A
SOT-231 P-Channel 14nC@10V 30V 47mΩ@10V、60mΩ@4.5V、85mΩ@2.5V 4A 55pF 645pF 80pF 900mV SOT-23 MOSFETs ROHSAlpha & Omega SemiconductorMOSFETsBSS138LT1G
SOT-23-55℃~+150℃ 1 N-channel 200mA 225mW 5.6Ω@2.75V 500mV 50V 50pF 5pF SOT-23 MOSFETs ROHSonsemiMOSFETsBZT52C10
SOD-123-65℃~+150℃ 1 Independent 10V 150Ω 200nA 20Ω 370mW 9.4V~10.6V SOD-123 Zener Diodes ROHSDiodes IncorporatedZener DiodesSMBJ13A
DO-214AA(SMB)-55℃~+150℃ 13V 15.9V 21.5V 27.91A@10/1000us 5uA 600W@10/1000us IEC 61000-4-2 TVS Unidirectional DO-214AA(SMB) ESD and Surge Protection (TVS/ESD) ROHShongjiachengESD And Surge Protection (TVS/ESD)SS14
SMA(DO-214AC)-55℃~+150℃ 1A 25A 300uA@40V 40V 550mV@1A Independent SMA(DO-214AC) Schottky Diodes ROHSMDDSchottky DiodesSS310
SMA(DO-214AC)-55℃~+150℃ 100V 3A 50uA@100V 80A 850mV@3A Independent SMA(DO-214AC) Schottky Diodes ROHShongjiachengSchottky Diodes
README
electrical/README.mdElectrical and PCB prototype v0.3
This directory contains the native KiCad schematic and routed four-layer PCB for the four-channel Atlas Scientific EZO-PMP controller. It implements the power input, logic rails, Feather carrier, per-pump I2C isolation, and four unswitched pump supply taps inside the frozen 195 x 58 mm mechanical envelope.
Open ezo-pmp-controller.kicad_pro in KiCad.
The project manager shows both editors: Schematic Editor opens the
hierarchical circuit and PCB Editor opens the placed and routed board in
ezo-pmp-controller.kicad_pcb. In PCB Editor,
press Home to fit the board, B to refill copper zones, and Alt+3 for the
3D Viewer. A board-only STEP is also checked in under exports/pcb; component
models shown by KiCad depend on the optional KiCad 3D package libraries
installed on the workstation.
The root schematic links these functional sheets:
| Sheet | Function |
|---|---|
power_input.kicad_sch | 12 V input, fuse, reverse-polarity pass FET, TVS, bulk filtering, and the four pump supply taps |
logic_power_feather.kicad_sch | 5 V buck, dedicated 3.3 V EZO rail, and ESP32-S3 TFT Feather |
pump_logic.kicad_sch | TCA9548A, four Atlas headers, pull-ups, series damping, and ESD protection |
PCB layout
- 195 x 58 mm rounded four-layer board; 1.6 mm FR-4 is the intended stack.
- Four 3.4 mm non-plated M3 mounting holes on the frozen 185 x 48 mm pattern.
- Feather/TFT at the left with its antenna and USB end at the board edge.
- Four repeated pump control headers and supply terminals along the service edge, ordered pump 1 through pump 4.
- 12 V input, fuse, protection, and regulators grouped on the right; the mux and its reset barrier sit between the Feather and power.
- Ground planes on In1 and the back, protected-12 V and 5 V planes on In2, and 1.0 mm input, 0.50 mm logic power, and 0.20 mm minimum signal routing.
- 84 footprints, including 57 top-side JLCPCB SMT placements and the hand-soldered connectors, jumper, and Feather socket/module parts.
The placement drawing is exports/pcb/ezo-pmp-controller-assembly.png,
and the copper/silkscreen overview is
exports/pcb/ezo-pmp-controller-top.png.
These files make the physical arrangement reviewable without KiCad, but the
.kicad_pcb file remains the editable source of truth.
The Feather pad map was checked against Adafruit's PID 5483 PrettyPins drawing, including its two 3.3 V header pins, bottom TXD0 debug pad, and reverse-numbered right header row. The onboard TFT shares the Feather's SCK/GPIO36 and MOSI/GPIO35 header pins, so both are intentionally left unconnected on this carrier.
Pump connections
Each pump uses two separate connectors on this controller:
| Controller connection | Pins |
|---|---|
J2-J5, Atlas control cable | 1 white SCL, 2 green SDA, 3 black GND, 4 red 3.3 V, 5 blue INT |
J7-J10, pump supply | 1 unswitched 12 V positive, 2 ground |
Every pump ships in UART mode and must be switched to I2C once before
installation; see commissioning.md. The switch sets the
factory address 0x67, which is what the mux design expects, so all four pumps
stay identical and a replacement needs no address assignment.
The INT pin is wired only to its ESD clamp; it does not reach the MCU. Atlas asserts INT while a pump is dispensing, and dispensing state is polled over I2C instead, so the pin exists to keep the five-wire cable mating correctly. Atlas's own hydroponics kit lands the pump on a four-wire I2C terminal for the same reason.
The control footprint is a horizontal 1-by-5 2.54 mm header selected to mate
with the Atlas cable's Molex 2695 controller-end housing. The prototype gate is
to test the real cable on the selected C138801 header and confirm pitch,
friction-lock engagement, pin-1 orientation, and service clearance before PCB
release. The board silkscreen must include both pin numbers and wire colors.
Safety behavior
Dosing is stopped in firmware, not in hardware. Each EZO-PMP contains its own motor controller and dispenses by volume: the carrier tells a pump to move a number of millilitres and the pump decides when to stop. Earlier revisions put a quad high-side switch, a watchdog, and an arm gate between the supply and the pumps so the carrier could cut a pump off mid-dose. That was removed in v0.3. These pumps are normally deployed straight off a wall brick with no such hardware anywhere in the path, so the failure it guarded against -- a pump that will not stop -- is one the pump's own firmware has to handle. Adding a cutoff here would have protected this one carrier and nothing else.
What remains:
- The 12 V input is fused, reverse-polarity protected by a P-channel pass FET, and clamped by a 13 V TVS. Supply the board from 12 V only; at 24 V the TVS conducts continuously rather than protecting anything.
- The TCA9548A reset is pulled up only by the EZO rail and asserted through an open-drain MOSFET, so a USB-powered Feather GPIO can never drive the unpowered pump-logic rail. The gate is pulled down, so the mux releases itself at power-on and needs no firmware co-operation to be reachable.
- The I2C trunk pull-ups reference
EZO_3V3, not the Feather rail, so the bus is only driven when both domains are up. - The Feather VBUS header and its USB-C connector are the same net inside the
module. Remove the Feather from its socket to flash it.
D4makes an attached host harmless rather than merely forbidden: the host's 5.0 V exceeds the 4.6 V the carrier offers, so the diode turns off and USB powers the Feather alone.
None of this is an emergency-stop-rated safety system, and there is no hardware path that can interrupt a dose in progress. Wet energized use remains prohibited until the mechanical and powered electrical gates pass.
Regenerate and validate
The checked-in KiCad files and BOMs are generated from the explicit component
and connectivity model in tools/generate_kicad.py. Dependencies are declared in
the repository's pyproject.toml and managed with uv:
uv venv --system-site-packages
uv sync --inexact--system-site-packages is required because the PCB scripts import KiCad's
pcbnew, which ships with KiCad rather than from PyPI; --inexact stops uv from
removing packages it did not install itself.
Routing needs Freerouting on PATH. nix profile install nixpkgs#freerouting
pins 2.2.4 and brings its own JRE; route_pcb.py passes --gui.enabled=false
and clears DISPLAY, so it routes and exits without opening a window.
Then, using .venv/bin/python:
python3 electrical/tools/generate_kicad.py
python3 electrical/tools/validate_kicad.py
python3 electrical/tools/create_pcb.py --force
python3 electrical/tools/route_pcb.py
python3 electrical/tools/finalize_pcb.py electrical/ezo-pmp-controller.kicad_pcb
python3 electrical/tools/validate_pcb.py
python3 electrical/tools/generate_fabrication.pyroute_pcb.py exports the Specctra DSN, checks its net-class membership, runs
Freerouting, and imports the session back onto the board. It prints whatever the
router could not finish, though that list overstates the problem — the router
does not credit the locked escape copper it is handed, and the planes do not
exist yet. validate_pcb.py is the gate that decides whether the board is
fabricable.
Everything through routing is byte-reproducible: two rebuilds give identical
bootstrap boards, identical Specctra exports and an identical routing session.
That relies on tools/canonical_board.py, which every script that writes the
board calls. pcbnew assigns random UUIDs to the items it creates and KiCad orders
and writes items by UUID, so without canonicalising, each rebuild emitted the
same geometry in a different order and the autorouter followed it to a different
result.
The final zone fill is the one step that is not bit-identical. KiCad's filler is
multithreaded and lands a point or two differently between runs, so the copper is
electrically the same while the file churns slightly. Everything that decides the
design is stable; only the computed fill polygons move.
The session is checked in under routing/, so when the netlist has not changed
route_pcb.py --reuse-session skips the router and reproduces identical copper.
Re-run without that flag whenever the netlist changes — a session routed against
a stale netlist will attach copper to the wrong pads.
The generator also owns the schematic presentation, split across four modules:
| Module | Responsibility |
|---|---|
tools/kicad_symbols.py | Vendors real symbols from the stock KiCad libraries and generates the IC rectangles and project power ports |
tools/kicad_draw.py | Sheet objects: symbol instances, wires, junctions, labels; grid snapping and the connectivity check |
tools/kicad_blocks.py | The circuit idioms — rails, shunts, series elements, grounds |
tools/layouts/*.py | One module per sheet deciding what that sheet looks like |
tools/canonical_board.py | Rewrites a written board so its bytes depend only on the design, not on the UUIDs pcbnew happened to assign |
Component symbols come from KiCad's own libraries, so a capacitor looks like a capacitor and a diode shows its polarity. Rails and grounds are power ports. Series elements are drawn horizontally and shunt elements vertically, which is what tells a reader at a glance who is in the signal path and who is parallel. Visible wires show connections inside a functional block; named local/global ports are reserved for connections that would otherwise cross blocks or sheets.
See tools/layouts/README.md for the layout API. Edit
those layout definitions rather than moving generated symbols by hand in KiCad;
regeneration intentionally replaces the child sheets. To iterate on one sheet,
python3 electrical/tools/preview_sheet.py <sheet_name> renders it on its own
and reports any netlist mismatch.
Test points are PCB features and therefore do not carry KiCad's red DNP cross; crosses that remain mark intentionally unpopulated options.
Connectivity rides on drawn wires, so a placement mistake is a wiring mistake.
The generator checks the drawn geometry against the model before writing each
sheet — nets that split, nets shorted together, and nets left unnamed all fail
the build — and validate_kicad.py re-derives the whole netlist through
kicad-cli afterwards.
The schematic validator invokes KiCad's own parser/exporter, resolves every
footprint and pad, compares every exported pin/net pair with the source model,
and checks both BOMs. It also runs kicad-cli sch erc, which KiCad 10 provides,
so ERC is part of the automated gate rather than a manual step.
The PCB validator checks the 195 x 58 mm outline, four-layer stack, mounting pattern, footprints, power copper, routes, JLCPCB placements, and KiCad DRC. It fails on any DRC error or unconnected pad. The complete board-rebuild sequence intentionally overwrites hand edits; capture deliberate layout changes in the PCB scripts before using it.
The validator discovers standard footprints from KiCad's footprint environment
variables and the kicad-cli installation prefix. For a custom installation,
override the location explicitly:
DOSING_PUMP_KICAD_FOOTPRINT_DIR=/path/to/kicad/footprints \
python3 electrical/tools/validate_kicad.pyThe generated sourcing files are:
bom/full-bom.csv: all parts, hand-solder items, test points, and DNP options.bom/jlcpcb-smt-bom.csv: populated SMT parts only, grouped by value, footprint, and LCSC/JLCPCB part number.bom/sourcing-status.md: availability snapshot and purchasing caveats.
The generated JLCPCB package is under fabrication/jlcpcb:
Gerber ZIP, separate PTH/NPTH drills, grouped BOM, and a 57-row top-side CPL.
See fabrication/manufacturing-notes.md
for the order settings and upload checklist. These files are prototype-review
outputs, not authorization to order boards before the gates below pass.
Gates before PCB fabrication
- Verify the Atlas cable/header mechanical fit and pin-1 orientation using the real cable and the supplied carrier-board STEP reference.
- Measure pump startup and stall current at 12 V; confirm the 3 A input fuse tolerates four pumps starting together without nuisance opening.
- Bench-check USB-only with the Feather out of its socket, 12 V with it
seated, and USB attached while seated, using a current-limited sacrificial
setup. Confirm
D4isolatesSYS_5Vin the last case. - Compile and exercise four ESPHome
ezo_pmpinstances through explicit TCA9548A virtual-bus IDs; test a stuck bus and hot replacement. - Confirm reverse polarity on the input terminal does not power the board and
does not damage
Q2, and that brownout and MCU reset leave the pumps in a known state. - Review the completed placement against a printed 1:1 board and the real enclosure/deck, inspect every Gerber and check the ten corrected JLC placement rotations against JLCPCB's preview, run GUI ERC, and obtain an independent schematic/layout review before ordering.
The board carries four pumps and nothing more. To run more, build a second board: it brings its own regulation, its own MCU and its own current budget. There is no expansion header and no inter-enclosure I2C -- ordinary I2C must not be routed between enclosures.
Parts
83| Qty | Value | Footprint | References |
|---|---|---|---|
| 10 | 4.7k | Resistor_SMD:R_0603_1608Metric | R6, R7, R12, R13, R16, R17, R20, R21, R24, R25 |
| 8 | 33R | Resistor_SMD:R_0603_1608Metric | R10, R11, R14, R15, R18, R19, R22, R23 |
| 7 | 100nF 50V | Capacitor_SMD:C_0603_1608Metric | C5, C9, C13, C20, C21, C22, C23 |
| 4 | MountingHole_3.2mm_M3 | MountingHole:MountingHole_3.2mm_M3 | H1, H2, H3, H4 |
| 4 | PUMP 12V | Dosing_Pump:TerminalBlock_DB103_1x02_P5.08mm | J7, J8, J9, J10 |
| 4 | SRV05-4 | Package_TO_SOT_SMD:SOT-23-6 | U7, U8, U9, U10 |
| 3 | 1uF 16V | Capacitor_SMD:C_0603_1608Metric | C10, C11, C14 |
| 3 | 22uF 25V | Capacitor_SMD:C_1210_3225Metric | C6, C7, C8 |
| 3 | Fiducial_1mm_Mask2mm | Fiducial:Fiducial_1mm_Mask2mm | FID1, FID2, FID3 |
| 2 | 100k | Resistor_SMD:R_0603_1608Metric | R2, R8 |
| 2 | 10k | Resistor_SMD:R_0603_1608Metric | R4, R5 |
| 2 | 2.2uF 100V | Capacitor_SMD:C_1210_3225Metric | C3, C4 |
| 2 | CABLE_TIE_NPTH_3.2 | CT1, CT2 | |
| 1 | 100nF 100V | Capacitor_SMD:C_0805_2012Metric | C2 |
| 1 | 10uF 10V | Capacitor_SMD:C_0603_1608Metric | C12 |
| 1 | 10uH 4A | Dosing_Pump:L_Bourns_SRP6540_7.2x6.5mm | L1 |
| 1 | 12V INPUT | Dosing_Pump:TerminalBlock_DB103_1x02_P5.08mm | J1 |
| 1 | 17.8k 1% | Resistor_SMD:R_0603_1608Metric | R3 |
| 1 | 3A FAST | Fuse:Fuse_Littelfuse-NANO2-451_453 | F1 |
| 1 | 41.2k 1% | Resistor_SMD:R_0603_1608Metric | R1 |
| 1 | 47uF 50V | Capacitor_SMD:CP_Elec_6.3x5.8 | C1 |
| 1 | AO3401A | Package_TO_SOT_SMD:SOT-23 | Q2 |
| 1 | AP2112K-3.3TRG1 | Package_TO_SOT_SMD:SOT-23-5 | U2 |
| 1 | ATLAS_EZO_PMP_1 | Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Horizontal | J2 |
| 1 | ATLAS_EZO_PMP_2 | Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Horizontal | J3 |
| 1 | ATLAS_EZO_PMP_3 | Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Horizontal | J4 |
| 1 | ATLAS_EZO_PMP_4 | Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Horizontal | J5 |
| 1 | Adafruit ESP32-S3 TFT Feather PID 5483 | Module:Adafruit_Feather | A1 |
| 1 | BSS138LT1G | Package_TO_SOT_SMD:SOT-23 | Q1 |
| 1 | BZT52C10 | Diode_SMD:D_SOD-123 | D1 |
| 1 | EZO_3V3 | TestPoint:TestPoint_Pad_D1.5mm | TP5 |
| 1 | FEATHER_3V3 | TestPoint:TestPoint_Pad_D1.5mm | TP6 |
| 1 | FEATHER_USB | TestPoint:TestPoint_Pad_D1.5mm | TP7 |
| 1 | GND | TestPoint:TestPoint_Pad_D1.5mm | TP3 |
| 1 | LMR16020PDDAR | Package_SO:TI_SO-PowerPAD-8_ThermalVias | U1 |
| 1 | PROTECTED_VIN | TestPoint:TestPoint_Pad_D1.5mm | TP2 |
| 1 | RAW_VIN | TestPoint:TestPoint_Pad_D1.5mm | TP1 |
| 1 | SMBJ13A | Diode_SMD:D_SMB | D2 |
| 1 | SS14 | Diode_SMD:D_SMA | D4 |
| 1 | SS310 | Diode_SMD:D_SMA | D3 |
| 1 | SYS_5V | TestPoint:TestPoint_Pad_D1.5mm | TP4 |
| 1 | TCA9548APWR | Package_SO:TSSOP-24_4.4x7.8mm_P0.65mm | U3 |
Questions
How big is the Ezo pmp controller PCB?
It measures 195 × 58 mm and has 4 copper layers, 1.6 mm thick.
What parts does the Ezo pmp controller use?
83 parts, built around the SRV05-4, AP2112K-3.3TRG1, AO3401A and BSS138LT1G. 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.
Where can I download the Ezo pmp controller KiCad files?
From the dephekt/dosing-pump-hardware repository on GitHub, at the commit this page was made from: the board is electrical/ezo-pmp-controller.kicad_pcb and its schematic electrical/ezo-pmp-controller.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Can I use the Ezo pmp controller design in my own project?
Its repository publishes it under the CERN-OHL-S-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).
Similar boards
- 4 layers100 × 100 mm

- 4 layers100 × 75 mm

- 4 layers100 × 100 mm

- 4 layers414 × 62 mm

- 4 layers195.5 × 114.3 mm

- 2 layers81 × 78 mm

- 4 layers148 × 78 mm

Hub
Bruhdows/klink
Klink is a magnetic tile system for building programmable machines. First up: a marble run with sensors, buttons, lights, gates, splitters and an elevator.
85 parts - 4 layers40 × 58 mm

STM32-S Plant
Fred98246/STM32-Environmental-Sensor-and-Datalogging-Board
An open-source STM32F103C8T6 development board designed for environmental monitoring, featuring integrated Li-Ion battery management, USB-C, MicroSD data logging, and onboard I2C sensors for temperature, humidity, and…
59 parts2MIT