Building a Smart Aquarium Temperature Monitor

From ESP32 to FastAPI - How I built a complete IoT solution to keep my fish happy and healthy

← Back to Blog

fish

My family has recently added 2 betta tanks swimming with aquatic life! It's been a great hobby and source of joy and conversation, but early on we discovered one of the tanks heaters was faulty, and another time it simply came unplugged. After too many close calls, I decided to build my own aquarium monitoring system that would alert me immediately if temperatures went out of range. Over a weekend, I built a comprehensive IoT solution that I'm genuinely proud of.

The Challenge

I needed a system that could:

The Solution: A Three-Part Architecture

tl;dr - Code is on github

1. The Hardware: ESP32 + DS18B20 Temperature Sensor

esp32+ds18b20

The heart of the system is an ESP32 microcontroller paired with a waterproof DS18B20 temperature sensor. The ESP32 is perfect for this application. It's got built-in WiFi, more than enough processing power, and runs MicroPython beautifully. I've got a few of these doing various tasks around the house already (alerting when laundry cycles complete, etc) so it's a platform I know and trust.

I chose the DS18B20 because it's specifically designed for temperature monitoring applications. It's accurate, waterproof, and communicates over a simple one-wire protocol. Plus, you can daisy-chain multiple sensors if I wanted to add water level monitoring or other sensors.

2. The Firmware and A New Requirement: Smart OTA Updates with Robust Error Handling

Here's where things get interesting. During development I ran into some issues with the format of the json I'm sending over the network. I got frustrated having to connect my laptop to the device every time I needed to make a change. I'd write the new firmware, then connect the cable to a dongle to my macbook, and finally use unreliable tools like ampy to maybe upload it on the 3rd try. So, I implemented a two-stage boot process:

Stage 1: The OTA Bootloader (boot.py)

  • Connects to WiFi using credentials from a config file
  • Downloads and executes the latest code from my git server
  • Implements a retry loop on failures along with hardware watchdog protection

Stage 2: The Firmware (esp_runtime.py)

  • Reads temperature from the DS18B20 sensor
  • Collects comprehensive system metrics (memory usage, WiFi signal strength, CPU frequency)
  • Posts data to the API server every 60 seconds
  • Implements intelligent reset behavior for graceful recovery from different failure modes

The beauty of this approach is that I can push firmware updates remotely just by committing to the master branch of my local git server. If I want to add a new feature or fix a bug, I just push a new file, and the device automatically pulls the new code on its next boot cycle. I can then just press a reset button on the board, and boom, restart with fresh code.

3. The Backend: FastAPI with Smart Alerting

The server component is a Dockerized Python/FastAPI application that does much more than just log temperatures:

Data Management:

  • Stores readings in SQLite with full JSON payloads
  • Provides REST endpoints to query historical data
  • Handles device identification and timestamps server-side

Smart Alerting System:

  • Monitors temperature thresholds (I've set mine to 73.4°F - 80.6°F)
  • Tracks device health and alerts if a sensor goes offline
  • Sends SMS alerts via email-to-SMS gateways
  • Implements alert deduplication to prevent spam

Background Monitoring:

  • Runs periodic checks for stale readings
  • Automatically clears alert states when devices come back online

Real-World Performance

The system has been tested and running smoothly all weekend. The ESP32 draws minimal power (a future project involves running one off an 18650 battery and small solar cell for truly wireless operation), and the temperature readings align with my existing physical sensors within half a degree. More than accurate enough for fish care.

I've successfully tested the alerting system by warming the sensor in my hand, which triggered an immediate SMS notification. So far, both tanks have maintained stable temperatures, which is exactly what I want to see.

The OTA update system will be a game changer for my broader IoT ecosystem. Being able to push firmware updates remotely without plugging my laptop into a controller mounted on the tank opens up possibilities for all my ESP32 projects around the house.

Next Steps and Future Improvements

The system is working great as-is, but there's always room for enhancement:

Quick Hits:

  • Automatic runtime restarts every few hours to grab the latest code without manual power cycles
  • Data buffering during network outages to prevent lost readings
  • Grafana dashboard for historical data visualization and trend analysis

Longer-term possibilities:

  • Expanding to support additional sensor types (pH, dissolved oxygen, water level)
  • Integration with other home automation systems for coordinated responses

Bringing it all together

The project encompasses several things I enjoy tinkering with: embedded programming, API design, custom hardware, and system reliability.

What I'm most proud of is threading the needle between sophistication and simplicity. The system is robust enough to handle real-world conditions (network outages, power cycles, hardware failures) while remaining maintainable and extensible. The OTA architecture means I can iterate and improve the system over time without physical access to the hardware, and will be something I can reuse in future projects.

Plus, there's something deeply satisfying about knowing that my bettas are happy and healthy, and if anything goes wrong, I'll know about it. Good technology should make life better, not more complicated.

The code is available on my GitHub if you want to dive into the implementation details or adapt it for your own monitoring needs.

With apologies: I'd have made this blog shorter, but I ran out of time.