Aly Farghali

All work

SpinCycle

Live washer and dryer availability for campus laundry rooms, read from vibration sensors on the machines, with an email when your load is done.

When
Oct 2026 – present, in progress
Built with
ESP32-C6, MQTT (Mosquitto), Go, TimescaleDB, Redis, FastAPI, React, Prometheus
Layers

Touches: Hardware, Firmware, Backend, Frontend

Code
GitHub

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

  1. Sensor

    Accelerometer on each machine, sampled at 100 Hz

    Twyla

  2. ESP32-C6

    Vibration RMS every 5 s over MQTT/TLS

    Twyla

  3. Mosquitto

    Per-device credentials; each device can only publish to its own topics

    Aly

  4. Ingest (Go)

    Validate, dedupe, batch-write, and turn vibration into machine state

    Aly

  5. TimescaleDB + Redis

    History in Postgres, live state in a cache that can be rebuilt

    Aly

  6. API + web

    Live status, busy times, and finished-load emails

    Aly

How a reading moves through SpinCycle, from the machine to your phone.

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.

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.