← All work

Everyday Automation

Purrtector

A litter box activity tracker that watches for feline cystitis: every visit and Milo's weight, logged by a scale under the box.

Year
2025~now
Role
Solo — firmware, dashboard, printed mounts
Status
In development — hardware being assembled
Purrtector, a litter box activity tracker and early warning for feline cystitis: the cat-from-behind logo, an ESP32 board, and the phone dashboard's October calendar with a red cross on today, with a crying cat giving a thumbs up in the corner
The board under the box and the phone dashboard. Dashboard data is simulated.

TL;DR

A litter box monitor built to help prevent cystitis in my cat. An ESP32 and load cells under the box track his body weight and toilet activity.

Dashboard

Built phone-first: one column, and every indicator opens its own trend in place.

  • Calendar — one mark per day from the day's status: asterisk for normal, a smaller one for a warning, a red cross for danger.
  • Indicator sheet — the selected day's eight values, each as a bar against the 14-day band. Tap a row and its trend opens under it, over 2 weeks, 1 month or 3 months.
  • Alerts — behind the bell, ordered by severity.
  • Log — every visit of the day, with a tag control that feeds the urine/stool model.
Phone view: header with weight 4.17 kg and a bell with two alerts, October calendar with an asterisk per normal day and a red cross on the 11th with 4 failed
Calendar.
Phone view: indicator sheet with Litter box visits tapped open, showing its one-month trend with the usual band and today's 8 above it
Tap a row for its trend.
Phone view: alert panel with a red card, 3 failed attempts in the last 6 hours, and a yellow card, 8 visits today against a usual 4.6
Alerts.

Circuit

Load cells under the box, an ESP32 that detects visits, and a web service the phone reads.

  • Sensing — four 50 kg load cells into an HX711.
  • ESP32 — detects each visit and logs it.
  • Web service — stores events and serves the dashboard to the phone.
  • Tail gadget — a tail that rises when he is well and drops when something is off, so his health reads at a glance without opening the dashboard. Still being built.
System diagram: litter box on four load cells into an HX711, into an ESP32 running the visit detector, which posts each event over HTTPS to a web service with an events API, database, indicators and live dashboard, read in any browser; a tail gadget, still being built, hangs off the web service
Highlighted: where detection happens, and the path to the phone.
Cat Tail Speak chart: nine cat silhouettes, each tail position labelled with a mood — tail straight up for happy to see you, low and tucked for worried, puffed for scared or mad
The tail gadget borrows this vocabulary: up when he is well, down when something is off.
OpenSCAD render of the load cell holder: a block with a square pocket for the cell, a relief under the beam, two countersunk screw holes, and a separate round foot
Cell holder and foot, OpenSCAD.

Algorithm

One weight signal is turned into events on the device, and events into daily judgements in the browser.

  1. Visit detection (C++, on device) — a state machine over the smoothed weight. Over 1.5 kg for 1 s means a cat is in; under 0.7 kg for 2 s means it left; the box must then hold within 15 g for 5 s. The settled difference is the deposit, the mean load while inside is the cat's weight. Visits under 5 s are dropped.
  2. Cleaning and drift — a change over 20 g with no cat is logged as cleaning, as is a press that leaves the box lighter. Idle drift is tracked with an EMA (α = 0.02). A “visit” over 15 minutes is an object on the scale and triggers a re-baseline.
  3. Classification — deposit under 10 g: a failed attempt (or a hop-in if under 30 s). Urine and stool weigh alike, so visits I tag train a nearest-centroid model on log mass and log duration; after five of each it labels the rest as estimates.
  4. Personal baseline — eight daily indicators, each judged against the mean ± spread of the cat's own previous 14 days (defaults until 3 days exist; count bands rounded to whole numbers).
  5. Alerts — three failed attempts within 6 h is danger; any failure, 1.5× the usual visits, or 12 h without output is a warning.
Visit detector state machine: Idle to Entering above 1.5 kg, to InBox after 1 s, to Exiting below 0.7 kg, to Settling after 2 s, and back to Idle once stable within 15 g for 5 s, emitting a Visit or Cleaning event; IdleShift handles changes with no cat; a 15-minute guard re-baselines
The detector is plain C++ with no Arduino dependency, so it is unit-tested on the laptop.

Stack

LayerWhat is used
Sensing4 × 50 kg half-bridge load cells, HX711 (bogde/HX711), median filter
FirmwareESP32 Arduino core on PlatformIO, C++
StorageLittleFS (events.csv, labels.csv), NVS for calibration
NetworkWi-Fi STA, HTTPS event upload, NTP time, ArduinoOTA
Web serviceEvents API, database, dashboard served to phone and laptop
DashboardVanilla JS + hand-written SVG charts, phone-first layout, no build step
HardwareOpenSCAD, 3D-printed PETG holders
Testsg++ with ASan/UBSan for the detector and alert logic; Node for dashboard metrics; Python mock device
Behind the project — how it started and how it changed

The problem

Male cats have a narrow urethra, and cystitis can block it within days. The early signs are behavioural — more visits, longer stays, less output — so the project measures exactly those, plus body weight, with nothing but a scale.

How it changed

Each version below exists because the one before it was wrong about something specific.

  1. v0retired

    ToF sensor at the entry

    Why
    Wanted presence detection independent of weight.
    Change
    Planned a time-of-flight sensor on the lid.
    Result
    Cut before building: a 1.5 kg step on the scale already marks presence.
  2. v0retired

    Cloud backend and push alerts

    Why
    The default IoT design ships every event to a server.
    Change
    Sketched a hosted database and notifications.
    Result
    Cut for v1: the ESP32 kept the log on flash and served the dashboard on the home Wi-Fi only.
  3. v1

    Single weight signal, on-device state machine

    Why
    Presence, duration, deposit and cat weight all have to come from one signal.
    Change
    Six-state detector in plain C++, LittleFS log, browser-side indicators with a 14-day personal baseline.
    Result
    Firmware builds; 46 host-run tests pass against simulated traces and a mock device. Hardware assembly is next.
  4. v2

    Web service, live on the phone

    Why
    A dashboard that only works at home is no use when the warning comes while I am out.
    Change
    The ESP32 posts each event over HTTPS to a web service that stores them, computes the indicators and serves the dashboard.
    Result
    In progress.

Built with

  • ESP32
  • C++ / PlatformIO
  • HX711 + load cells
  • LittleFS
  • HTTPS / OTA
  • Web service
  • JavaScript
  • SVG charts
  • OpenSCAD
  • 3D printing