Back to the irrigation system!

Well it is getting close to time that I am going to have to water the yard more than once a month, so I finally decided to get back to work on the irrigation system.  I had run into a problem with the pronto related to the number of particle.functions I was using.  I guess there is a limit based on the ram use on the board.  Well, I thought I was well within the limit, but I guess not.  After going back and forth with technical support, they suggested that I reduce the number of particle.functions calls and use the string passed in to differentiate the call for the different zones, programs, etc.  I have done it and it seems that it is working again.  I am not going to attach the whole program like I did before, as it has some sensitive information in it, but I hope to make it easy enough to follow.

So, let’s get started.  First we need some variables: (Sorry in advance the spacing is not great with these posts.

//PUT YOUR VARIABLES HERE

//The "relay" is for the partice interfaces that will need to be written in order to start the zones.
int relay[] = {D0, D1, D2, D3, D4, D5, D6};
bool disabled = true;
//The relayDelay is an array to hold the timers for each of the zones to run.int relayDelay[] = {480000, 480000, 480000, 480000, 480000, 480000};
bool relayIsOn[] = {false, false, false, false, false, false};
//aRelayIsOn is my way of checking to make sure no two zones run at once.
bool aRelayIsOn = false;
//The rain variables are used to store the rain totals that are used to determine if it needs to run.
int yesterdaysRain = 0;
int todaysRain = 0;
int totalRain = 0;
int rainChecked = false;
//This is a bit map to use for the days in which to run setting.
int daysToRun = 0;
//The time values are the times of the day to run each program
int Time1hour = 1;
int Time1min = 0;
int Time2hour = 4;
int Time2min = 0;
//Current Time
int now = 0;

 

Now let’s get to the part where set everything up, think of this as the constructor I guess.

void setup()
{
 Serial.begin(9600);

//The get functions allow the web page/app to check to see what the current settings/state are.
Particle.function("getState", getState);
 Particle.function("status", statusCheck);
 Particle.function("getTime", getTime);
 Particle.function("getRain", getRain);

//Enable/Disable the system remotely
 Particle.function("setEnabled", setEnabled);
 Particle.function("setDisabled", setDisabled);
 
 //Start/Stop one of the relays(zones) of the system remotely
 Particle.function("relayPulse", relayPulse);
 
 //Run ever zone based on the configured values of the system
 Particle.function("runAllZones", runAllZones);
 
 //Get/Set the days that the programs run
 Particle.function("setDay", setDay);
 Particle.function("getDays", getDays);

 //Set the run time for each of the zones where the string passed in has the "zone,minutes" (ie. "1,10" where 1 is zone 1 (1's based value)
 Particle.function("setRunTime", setRunTime);
 Particle.function("getRunTime", getRunTime);

 //Get/Set the start time for each of the programs
 Particle.function("setTime1", setTime1);
 Particle.function("getTime1", getTime1);
 Particle.function("setTime2", setTime2);
 Particle.function("getTime2", getTime2);

 //Set the pin for each of the relays(zones) on the particle board
 pinMode(relay[0], OUTPUT);
 pinMode(relay[1], OUTPUT);
 pinMode(relay[2], OUTPUT);
 pinMode(relay[3], OUTPUT);
 pinMode(relay[4], OUTPUT);
 pinMode(relay[5], OUTPUT);
 pinMode(relay[6], OUTPUT);

 //Set the initial value for each of the pins to HIGH 
 digitalWrite(relay[0],HIGH);
 digitalWrite(relay[1],HIGH);
 digitalWrite(relay[2],HIGH);
 digitalWrite(relay[3],HIGH);
 digitalWrite(relay[4],HIGH);
 digitalWrite(relay[5],HIGH);
 digitalWrite(relay[6],HIGH);
 
 //Check the rain when the system is initialized so that we have a starting value.
 totalRain = checkRain("today");
}

Now I’ll go through roughly based on the order in the setup function.

int getState(String command) {
 if(disabled==true) {
   return 0;
 }
 else {
   return 1;
 }
}
//Check if any of the zones are running
int statusCheck(String command) {
 if(relayIsOn[0]==true) { return 1; }
 else if(relayIsOn[1]==true) { return 2; }
 else if(relayIsOn[2]==true) { return 3; }
 else if(relayIsOn[3]==true) { return 4; }
 else if(relayIsOn[4]==true) { return 5; }
 else if(relayIsOn[5]==true) { return 6; }
 else { return 0; }
}
/Get the current time after setting it to CST/CDT 
int getTime(String command) {
 int currTime = Time.hour()-6;
 
 if (isDST) {
   currTime++;
 }
 if (currTime < 0) {
   currTime = currTime + 24;
 }
 return (currTime*100+Time.minute());
}
//Send notification to the user via a web email script.
void sendNotice(String command) {
 RestClient clientNotif = RestClient("yourdomainhere");
 
 String throwAwayResponse = "";
 clientNotif.get("where you save a php script to send an email", &throwAwayResponse);

 return; //No return needed on this call
}

//Weather information retrieval
int checkRain(String command) {
 RestClient client = RestClient("yourdomainhere");
 
 String weatherResponse = "";
 int statusCode = client.get("where you save a php script to get rain fall totals for the day", &weatherResponse);

 return weatherResponse.toInt();
}

int getRain(String command) {
 
 return totalRain;
}
//Handle the disabled flag
int setDisabled(String command) {
 disabled = true;
 return 0;
}
int setEnabled(String command) {
 disabled = false;
 return 0;
}
//Methods to pulse the relays and turn on or off the zones manually.
int relayPulse(String relayStr) {
 int relayNum = relayStr.toInt(); //the zone to run
 if(disabled==true){
   return 2;  //oops you shouldn't be doing this
 }
 if(relayIsOn[relayNum]==true) {  //It's already on, turn it off
   aRelayIsOn = false;
   relayIsOn[relayNum] = false;
   digitalWrite(relay[relayNum],HIGH);
   return 3;
 }
 //Another relay is on, you can't turn another one on
 if(aRelayIsOn==true) { 
   return 1; 
 } 
 //Turn on the zone and run it for the set delay then turn it off
 digitalWrite(relay[relayNum],LOW);
 digitalWrite(relay[6],LOW);
 aRelayIsOn = true;
 relayIsOn[relayNum] = true;
 delay(relayDelay[relayNum]);
 digitalWrite(relay[relayNum],HIGH);
 digitalWrite(relay[6],HIGH);
 aRelayIsOn = false;
 relayIsOn[relayNum] = false;
 return 1;
}
//Days to run get/set
int getDays(String command) {
 return daysToRun;
}

int setDay(String dayToMod) {
 daysToRun = daysToRun + dayToMod.toInt();
 return 0;
}

So this is one of the tricky sets.  I need to pass in the zone and the delay time.  So I used the parameter as a string and passed in multiple values that were comma delineated.  This saved me an external function for each of the zones, like I had before.

//Update the delays based on input from the web interface
int setRunTime(String runTime) {
 int relay, relayDelayTmp;

 char szArgs[13];
 runTime.toCharArray(szArgs, 12);

 sscanf(szArgs, "%d,%d", &relay, &relayDelayTmp);

 relayDelay[relay-1] = relayDelayTmp*1000*60;
 return 0;
}

int getRunTime(String relay) {
 return relayDelay[relay.toInt()-1]/1000/60;
}

And for the next few, I will probably clean them up and use arrays like the rest of the similar values.

//Time of day to run get/set
int getTime1(String command) {
 return((Time1hour*100)+Time1min);
}

int setTime1(String TToMod) {
 char inputStr[64];
 TToMod.toCharArray(inputStr,64);
 char *p = strtok(inputStr,":");
 Time1hour = atoi(p);
 p = strtok(NULL,":");
 Time1min = atoi(p); 
 return 0;
}

int getTime2(String command) {
 return((Time2hour*100)+Time2min);
}

int setTime2(String TToMod) {
 char inputStr[64];
 TToMod.toCharArray(inputStr,64);
 char *p = strtok(inputStr,":");
 Time2hour = atoi(p);
 p = strtok(NULL,":");
 Time2min = atoi(p); 
 return 0;
}

So now for the meat of the program.  The most important part.  The run all zones function.

//Run the system based on all of the data we have
int runAllZones(String command) {
 int i;
 //Send notice that we are running the system
 sendNotice("today");

 if(totalRain > 1) {
 return 1;
 }
 //Loop through and run all the zones
 for(i=0; i<=sizeof(relay);i++) {
   if(disabled==true){
     return 1;
   }
   // In my case relay 6 is the line that is alway on for each zone
   if(aRelayIsOn==true) { return 1; }
   //LOW is the value that activates the relay
   digitalWrite(relay[i],LOW);
   digitalWrite(relay[6],LOW);
   aRelayIsOn = true;
   relayIsOn[i] = true;
   // delay with the values set for this relay (ms)
   delay(relayDelay[i]);
   digitalWrite(relay[i],HIGH);
   digitalWrite(relay[6],HIGH);
   aRelayIsOn = false;
   relayIsOn[i] = false;
 }
 return 1;
}

Now for the loop at the end of the program:

void loop()
{
 now = getTime("now");
 //Check the rainfall for the past two days and store the value. *should only run once a day 
 if(now==2350) {
   yesterdaysRain = todaysRain;
   todaysRain = checkRain("today");
   totalRain = todaysRain+yesterdaysRain;
 }
 
 //Check if the system needs to run for the two programs
 // First check if it has rained more than an inch total over the past two days and if it is time to run the system
 if(((now==(Time1hour*100+Time1min))||(now==(Time2hour*100+Time2min))) && (totalRain < 1)) {
 // Next check it is the right day to run
 if(getToday("todaysDay") & daysToRun) {
   // And finially if it has rained today over an inch (just incase it rained a lot since midnight) skip it
   if(checkRain("today") < 1) {
     runAllZones("runNow");
     delay(relayDelay[0] + relayDelay[1] + relayDelay[2] + relayDelay[3] + relayDelay[4] + relayDelay[5] + 2000); // This allows the run to complete as it appears that functions don't wait to complete... Not sure on this
     }
   }
 }

 delay(55000);
}


So, there you have it.  A functioning irrigation system on the particle side.  Now all you need is an interface to set it all up and manually run zones, etc.  I will try to get that part posted soon, but until then, happy coding.

Published by

Kyle

I am a software developer that likes tinkering in the garage. I hope you like my projects and have fun reproducing them.

Leave a Reply

Your email address will not be published. Required fields are marked *