OK. I have finally finished my sprinkler system controller box and want to post it here for the sake of sharing. It has a small problem that I’m sure I can fix but frankly right now my mind is mush. I have commented my code as much as I can so maybe that will help. I plan to add a parts list and find a place to post some pictures in the future too.
My problem is simple (famous last words). Some times when I reset the system it doesn’t cycle and all the relays engage. Somewhere I need to initialize all of my outputs to a known state upon startup. (Done such before but can’t pull it out of my… you get the point.) Also in the comments you will see a section called “Futurum Consilium” which is Latin for “Future Plan” (Sorry but I throw Latin in my descriptions of all my code as a sort of trademark/schema) where if you have any ideas please post them.
Ego sum panis tosti,
Russ
/*
Aspersorio Moderatorem
Auctor: Russ Lewis
WARNING
Do not plug in the USB connector to the Arduino unless the system is receiving power from the
115vac/12vdc/5vdc power system. The Arduino cannot handle the current and will become a smoke
generator instead of a sprinkler controller.
Summarium:
Ground moisture sensor turns on sprinkler for a specified time if the sensor
indicates that the ground moisture drops below a specified level.
Descriptio:
Controller is contained within a 6"x6"x4" watertight enclosure. One side has two 16 pin
connectors (115vac in and I/O). Internally on the cover there is a small fan and an LCD display
with 20 characters x 4 lines. Affixed to one side of the box is a 12vdc wall wart supply that
is hard wired to the AC input connector. Mounted to the base of the box is a 4 relay board on
one side and an Arduino UNO R3 micro-controller on the other side. Stacked on top of the
Arduino is a custom made "shield" that has a LM7805 regulator (and infrared receiver for
future expansion). The shield also has a switch to turn off the LCD display when not in use.
Singula Res:
Initially I planned to use 12vdc throughout the system since the sprinkler solenoid valves are
controlled by 12vdc however the relay board uses 5vdc for its control signals. While this
particular relay board has the ability to use the 5vdc from the Arduino it incurred too much
of a current draw and overheated the Arduino fairly quick. As a result I had to build a 5vdc
supply using a LM7805 regulator with a high efficiency heatsink and add a cooling fan to stir
the air around for cooling. This solved the power problems and provided 5vdc to the Arduino,
the LCD, and the relay board and the 12vdc was used to run the small fan and outputs from the
relays.
Ratio Operandi:
When the system starts it begins to interrogate the soil moisture sensors PINmoisture[]. It
does this through the iteration of an array so the primary section of the code is "reusable".
The moisture is stored in a temporary variable called VALUEmoisture which is compared to the
corresponding preset value of LEVELmoisture[]. If the moisture level is less than the preset
level then the system turns on the sprinkler for that system using the array PINsprinkle[].
Then since the system is no longer idling the fan is turned on using PINfan. PINsprinkle[]
stays on for a preset time set by TIMEsprinkle[] and then it shuts off. It doesn't matter if
it delivered enough water to saturate the soil right now since based on the water delivery
system (sprinkler or subterainean weep hose) the moisture sensor just got wet so its readings
are no longer reliable until the soil percolates the newly delivered water. When the system
is done watering it shuts off the sprinlkler and runs the fan for an additional time set by
TIMEcooldown to allow the 5vdc regulator board to recover slightly before the system proceeds
to the next moisture sensor and repeating the above process.
Two functions are used; Checkdisplay() and MSGdisplay(). Checkdisplay() is used as a logic
check for the LCD switch. If it's off then it returns to whatever it was doing. If it's on
then it will pass on the three variables NUMmessage, NUMvalue, NUMbox to the MSGdisplay()
function. The MSGdisplay() formats and outputs these values to the LCD and then returns to
whatever was going on beforehand.
Error Tenendo et Sustentacionem:
Unum) The system has a 20 character by 4 line LCD display that allows for monitoring of the
operation of the system. the display is controlled by the tiny switch on the shield board by
reading PINdisplaySwitch. The logic software uses a variable called SWdisplay to decide
wheather to turn on the LCD and its backlight based on the value of PINdisplaySwitch
(ergo, turn on the switch and the display comes on and vice versa).
Duobus) Each sensor is checked to be sure that its value falls within a certain range
(MINsensor - MAXsensor). This is done to check for shorts or opens in the moisture sensors.
Some open circuit conditions however can still cause the PINmoisture[] values to float and
may not always be 100% reliable.
Tribus) After so many cycles (NUMfancount) as specified by NUMfancycle the fan is turned on
for cooling whether it needs it or not. It will run for the time specified by TIMEcooldown
before continuing. In all cases of fan operation blnFan is used to indicate the status of
the fan so it can be displayed on the LCD display.
Quatuor) The value of TIMEconstant can be changed to alter the speed of the cycling of
everything. This value is used as a multiplier to set the number of milliseconds that
it takes for each operation. 60000 will set everything to happen in a matter of minutes and
1000 will set everything to happen in a matter of seconds. This is especially useful for
verifying operation and troubleshooting.
Futurum Consilium:
Temperature sensor to control the fan operation rather than just time based operation.
Implement the remote control capability so it doesn't have to be reprogrammed to change the
moisture levels and watering times.
Use the EEPROM capability of the Arduino to allow for permanent storage of values changed by
remote control so they aren't lost if the system loses power.
Implement the "sleep" function of the Arduino to minimize heat inside the box and have it
alive once in a while to check the soil. If it makes a few passes and the moisture values
are good then go to sleep and check again in some specified time.
Nominando Variabiles:
PIN... = Pin assignments
LEVEL... = Pre-defined (or calibrated) level
VALUE... = Value obtained from an input
TIME... = A value corresponding to a time measurement
NUM... = A generic number type but most commonly an integer
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Variable definitions
int PINdisplaySwitch = 11; // controller status display
int SWdisplay = 0; // display on/off switch
int VALUEmoisture = 0; // initialize the soil moisture sensor
int NUMboxes = 3; // number of boxes (= array elements)
int TIMEconstant = 60000; // time multiplier constant in milliseconds
int PINfan = 6; // cooling fan pin
int TIMEcooldown = 1; // delay time to leave fan on after each cycle
int MAXsensor = 1020; // max sensor reading anything greater is an error
int MINsensor = 20; // min sensor reading anything less is an error
int PINsprinkle[] = {3, 4, 5}; // sprinkler valve control pins (DOs)
int PINmoisture[] = {0, 1 ,2}; // moisture sensors (AIs)
int TIMEsprinkle[] = {9, 9, 9}; // time each sprinkler stays on
int LEVELmoisture[] = {5, 5, 5}; // minimum soil moisture levels (value = 1-10)
int NUMfancycle = 20; // fan cycles on every 20 minutes at least
int NUMfancount = 0; // running count to cycle the fan
boolean blnFan = false; // fan status
LiquidCrystal_I2C lcd(0x3f,20,4); // set the LCD address to 0x3f for a 20 chars and 4 line I2C 2004 display
void setup()
{
lcd.init(); // initialize the lcd
lcd.noBacklight(); // turn off backlight
pinMode(PINfan, OUTPUT); // declare the PINfan as an OUTPUT
digitalWrite(PINfan, HIGH); // and make sure it is off (HIGH = Off)
pinMode(PINdisplaySwitch, INPUT); // declare the PINdisplaySwitch as an INPUT
digitalWrite(PINdisplaySwitch, HIGH); // and make sure it is off
TIMEcooldown = TIMEcooldown * TIMEconstant; // set cooldown time and
for (int k = 0; k < NUMboxes; k++) { // set each box's individual values
pinMode(PINsprinkle[k], OUTPUT); // declare the PINsprinkle as an OUTPUT
}
}
void loop() {
NUMfancount += 1;
for (int x = 0; x < NUMboxes; x++) { // iterate through each box
Checkdisplay(0, 0, x); // update display
delay(1000); // slow things down a little
VALUEmoisture = analogRead(PINmoisture[x]); // Read soil moisture
Checkdisplay(1, VALUEmoisture, x); // update display
int LEVELmoist = (LEVELmoisture[x] * 100); // convert LEVELmoisture[x] to a number 100-1000 called LEVELmoist
if (VALUEmoisture < MAXsensor && VALUEmoisture > MINsensor) { // Check for sensor errors and only turn on if the sensor is good
if (VALUEmoisture < LEVELmoist){ // if the soil moisture is below this level
digitalWrite(PINsprinkle[x], LOW); // turn on sprinkler valve
Checkdisplay(2, 1, x); // update display
delay(1000); // slow things down a little
digitalWrite(PINfan, LOW); // turn on the cooling fan
blnFan = true; // indicate that the fan is on
Checkdisplay(3, 1, 0); // update display
delay((TIMEsprinkle[x] * TIMEconstant)); // leave sprinkler on for this long
}
}
digitalWrite(PINsprinkle[x], HIGH); // turn off box sprinkler valve
Checkdisplay(2, 0, x); // update display
if (blnFan == true) { // if the cooling fan is on
delay(TIMEcooldown); // leave it on for this long
digitalWrite(PINfan, HIGH); // then turn it off
}
Checkdisplay(3, 0, x); // update display
}
if (NUMfancount == NUMfancycle) { // check to see if we need a cooling cycle
digitalWrite(PINfan, LOW); // if so turn on the cooling fan
Checkdisplay(3, 0, 0); // update display
delay(TIMEcooldown); // leave cooling fan on for this long
digitalWrite(PINfan, HIGH); // then turn off the cooling fan
Checkdisplay(3, 0, 0); // update display
NUMfancount = 0; // reset count
}
}
void Checkdisplay(int NUMmessage, int NUMvalue, int NUMbox) {
SWdisplay = digitalRead(PINdisplaySwitch); // check to see if the display switch is on
if (SWdisplay == LOW) { // LOW = On
MSGdisplay(NUMmessage, NUMvalue, NUMbox); // if it is on then output the values to the LCD
}
else { // if the switch is off
lcd.init(); // then make sure it is blank
lcd.noBacklight(); // and turn off the backlight
}
}
void MSGdisplay(int MSGmessage, int MSGvalue, int Box) {
String strStatus; // convert the status to text
if (MSGvalue == 1) { // 1 is ON
strStatus = "ON "; // text to display if it is on
}
else { // and 0 is OFF
strStatus = "OFF"; // text to display if it is on
}
lcd.backlight(); // make sure the backlight is on
switch (MSGmessage) { // Set up a switch/case where the case value corresponds to line number
case 0: // line 0 is the title line
lcd.setCursor(3,0); // start on first line 3rd character (for centering purposes)
lcd.print("Watering System"); // output text for the title line
break; // break out of the case
case 1: // line 1 shows moisture read by the sensor
lcd.setCursor(0,1); // start on second line 1st character
lcd.print("Box "); // output text for this line
lcd.print(Box + 1); // fill in the value with the Box value + 1 since it starts at 0
lcd.print(" Mositure = "); // stay on this line and add some more text
lcd.print(MSGvalue); // fill in the value with the MSGvalue which has the current system moisture value
break; // break out of the case
case 2: // line 2 shows the sprinkler status
lcd.setCursor(0,2); // start on third line 1st character
lcd.print("Sprinkler "); // output text for this line
lcd.print(Box + 1); // fill in the value with the Box value + 1 since it starts at 0
lcd.print(" is "); // output some more text for this line
lcd.print(strStatus); // fill in the value with the strStatus which will be ON or OFF
break; // break out of the case
case 3: // line 3 shows the cooling fan status
lcd.setCursor(0,3); // start on forth line 1st character
lcd.print("Cooling fan is "); // output text for this line
lcd.print(strStatus); // fill in the value with the strStatus which will be ON or OFF
break; // break out of the case
}
}
// The geek language is Latin.