Zeven Development

Soil Moisture Control System x4 Plants

Under Construction!


Build a Soil Moisture Control System for x4 Plants

Build a Soil Moisture Control System that monitors the soil moisture and waters your plants automatically. Take the guessing out of when your plant is thirsty.


Setup

Soil Moisture Control System Parts


Qty Part Description Cost
1 IO Expander. $40.00
1 IO Expander Soil Bundle. $30.00
1 NodeMcu ESP8266 ESP-12E Wireless WiFi board. $4.75
1 4 Channel 5V Relay Shield Module Board. $5.95
1 FS200-SHT10 Soil Temperature and Humidity Sensor Probe. $22.08
1 Optical Infrared Liquid Water Level Control Switch. $7.04
4 1M Waterproof DS18B20 Digital Temperature Sensor. $1.99
4 Capactive Soil Moisture Module V1.2. $5.82
1 1.3" I2C 128x64 SSD1306 OLED LCD Display White. $8.65
4 12V Small IP68 Submersible Brushless Water pump. $11.99
1 Rain Drip Black Raindrip 1/4" by 100-Feet Tubing. $7.58
1 Orbit 25-Count 1/4" Barb Tee, Black. $3.86
1 10 Pack Drip Irrigation Tubing Stakes. $5.31
1 Gardner Bender Black Electrical Liquid Tape. $7.48
1 200mmx120mmx56mm Waterproof ABS Clear Cover Junction Box Enclosure. $17.82
1 PG16 Cable Gland 2 Holes aterproof Nylo9n Joint Locknet. $6.98
8 RJ11 4 Conductor Straight Wired Modular Telephone Calbe - 7ft. $0.61
1 Pawfly 5 Pcs Non-Return Oxygen Air Pump Regulator Check Valve. $4.99
1 5-Gal. Black Bucket (Pack of 3). $14.97
1 5-Gal. REusable Bucket Lid. $2.48
1 24 Gauge Red Black Speaker Wire 100'. $14.35

Potted Plants

We have x4 potted plants that we need to build an automated soil moisture control system that will automatically water the plants so that we can just watch our plants grow taking the guess work out of when and how much we need to water. As the plants grow larger their water requirements increase and using a simple water timer will not work well. Also under watering your plants will result in smaller yields, and over watering can result in root rot.

Plants

Potted Plants Parts


Qty Part Description Cost
1 iPower 5-Pack 5 Gallon Grow Bags (14" Base). $11.99
1 Austin Planter 16" (14.2" Base) Case of 5 Plant Saucers - Black Polypropylene. $29.00

Use 5 gallon fabric grow bags that hold plenty of soil medium, and allow the roots to breath, for healthy root development. If your soil is more porous, use the plant saucer to catch and reabsorb water leakage to keep your tent clean, then adjust your water duration/cycles.


1-Wire® Buses

To properly support x2 1-wire® buses on pins 7 and 8 for the temperature sensors we need to add an additional 2.2k pullup on pin 7 of the IO Expander. Pin 8 already has 5.1k pullup and is ok since the DS18B20 temperature sensors will only work in standard mode speeds anyway and the distances are relatively short.

PullUp

Project Box

DrillLocations

Using the provided drill guide, attach it to the underside of the project box. Then using a 1/8" drill bit, drill the marked points.

Drill

On the bottom right side, drill a 13/16" hole 1" from the edge, for the wires and gland nut.

DrillGland

Screw in all the standoffs and gland nut. Make sure the standoffs for the NodeMCU is higher than the rest.

StandOff

Attach all the boards, and connect the relays, and NodeMCU to the IO Expander.

Boards

Using a single 12V 1A power supply solder the wires as shown to provide power to all the boards. The header wires connect to the NodeMCU and the x3 2" 12V line connects to the relay board.

Power

Connect the power wires as shown. The loose ground wire will be used later to connect to all the pumps. Run all the wires under the NodeMCU as a wire guide.

PowerConnect

Connect the OLED display and install with a styro foam spacer, and connect all the phone cables.  To connect the water pumps add 4ft wire extenders with heat shrink tubing to properly protect the connection from water. Use a wire nut to connect all the water pump grounds together with the remaining ground wire.

Final

Water Reservoir

Drill a 9/16" hole 3" from the bottom of a 5 gallon bucket and secure the optical level sensor with the silicon washer on the inside of the bucket. Fill it full of water and make sure there are no leaks. The optical level sensor is used to turn off the pumps and alert you if you run out of water.

Bucket

Soil Moisture Sensor

Now we need to insulate or conformal coat and protect the circuit on the soil moisture sensor. Using painters tape cover both sides of the sensor exposing the components and the PCB edge. Using liquid tape, cover the exposed areas. It's important to make sure you cover all the edges of the PCB as well to protect water from leaching into the PCB. Remove the tape before the liquid tape has time to dry.

