Back to work

Case Study 01 · Side Project

PoolPilot

A self-hosted smart controller for a "dumb" pool pump — reverse-engineered RS-485 protocol, hand-wired ESP32 bridge hardware, and a real-time mobile app with scheduling and energy analytics. Designed, engineered, and shipped end-to-end. It runs the family pool every day.

Product DesignIoT / HardwareReal-Time UIFull-StackSelf-HostedAI-Augmented Build

A variable-speed pump with no app, no remote, no API

The Pentair SuperFlo VST is a great variable-speed pool pump with one catch — it's the budget model. No app, no WiFi, no integration story. Control means walking to the equipment pad and pressing buttons on a tiny keypad. Even Pentair's own smart accessories can't drive it over its comm port.

The open-source alternative technically worked, but its dashboard felt like an industrial control panel from 2012 — and it couldn't actually command this pump. Pool software is a category where nobody has cared about design. That was the opportunity: build the thing I wished existed — consumer-product polish, real scheduling, energy analytics — on top of hardware I'd have to crack open myself.

First, make the pump talk

The pump's RS-485 protocol is proprietary and undocumented for this model. Getting two-way communication meant building the physical layer from scratch: a $20 aftermarket cable into the pump's comm port, an ESP32 microcontroller acting as a transparent WiFi-to-RS-485 bridge, and a lot of nights at the equipment pad with a logic probe mentality — sending candidate command frames and listening for anything to come back.

The bus stayed silent for days. The breakthrough was hardware, not software: the "auto-direction" RS-485 module everyone recommends never actually drove the transmit line. Swapping it for a MAX485 transceiver with an explicit flow-control pin — wired to a GPIO the firmware toggles around each write — brought the bus to life. The pump answered with its first status frame: 135 watts, 1,500 RPM. From there I decoded the frame format, mapped the command registers, and wrote a full protocol reference — then ported it all into a typed, unit-tested TypeScript codec.

The Hardware / ESP32 RS-485 Bridge / Wiring

Wiring diagram — ESP32 on expansion board connected to a MAX485 transceiver module with explicit flow-control pin, out to the Pentair pump cable's RS-485 pair
ESP32 ↔ MAX485 ↔ pump — the WiFi bridge that finally drove the bus

Pad to product

Energy monitoring & baseline analysisRS-485 protocol researchESP32 bridge build & firmwareBus probing — silent for daysMAX485 flow-control fixProtocol reference & TypeScript codecFailsafe architectureAPI + real-time serviceMobile-first UI designSelf-hosted deploy + CI

The build was AI-augmented throughout — Claude Code as the engineering pair, me directing architecture, design, and every hardware session at the pad. Once the pump obeyed its first speed command, the entire application — protocol codec, API service, database, and the full UI — was designed, built, and deployed to my home server the same day.

A deep-water instrument panel

Pool equipment lives in utility-software land — gray tables, tiny buttons, desktop layouts. PoolPilot went the other way: a dark, glowing, thumb-first instrument panel that feels closer to a premium EV dashboard than a sprinkler timer.

Signature controlA radial RPM slider built for thumbs — drag the glowing arc, feel haptic ticks, and the setpoint applies on release after a short debounce. One gesture replaces the keypad's button-mashing.
Presets over numbersEco, Clean, and Boost chips map pump speeds to intents. Nobody thinks in RPM — they think "the pool guy is coming" or "party tomorrow."
Schedule as a timelineThe daily plan is a 24-hour color-coded bar — speed bands you can read at a glance, with a live cursor showing what the pump is doing right now. Editable presets (Summer, Maintenance, Vacation) switch with one tap.
Energy translated to dollarsTelemetry is stored in a time-series database and surfaced as what owners actually ask: cost today, kWh this week, water turnovers, projected monthly bill — not raw watt logs.
Optimistic, honest UIControls react instantly and the server's authoritative state confirms a beat later. The status pill never lies — Connected means a real socket to a real pump.
Fail back to dumbThe pump's onboard schedule is the safe baseline. App overrides are transient, sustained only by a keep-alive heartbeat — if the app, server, or bridge dies, the pump reverts to its own schedule. The physical Start/Stop button always wins.

The Product / Production UI / iPhone

PoolPilot Live screen — glowing radial RPM slider at 1,500 RPM, Eco/Clean/Boost presets, active Summer schedule card, and twin speed and power gauges
Live — radial control + gauges
PoolPilot Schedule screen — 24-hour color-coded timeline with live cursor, and Summer, Maintenance, and Vacation presets with one-tap Use buttons
Schedule — timeline + presets
PoolPilot Energy screen — cost and kWh stat cards, power-draw area chart, and energy-per-hour bars
Energy — cost & turnover analytics
PoolPilot Settings screen — failsafe explainer, system poll and keep-alive readouts, bridge status, and pool volume and rate facts
Settings — failsafe & system

Hardware to pixels, all of it mine

ESP32 + ESPHome
Transparent WiFi-to-RS-485 bridge at the equipment pad — MAX485 transceiver with GPIO flow control, flashed over-the-air.
Pentair RS-485 Protocol
Reverse-engineered command and status frames, documented as a reference, implemented as a unit-tested TypeScript codec.
Fastify + Socket.IO
Node service that owns the bus — command queue, poller, scheduler, keep-alive engine, watchdog, and authenticated real-time telemetry. A tiny failover proxy holds the bridge connection so redeploys are invisible to the pump.
TimescaleDB
Time-series Postgres for telemetry history — powers the energy charts, cost math, and turnover model. The app degrades gracefully without it.
Next.js + Tailwind
Mobile-first PWA with custom gauges, the radial slider, and motion-driven micro-interactions. Includes a full demo mode that simulates the pump.
Self-Hosted Deploy
Docker Compose on my home server behind Traefik with auto-SSL — push to main, and a self-hosted GitHub Actions runner triggers the rebuild.

The daily driver for an actual pool

PoolPilot has run our pool since the day it shipped — schedules executing on time, energy history accumulating, and pump control from anywhere on a phone. It replaced both the keypad ritual and the open-source dashboard, and it's the proof point I care most about as a designer: I can take a product from "the hardware won't even talk" to a polished, live, self-hosted app people actually use.

5s
The keep-alive heartbeat that sustains every override. If the app, server, or bridge dies, the pump falls back to its onboard schedule on its own — no stuck states, no dead pool. Safety isn't a feature here; it's the architecture.