Oct 2026 – Present

SpinCycle

IoT
Backend
Embedded

I own the MQTT broker, the Go ingest service, storage and the API. Twyla builds the hardware and firmware.

Tech Stack

ESP32-C6
MQTT
Go
TimescaleDB
Redis
FastAPI
React
Prometheus

Description

The problem

Walking down to the laundry room only to find every machine taken is a small, daily waste of time. SpinCycle puts a vibration sensor on each washer and dryer, works out whether it's free, running or finished, and shows it on a live page. Tap "Notify me" and you get an email when your load is done.

Underneath, it's a general IoT fleet platform: devices, device types and fleets are modeled generically, so a laundry room is the first fleet rather than the only one.

How a reading gets to your phone

  1. Sensor: an accelerometer on each machine, sampled at 100 Hz.
  2. ESP32-C6: sends a vibration reading every 5 seconds over MQTT with TLS.
  3. Mosquitto broker: each device has its own credentials and can only publish to its own topics.
  4. Ingest service (Go): validates, dedupes and batch-writes readings, and turns vibration into machine state.
  5. TimescaleDB and Redis: history in Postgres, live state in a cache that can be rebuilt.
  6. API and web page: live status, busy times, and finished-load emails.

Decisions that matter

A machine never goes from offline straight to free. If a sensor drops off Wi-Fi mid-cycle, showing the machine as available would send someone to a running dryer, so the state engine forbids that transition outright. Every recorded wash cycle is replayed through the engine in tests.

Readings are acknowledged to the broker only after they're committed to the database, so a crash can't silently lose data. In the first crash test, ingest was killed with kill -9 partway through a 3,000-reading stream: after restart all 3,000 were stored exactly once, and the 10 redelivered duplicates were dropped.

Redis is a cache with persistence turned off, and every key is documented with how it rebuilds from Postgres, so losing it costs a few seconds rather than data.

Status

Targeting launch on November 8, 2026, with a live demo to follow.