Having a pool at home is fun, but comes with great responsibility. My biggest worry is monitoring if anyone is near the pool unattended (especially younger kids). My biggest annoyance is making sure the pool water line never goes below the pump entry, which would run the pump dry and destroy it costing $$$ in repairs.
I have recently figured out how to use a Raspberry Pi with OpenCV and TensorFlow, along with a water level sensor and a solenoid valve to resolve both issues - and have fun doing it!
It turns out to also be a great alarm system too - motion activated, AI-controlled, infinitely customizable.
Let's dive in.
In this instructable we will show how to:
All the components are readily available from Amazon. Feel free to experiment and exchange components - that's half the fun!
Raspberry Pi is a great little computer. It costs just $35, runs consistently, and has lots of compatible software and hardware. Setting it up is quite easy:
Linux raspberrypi 4.14.98-v7+ #1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Mon May 13 10:41:40 2019 from 104.36.248.13 pi@raspberrypi:~ $
OpenCV is an amazing collection of image manipulation functions for computer vision. It will allow us to read images from the Webcam, manipulate them to find areas of motion, save them and more. Setup on the Raspberry Pi is not difficult but requires some care.
Start by installing virtaulenvwrapper: we will use python to do all of our programming, and virtualenv would help us keep dependencies separate for OpenCV and TensorFlow vs. Flask or GPIO:
pi@raspberrypi:~ $ sudo pip install virtualenvwrapper
Now you can execute "mkvirtualenv" to create a new environment, "workon" to work on it, and more.
So, let's create an environment for our image manipulation, with python 3 as the default interpreter (it's 2019, there is no reason to stick with the older python 2):
pi@raspberrypi:~ $ mkvirtualenv cv -p python3 ... (cv) pi@raspberrypi:~
We are now ready to install OpenCV. We will mostly follow the excellent tutorial in Learn OpenCV. Specifically follow their step 1 and 2:
sudo apt -y update<br>sudo apt -y upgrade ## Install dependencies sudo apt-get -y install build-essential checkinstall cmake pkg-config yasm sudo apt-get -y install git gfortran sudo apt-get -y install libjpeg8-dev libjasper-dev libpng12-dev sudo apt-get -y install libtiff5-dev sudo apt-get -y install libtiff-dev sudo apt-get -y install libavcodec-dev libavformat-dev libswscale-dev libdc1394-22-dev sudo apt-get -y install libxine2-dev libv4l-dev cd /usr/include/linux sudo ln -s -f ../libv4l1-videodev.h videodev.h sudo apt-get -y install libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev sudo apt-get -y install libgtk2.0-dev libtbb-dev qt5-default sudo apt-get -y install libatlas-base-dev sudo apt-get -y install libmp3lame-dev libtheora-dev sudo apt-get -y install libvorbis-dev libxvidcore-dev libx264-dev sudo apt-get -y install libopencore-amrnb-dev libopencore-amrwb-dev sudo apt-get -y install libavresample-dev sudo apt-get -y install x264 v4l-utils sudo apt-get -y install libprotobuf-dev protobuf-compiler sudo apt-get -y install libgoogle-glog-dev libgflags-dev sudo apt-get -y install libgphoto2-dev libeigen3-dev libhdf5-dev doxygen sudo apt-get install libqtgui4 sudo apt-get install libqt4-test
Now we can just install OpenCV with python bindings inside the cv virtualenv (you are still in it, right?) using
pip install opencv-contrib-python
And that's it! We have OpenCV installed on our Raspberry Pi, ready to capture photos and videos, manipulate them and be cool.
Check that by opening a python interpreter and importing opencv and check that there are no errors:
(cv) pi@raspberrypi:~ $ python Python 3.5.3 (default, Sep 27 2018, 17:25:39) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import cv2 >>>
TensorFlow is a machine learning / AI framework developed and maintained by Google. It has extensive support for deep-learning models for a variety of tasks including object detection in images, and is now fairly simple to install on Raspberry Pi. The performance of its lightweight models on the tiny Pi is around 1 frame per second, which is perfectly adequate for an application like ours.
We will basically follow the excellent tutorial by Edje Electronics, with modifications made possible by more recent TensorFlow distributions:
pi@raspberrypi:~ $ workon cv (cv) pi@raspberrypi:~ $ pip install tensorflow (cv) pi@raspberrypi:~ $ sudo apt-get install libxml2-dev libxslt-dev (cv) pi@raspberrypi:~ $ pip install pillow lxml jupyter matplotlib cython (cv) pi@raspberrypi:~ $ sudo apt-get install python-tk
Now we need to compile Google's protobuf. Just follow the instructions in step 4 of the same excellent tutorial
Finally, clone and setup TensorFlow's model definitions - follow step 5 in Edje Electronics tutorial
Feel free to follow their example in step 6 as well, it is a great introduction to object detection on the Raspberry Pi.
Let's start by testing that OpenCV can interface with our webcam: ssh into the Raspberry Pi, move to the cv virtualenv (workon cv), open a python interpreter (just type python), and enter the following python commands:
import cv2
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
ret, frame = cap.read()
print('Read frame size: {}x{}'.format(frame.shape[1], frame.shape[0])
With any luck you will see that OpenCV was able to read an HD frame from the camera.
You can use cv2.imwrite(path, frame) to write that frame to disk and sftp it back to take an actual look.
The strategy to detect motion is fairly straight forward:
I have encapsulated the code to do this in the DeltaFinder python class that you can find in my github here
If you have followed the TensorFlow installation procedure, you have already tested that you have TensorFlow installed and working.
For the purpose of detecting people in a general outdoor scene, models that are pre-trained on the COCO data set perform quite well - which is exactly the model we have downloaded at the end of the TensorFlow installation. We just need to use it for inference!
Again, I have encapsulated the model loading and inference in the TFClassify python class to make things easier, which you can find here.
The easiest way to access the object detection results is a web browser, so let's set up a web server on the Raspberry Pi. We can then set it up to serve up pictures from a given directory.
There are multiple options for a web server framework. I chose Flask. It is extremely configurable and easy to extend with Python. Since the "scale" we need is trivial, it was more than enough.
I suggest installing it in a new virtualenv, so:
pi@raspberrypi:~ $ mkvirtualenv webserv (webserv)pi@raspberrypi: ~ $ pip install Flask
Note that with a normal network setup it will only be reachable when your browser is on the same wireless LAN as your Raspberry Pi. You could create a port mapping / NAT configuration on your Internet router to allow external access - but I recommend against that. The code I wrote does not attempt to provide the security you would need when allowing general Internet access to your Raspberry Pi.
Test your installation by following the Flask quick start guide
I really want to get mobile notifications when events occur. In this case, when a person is detected and when the water level goes low. The simplest way I found to do that, without having to write a custom mobile app, is using IFTTT. IFTTT stands for "If This Then That" and enables many types of events to trigger many types of actions. In our case, we are interested in the IFTTT Maker Webhook trigger. This allows us to trigger an IFTTT action by making an HTTP POST request to the IFTTT server with a special key assigned to our account, along with data that specifies what happened. The action we take can be as simple as creating a notification on our mobile device using the IFTTT mobile app, or anything more complex than that.
Here is how to do that:
import requests
requests.post('https://maker.ifttt.com/trigger/PoolEvent/with/key/<YOUR KEY>', json={"value1":"Hello Notifications"})Before proceeding with this step TURN OFF your Raspberry Pi: ssh to it and type "sudo shutdown now", then disconnect it from power
Our goal is to turn on and off the power supply to a solenoid valve - a valve that can open or close the water supply based on 24V AC power it gets from a power supply. Relays are the electrical components that can open or close a circuit based on a digital signal that our Raspberry Pi can provide. What we do here is hook up a relay to these digital signal pins of the Raspberry Pi, and have it close the circuit between the 24V AC power supply and the solenoid valve.
The pins on the Raspberry Pi that can act as digital input or output are called GPIO - General Purpose Input/Output and they are the row of 40 pins on the side of the Pi. With the Pi turned off and insert the relay HAT firmly into it. The HAT I chose has 3 relays in it, and we will use just one of them. Imagine all you can do with the other two :)
Now turn the Raspberry Pi back on. The red "power" LED on the relay HAT should turn on, indicating it is getting power from the Pi through the GPIO. Let's test that we can control it: ssh into the Pi again, enter python and type:
import gpiozero dev = gpiozero.DigitalOutputDevice(26, initial_value = True) dev.off()
You should hear an audible "click", indicating that the relay is engaged, and see a LED turn on showing that the first relay is in the connected position. You can now type
dev.on()
Which would turn the relay to the "off" position (odd, I know...) and exit() from python.
Now using jumper cables and the longer cable connect the relay between the 24V power supply and the solenoid valve. See the diagram. Finally, connect the solenoid valve to a faucet using the adapters and get ready to test it all by repeating the commands above - they should turn the water on and off.
Attach a hose to the solenoid valve and put the other end deep in the pool. You now have a computer-controlled pool top-off system, and it's time to connect a sensor to tell it when to run.
A water level sensor is simply a float that connects an electrical circuit when the float is down, and breaks it when it floats up. If you insert it in the pool at the right height the float will be up when the water level is adequate but fall down when there aren't enough water.
For the Raspberry Pi to know the status the water level sensor we need the Pi to sense an open or closed circuit. Luckily that is very simple: the same GPIO connectors that we use as digital output to control the relays can act as inputs (hence the I in GPIO). Specifically, if we connect one wire of the sensor to +3.3V on the GPIO connector and the other sensor wire to a pin that we configure as pull-down input (meaning it will be normally at GND voltage level), that pin will measure a digital "high" or "on" voltage only when the water level sensor closes the circuit - when the water level is low. I used GPIO pin 16 as input, which I marked in the image above.
The python code to configure the pin as input and test its current state is:
import gpiozero level_input = gpiozero.Button(16) water_low = level_input.is_pressed
One potential challenge is that when the sensor just changes state it would oscillate fast between on and off states. The solution to that is known as "debouncing" and looks for a consistent state change before taking action. The GPIOZERO library has code to do that, but for some reason that code did not work well for me. I wrote a simple loop to trigger IFTTT alerts when a consistent state change is detected, which you can find in my repository here.
That's it. Our setup is complete. You can write your own code to tie things together into a full system, or use the code I provide. To do that just create the directory structure and clone the repository, like so:
mkdir poolpi cd poolpi git clone https://github.com/rafitzadik/PoolPiGuy.git
Next, edit the files named ifttt_url.txt in the motion_alert and water_level directories to have the URL for your own IFTTT web hook with your secret key. You could use two different web hooks for different actions.
Finally, we want this code to run automatically. The easiest way to accomplish that is through the Linux crontab service. We can add some crontab lines for two main tasks:
To do that type crontab -e which will open up your nano text editor. Add the following lines to the bottom of the file:
0 1 * * * find /home/pi/poolpi/output -type f -name "*.avi" -mtime +1 -delete 0 2 * * * find /home/pi/poolpi/output -type f -name "*.jpg" -mtime +7 -delete @reboot python3 /home/pi/poolpi/motion_alert/webserv/webserv.py @reboot python3 /home/pi/poolpi/motion_alert/motion_obj_alert.py @reboot python3 /home/pi/poolpi/water_level/test_water_level.py
Finally, reboot your Raspberry Pi. It is now ready to keep your pool full and safe.
Do tinker with the setup, the code, and don't forget to star my github repository and comment on the instructable if you find it useful. I am always looking to learn more.
Happy making!