SensorTape
SensorSpray

Note: Capacitive Soil Moisture Sensors are very sensitive to electro magnetic feedbacks from solenoids. Make sure your power supply is connected to an isolated ground.


Watering Ring

For the soil moisture sensor to work properly, it's important to make sure that we are watering evenly. To achive this we will use a watering ring. They are easy to make and very effective.

Cut 27" of the 1/4" drip tubing and form a ring using a T connector. Use a heat gun on the ends to make it easier to insert. With the ring laying flat on the table mark every 1" apart on the top.

RingMark

Using a fine drill or dremel press, drill small holes at the marks.

RingDrill

Watering Pump

If your 5 gallon water reservoir is on the side of your pots, cut x2 4' 1/4" tubing, and x2 6' 1/4" tubing. Using a heat gun soften the 1/4" tubing to make it easier to insert onto the pump.

Pump

If the water level in the 5 gallon reservoir can be higher than the watering ring then from the pump cut the 1/4" tubing after 15" and add a tee connector, with a short 2" 1/4" tubing upward connection to an air regulator check valve. This will allow air into the tubing after the pump turns off to stop a possible siphoning effect. The check valve must be the highest point in the tubing. Feed the wires and hoses through a 3" drill cut on the side of the lid.

CheckValve

Watering ring in action. The soil moisture sensor needs to be in the ring about half way to the plant and the temperature sensor burried near the ring. Use the tubing stake to hold the ring in place.

RingWater

Windows Web Hosted MSSQL Database Management

Use SQL Server Management Studio to connect to your Microsoft SQL Server and create your database and tables using the following SQL script.


CREATE TABLE Plants (
    ReadingTime DATETIME PRIMARY KEY NOT NULL,
    Temp DECIMAL(9,2) NULL,
    Relative DECIMAL(9,2) NULL,
    Absolute DECIMAL(9,2) NULL,
    Plant1SoilTemp DECIMAL(9,2) NULL,
    Plant1SoilMoisture DECIMAL(9,2) NULL,
    Plant1WaterLevel BIT NULL,
    Plant2SoilTemp DECIMAL(9,2) NULL,
    Plant2SoilMoisture DECIMAL(9,2) NULL,
    Plant2WaterLevel BIT NULL,
    Plant3SoilTemp DECIMAL(9,2) NULL,
    Plant3SoilMoisture DECIMAL(9,2) NULL,
    Plant3WaterLevel BIT NULL,
    Plant4SoilTemp DECIMAL(9,2) NULL,
    Plant4SoilMoisture DECIMAL(9,2) NULL,
    Plant4WaterLevel BIT NULL
);

Use Microsoft IIS Manager to setup and manage your web site. Use the following Active Server Page Extended 'adddatasoil.aspx' to connect and transfer the data payload.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Script;
using System.Web.Script.Serialization;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;

namespace Hydroponics
{
    class Plant
    {
        public decimal SoilTemp { get; set; }
        public decimal SoilMoisture { get; set; }
        public bool WaterLevel { get; set; }
    }

    class PlantsData
    {
        public DateTime ReadingTime { get; set; }
        public decimal Temp { get; set; }
        public decimal Relative { get; set; }
        public decimal Absolute { get; set; }
        public List<Plant> Plant { get; set; }
    }
    public partial class adddatasoil : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var data = new JavaScriptSerializer().Deserialize<PlantsData>(Request["data"].ToString());
            int i;
            int PLANTS = 4;

            String strSQL = "INSERT INTO Plants (ReadingTime," ez_plus
                "Temp,Relative,Absolute,";

            for (i = 1; i <= PLANTS; i++)
            {
                strSQL += "Plant" ez_plus i.ToString() ez_plus "SoilTemp,";
                strSQL += "Plant" ez_plus i.ToString() ez_plus "SoilMoisture,";
                strSQL += "Plant" ez_plus i.ToString() ez_plus "WaterLevel";
                if (i < PLANTS) strSQL += ",";
                else strSQL += ")";
            }

            strSQL += " VALUES (" ez_plus
            "'" ez_plus data.ReadingTime.ToString() ez_plus "',";
            if (data.Temp >= 0) strSQL += data.Temp.ToString() ez_plus "," ez_plus data.Relative.ToString() ez_plus "," ez_plus data.Absolute.ToString() ez_plus ",";
            else strSQL += "NULL,NULL,NULL,";

            for (i = 0; i < PLANTS; i++)
            {
                if (data.Plant[i].SoilTemp >= 0) strSQL += data.Plant[i].SoilTemp.ToString() ez_plus ",";
                else strSQL += "NULL,";
                if (data.Plant[i].SoilMoisture >= 0) strSQL += data.Plant[i].SoilMoisture.ToString() ez_plus ",";
                else strSQL += "NULL,";
                strSQL += ((data.Plant[i].WaterLevel) ? "1" : "0");
                if (i < PLANTS - 1) strSQL += ",";
                else strSQL += ")";
            }

