---
title: "The Piquarium Project"
description: "Building a fish tank monitoring system using serverless databases and a Raspberry Pi B+ 🍇"
datePublished: 2021-12-15T12:30:00.000Z
slug: "the-piquarium-project"
previewImage: "/preview/the-piquarium-project.jpeg"
permalink: "/blog/the-piquarium-project"
---
import Image from "../../components/OptimizedImage.astro";
import Piquarium from "../../assets/images/piquarium/piquarium-1-min.jpeg";
import Piquarium2 from "../../assets/images/piquarium/piquarium-2.jpeg";
import Piquarium3 from "../../assets/images/piquarium/piquarium-3.jpeg";
import Piquarium4 from "../../assets/images/piquarium/piquarium-4.jpeg";
import Piquarium5 from "../../assets/images/piquarium/piquarium-5.jpeg";
import Piquarium6 from "../../assets/images/piquarium/piquarium-6.jpeg";
import DS18B20 from "../../assets/images/piquarium/ds18b20-wiring-diagram.jpeg";
import chart from "../../assets/images/piquarium/chart.jpeg";

A while ago, my Dad bought a **really** large aquarium. He wanted to use it to display the [_Symphysodon_](https://en.wikipedia.org/wiki/Discus_%28fish%29) fish.

<figure>
	<Image src={Piquarium} alt="Image of the aquarium" />
	<figcaption>The aquarium measures almost 180cm x 50cm</figcaption>
</figure>

It was a functional setup, where my Dad connected a few heaters and pumps to get the water up to the ideal temperature of 30C for the fishes. However, if he wanted to check up on it while he wasn’t home, he had to call someone at home to check what the LED displays read.

<figure>
	<Image src={Piquarium2} alt="Image of the pumps" />
	<figcaption>Archaic</figcaption>
</figure>

And with [Delhi’s winters](https://www.ndtv.com/delhi-news/delhi-weather-cold-wave-sweeps-through-delhi-as-temperature-drops-to-3-8-degrees-celsius-2680938), if a heater malfunctions the fishes could become sick, or worse, die.

I had an old Raspberry Pi lying around which I only ever used to run PiHole, long ago. And thus, the Piquarium project was born.

## Equipment

<figure>
	<Image
		src={Piquarium3}
		alt="Image of the DS18B20 probe, a couple of Dupont connectors and a 4.7KΩ resistor"
	/>
	<figcaption>A DS18B20 probe, a couple of Dupont connectors and a 4.7KΩ resistor</figcaption>
</figure>

- 2x DS18B20 Temperature probes
- 2x 4.7KΩ resistors
- 1x Raspberry Pi B+

<figure>
	<Image src={DS18B20} alt="Wiring diagram for the DS18B20" />
	<figcaption>Wiring diagram for the DS18B20</figcaption>
</figure>

I wanted the entire setup to be as tiny as possible, but almost every single guide for using a DS18B20 involved the use of a breadboard. I probably could have made it work but instead I decided to make that my last resort.

It seemed like the Raspberry Pi 3B+ supports having an internal [Pull-Up Resistor](https://github.com/raspberrypilearning/physical-computing-guide/blob/master/pull_up_down.md). This [post](https://blog.robertelder.org/ds18b20-raspberry-pi-setup-pullup/) by Robert Elder and the _fantastic_ accompanying [video](https://youtu.be/Bg9yJSrkMl8) confirmed my findings AND proved that couple of DS18B20 probes without an external resistor could work just fine.

## Setup

Rather than using a soldering iron to connect the wires from the probe, I went the unnecessarily complicated route of using a Dupont connector.

And then simply connecting the Dupont connectors to their GPIO pins:

<figure>
	<Image src={Piquarium4} alt="Simple GPIO connections to the Pi" />
	<figcaption>Simple GPIO connections to the Pi</figcaption>
</figure>

Finally, to enable the pull-up resistor on GPIO pin 04, I use this Python script:

```python
import RPi.GPIO as GPIO

GPIO_PIN_NUMBER=4
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN_NUMBER, GPIO.IN, pull_up_down=GPIO.PUD_UP)
```

To make sure it persists through restarts, I added the script as a `systemd` unit.

Now to actually read the values from the temperature probes:

```python
import  glob

base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')
device_file = '/w1_slave'

def  read_temp_raw(device_file):
    f = open(device_file, 'r')
    lines = f.readlines()
    f.close()
    return  lines

def  read_temp():
    result = []
    for  folder  in  device_folder:
        lines = read_temp_raw(folder + device_file)
        value = lines[1].split()[-1].split("=")[1]

        value = int(value)
    result.append((folder.split('/')[-1], value))

    return  result
```

All readings from the sensors will be stored in the `/sys/bus/w1/devices` directory with each sensor having a name similar to `28-19a09fbc2` with the `w1_slave` file holding the values we need.

I had two sensors connected, I planned to use one for the primary tank and one for the ambient temperature.

## Delivering the Data

Now that I was able to read values from the sensors, I moved on to storing it and actually making it available for processing/consumption.

The day I was looking for a solution is also when PlanetScale (a serverless database platform) [entered general availability](https://planetscale.com/blog/announcing-planetscale-the-database-for-developers). On a whim, I decided to use their free-tier to store my temperature readings every minute from the Raspberry Pi.

<figure>
	<Image src={Piquarium5} alt="PlanetScale Dashboard" />
	<figcaption>PlanetScale Dashboard</figcaption>
</figure>

For making the data available, I created a simple Telegram bot which queries the PlanetScale MySQL database. It would’ve been cleaner to have a dynamically updating website but I’m not very proficient with generating charts on the fly on the front-end. I did look into using Observable as well, but it seems I cannot embed their charts when connected to an external database.

<figure>
	<Image src={chart} alt="A Chart of the Last 1 Month" />
	<figcaption>A Chart of the Last 1 Month</figcaption>
</figure>

And the bot:

<figure class="w-48">
	<Image src={Piquarium6} alt="The final Telegram Bot" />
	<figcaption>The final Telegram Bot</figcaption>
</figure>

## Wishlist

- Add temperature alerts whenever it deviates from the weekly average
- More interesting statistics
- Measure pH level of the water in the tank (I’m unable to find much on this topic, yet)
- Add a camera feed
- Automatically turn the heaters on and off