Remotely Controlled Plant Tending Station
by Henkeify in Circuits > Raspberry Pi
881 Views, 6 Favorites, 0 Comments
Remotely Controlled Plant Tending Station
If you google beginner raspberry pi projects, a dozen tutorials for simple plant watering systems show up. The most basic form of which just include a raspberry pi and a relay which controls the flow of electricity to a water pump. This was also my very first raspberry pi project a couple of years ago, I was able to go on vacation and leave my precious tomato plants with a watering system which fed them once a day and even included a camera feed so I could see if the water tank was running out.
This project is basically an over-engineered version of that setup, this plant tending system includes following sensor inputs:
- Air temperature sensor (why wouldn't you need to know how hot your vegetables are?)
- Air humidity (Nothing you can do to control it anyways)
- Soil moisture (Now that's actually interesting, this shows if the pump is watering as it should)
- Soil temperature (Again, a bit pointless but oh so fun to include)
All of the above are presented on a raspberry pi hosted apache server, which also contains a camera live feed (live feed is maybe a bit exaggerated but we will dive more into details about that later) and the ability to set the time of watering and the duration of the daily watering via an html form. Speaking of said watering, the system also includes a relay which controls a small but rather powerful water pump which will provide your plant(s) with delicious water.
The system is divided into:
- 1 web server
- 2 php programs
- 1 css file (can be excluded if you don't want to mess with that)
- 2 python scripts
- 1 database which connects the python scripts with the php files on the server
This project was a bit of a learning experience for me, especially about i2c sensors so I will try my best to convey what I've picked up along the way.
This project may not be the best to start with if you are getting into tinkering with your pi, none of the parts are very advanced in themselves but put together they may be a bit much to chew. I will also make the assumptions that you know how to set up a pi, use a relay module and use the raspberry pi camera module (and if you are accustomed to 3D-printing your own stuff that's a big plus).
One thing which will not be included in this project is the lighting. Many simple pi-powered systems use a smaller led light to provide faux sun to the plant but I use a bigger high powered lamp myself, which is on a timer. If you use a low power lamp you can modify the code to make it work like the pump ans control it however you like. I am terrified by messing with mains power and would not think to encourage you to control it with a relay (mains power will kill you if you make mistake). However, I have a little project in mind which would make this possible without the hazard, but that's for another instructable... In the meantime, I encourage you to use a growing lamp controlled with a simple timer.
With that said, let's get into making!
Supplies
For this project you will need:
- Raspberry pi (I used an old 3B+ I had laying around)
- Memory card
- Peripepherals such as keyboard, mouse, HDMI-cable power supply for pi etc.
- Camera module (I think you can use a web cam instead but I haven't tried myself) https://www.raspberrypi.com/products/camera-module-v2/
- Combined air temperature and humidity sensor https://www.adafruit.com/product/2857
- Combined soil temperature and moisture sensor https://www.adafruit.com/product/4026
- You don't NEED to buy this but i recommend it. I didn't and I regret it bitterly: https://www.adafruit.com/product/3950 It is a pretty great wire adapter for connecting the sensor to the pi.
- Relay module link is really long (you only need one though)
- 3D-printed parts
- Some screws (it's not mandatory on any level but I recommend buying a kit with some M3-M6 screws in different lengths, like this)
- Some nylon spacer standoffs (Again, this is not mandatory for the project but I really like to use spacers. I recommend buying a kit similar to this)
- Jumper wires (male to female)
- Wire, like this
- Breadboard
- Water pump and fitting hose, something like this
- A 5V power supply for the water pump
Tools:
- Wire cutters
- Pliers
- Soldering Iron
- Wire stripper (You can of course do this with the wire cutter but that's hard work)
- Screwdriver
Print the Parts
I mounted all the electronics on a plate which is easy to install under my desk but you can skip this part if you desire to just duct tape everything to the wall like a cave man.
Slice the parts with your favorite slicer, I went with 20% infill which works great for something which will not carry any real load.
Connect All the Electronics
Connect the camera with the ribbon cable to the pi. Make sure it faces the right way (the shiny bits should face away from the USB-ports).
Solder headers to your temperature and humidity sensor and place it on the breadboard so that the channels VIN, SDA, SCL and GND each has one row on the breadboard. Connect jumper wires from the pi (3.3V, SDA, SCL and GND) to the breadboard on the corresponding rows. Connect you soil sensor to the breadbord by placing the wires in the corresponding rows. If you hold the soil sensor with the pointy bit to the left and the electronics facing towards you, the wiring is in falling order: GND, VIN, SDA, SCL.
Connect jumper wires from 5V, GND and a GPIO of your choice (I recommend 15) to the corresponding pins of the relay module.
Connect the water pump to the power supply with the relay as a switch somewhere on either cable. There are 3 ports to connect wires to the relay, use the one in the middle which is common and the one which is marked with something like NO, this means that the circuit is open normally and closes when the relay gets a signal from the microcontroller.
You might need to extend the wires to make everything fit your intended setup.
Assemble the pi, relay and breadboard to the mounting plate with the nylon standoffs and screw the camera to the adjustable camera mount.
Enable Camera
Open a terminal and enter the following command:
sudo raspi-config
This opens up the control panel of the pi.
Go to interface options and click enter.
Choose legacy camera and click enter.
Use arrow keys to move cursor to yes and click enter.
This will prompt you to restart your pi, the changes will not take effect until you do that.
Server Time
Connect your pi and boot it up.
Open a fresh terminal and enter the following command:
sudo apt install apache2 -y
This will install Apache, which is the web server we will use.
Open index.html file in the directory var/www/html and replace the content in it with "Hello World!".
Save it, open a browser and in the adress field, just type localhost followed by enter. If you see your previously saved message it means the install went well.
Delete the index.html afterwards, we won't need it.
Database Installation and Creation
In the terminal, type in the following commands:
sudp apt-get install mysql-server
After this, you will run the command
sudo mysql_sercure_installation
And when prompted with a lot of questions, just hit y to answer yes on all of them unless you know what you're doing, the you can make the decisions yourself.
When you are prompted to set root password, you have to come up with a good password which we will need to access the database later.
Now we will create a database and in that database we will create a table. In that table we will create a single entry with 6 columns. You might think is is unnecessary to use a database for just one row but it's not always about the goal, it's about learning SQL which is a really good thing to know, I've been told.
In a terminal enter:
sudo mysql -u root -p
This will prompt you for a password. Remember the one we just created? It's that one.
Now you will be able to create and alter databases. Start with enter the command
show databases;
This will show you the existing databases, as you can see I already have one called plant but you probably don't just yet.
Enter the commands:
CREATE DATABASE plant;
USE plant;
CREATE TABLE temp_values( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, hum INT(4) NOT NULL, temp INT(4) NOT NULL, soil_temp INT(4) NOT NULL, soil_moisture INT(4) NOT NULL, time INT(4) NOT NULL, duration INT(4) NOT NULL, );
Now, if everything went as it should, try:
SHOW TABLES;
And one entry should show up (the table temp_values that is).
That's the database creation portion done, now we need software to fill the database values with!
Programming
You can just copy and paste the following chunks in separate programs.
Open geany or the non-python code editor of your choice and enter the following code:
<html> <head> <link href="stylesheet.css" rel="stylesheet"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script type="text/javascript"> var auto_refresh = setInterval( function () { $('#values').load('load_values.php'); }, 1000); </script> <script> function updateImage() { obj = document.imagename; obj.src = obj.src.split("?")[0] + "?" + new Date().getTime(); //Following statement sets the delay before the function will //call itself again (in milliseconds) setTimeout("updateImage()",1234); } </script> </head> <h1 id="title"> Plant monitoring system </h1> <body onload="updateImage();"> <div> <img id="image" name="imagename" src="image.jpg" onerror="this.onerror=null; this.src='image_backup.jpg'"/> </div> <div id="values"> </div> <?php ?> <form id="form" method="post" action="index.php"> Time for watering:<br> <input type="int" name="time"><br> Duration of watering:<br> <input type="int" name="duration"> <br> <input type="submit"> </form> <h2> Explanation </h2> <div id="explanation"> <p id="a"> Humidity and soil moisture are both in % from 0 to 100.<br> </p> <p> Temperature is in degrees Celsius.<br> </p> <p> Time for watering is in 24 hours format and should be given in integers only: 1200 is the correct input for 12:00 and 1845 is the correct input for 18:45. Entering incorrect format will result in the program not watering. There is currently no alternative for watering less often than once a day. </p> <p> Duration of watering is in seconds the pump will be on, not in volume of water. This is beacuse each setup will work slightly differently. <br> </p> </div> </html> <?php $servername = "localhost"; $username = "root"; $password = "Cutekitten1"; $database = "plant"; $conn = new mysqli($servername, $username, $password, $database); $sql = "SELECT time, duration FROM temp_values WHERE id=1"; $result = $conn->query($sql); if($result->num_rows > 0){ while ($row = $result->fetch_assoc()) { $time = $row["time"]; $duration = $row["duration"]; } } if(!empty($_POST['time'])) $time = $_POST['time']; if(!empty($_POST['duration'])) $duration = $_POST['duration']; $sql = "UPDATE temp_values SET time=$time, duration=$duration WHERE id=1"; $conn->query($sql); $conn->close(); ?>
Save it as index.php in var/www/html folder.
This code is the main code on the server, it renders the page with the image and calls the second php script every second so they update without having to refresh the page!
In a similar way it refreshes the image of the plant every 1234 milliseconds, the camera feed isn't really a camera feed, it is a continuosly refreshing image (isn't that what all video is really?).
This is because streaming video is hard and I'm lazy and you won't need to know how your plant looks more often than every other second.
Create another file and paste this code:
<?php $servername = "localhost"; $username = "root"; $password = "Cutekitten1"; $dbname = "plant"; //create connection $conn = new mysqli($servername, $username, $password, $dbname); //check connection if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } else { echo ""; } $sql = "SELECT hum, temp, soil_temp, soil_moisture, time, duration FROM temp_values WHERE id=1"; $result = $conn->query($sql); $row = $result->fetch_assoc(); $hum = $row["hum"]; $temp = $row["temp"]; $soil_temp = $row["soil_temp"]; $soil_moisture = $row["soil_moisture"]; $time = $row["time"]; $duration = $row["duration"]; echo "Humidity is: " . $hum . "<br>"; echo "Temperature is: " . $temp . "<br>"; echo "Soil temperature is: " . $soil_temp . "<br>"; echo "Soil moisture is: " . $soil_moisture . "<br>"; echo "Time is set to start watering at " . $time . "<br>"; echo "Duration of watering is " . $duration . " seconds" . "<br>"; $conn->close(); ?>
Save this as load_values.php in the var/www/html folder.
This code fetches the values from the database which will be displayed on the site.
Create yet another file with geany and paste this code:
body { background-color:AliceBlue; font-family:"Garamond",Times; padding:20px; margin:10px; } #title { text-align:center; } #image { max-width:70%; height:auto; margin-bottom:20px; border-style:double; border-width:5px; margin-right:20px; } #values { margin-bottom:20px; font-family:Garamond; } @media only screen and (max-width: 1200px) { #image { max-width:100%; max-height:auto; } }
Save this as stylesheet.css in the same folder as the other two.
I'm no CSS expert but this is a little bit prettier than just the raw html site.
The CSS also makes the image scale to the width of your device if you are on your phone.
Now for the two python programs.
Open Thonny or the python editor of choice and create 2 programs:
Paste this code in one and save it as whatever you like, in my case it is called double_sensor.py, it doesn't matter here.
import board import busio import time import adafruit_sht31d from adafruit_seesaw.seesaw import Seesaw import mysql.connector from picamera import PiCamera import shutil camera = PiCamera() camera.resolution = (1920,1080) i2c_bus = board.I2C() sensor = adafruit_sht31d.SHT31D(i2c_bus) seesaw = Seesaw(i2c_bus, addr=0x36) print("active") while True: camera.capture('/var/www/html/image.jpg') time.sleep(0.5) shutil.copy2('/var/www/html/image.jpg','/var/www/html/image_backup.jpg') air_hum = int(sensor.relative_humidity) air_temp = int(sensor.temperature) soil_temp = int(seesaw.get_temp()) soil_moisture = int(seesaw.moisture_read()) soil_moisture = int((soil_moisture/1024)*100) try: mydb = mysql.connector.connect( host = 'localhost', user = 'root', password = 'Cutekitten1', database = 'plant' ) cur = mydb.cursor() sql = "UPDATE temp_values SET hum=%s, temp=%s, soil_temp=%s, soil_moisture=%s WHERE id=1" val = (air_hum, air_temp, soil_temp, soil_moisture) cur.execute(sql,val) mydb.commit() cur.close() mydb.close() except Exception as e: print(e) time.sleep(2)
This is the python program which uses the i2c bus to fetch the values of the two sensors and save them to the database.
I2c is really neet because you only need two signal wires and you can control many devices at the same time!
Connect all the sensors to Vin and GND in parallel and then connect the SDA and SCL wires in parallell and you are done. This is possible because every sensor then gets it's own adress so the microcontroller can kepp them apart when the data start flowing.
If you want to know which adresses your sensors use (or to check if the microcontroller can detect them at all) just type in a terminal:
i2cdetect -y 1
The other program is called relay.py on my machine but here you will get all the artistic freedom you need:
from gpiozero import LED import mysql.connector import time import datetime relay = LED(15) relay.off() #default values water_time = 1200 duration=3 print("active") while True: now = datetime.datetime.now() now = int(now.strftime("%H%M")) try: mydb = mysql.connector.connect( host = 'localhost', user = 'root', password = 'Cutekitten1', database = 'plant' ) cur = mydb.cursor() sql = "SELECT time, duration FROM temp_values" cur.execute(sql) result = cur.fetchall() if result != None: water_time = int(result[0][0]) duration = result[0][1] cur.close() mydb.close() except Exception as e: print(e) #safety limit, remove if desired if duration >=20: duration = 20 if now == water_time: relay.on() time.sleep(duration) relay.off() time.sleep(100)
This code just looks inside the database and checks at which time you have set the watering to occur at and then continually checks what time it is now and if they match, it will trigger the relay. If the relay is triggered, the program will wait 100 seconds to check again (So it doesn't water the hole minute when the two times match).
It has a limit regarding the duration set to 20 seconds, you can remove that if you want to.
That's it! That's all of the code needed!
Installation
Mount your control station where you want it and place the camera so that you get a great shot at whatever greenery you want to observe. Place a container of water near the plant and submerge your pump in it. Place the end of the hose in the pot and secure with hose clamp. Connect the pi to power supply and pump to power supply.
Testing
Go to localhost in your browser or just enter the IP adress of the pi in the adress field. If you do not know what that is, enter the following command in a terminal window and look for the second line in the third chunk of text.
ifconfig
If you are met with a gorgeous picture and sensor values which should be responsive you are in the clear. Test it out by entering a time which is about a minute or two in to the future and wait. Hopefully the pump will start and provide your plant with water for the time specified by you and then turn off again.
Making It Available Outside Your Local Network
This part is a bit on your own risk, I am not really good at network security so the code contains little to no security measures (more no than little). I am not in any position to tell you about what a potentially malicious person may do to your computer or your network but at least now you have been warned.
Go to your router login page, for Netgear router that is 192.168.1.1 and login. If you don't know your credentials, try admin as username and admin as password, I think that is default for most routers.
Now there are better guides out there on how to do this on all the possible brands of routers but I'll walk you through how to do it on a netgear router.
When you log in you are met with a summary page of your network. In the left most panel, there is a button labeled WAN which you will press. Now in the top panel, go to Port Forwarding and the press Add profile.
Everything you need to alter is the external port which should be set to 80 (80 is apparently the defaul port for apache servers) and Internal IP adress should be your pi:s IP adress. Again if you do not know this go to a terminal and type in:
ifconfig
and scroll through until you find it, it is on the second line of the third chunk of text.
The field Service name is only the name of you application, mine is called plant and this makes it easier to keep track och which ports are opened by what reason later on.
Done
That should be it!
Feel free to go on vacation and still take care of you plant(s).
Remember to use a large enough water bowl if you are going away for a long time. One safety measue which I encourage you to take is to place tha plant and the water container in some sort of bigger container which should have high enough walls to contain all the water if the pump decides to go crazy and pump all the water at once.
This was incredibly fun for me to build in the first place and I will gladly try to respond to any comments or questions about it. Do not hesitate to contact me if there's something missing or wrong in the project, it is hard to keep everything tight when there are so many steps and code snippets involved. Feel free to also send a DM if you would rather do that than comment on the post.
Thanks for reading! Happy making :)