            //Response.Write(strSQL + "\r\n");

            try
            {
                SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ServerConnectionString"].ConnectionString);
                con.Open();

                SqlCommand cmd = new SqlCommand(strSQL, con);
                cmd.ExecuteNonQuery();

                con.Close();
            }
            catch (SqlException sqlex)
            {
                Response.Write(sqlex.Message.ToString() ez_plus "\r\n");
            }

        }
    }
}

Using the IIS Manager add a connection string 'ServerConnectionString' that will allow the .aspx web page to connect to your database.

By now we should be capturing data every minute. To dynamically view the database data we will be using Grafana.  This dynamic graphical tool will allow you to modify and add graphs that will help you view the time series data to manage your hydroponics system.

Use MSSQL and Grafana to plot all your data.

Grafana


Download and install Grafana on your computer or get your free Managed Grafana instance and create a localhost server.

After you login to Grafana create a database connection with MSSQL and then import the folloing script to create the Garage Hydroponics dashboard.

Grafana MSSQL v1.0


Local Raspberry Pi 4 MySQL Database Management

Use phpMyAdmin to create a new database called 'mydata', and create the following table using the SQL script.


CREATE TABLE Plants(
    ReadingTime DATETIME NOT NULL,
    Temp DECIMAL(9, 2) NULL,
    Relative DECIMAL(9, 2) NULL,
    Absolute DECIMAL(9, 2) NULL,
    Plant1SoilTemp DECIMAL(9, 2) NULL,
    Plant1SoilMoisture DECIMAL(9, 2) NULL,
    Plant1WaterLevel BIT NULL,
    Plant2SoilTemp DECIMAL(9, 2) NULL,
    Plant2SoilMoisture DECIMAL(9, 2) NULL,
    Plant2WaterLevel BIT NULL,
    Plant3SoilTemp DECIMAL(9, 2) NULL,
    Plant3SoilMoisture DECIMAL(9, 2) NULL,
    Plant3WaterLevel BIT NULL,
    Plant4SoilTemp DECIMAL(9, 2) NULL,
    Plant4SoilMoisture DECIMAL(9, 2) NULL,
    Plant4WaterLevel BIT NULL,
    PRIMARY KEY(ReadingTime)
);

Use the following PHP file 'adddatasoil.php' in your Apache Web Server to connect to MySQL and injest the JSON data payload.


<?php
 
$servername = "localhost";
$dbname = "mydata";
$username = "admin";
$password = "mysql";
 
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $data = json_decode($_POST["data"]);
 
    // Create database connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    // Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }
 
    ini_set("date.timezone", "UTC");
 
    $sql = "INSERT INTO Plants (ReadingTime," .
    "Temp,Relative,Absolute," .
    "Plant1SoilTemp,Plant1SoilMoisture,Plant1WaterLevel," .
    "Plant2SoilTemp,Plant2SoilMoisture,Plant2WaterLevel," .
    "Plant3SoilTemp,Plant3SoilMoisture,Plant3WaterLevel," .
    "Plant4SoilTemp,Plant4SoilMoisture,Plant4WaterLevel) " .
    "VALUES (" .
    "STR_TO_DATE('" . $data->ReadingTime . "','%m/%d/%Y %H:%i:%s'),";
    if ($data->Temp >= 0) $sql .= $data->Temp . "," . $data->Relative . "," . $data->Absolute . ",";
    else $sql .= "NULL,NULL,NULL,";

    for ($i = 0; $i < 4; $i++) {
      if ($data->Plant[$i]->SoilTemp >= 0) $sql .= $data->Plant[$i]->SoilTemp . ",";
      else $sql .= "NULL,";
      if ($data->Plant[$i]->SoilMoisture >= 0) $sql .= $data->Plant[$i]->SoilMoisture . ",";
      else $sql .= "NULL,";
      $sql .= ($data->Plant[$i]->WaterLevel ? "1":"0");
      if ($i < 3) $sql .= ",";
    }
    $sql .= ")";

    if ($conn->query($sql) == FALSE) {
        echo "Error: " . $sql . "\r\n" . $conn->error . "\r\n";
    }
 
    $conn->close();
}
else {
    echo "No data posted with HTTP POST.";
}
 

After you login to Grafana on the Pi 4 at port 3000, create a database connection with MySQL and then import the folloing script to create the Plants dashboard.

Grafana MySQL v1.0

Note: In order for Grafana to retrieve the correct time series data make sure you set the default time zone in MySQL to UTC.