An ESP32-C6 based smart litter box monitor that tracks cat visits, measures waste weight, and monitors air quality. Integrates with Home Assistant via MQTT autodiscovery. The project goal was to use cheap electronics to modify an off the shelf litterbox into a smart litterbox.
Disclaimer: This project implemented with support of CoPilot. Architecture is mine, but some of the methods are fully implemented by CoPilot.
| Component | Model | Role |
|---|---|---|
| Microcontroller | ESP32-C6 | Main MCU |
| Gas sensor | MICS-5524 | Air quality / odor detection (A1) |
| PIR motion sensor | HW740 | Presence detection (GPIO 2) |
| Load cell amplifier | HX711 | Weight measurement (DOUT=20, SCK=19) |
| Load cells | 4x 50 kg | Sensing elements for weight |
In addition to this, I designed two PCBs for the project. One that is mounted to the ceiling of the litterbox. It houses the ESP32, the MICS-5524 and the PIR sensor. The second board is mounted to the back of the litterbox and houses the HX711 as well as a micro-usb port for powering. A cable goes into the litterbox to connect to the first PCB.
- Cat visit detection — PIR motion sensor triggers weight analysis
- Cat weight estimation — calculates cat weight as the difference between peak stable weight and post-visit weight
- Air quality monitoring — low-pass filtered MICS-5524 readings with a configurable baseline delta
- Cleaning mode — detected automatically when weight drops sharply below a threshold
- Home Assistant integration — full MQTT autodiscovery with 20+ entities
- Runtime configuration — all key parameters adjustable via MQTT at runtime
- OTA updates — firmware updates over WiFi via ArduinoOTA
Fill in your credentials in config.h:
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
#define MQTT_BROKER ""
#define MQTT_PORT 1883
#define MQTT_USER "" // Leave empty if no authentication
#define MQTT_PASSWORD "" // Leave empty if no authentication
#define MQTT_CLIENT_ID ""
#define OTA_HOSTNAME ""
#define OTA_PASSWORD ""Adjust the timezone if needed in smart-litterbox.ino:
const long gmt_offset_sec = 3600; // UTC+1
const int daylight_offset_sec = 3600; // DST offsetCalibrate the load cell by setting the calibration factor:
const float HX711_CALIBRATION = 27.96;All topics are prefixed with esp/smart-litterbox/.
| Topic | Payload |
|---|---|
weight |
{"weight": x.xx} — live weight (g) during a visit |
cat_analysis |
{"cat_weight": x, "waste_weight": x, "highest_stable_weight": x} |
presence |
{"detected": true/false} |
airquality/mics5524 |
{"raw": x, "baseline": x, "delta": x} |
last_use |
ISO 8601 timestamp of last cat visit |
last_cleaned |
ISO 8601 timestamp of last cleaning |
cleaning_mode/state |
ON / OFF |
| Topic | Payload | Effect |
|---|---|---|
calibrate |
CALIBRATE |
Re-baseline air quality sensor |
tare |
TARE |
Tare the scale |
filter_alpha |
0.01–1.0 |
Set low-pass filter alpha |
analyzer/stability_threshold |
5–100 (g) |
Set weight stability threshold |
analyzer/stability_samples |
3–20 |
Set stability sample window |
analyzer/sampling_rate |
1–10 (Hz) |
Set weight sampling rate |
analyzer/min_weight_threshold |
500–5000 (g) |
Set minimum cat weight |
motion_timeout |
10–300 (s) |
Set motion clear timeout |
cleaning_threshold |
-5000–0 (g) |
Set cleaning trigger threshold |
- PubSubClient — MQTT client
- HX711 — load cell library
- ArduinoOTA (included with ESP32 Arduino core)
The internal PCB is mounted in a small casing and attached to the ceiling of the litterbox. It houses the ESP32-C6, the MICS-5524 gas sensor, and the PIR motion sensor.
From it, a cable with communications and power goes to the external PCB on the back of the litterbox. The external PCB houses the HX711 load cell amplifier and a micro-USB port for power.
The four load cells are mounted under the base of the litterbox in a cross pattern, one at each corner.




