HomeAbout MeProjectsContactMeow

Building an Over-engineered Basement Monitor

24 Nov 2024

My basement, like many basements, is mortally dependent on a sump pump. To be more specific, it's basically always slowly flooding, and the sump pump's job is to take that water and push it somewhere else. Normally it does its job fine, but if I lose power, or if the sump pump dies, my basement will gradually flood. My basement is unfinished, so this isn't that big of a deal, but it's certainly annoying and something I'd rather avoid.

The software developer in me hates single points of failure, because eventually, they will be hit. Ideally there would be two pumps. One would be fed by mains and one would be fed by a battery (which is then charged off of mains). But of course, the software developer in me is also scared of complexity, because it will fail too, and in ways that can't be easily anticipated. Not only that, but the current system works, and messing with it just introduces exciting opportunities to screw it up.

So instead, I just monitor the system. As long as my basement isn't full of water, I'm happy. And this has worked pretty well, except for another issue - I like to travel. Ideally I would have a way to check in on my basement from afar. So, I decided to build my own basement monitor, primarily using whatever I could find around the house.

The basic idea was simple. I would modify a CATS mobile transceiver to read water levels from sensors. This data would be transmitted in a CATS packet. A CATS I-Gate would pick it up, and forward it to FELINET. Then I'd have a daemon fetch these packets off of FELINET and send the data to a StatsD daemon. Finally, I would display the data on a Grafana dashboard.

Okay, this might be a little over-complicated. And I might be biased because I did write the CATS protocol spec (as well as design the CATS mobile transceiver). But this meant I didn't have to run wires to my basement, nor did I have to rely on something finicky like WiFi (I've just never had great luck with it). It also made the project pretty straightforward, because I didn't have to reinvent the wheel too much.

The first step was getting the water sensors interfaced to the mobile transceiver. The sensors are essentially just two electrodes. Bias one electrode, and if there's water bridging them together, some current will flow to the other electrode. I used some 680K resistors to bias the output of the sensors to ground by default, and connected each sensor to a different analog input of the CATS mobile transceiver. I used 3 sensors in total so I'd be able to see roughly what the water level is at. They also provide some redundancy.

Updating the firmware was pretty straightforward. I'd just read the values from the analog pins and send them along in the CATS packet via the comment field.

Next was getting the metrics into Grafana. I ended up writing a completely new project for this, called felinet-statsd. This allows specifying a bunch of rules for extracting metrics from FELINET data in a config file. They can either be specified in terms of NodeInfo whisker values, or as a Regex to pull metrics out of a CATS comment whisker. This is overkill for what I need, but I hope this project is useful to others, and I wanted to make it as generic as possible.

Here's what my testing apparatus looks like. Yes, my desk it a mess, mainly due to other projects that are currently ongoing (and a few finished ones I have yet to clean up). I have the CATS mobile transceiver connected to a dummy load, with one of the water sensors dangling in water.

And here's the Grafana dashboard. A value of roughly 3000mV means the sensor is fully submerged, and near 0mV means the sensor is dry. What's interesting is the decay in sensor 2 at the end of the graph. I had submerged it in water and then let it dry out on the desk - you can see it evaporating as the graph drops.

And that's pretty much it - we're done! I mean, it still has to be deployed to the basement, but the core of the project is finished.

Although.

Wouldn't it be nice if I could also grab some weather data from the basement? There's a dehumidifier down there. It'd be nice to verify it's working too. And if the temperature drops below 0C, the pipes could freeze - I should monitor that as well!

Ah, scope creep. Luckily not a lot, though. I had a spare BME280 sensor kicking around, which I had initially purchased for a different project that didn't end up happening. This sensor reads humidity, temperature, and even pressure - everything I need and then some!

The BME280 can be configured either as a SPI device or an I2C device. Unfortunately, the remaining GPIO on the CATS mobile transceiver supports neither. I was considering bitbanging SPI, or piggy-backing off the SPI used for the rf4463. I also checked the now-unused GPS header, but its GPIO also didn't support what I needed. The unpopulated chip on the board ended up saving me - two of the GPIO it was connected to supported I2C's SCL and SDA!

I didn't have any sufficiently-small connecting wire. I ended up using pieces of component legs to break out the connections on the board. This worked well until I accidentally ripped up a trace. There was just so much leverage from the wires that it was basically inevitable. I applied a liberal amount of hot glue to add a bit of structural integrity and closed up the transceiver for the final time.

On the BME280 side, soldering was straightforward. It's on a breakout board so it was just a matter of connecting the wires where they needed to go.

Updating the firmware to support the BME280 was just as straightforward as last time. There's a Rust crate for interfacing with the BME280, so it was just a matter of pulling that into the firmware. Ideally some of these values would be added into the NodeInfo whisker, but I added everything to the comment field instead. It was just easier, and this is a one-off project anyway.

On the felinet-statsd side all I had to do was update the config. Oh, and I had to create some new Grafana panels.

Back onto my project desk it went so that I could verify everything worked correctly. On the right is a CATS Companion - another project I'm working on. I find it super useful for debugging because it displays CATS packets right on its screen. In the comment field, the first three values are the raw sensor values (mV). This is followed by temperature (Celsius), pressure (pascals), and humidity (percent).

Satisfied everything was working, I moved it into my basement. I bolted the three sensors onto the bottom of a shelf. The bottom one is right at ground level, and I placed the rest so that they were a few centimetres apart from each other. I also replaced the dummy load with a proper dipole antenna, and replaced my bench-top power supply with a wall wart. I placed the BME280 as far away as possible so that it wouldn't be influenced by the temperature of the transceiver itself.

And here's the dashboard I ended up with. Aside from all the sensors, I'm also monitoring supply voltage and uptime. Supply voltage is useful in case my cheap adapter starts to fail, and uptime lets me check for power blips (and/or the firmware crashing and restarting for whatever reason). My basement humidity is right where the dehumidifier says it is - 45% - but it's a lot chillier down there! One interesting thing is how the water sensor numbers have changed. They used to float around 50mV on my project bench, but down there they fluctuate quite a bit more - almost up to 250mV! I guess that's partly due to the humidity down there.

Pressure I really only added as a curiosity. It's neat that the pressure is noticeably higher in the basement than on my project desk, which is located on the first floor of my house. It's lower down, so I'm not too surprised, but it's cool the sensor is sensitive enough to pick it up.

In terms of source code for the project, I ended up with the following:

felinet-statsd

mobile-transceiver-software (custom branch)

And look, as over-engineered as this project is, it's still less complicated than the average Kubernetes-deployed CRUD app :P