|
| 1 | +/* |
| 2 | + Example 8-4. The irrigation system sketch |
| 3 | + */ |
| 4 | + |
| 5 | +/* |
| 6 | + Sketch to accompany the automatic garden irrigation system in |
| 7 | + "Getting Started With Arduino, Third Edition". See that book for |
| 8 | + the schematic. |
| 9 | + |
| 10 | + http://shop.oreilly.com/product/0636920029267.do |
| 11 | + |
| 12 | + 09/04/2014 - Written by Michael Shiloh |
| 13 | + 12/09/2014 - 1) When displaying on and off times, |
| 14 | + must convert back to hours and minutes |
| 15 | + 2) Humidity threshold was too low |
| 16 | + 3) Forgot to set digital pins as output |
| 17 | + 4) new function printSettings() since the same |
| 18 | + thing was being done in two different places |
| 19 | + |
| 20 | + This example code is in the public domain. |
| 21 | + */ |
| 22 | + |
| 23 | +#include <Wire.h> // Wire library, used by RTC library |
| 24 | +#include "RTClib.h" // RTC library |
| 25 | +#include "DHT.h" // DHT temperature/humidity sensor library |
| 26 | + |
| 27 | +// Analog pin usage |
| 28 | +const int RTC_5V_PIN = A3; |
| 29 | +const int RTC_GND_PIN = A2; |
| 30 | + |
| 31 | +// Digital pin usage |
| 32 | +const int DHT_PIN = 2; // temperature/humidity sensor |
| 33 | +const int WATER_VALVE_0_PIN = 8; |
| 34 | +const int WATER_VALVE_1_PIN = 7; |
| 35 | +const int WATER_VALVE_2_PIN = 4; |
| 36 | + |
| 37 | +const int NUMBEROFVALVES = 3; // How many valves we have |
| 38 | +const int NUMBEROFTIMES = 2; // How many times we have |
| 39 | + |
| 40 | +// Array to store ON and OFF times for each valve |
| 41 | +// Store this time as the number of minutes since midnight |
| 42 | +// to make calculations easier |
| 43 | +int onOffTimes [NUMBEROFVALVES][NUMBEROFTIMES]; |
| 44 | +int valvePinNumbers[NUMBEROFVALVES]; |
| 45 | + |
| 46 | +// Which column is ON time and which is OFF time |
| 47 | +const int ONTIME = 0; |
| 48 | +const int OFFTIME = 1; |
| 49 | + |
| 50 | +#define DHTTYPE DHT11 |
| 51 | +DHT dht(DHT_PIN, DHTTYPE); // Create a DHT object |
| 52 | + |
| 53 | +RTC_DS1307 rtc; // Create an RTC object |
| 54 | + |
| 55 | +// Global variables set and used in different functions |
| 56 | +DateTime dateTimeNow; // to store results from the RTC |
| 57 | + |
| 58 | +// to store humidity result from the DHT11 sensor |
| 59 | +float humidityNow; |
| 60 | + |
| 61 | +void setup(){ |
| 62 | + |
| 63 | + // Power and ground to RTC |
| 64 | + pinMode(RTC_5V_PIN, OUTPUT); |
| 65 | + pinMode(RTC_GND_PIN, OUTPUT); |
| 66 | + digitalWrite(RTC_5V_PIN, HIGH); |
| 67 | + digitalWrite(RTC_GND_PIN, LOW); |
| 68 | + |
| 69 | + // Initialize the wire library |
| 70 | + #ifdef AVR |
| 71 | + Wire.begin(); |
| 72 | + #else |
| 73 | + // Shield I2C pins connect to alt I2C bus on Arduino Due |
| 74 | + Wire1.begin(); |
| 75 | + #endif |
| 76 | + |
| 77 | + rtc.begin(); // Initialize the RTC object |
| 78 | + dht.begin(); // Initialize the DHT object |
| 79 | + Serial.begin(9600); // Initialize the Serial object |
| 80 | + |
| 81 | + // Set the water valve pin numbers into the array |
| 82 | + valvePinNumbers[0] = WATER_VALVE_0_PIN; |
| 83 | + valvePinNumbers[1] = WATER_VALVE_1_PIN; |
| 84 | + valvePinNumbers[2] = WATER_VALVE_2_PIN; |
| 85 | + |
| 86 | + // and set those pins all to outputs |
| 87 | + for (int valve = 0; valve < NUMBEROFVALVES; valve++) { |
| 88 | + pinMode(valvePinNumbers[valve], OUTPUT); |
| 89 | + } |
| 90 | + |
| 91 | +}; |
| 92 | + |
| 93 | +void loop() { |
| 94 | + |
| 95 | + // Remind user briefly of possible commands |
| 96 | + Serial.print("Type 'P' to print settings or ");$ |
| 97 | + Serial.println("'S2N13:45' to set valve 2 ON time to 13:34");$ |
| 98 | + |
| 99 | + |
| 100 | + // Get (and print) the current date, time, |
| 101 | + // temperature, and humidity |
| 102 | + getTimeTempHumidity(); |
| 103 | + |
| 104 | + // Check for request from the user |
| 105 | + checkUserInteraction(); |
| 106 | + |
| 107 | + // Check to see whether it's time to turn any valve ON or OFF |
| 108 | + checkTimeControlValves(); |
| 109 | + |
| 110 | + // No need to do this too frequently |
| 111 | + delay(5000); |
| 112 | +} |
| 113 | + |
| 114 | + |
| 115 | +/* |
| 116 | + * Get, and print, the current date, time, |
| 117 | + * humidity, and temperature |
| 118 | + */ |
| 119 | +void getTimeTempHumidity() { |
| 120 | + // Get and print the current time |
| 121 | + dateTimeNow = rtc.now(); |
| 122 | + |
| 123 | + if (! rtc.isrunning()) { |
| 124 | + Serial.println("RTC is NOT running!"); |
| 125 | + // use this to set the RTC to the date and time |
| 126 | + // this sketch was compiled |
| 127 | + // use this ONCE and then comment it out |
| 128 | + // rtc.adjust(DateTime(__DATE__, __TIME__)); |
| 129 | + return; // if the RTC is not running don't continue |
| 130 | + } |
| 131 | + |
| 132 | + Serial.print(dateTimeNow.hour(), DEC); |
| 133 | + Serial.print(':'); |
| 134 | + Serial.print(dateTimeNow.minute(), DEC); |
| 135 | + Serial.print(':'); |
| 136 | + Serial.print(dateTimeNow.second(), DEC); |
| 137 | + |
| 138 | + // Get and print the current temperature and humidity |
| 139 | + humidityNow = dht.readHumidity(); |
| 140 | + // Read temperature as Celsius |
| 141 | + float t = dht.readTemperature(); |
| 142 | + // Read temperature as Fahrenheit |
| 143 | + float f = dht.readTemperature(true); |
| 144 | + |
| 145 | + // Check if any reads failed and exit early (to try again). |
| 146 | + if (isnan(humidityNow) || isnan(t) || isnan(f)) { |
| 147 | + Serial.println("Failed to read from DHT sensor!"); |
| 148 | + return; // if the DHT is not running don't continue; |
| 149 | + } |
| 150 | + |
| 151 | + Serial.print(" Humidity "); |
| 152 | + Serial.print(humidityNow); |
| 153 | + Serial.print("% "); |
| 154 | + Serial.print("Temp "); |
| 155 | + Serial.print(t); |
| 156 | + Serial.print("C "); |
| 157 | + Serial.print(f); |
| 158 | + Serial.print("F"); |
| 159 | + Serial.println(); |
| 160 | +} // end of getTimeTempHumidity() |
| 161 | + |
| 162 | + |
| 163 | +/* |
| 164 | + * Check for user interaction, which will be in the form of |
| 165 | + * something typed on the serial monitor |
| 166 | + * If there is anything, make sure it's proper, and perform the |
| 167 | + * requested action |
| 168 | + */ |
| 169 | +void checkUserInteraction() { |
| 170 | + // Check for user interaction |
| 171 | + while (Serial.available() > 0) { |
| 172 | + |
| 173 | + // The first character tells us what to expect |
| 174 | + // for the rest of the line |
| 175 | + char temp = Serial.read(); |
| 176 | + |
| 177 | + // If the first character is 'P' then |
| 178 | + // print the current settings |
| 179 | + // and break out of the while() loop |
| 180 | + if ( temp == 'P') { |
| 181 | + printSettings(); |
| 182 | + Serial.flush(); |
| 183 | + break; |
| 184 | + } // end of printing current settings |
| 185 | + |
| 186 | + // If first character is 'S' then the rest will be a setting |
| 187 | + else if ( temp == 'S') { |
| 188 | + expectValveSetting(); |
| 189 | + } |
| 190 | + |
| 191 | + // Otherwise, it's an error. |
| 192 | + // Remind the user what the choices are |
| 193 | + // and break out of the while() loop |
| 194 | + else |
| 195 | + { |
| 196 | + printMenu(); |
| 197 | + Serial.flush(); |
| 198 | + break; |
| 199 | + } |
| 200 | + } // end of processing user interaction |
| 201 | +} |
| 202 | + |
| 203 | + |
| 204 | +/* |
| 205 | + * Read a string of the form "2N13:45" and separate it |
| 206 | + * into the valve number, the letter indicating ON or OFF, |
| 207 | + * and the time |
| 208 | + */ |
| 209 | +void expectValveSetting() { |
| 210 | + |
| 211 | + // The first integer should be the valve number |
| 212 | + int valveNumber = Serial.parseInt(); |
| 213 | + |
| 214 | + // the next character should be either N or F |
| 215 | + char onOff = Serial.read(); |
| 216 | + |
| 217 | + // next should come the hour |
| 218 | + int desiredHour = Serial.parseInt(); |
| 219 | + |
| 220 | + // the next character should be ':' |
| 221 | + if (Serial.read() != ':') { |
| 222 | + Serial.println("no : found"); // Sanity check |
| 223 | + Serial.flush(); |
| 224 | + return; |
| 225 | + } |
| 226 | + |
| 227 | + // next should come the minutes |
| 228 | + int desiredMinutes = Serial.parseInt(); |
| 229 | + |
| 230 | + // finally expect a newline which is the end of |
| 231 | + // the sentence: |
| 232 | + if (Serial.read() != '\n') { // Sanity check |
| 233 | + Serial.println( |
| 234 | + "Make sure to end your request with a Newline"); |
| 235 | + Serial.flush(); |
| 236 | + return; |
| 237 | + } |
| 238 | + |
| 239 | + // Convert the desired hour and minute time |
| 240 | + // to the number of minutes since midnight |
| 241 | + int desiredMinutesSinceMidnight |
| 242 | + = (desiredHour*60 + desiredMinutes); |
| 243 | + |
| 244 | + // Now that we have all the information set it into the array |
| 245 | + // in the correct row and column |
| 246 | + |
| 247 | + if ( onOff == 'N') { // it's an ON time |
| 248 | + onOffTimes[valveNumber][ONTIME] |
| 249 | + = desiredMinutesSinceMidnight; |
| 250 | + } |
| 251 | + else if ( onOff == 'F') { // it's an OFF time |
| 252 | + onOffTimes[valveNumber][OFFTIME] |
| 253 | + = desiredMinutesSinceMidnight; |
| 254 | + } |
| 255 | + else { // user didn't use N or F |
| 256 | + Serial.print("You must use upper case N or F "); |
| 257 | + Serial.println("to indicate ON time or OFF time"); |
| 258 | + Serial.flush(); |
| 259 | + return; |
| 260 | + } |
| 261 | + |
| 262 | + // now print the entire array so user can see what they set |
| 263 | + printSettings(); |
| 264 | +} // end of expectValveSetting() |
| 265 | + |
| 266 | + |
| 267 | +void checkTimeControlValves() { |
| 268 | + |
| 269 | + // First figure out how many minutes have passed |
| 270 | + // since midnight, since we store ON and OFF time |
| 271 | + // as the number of minutes since midnight. The |
| 272 | + // biggest number will be at 2359 which is |
| 273 | + // 23 * 60 + 59 = 1159 which is less than the |
| 274 | + // maximum that can be stored in an integer so an |
| 275 | + // int is big enough |
| 276 | + int nowMinutesSinceMidnight = |
| 277 | + (dateTimeNow.hour() * 60) + dateTimeNow.minute(); |
| 278 | + |
| 279 | + // Now check the array for each valve |
| 280 | + for (int valve = 0; valve < NUMBEROFVALVES; valve++) { |
| 281 | + Serial.print("Valve "); |
| 282 | + Serial.print(valve); |
| 283 | + |
| 284 | + Serial.print(" is now "); |
| 285 | + if ( ( nowMinutesSinceMidnight >= |
| 286 | + onOffTimes[valve][ONTIME]) && |
| 287 | + ( nowMinutesSinceMidnight < |
| 288 | + onOffTimes[valve][OFFTIME]) ) { |
| 289 | + |
| 290 | + // Before we turn a valve on make sure it's not raining |
| 291 | + if ( humidityNow > 70 ) { |
| 292 | + // It's raining; turn the valve OFF |
| 293 | + Serial.print(" OFF "); |
| 294 | + digitalWrite(valvePinNumbers[valve], LOW); |
| 295 | + } |
| 296 | + else { |
| 297 | + // No rain and it's time to turn the valve ON |
| 298 | + Serial.print(" ON "); |
| 299 | + digitalWrite(valvePinNumbers[valve], HIGH); |
| 300 | + } // end of checking for rain |
| 301 | + } // end of checking for time to turn valve ON |
| 302 | + else { |
| 303 | + Serial.print(" OFF "); |
| 304 | + digitalWrite(valvePinNumbers[valve], LOW); |
| 305 | + } |
| 306 | + Serial.println(); |
| 307 | + } // end of looping over each valve |
| 308 | + Serial.println(); |
| 309 | +} |
| 310 | + |
| 311 | + |
| 312 | +void printMenu() { |
| 313 | + Serial.println( |
| 314 | + "Please enter P to print the current settings"); |
| 315 | + Serial.println( |
| 316 | + "Please enter S2N13:45 to set valve 2 ON time to 13:34"); |
| 317 | +} |
| 318 | + |
| 319 | + |
| 320 | +void printSettings(){ |
| 321 | + |
| 322 | + // Print the current on and off settings, converting the |
| 323 | + // number of minutes since midnight back to the time |
| 324 | + // in hours and minutes |
| 325 | + |
| 326 | + Serial.println(); |
| 327 | + |
| 328 | + for (int valve = 0; valve < NUMBEROFVALVES; valve++) { |
| 329 | + Serial.print("Valve "); |
| 330 | + Serial.print(valve); |
| 331 | + Serial.print(" will turn ON at "); |
| 332 | + |
| 333 | + // integer division by 60 gives the hours |
| 334 | + // since integer division drops any remainder |
| 335 | + Serial.print((onOffTimes[valve][ONTIME])/60); |
| 336 | + |
| 337 | + Serial.print(":"); |
| 338 | + |
| 339 | + // the minutes are the remainder after dividing by 60. |
| 340 | + // get the remainder with the modulo (%) operator |
| 341 | + Serial.print((onOffTimes[valve][ONTIME])%(60)); |
| 342 | + |
| 343 | + Serial.print(" and will turn OFF at "); |
| 344 | + Serial.print((onOffTimes[valve][OFFTIME])/60); // hours |
| 345 | + Serial.print(":"); |
| 346 | + Serial.print((onOffTimes[valve][OFFTIME])%(60)); // minutes |
| 347 | + Serial.println(); |
| 348 | + } |
| 349 | +} |
0 commit comments