├── GradyHillhouse_Garduino.ino ├── LICENSE └── README.md /GradyHillhouse_Garduino.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Automatic Garden Waterer and Datalogger 3 | 4 | Grady Hillhouse (March 2015) 5 | */ 6 | 7 | #include "DHT.h" 8 | #include 9 | #include 10 | #include "RTClib.h" 11 | #include "SD.h" 12 | #define DHTTYPE DHT22 13 | #define ECHO_TO_SERIAL 1 //Sends datalogging to serial if 1, nothing if 0 14 | #define LOG_INTERVAL 360000 //milliseconds between entries (6 minutes = 360000) 15 | 16 | const int soilTempPin = A0; 17 | const int soilMoisturePin = A1; 18 | const int sunlightPin = A2; 19 | const int dhtPin = 2; 20 | const int chipSelect = 10; 21 | const int LEDPinGreen = 6; 22 | const int LEDPinRed = 7; 23 | const int solenoidPin = 3; 24 | const int wateringTime = 600000; //Set the watering time (10 min for a start) 25 | const float wateringThreshold = 15; //Value below which the garden gets watered 26 | 27 | DHT dht(dhtPin, DHTTYPE); 28 | RTC_DS1307 rtc; 29 | 30 | float soilTemp = 0; //Scaled value of soil temp (degrees F) 31 | float soilMoistureRaw = 0; //Raw analog input of soil moisture sensor (volts) 32 | float soilMoisture = 0; //Scaled value of volumetric water content in soil (percent) 33 | float humidity = 0; //Relative humidity (%) 34 | float airTemp = 0; //Air temp (degrees F) 35 | float heatIndex = 0; //Heat index (degrees F) 36 | float sunlight = 0; //Sunlight illumination in lux 37 | bool watering = false; 38 | bool wateredToday = false; 39 | DateTime now; 40 | File logfile; 41 | 42 | /* 43 | Soil Moisture Reference 44 | 45 | Air = 0% 46 | Really dry soil = 10% 47 | Probably as low as you'd want = 20% 48 | Well watered = 50% 49 | Cup of water = 100% 50 | */ 51 | 52 | void error(char *str) 53 | { 54 | Serial.print("error: "); 55 | Serial.println(str); 56 | 57 | // red LED indicates error 58 | digitalWrite(LEDPinRed, HIGH); 59 | 60 | while(1); 61 | } 62 | 63 | void setup() { 64 | 65 | //Initialize serial connection 66 | Serial.begin(9600); //Just for testing 67 | Serial.println("Initializing SD card..."); 68 | 69 | 70 | pinMode(chipSelect, OUTPUT); //Pin for writing to SD card 71 | pinMode(LEDPinGreen, OUTPUT); //LED green pint 72 | pinMode(LEDPinRed, OUTPUT); //LED red pin 73 | pinMode(solenoidPin, OUTPUT); //solenoid pin 74 | digitalWrite(solenoidPin, LOW); //Make sure the valve is off 75 | analogReference(EXTERNAL); //Sets the max voltage from analog inputs to whatever is connected to the Aref pin (should be 3.3v) 76 | 77 | //Establish connection with DHT sensor 78 | dht.begin(); 79 | 80 | //Establish connection with real time clock 81 | Wire.begin(); 82 | if (!rtc.begin()) { 83 | logfile.println("RTC failed"); 84 | #if ECHO_TO_SERIAL 85 | Serial.println("RTC failed"); 86 | #endif //ECHO_TO_SERIAL 87 | } 88 | 89 | //Set the time and date on the real time clock if necessary 90 | if (! rtc.isrunning()) { 91 | // following line sets the RTC to the date & time this sketch was compiled 92 | rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 93 | } 94 | 95 | //Check if SD card is present and can be initialized 96 | if (!SD.begin(chipSelect)) { 97 | error("Card failed, or not present"); 98 | } 99 | 100 | Serial.println("Card initialized."); 101 | 102 | // create a new file 103 | char filename[] = "LOGGER00.CSV"; 104 | for (uint8_t i = 0; i < 100; i++) { 105 | filename[6] = i/10 + '0'; 106 | filename[7] = i%10 + '0'; 107 | if (! SD.exists(filename)) { 108 | // only open a new file if it doesn't exist 109 | logfile = SD.open(filename, FILE_WRITE); 110 | break; // leave the loop! 111 | } 112 | } 113 | 114 | if (! logfile) { 115 | error("couldnt create file"); 116 | } 117 | 118 | Serial.print("Logging to: "); 119 | Serial.println(filename); 120 | 121 | 122 | logfile.println("Unix Time (s),Date,Soil Temp (F),Air Temp (F),Soil Moisture Content (%),Relative Humidity (%),Heat Index (F),Sunlight Illumination (lux),Watering?"); //HEADER 123 | #if ECHO_TO_SERIAL 124 | Serial.println("Unix Time (s),Date,Soil Temp (F),Air Temp (F),Soil Moisture Content (%),Relative Humidity (%),Heat Index (F),Sunlight Illumination (lux),Watering?"); 125 | #endif ECHO_TO_SERIAL// attempt to write out the header to the file 126 | 127 | now = rtc.now(); 128 | 129 | } 130 | 131 | void loop() { 132 | 133 | //delay software 134 | delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL)); 135 | 136 | //Three blinks means start of new cycle 137 | digitalWrite(LEDPinGreen, HIGH); 138 | delay(150); 139 | digitalWrite(LEDPinGreen, LOW); 140 | delay(150); 141 | digitalWrite(LEDPinGreen, HIGH); 142 | delay(150); 143 | digitalWrite(LEDPinGreen, LOW); 144 | delay(150); 145 | digitalWrite(LEDPinGreen, HIGH); 146 | delay(150); 147 | digitalWrite(LEDPinGreen, LOW); 148 | 149 | //Reset wateredToday variable if it's a new day 150 | if (!(now.day()==rtc.now().day())) { 151 | wateredToday = false; 152 | } 153 | 154 | now = rtc.now(); 155 | 156 | // log time 157 | logfile.print(now.unixtime()); // seconds since 2000 158 | logfile.print(","); 159 | logfile.print(now.year(), DEC); 160 | logfile.print("/"); 161 | logfile.print(now.month(), DEC); 162 | logfile.print("/"); 163 | logfile.print(now.day(), DEC); 164 | logfile.print(" "); 165 | logfile.print(now.hour(), DEC); 166 | logfile.print(":"); 167 | logfile.print(now.minute(), DEC); 168 | logfile.print(":"); 169 | logfile.print(now.second(), DEC); 170 | logfile.print(","); 171 | #if ECHO_TO_SERIAL 172 | Serial.print(now.unixtime()); // seconds since 2000 173 | Serial.print(","); 174 | Serial.print(now.year(), DEC); 175 | Serial.print("/"); 176 | Serial.print(now.month(), DEC); 177 | Serial.print("/"); 178 | Serial.print(now.day(), DEC); 179 | Serial.print(" "); 180 | Serial.print(now.hour(), DEC); 181 | Serial.print(":"); 182 | Serial.print(now.minute(), DEC); 183 | Serial.print(":"); 184 | Serial.print(now.second(), DEC); 185 | Serial.print(","); 186 | #endif //ECHO_TO_SERIAL 187 | 188 | //Collect Variables 189 | soilTemp = (75.006 * analogRead(soilTempPin)*(3.3 / 1024)) - 42; 190 | delay(20); 191 | 192 | soilMoistureRaw = analogRead(soilMoisturePin)*(3.3/1024); 193 | delay(20); 194 | 195 | //Volumetric Water Content is a piecewise function of the voltage from the sensor 196 | if (soilMoistureRaw < 1.1) { 197 | soilMoisture = (10 * soilMoistureRaw) - 1; 198 | } 199 | else if (soilMoistureRaw < 1.3) { 200 | soilMoisture = (25 * soilMoistureRaw) - 17.5; 201 | } 202 | else if (soilMoistureRaw < 1.82) { 203 | soilMoisture = (48.08 * soilMoistureRaw) - 47.5; 204 | } 205 | else if (soilMoistureRaw < 2.2) { 206 | soilMoisture = (26.32 * soilMoistureRaw) - 7.89; 207 | } 208 | else { 209 | soilMoisture = (62.5 * soilMoistureRaw) - 87.5; 210 | } 211 | 212 | humidity = dht.readHumidity(); 213 | delay(20); 214 | 215 | airTemp = dht.readTemperature(true); 216 | delay(20); 217 | 218 | heatIndex = dht.computeHeatIndex(airTemp,humidity); 219 | 220 | //This is a rough conversion that I tried to calibrate using a flashlight of a "known" brightness 221 | sunlight = pow(((((150 * 3.3)/(analogRead(sunlightPin)*(3.3/1024))) - 150) / 70000),-1.25); 222 | delay(20); 223 | 224 | //Log variables 225 | logfile.print(soilTemp); 226 | logfile.print(","); 227 | logfile.print(airTemp); 228 | logfile.print(","); 229 | logfile.print(soilMoisture); 230 | logfile.print(","); 231 | logfile.print(humidity); 232 | logfile.print(","); 233 | logfile.print(heatIndex); 234 | logfile.print(","); 235 | logfile.print(sunlight); 236 | logfile.print(","); 237 | #if ECHO_TO_SERIAL 238 | Serial.print(soilTemp); 239 | Serial.print(","); 240 | Serial.print(airTemp); 241 | Serial.print(","); 242 | Serial.print(soilMoisture); 243 | Serial.print(","); 244 | Serial.print(humidity); 245 | Serial.print(","); 246 | Serial.print(heatIndex); 247 | Serial.print(","); 248 | Serial.print(sunlight); 249 | Serial.print(","); 250 | #endif 251 | 252 | if ((soilMoisture < wateringThreshold) && (now.hour() > 19) && (now.hour() < 22) && (wateredToday = false)) { 253 | //water the garden 254 | digitalWrite(solenoidPin, HIGH); 255 | delay(wateringTime); 256 | digitalWrite(solenoidPin, LOW); 257 | 258 | //record that we're watering 259 | logfile.print("TRUE"); 260 | #if ECHO_TO_SERIAL 261 | Serial.print("TRUE"); 262 | #endif 263 | 264 | wateredToday = true; 265 | } 266 | else { 267 | logfile.print("FALSE"); 268 | #if ECHO_TO_SERIAL 269 | Serial.print("FALSE"); 270 | #endif 271 | } 272 | 273 | logfile.println(); 274 | #if ECHO_TO_SERIAL 275 | Serial.println(); 276 | #endif 277 | delay(50); 278 | 279 | //Write to SD card 280 | logfile.flush(); 281 | delay(5000); 282 | } 283 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Grady Hillhouse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | Gardening in the modern age means making things more complicated and arduous, with electrons, bits, and bytes. Behold: the garduino. My brother got me an arduino microcontroller board for Christmas, which to me was a solution looking for a problem. I finally found the problem: fresh herbs are expensive at the grocery store. But apparently not as expensive as adding a bunch of sensors and electronics to your garden. 3 | 4 | For a brief (and hopefully funny) introduction to this project, please watch the YouTube video by clicking the image below. 5 | 6 | Garden Controller Introduction Video 8 | 9 | I'm about as novice as they come as an electrical hobbyist, not to mention as a gardener, but lots of people have asked me to document this project in "replicable" detail, so this is that. I am open to constructive criticism, but go easy because just about everything in this project was a first for me. Also, feel free to join the conversations in the following Reddit threads: 10 | * [This project on /r/DIY](http://www.reddit.com/r/DIY/comments/316rjl/i_made_an_automatic_garden_controller_and_data/) 11 | * [This project on /r/Gardening](http://www.reddit.com/r/gardening/comments/316rp4/combining_hobbies_automatic_garden_controller_and/) 12 | * [This project on /r/Arduino](http://www.reddit.com/r/arduino/comments/316uj7/another_garden_controller_but_this_time_with_cool/) 13 | 14 | # Parts List 15 | The following list covers the major parts I used in the build. Depending on the electrical parts you already own, your plumbing and enclosures you use, you'll need to season to taste. I've linked to the actual parts I used, when applicable. 16 | 17 | 1. Arduino Uno 18 | 2. [SD Card Shield](https://www.adafruit.com/product/1141) 19 | 3. [9V Power Supply](http://www.adafruit.com/products/63) 20 | 4. [Solenoid Valve](http://www.adafruit.com/products/997) 21 | 5. [DHT22 Digital Temperature and Humidity Sensor](http://www.adafruit.com/products/385) 22 | 6. [CdS Photoresistor](http://www.adafruit.com/products/161) 23 | 7. [VH400 Soil Moisture Sensor](http://www.vegetronix.com/Products/VH400/) 24 | 7. [THERM200 Soil Temperature Sensor](http://www.vegetronix.com/Products/THERM200/) 25 | 7. Some resistors 26 | 8. TIP120 transistor (or a mosfet or relay) for switching on the solenoid 27 | 9. Diode to clamp voltage spikes from the solenoid 28 | 10. A stevenson screen or other enclosure to protect the electronics from exposure to weather 29 | 30 | # Wiring Diagram 31 | The schematic below shows the wiring of the controller. 32 | ![Wiring diagram](http://i.imgur.com/zwdiB8F.png) 33 | 34 | # General Circuit Information 35 | ## Soil Moisture Sensors 36 | Moisture sensors that measure the resistance or conductivity across the soil matrix between two contacts are essentially junk. First of all, resistance is not a very good indicator of moisture content, because it is highly dependent on a number of factors which might vary from garden to garden including soil ph, dissolved solids in the water, and temperature. Second, most of them are of poor quality with contacts that easily corrode. For the most part you'd be lucky to get one to last through an entire season. Capacitive sensors are generally more accurate because they are just measuring the change in dialetric properties of the soil which is less sensitive to other environmental factors. They also don't require any exposed conductive surfaces which means they can last a bit longer in the harsh environment of your backyard. My soil moisture sensor (and soil temperature sensor) came from http://www.vegetronix.com. 37 | 38 | ## Using a Photoresistor to Measure Sunlight 39 | The arudino’s analog inputs read voltage, so to use a resistive sensor (like the photoresistor I used to measure sunlight), you have to set up a voltage divider. This is just a really simple circuit which divides the voltage drop between your sensor and a known resistor. You know the current is the same for both, so you can calculate the resistance of your sensor using ohm’s law. The only problem here is that a photoresistor’s relationship to illuminance is log-log, that is to say it spans several orders of magnitude. So if you use a big resistor (5k - 10k ohm) in your voltage divider, your sensor will be sensitive to low light levels, but you won’t be able to tell the difference between a sunny day and an overcast one. Since this thing’s going outside, I used a 100 ohm resistor, which should hopefully give me good differentiation between levels of brightness in the daylight. There are sensors out there that do a better job of measuring sunlight intensity than a simple photoresistor. I just included it in the project because I had one on hand. 40 | 41 | #Final Thoughts 42 | After posting this project to Reddit and YouTube, I got a few good suggestions. Also, since I've had it running for a few weeks now, I've had a few thoughts of my own. I'll try to update this section as I encounter issues or improvements. 43 | * Can you get something similar off the shelf for cheaper than the cost to build it yourself? Almost certainly. This was first and foremost a learning opportunity for me. 44 | * Can this be monitored wirelessly over wifi/bluetooth/etc.? Almost certainly. I don't have any experience in that, and I wasn't prepared to add another notoriously finicky element to the project (I struggle to get the wifi on my laptop to work sometimes!). I think it would be a neat next step for the project, though. 45 | * Consider mounting the solenoid directly on the hose bib. This reduces water hammer on your garden hose and (depending on the location of your hose bib and garden) may save you from flooding your yard if something bad happens. 46 | * As far as the garden itself: People said (1) don't put soil directly against the siding of your house, and (2) don't use wood mulch for an herb garden. I'm working on taking care of those two things. 47 | 48 | Combining microcontrollers and gardening is a really popular idea. I think that’s because gardens have very simple inputs and outputs that are easy to wrap your head around. I guess people (myself included) see a notoriously simple and relaxed hobby and can’t help but feel compelled to overcomplicate it. But just about anyone can connect the dots between "Garden needs water" and "I am not a responsible human being who is capable of remembering to water a garden every day" and realize, "Hey, I can use technology to overcome my personal shortcomings," and more than that, "I can bend technology to my will and that will feel good to my ego and my sense of self-worth." After all, no one’s hobby is to buy an irrigation controller off the shelf of a hardware store. Thanks for taking a look, and let me know what you think. 49 | --------------------------------------------------------------------------------