Jack ZhouSHUAI ZHANGMarvin ZhangKyle Wang
Published

UW-Makeathon: Fully Automated Watering System

A simple application of cooling plate to copndence water from air

AdvancedShowcase (no instructions)Over 1 day1,135
UW-Makeathon: Fully Automated Watering System

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Corsair Hydro Series High Performance Liquid CPU Cooler H60
×1
Kuman 5PCS Soil Moisture Sensor Kit: YL-38, YL-69
×1
2 pcs TEC1-12710 Thermoelectric Peltier Cooler 12V 100W 154Wmax
×1
ExpertPower EXP 1270 Power Supply
×1
Makerfocus 2pcs ESP8266 Module ESP-12E NodeMcu LUA WiFi Internet New Version Development Board
×1
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1
4n324n
×1
irfp450
×1
4pcs 20K Ohm A Pot Potentiometer
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)
Laser cutter (generic)
Laser cutter (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

Thermoelectric Cooler Mount

This piece is used to hold heat sink and cooler together. It is stored in a different file type but the content is essentially the same.

Thermoelectric Cooler Mount

We use this customized piece to hold our cooler and heat sink together.

Schematics

12Volt to 3.3 Volt Converter and Fuse-Back

convert 12 volt from power supply to power mcu and sensor.

12Volt to 3.3 Volt Converter and Fuse-Front

convert 12 volt from power supply to power mcu and sensor.

Driver Module-Front

Driver module is the skeleton of all the connected components.

Circuit Schematic-Air Moisture Censor and Soil Moisture Censor

Air moisture censor and soil moisture censor are original circuit designed by our member.

Driver Module-Back

This driver module is the skeleton of all the connected components.

Soil Moisture Sensor-Back

This module returns the value of moisture of the soil.

Soil Moisture Sensor-Front

This module returns the moisture level of the soil.

Code

connection.py

Python
this code is responsible connects between web page and censors.
import os  # not necessary for now
import sys  # not necessary for now
import serial
import time

if __name__ == '__main__':  # used as main program, delete this if imported
    ser = serial.Serial('/dev/ttyUSB0',9600, timeout = 5)  # the number could be USB1, USB2, even more. be aware which USB device you are dealing with
    while 1:  # repeat unless intrrupted
        try:  # Serial error could happen
            line = ser.readline()  #  receive message from sensor
            if len(line) < 3:  # random empty string cause by faluty sensor
                continue
            time.sleep(0.01)  # give sensor and microprocessor to rest, afterall, raspberry Pi is way more powerful than arduino
            file = open('client.txt','a')
            line = line.decode('utf-8')
            words = line.split(' ')  # save data
            print(line)
            for i in words:
                file.writs(i)
            file.close()  # save data to file
            hostFile = open('host.txt','r')
            feedback = hostFile.readline()
            hostFile.close()  # read data from server, data comes from webpage activity
            ser.write(bytes(str(feedback), encoding = "utf8"))  # pass data to microprocessor
            file = open('client.txt','r')
            list = file.readlines()
            file.close();
            if len(list) > 100:
                list = list[-100:]
            file = open('client.txt','w')
            file.writelines(list)
            file.close()  #  only keep the most 100 data points
        except:
            #print("an error occured")
            pass  # WTF have just happened?
    

Auto_Watering_System.ino

Arduino
this code is responsible for data collection from censor and controlling driver module
#include <DHT.h>
#include <DHT_U.h>

// Pin Definition

#define YL_38_A 9
#define YL_38_D 10

#define AM2302_D 0

#define Cooler_1 4
#define Cooler_2 5

#define Fan_1 16

#define Potentiometer_1 12  // Potentiometer 1 is controlling Cooling module
#define Potentiometer_2 14  // Potentiometer 2 is controlling Fan Speed

// DHT Declaration

DHT dht(AM2302_D, DHT22);

// Variable Declaration

float temperatureC = 0.0;
float temperatureF = 0.0;
float humidity = 0.0;

float soilHumidity = 0.0;
int soilHumidityStatus = 0; // 0 to be below pre-set, 1 to be above

float potentiometer1 = 0.0;
float potentiometer2 = 0.0;

int controlMode = 0; // 0 for Manual Control, 1 for Auto Control, 2 for Webpage Control

int serverResponse = -1;  // -1 for Manual Mode, -2 for Auto Mode, any value >= 0 will be seen as 
                          // the percentage of power(0 - 100). Disregard any value beyond 100.
int serverValue = 0;  // Value in serverResponse

void setup(){
  // Input and Output initialization
  pinMode(YL_38_A, INPUT);
  pinMode(YL_38_D, INPUT);
  pinMode(AM2302_D, INPUT);
  pinMode(Potentiometer_1, INPUT);
  pinMode(Potentiometer_2, INPUT);
  pinMode(Cooler_1, OUTPUT);
  pinMode(Cooler_2, OUTPUT);
  pinMode(Fan_1, OUTPUT);
  // Communication initialization
  Serial.begin(9600);
}

void loop(){
  // AM2302 Sensor Read.
  temperatureC = dht.readTemperature();
  while(isnan(temperatureC)){
    temperatureC = dht.readTemperature();
  }
  temperatureF = temperatureC * 1.8 + 32;
  humidity = dht.readHumidity();
  
  // YL_38 Sensor Read
  soilHumidity = analogRead(YL_38_A) / 1023;
  soilHumidityStatus = digitalRead(YL_38_D);

  // Potentiometer Read
  potentiometer1 = analogRead(Potentiometer_1) / 1023;
  potentiometer2 = analogRead(Potentiometer_2) / 1023;

  // Send data to server
  Serial.printf("%0.1f %d %0.1f %0.1f %0.1f\n", soilHumidity, soilHumidityStatus, temperatureC, temperatureF, humidity);
  
  // Request data from server (Manual/Auto)
  // serverResponse = Serial.read();
  if(Serial.available()){
    serverResponse = Serial.readString().toInt();
  }

  // Process ServerResponse
  switch(serverResponse){
    case -1:
      controlMode = 0; 
      break;
    case -2:
      controlMode = 1;
      break;
    default:
      controlMode = 2;
      if(serverResponse < -2 || serverResponse > 100){
        serverValue = 100;
      } else {
        serverValue = serverResponse;
      }
      break;
  }

  // Control modules
  switch(controlMode){
    case 0: // Manual Control
      analogWrite(Cooler_1, (potentiometer1 * 1023));
      analogWrite(Cooler_2, (potentiometer1 * 1023));
      analogWrite(Fan_1, (potentiometer2 * 1023));
      break;
    case 1: // Auto Control
      if(soilHumidityStatus == 0){
        analogWrite(Cooler_1, 1023);
        analogWrite(Cooler_2, 1023);
        analogWrite(Fan_1, 1023);
      } else {
        analogWrite(Cooler_1, 500);
        analogWrite(Cooler_2, 500);
        analogWrite(Fan_1, 600);
      }
      break;
    case 2: // Webpage Control
      analogWrite(Cooler_1, serverValue / 100 * 1023);
      analogWrite(Cooler_1, serverValue / 100 * 1023);
      analogWrite(Fan_1, serverValue / 100 * 1023);
      break;
    default:
      break;
  }
  delay(1000);
  }

output.php

PHP
<!-- <?php
// $myfile = fopen("python/client.txt", "w") or die("Unable to open file!");
// $input = $_GET['outputIndex'];
// $txt = $input;
// echo $input;
// fwrite($myfile, $txt);
// fclose($myfile);
?> -->

<?php
if(isset($_POST['mode']) && isset($_POST['powerslider'])) {
    $data = $_POST['mode'] . '-' . $_POST['powerslider'] . "\r\n";
    $ret = file_put_contents('/python/host.txt', $data);
    if($ret === false) {
        die('There was an error writing this file');
    }
    else {
        echo "$ret bytes written to file";
    }
}
else {
   die('no post data to process');
}
?>

webpage.zip

JavaScript
web page for remote configuration of the system.
html,css,javascript
No preview (download only).

index.html

JavaScript
HTML, Java script, css. web page configuration used for remote access to the system.
<!DOCTYPE html>
<html>
// special thanks to our webpage engineer in JKSM team, Kyle.
<head>
    <title> Auto-Watering System </title>
    <meta http-equiv="refresh" content="100" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
    <link href="css/index.css" rel="stylesheet">
</head>

<body>
    <div id="col1">
    	<h3> Relationship between time and soil&air humidities </h3>
        <canvas id="line-chart" width="100%" height="30%"></canvas>
        <h3> Relationship between soil and air humidities </h3>
        <canvas id="line-chart2" width="100%" height="30%"></canvas>
    </div>
    <div id="col2">
        <form action="output.php" method="POST">
            <input type="radio" id="man" name="mode" value="-1"> Manual
            <br>
            <input type="radio" id="auto" name="mode" value="-2"> Auto
            <br>
            <input type="radio" id="iot" name="mode" value="0"> IoT!!!
            <br>
            <div id="slideDiv">
                <div> Power Percentage of Colding Modula </div>
                <input class="slider" name="powerslider" id="power" type="range" min="0" max="100" value="50">
                <input type="submit" name="submit" value="Submit" id="submitPower">
                <div id="showPower" style="display: inline;"></div>
            </div>

        </form>            
        	<div class="box"> Is the soil's humidity is suitable for growth? <div id="soilSuitable"></div>
            </div>
    </div>
    <iframe id="frmFile" src="python/client.txt" onload="LoadFile();" style="display: none;"></iframe>
</body>
<script type="text/javascript">
var soilHu = [];
var airTemp = [];
var airHu = [];
var time = [];
var soilSuit;

// function LoadFile() {
// var oFrame = document.getElementById("file");
// console.log(oFrame);
// console.log(oFrame.contentWindow);
// console.log(oFrame.contentWindow.document);
// var strRawContents = oFrame.contentWindow.document.body.childNodes[0].innerHTML;
// var arrLines = strRawContents.split("\n");
// alert("File " + oFrame.src + " has " + arrLines.length + " lines");
// var data = arrLines[arrLines.length - 1].split(" ");
// soilHu.push(data[0]);
// soilSuit = data[1];
// airTemp.push(data[2]);
// airHu.push(data[3]);
function LoadFile() {
    var oFrame = document.getElementById("frmFile");
    var strRawContents = oFrame.contentWindow.document.body.childNodes[0].innerHTML;
    while (strRawContents.indexOf("\r") >= 0)
        strRawContents = strRawContents.replace("\r", "");
    var arrLines = strRawContents.split("\n");
    for (var i = 0; i < arrLines.length; i++) {
    	console.log(arrLines[i]);
        var curLine = arrLines[i].split(" ");
        time.push(i);
        soilHu.push(curLine[0]);
        soilSuit = curLine[1];
        airTemp.push(curLine[2]);
        airHu.push(curLine[3]);
    }
    if(soilSuit == 1){
    	document.getElementById("soilSuitable").innerHTML = "TRUE!!";
    } else{
    	document.getElementById("soilSuitable").innerHTML = "NOT YET... But soon!";
    }
    

    // var years = [1500, 1600, 1700, 1750, 1800, 1850, 1900, 1950, 1999, 2050];
    // // For drawing the lines
    // var africa = [86, 114, 106, 106, 107, 111, 133, 221, 783, 2478];
    // var asia = [282, 350, 411, 502, 635, 809, 947, 1402, 3700, 5267];
    // var europe = [168, 170, 178, 190, 203, 276, 408, 547, 675, 734];
    // var latinAmerica = [40, 20, 10, 16, 24, 38, 74, 167, 508, 784];
    // var northAmerica = [6, 3, 2, 2, 7, 26, 82, 172, 312, 433];

    var ctx = document.getElementById("line-chart");
    var ctx2 = document.getElementById("line-chart2");

    // var myChart = new Chart(ctx, {
    //     type: 'line',
    //     data: {
    //         labels: years,
    //         datasets: [{
    //             data: africa,
    //             label: "Africa",
    //             borderColor: "#3e95cd",
    //             fill: false
    //         }]
    //     }
    // });

    var myChart = new Chart(ctx, {
        type: 'line',
        data: {
            labels: time,
            datasets: [{
                data: soilHu,
                label: "Soil Humidity",
                borderColor: '#8e5ea2',
                fill: false
            },
            {
                data: airHu,
                label: "Air Humidity",
                borderColor: '#3cba9f',
                fill: false

            }]
        }
    });

    soilHu.sort();
    airHu.sort();
    var myChart = new Chart(ctx2, {
        type: 'line',
        data: {
            labels: airHu,
            datasets: [{
                data: soilHu,
                label: "Soil Humidity",
                borderColor: "#3e95cd",
                fill: false
            }]
        }
    });}
</script>
<script type="text/javascript">
// deal with output
var output;

var slider = document.getElementById("power");
var showOut = document.getElementById("showPower");
document.getElementById("iot").addEventListener("click", function() {
    output = slider.value;
    showOut.innerHTML = slider.value; // Display the default slider value
    $("#slideDiv").removeClass("disabledbutton");
    // update();
});

document.getElementById("man").addEventListener("click", function() {
    output = -1;
    $("#slideDiv").addClass("disabledbutton");
    // update();
});

document.getElementById("auto").addEventListener("click", function() {
    output = -2;
    $("#slideDiv").addClass("disabledbutton");
    // update();
});


// Update the current slider value (each time you drag the slider handle)
slider.oninput = function() {
    showOut.innerHTML = this.value;
    output = this.value;
}

document.getElementById("submitPower").addEventListener("click", function() {
    showOut.innerHTML = slider.value; // Display the default slider value
    // update();
});

// function update() {
// 	console.log("test" + output);
//     var xhttp;
//     xhttp = new XMLHttpRequest();
//     xhttp.onreadystatechange = function() {
//       if (this.readyState == 4 && this.status == 200) {
//         // document.getElementById("txtHint").innerHTML = this.responseText;
//       }
//     };
//     // xhttp.open("GET", "output.php?outputIndex=" + output, true);
//     // xhttp.send();
//     xhttp.open("POST", "output.php", true);
//     xhttp.send(output);
// }
</script>

</html>

Credits

Jack Zhou

Jack Zhou

1 project • 2 followers
SHUAI ZHANG

SHUAI ZHANG

7 projects • 5 followers
Marvin Zhang

Marvin Zhang

1 project • 3 followers
An undergraduate electrical engineering student at UW-Madison.
Kyle Wang

Kyle Wang

1 project • 3 followers

Comments