Things used in this project

Schematics

Sprinkler Control
Basic Wiring Schematic

Code

SprinklerControl.inoC/C++
Main project file
/*
 * Project SprinklerControl
 * Description: Home Sprinkler System with weather modification
 * Author: Martin Smaha
 * Date: 5/27/17
 */
#include "SprinklerControl.h"

TParameters Parameters;
bool sprinklers[8],manual[8];
int lastMinute,DOW,Minute,Hour,Second,StateA,StateB,MinutesA,MinutesB,ZoneA,ZoneB,SystemState;

// setup() runs once, when the device is first turned on.
void setup() {
  memset(&sprinklers[0],0,sizeof(sprinklers));
  memset(&manual[0],0,sizeof(manual));
  StateA = 0;
  StateB = 0;
  SystemState = -1;
  EEPROM.get(0, Parameters);
  if (Parameters.Magic != 0xAA55) {
    Parameters.Magic = 0xAA55;
    Parameters.isOperational = false;
    for (int i = 0; i < 6; i++) {
      Parameters.A_minutes[i] = 0;
      Parameters.B_minutes[i] = 0;
    }
    for (int i = 0; i < 7; i++) {
      strcpy(Parameters.A_hhmm[i],"off");
      strcpy(Parameters.B_hhmm[i],"off");
    }
    Parameters.isRain = true;//disable during Rain
    Parameters.isWind = true;//disable during wind
    Parameters.wind_mph = 10;
    Parameters.A_temp = 70;
    Parameters.B_temp = 80;
    EEPROM.put(0, Parameters);
  }
  // setup digital IO for zone control
  pinMode(D0, OUTPUT);
  pinMode(D1, OUTPUT);
  pinMode(D2, OUTPUT);
  pinMode(D3, OUTPUT);
  pinMode(D4, OUTPUT);
  pinMode(D5, OUTPUT);
  digitalWrite(D0, HIGH); //high is off
  digitalWrite(D1, HIGH);
  digitalWrite(D2, HIGH);
  digitalWrite(D3, HIGH);
  digitalWrite(D4, HIGH);
  digitalWrite(D5, HIGH);
  // start listening for clients
  WebInit();
  WeatherInit();
  lastMinute = Time.minute();
}

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  WebTask();
  Hour = Time.hour();
  Minute = Time.minute();
  Second = Time.second();
  DOW = Time.weekday() - 1;//we use 0-6 for their 1-7

  if (Minute != lastMinute && Second == 0) {
    lastMinute = Minute;
    WeatherMinute();
    MinutesA++;
    MinutesB++;
    if (Hour == 6 && Minute == 0) {//at 0600 each day
      high_temp = temp_f;//reset daily peak temperature
      isRain = false;//reset rain flag
      Particle.syncTime();//and resync with time service
    }
  }
  ScheduleTask();
  UpdateZones();
  WeatherTask();
}

void SprinklersOff(void) {
  memset(sprinklers,0,sizeof(sprinklers));
}

void UpdateZones(void) {
  digitalWrite(D0, (sprinklers[0] || manual[0])?LOW:HIGH); //high is off
  digitalWrite(D1, (sprinklers[1] || manual[1])?LOW:HIGH);
  digitalWrite(D2, (sprinklers[2] || manual[2])?LOW:HIGH);
  digitalWrite(D3, (sprinklers[3] || manual[3])?LOW:HIGH);
  digitalWrite(D4, (sprinklers[4] || manual[4])?LOW:HIGH);
  digitalWrite(D5, (sprinklers[5] || manual[5])?LOW:HIGH);
}

void ScheduleTask(void) {
  if (Parameters.isOperational) {
    SystemState = 1;
    if (Parameters.isWind && wind_mph >= Parameters.wind_mph) {
      SystemState = 2;
      SprinklersOff();
      StateA = 0;
      StateB = 0;
    } else if (Parameters.isRain && isRain) {
      SystemState = 3;
      SprinklersOff();
      StateA = 0;
      StateB = 0;
    } else {
      ProgramTaskA();
      ProgramTaskB();
    }
  } else {
    SystemState = 0;
  }
}

void ProgramTaskA(void) {//entered once each minute when seconds are 0
  switch (StateA) {
    case 0: //look for start
      if (high_temp >= Parameters.A_temp && isDigit(Parameters.A_hhmm[DOW][0])) {
        int t = atoi(Parameters.A_hhmm[DOW]);//get 4 digit time
        int hr = t/100;
        int mn = t%100;
        if (Hour == hr && Minute == mn){
          MinutesA = 0;
          ZoneA = -1;
          StateA = 1;
          StartZoneA();
        }
      }
      break;
    case 1: //run for Duration
      if (MinutesA >= Parameters.A_minutes[ZoneA]) {
        MinutesA = 0;
        SprinklersOff();
        StartZoneA();//next zone
      }
      break;
  }
}

void StartZoneA(void) {
  for (ZoneA++; ZoneA < 6; ZoneA++) {
    if (Parameters.A_minutes[ZoneA] > 0) {
      sprinklers[ZoneA] = true;
      break;
    }
  }
  if (ZoneA >= 6) {
    StateA = 0;//all done
  }
}
void ProgramTaskB(void) {//entered once each minute when seconds are 0
  switch (StateB) {
    case 0: //look for start
      if (high_temp >= Parameters.B_temp && isDigit(Parameters.B_hhmm[DOW][0])) {
        int t = atoi(Parameters.B_hhmm[DOW]);//get 4 digit time
        int hr = t/100;
        int mn = t%100;
        if (Hour == hr && Minute == mn){
          MinutesB = 0;
          ZoneB = -1;
          StateB = 1;
          StartZoneB();
        }
      }
      break;
    case 1: //run for Duration
      if (MinutesB >= Parameters.B_minutes[ZoneB]) {
        MinutesB = 0;
        SprinklersOff();
        StartZoneB();//next zone
      }
      break;
  }
}

void StartZoneB(void) {
  for (ZoneB++; ZoneB < 6; ZoneB++) {
    if (Parameters.B_minutes[ZoneB] > 0) {
      sprinklers[ZoneB] = true;
      break;
    }
  }
  if (ZoneB >= 6) {
    StateB = 0;//all done
  }
}
SprinklerControl.hC/C++
Project includes and structure definition
/*
 * Project SprinklerControl
 * Description: Home Sprinkler System with weather modification
 * Author: Martin Smaha
 * Date: 5/27/17
 * File: Project Includes
 */

typedef struct {
  int Magic;//should be 0xAA55
  bool isOperational;//sprinker system is operational
  int A_minutes[6];//minutes per zone
  int B_minutes[6];
  char A_hhmm[7][6];//daily start times in 24hr notation
  char B_hhmm[7][6];
  bool isRain;//disable during Rain
  bool isWind;//disable during wind
  int wind_mph;
  int A_temp;
  int B_temp;
} TParameters;

extern TParameters Parameters;
extern char icon_url[],weather_string[],observation_time[],WeatherBuf[];
extern float temp_f,wind_mph,precip_today_in,high_temp;
extern bool sprinklers[],manual[],isRain;
extern int DOW,Minute,Hour,Second,StateA,StateB,MinutesA,MinutesB,ZoneA,ZoneB,SystemState;

extern void WebInit(void);
extern void WebTask(void);
extern void WeatherInit(void);
extern void WeatherMinute(void);
extern void WeatherTask(void);
WeatherClient.cppC/C++
Weather Underground API interface
/*
 * Project SprinklerControl
 * Description: Home Sprinkler System with weather modification
 * Author: Martin Smaha
 * Date: 5/27/17
 * File: Weather Client
 */
#include "SprinklerControl.h"
#include "application.h"

TCPClient WeatherClient;
char WeatherBuf[4096],icon_url[128],weather_string[64],observation_time[64];
float temp_f,wind_mph,precip_today_in,high_temp;
int WeatherState,WeatherTimer,WeatherBufCount,TZOffset;
bool isRain;

void WeatherTask(void);
void ParseWeather(void);
void ParseFloat(const char* key, float* value);
void ParseInt(const char* key, int* value);
void ParseString(const char* key, char* value);

void WeatherInit(void) {
  WeatherState = 0;
  WeatherTimer = 0;
  isRain = false;
}

void WeatherMinute(void) {
  WeatherTimer++;
}

// Parse Weather into the values we need
void ParseWeather(void) {
  char buf[32];
  ParseFloat("temp_f",&temp_f);
  ParseFloat("wind_mph",&wind_mph);
  ParseString("precip_today_in",buf);
  precip_today_in = atof(buf);
  memset(weather_string,0,sizeof(weather_string));
  ParseString("weather",weather_string);
  memset(icon_url,0,sizeof(icon_url));
  ParseString("icon_url",icon_url);
  memset(observation_time,0,sizeof(observation_time));
  ParseString("observation_time",observation_time);
  ParseString("local_tz_offset",buf);
  TZOffset = atoi(buf)/100;
  Time.zone(TZOffset);//local time based on weather station
  if (temp_f > high_temp) {
    high_temp = temp_f;
  }
  if ((strcmp(weather_string,"Rain") == 0) || precip_today_in > 0.0) {
    isRain = true;
  }
}

// ParseFloat is a simple single level finder
void ParseFloat(const char* key, float* value) {
  char* p = strstr(WeatherBuf,key);
  if (p) {
    p = strstr(p,":");
    if (p) {
      *value = atof(p+1);
    }
  }
}
// ParseInt is a simple single level finder
void ParseInt(const char* key, int* value) {
  char* p = strstr(WeatherBuf,key);
  if (p) {
    p = strstr(p,":");
    if (p) {
      *value = atoi(p+1);
    }
  }
}
// ParseString is a simple single level finder
void ParseString(const char* key, char* value) {
  char keyword[32];
  memset(keyword,0,32);
  sprintf(keyword,"\"%s\"",key);
  char* p = strstr(WeatherBuf,keyword);
  if (p) {
    p = strstr(p,":");
    if (p) {
      p = strstr(p+1,"\"");
      if (p) {
        p++;
        char *p2 = strstr(p,"\"");
        if (p2) {
          strncpy(value,p,p2-p);
        }
      }
    }
  }
}

// Get current weather conditions every 10 minutes
void WeatherTask(void) {
  switch (WeatherState) {
    case 0: // connect to server
      if (WeatherClient.connect("api.wunderground.com", 80)) {
        WeatherClient.println("GET /api/Your_Key/conditions/q/CA/Loma_Linda.json HTTP/1.0"); //by city example
		    //WeatherClient.println("GET /api/Your_Key/conditions/q/pws:KCALOMAL4.json HTTP/1.0"); //by station example
        WeatherClient.println("Host: api.wunderground.com");
        WeatherClient.println("Content-Length: 0");
        WeatherClient.println();
        WeatherBuf[0] = 0;
        WeatherBufCount = 0;
        WeatherState = 1;
      }
      break;
    case 1: // get returned data
      if (WeatherClient.connected()) {
        if (WeatherClient.available()) { //get each data segment
          char buf[128];
          int n = WeatherClient.read((uint8_t*)buf,sizeof(buf)-1);
          if ((WeatherBufCount + n) <= sizeof(WeatherBuf)) { //prevent array overflow
            buf[n] = 0;
            strcat(WeatherBuf,buf);
            WeatherBufCount += n;
          }
        }
      } else {
        WeatherState = 2;
      }
      break;
    case 2: // parse data
      if (strstr(WeatherBuf,"404 Not Found")) {
        WeatherTimer = 8;//try again sooner
      } else {
        ParseWeather();
      }
      WeatherState = 3;
      break;
    case 3: // wait 10 minutes and update
      if (WeatherTimer >= 10) {
        WeatherTimer = 0;
        WeatherState = 0;
      }
      break;
  }
}
WebServer.cppC/C++
User interface
/*
 * Project SprinklerControl
 * Description: Home Sprinkler System with weather modification
 * Author: Martin Smaha
 * Date: 5/27/17
 * File: Web Server
 */
#include "SprinklerControl.h"
#include "application.h"

TCPServer server = TCPServer(8080);//web page port
TCPClient WebClient;
char LANBuf[4096];
int WebState,ContentLength,ContentStart,LANBufCount,next_hhmm,current_hhmm;
static const char *dowString[] = {"SUN","MON","TUE","WED","THU","FRI","SAT"};

void ParseWeb(void);
void WebServer(void);
void WebTask(void);
void ParseForm(void);
void ParseManual(void);

void WebInit(void) {
  WebState = 0;
  LANBufCount = 0;
  ContentLength = 0;
  ContentStart = 0;
  server.begin();
}

// Web Header
void WebHeader(void) {
  WebClient.print("HTTP/1.1 200 OK\r\n");
  WebClient.print("Cache-Control: max-age=0, no-cache\r\n");
  WebClient.print("Pragma: no-cache\r\n");
  WebClient.print("Connection: close\r\n\r\n");
  WebClient.print("<html><head><title>Sprinkler System</title><style>div {border: 2px solid #a1a1a1;padding: 10px 20px;width: 300px;border-radius: 25px;}button {width: 100px;height: 40px;background: blue;color: white;}button:hover {color: black;}</style>");
  WebClient.print("<meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\" />");
  WebClient.print("<meta http-equiv=\"Pragma\" content=\"no-cache\" />");
  WebClient.print("<meta http-equiv=\"Expires\" content=\"0\" />");
  WebClient.print("<meta content='text/html; charset=ISO-8859-1' http-equiv='Content-Type'>");
  WebClient.print("<meta name='viewport' content='width=device-width, initial-scale=1'></head>");
  WebClient.print("<body><div>");
}

void WebFooter(void) {
  WebClient.print("</div></body></html>");
}

// Web server
void ParseWeb(void) {
  if (WebClient.connected()) {
    char buf[256];
    int isRequest = 0;
    if (strstr(LANBuf,"GET /status.html") != NULL) {
        isRequest = 1;
    } else if (strstr(LANBuf,"GET /manual.html") != NULL) {
        isRequest = 2;
    } else if (strstr(LANBuf,"GET /schedule.html") != NULL) {
        isRequest = 3;
    } else if (strstr(LANBuf,"POST /schedule.cgi") != NULL) {
        ParseForm();
        isRequest = 3;
    } else if (strstr(LANBuf,"GET /manual.cgi") != NULL) {
        ParseManual();
        isRequest = 2;
    } else if (strstr(LANBuf,"GET /weather.html") != NULL) {
        isRequest = 4;
    }
    //send response
    if (isRequest == 0) { //standard http error message for page not found
      WebClient.print("HTTP/1.1 404 \r\n\r\n");
      WebClient.stop();
    } else if (isRequest == 1) { //status.html
      WebHeader();
      //Header
      WebClient.print("<H1>Sprinkler System</H1>");
      //Current Weather
      sprintf(buf,"<table><tr><td><a href=weather.html><b>Current Weather</b></a></td><td><img src=\"%s\"></td></tr>",icon_url);
      WebClient.print(buf);
      sprintf(buf,"<tr><td>Weather:</td><td>%s</td></tr>",weather_string);
      WebClient.print(buf);
      sprintf(buf,"<tr><td>Temperature:</td><td>%.1f F</td></tr><tr><td>Wind Speed:</td><td>%.1f mph</td></tr><tr><td>Today's Rain:</td><td>%.1f inch</td></tr>",temp_f,wind_mph,precip_today_in);
      WebClient.print(buf);
      sprintf(buf,"<tr><td>Today's High:</td><td>%.1f F</td></tr></table>",high_temp);
      WebClient.print(buf);
      sprintf(buf,"<font>%s</font><br>",observation_time);
      WebClient.print(buf);
      sprintf(buf,"<font>Time is: %s %02d:%02d:%02d</font>",dowString[DOW],Hour,Minute,Second);
      WebClient.print(buf);
      //current running program
      if (StateA > 0) {
        sprintf(buf,"<hr><font>Program A is Running: Zone %d</font>",ZoneA+1);
        WebClient.print(buf);
      } else if (StateB > 0) {
        sprintf(buf,"<hr><font>Program B is Running: Zone %d</font>",ZoneB+1);
        WebClient.print(buf);
      } else {
        switch (SystemState) {
          case -1:
            WebClient.print("<hr>System is Initializing");
            break;
          case 0:
            WebClient.print("<hr>System is DISABLED");
            break;
          case 1:
            WebClient.print("<hr>System is Operational");
            if (isDigit(Parameters.A_hhmm[DOW][0])) {
              next_hhmm = atoi(Parameters.A_hhmm[DOW]);
              current_hhmm = Minute + (Hour * 100);
              if (next_hhmm > current_hhmm) {
                if (high_temp >= Parameters.A_temp) {
                  sprintf(buf,"<br>A Prog starts at: %s",Parameters.A_hhmm[DOW]);
                  WebClient.print(buf);
                } else {
                  WebClient.print("<br>A Prog DISABLED due to temperature");
                }
              }
            }
            if (isDigit(Parameters.B_hhmm[DOW][0])) {
              next_hhmm = atoi(Parameters.B_hhmm[DOW]);
              current_hhmm = Minute + (Hour * 100);
              if (next_hhmm > current_hhmm) {
                if (high_temp >= Parameters.B_temp) {
                  sprintf(buf,"<br>B Prog starts at: %s",Parameters.B_hhmm[DOW]);
                  WebClient.print(buf);
                } else {
                  WebClient.print("<br>B Prog DISABLED due to temperature");
                }
              }
            }
            break;
          case 2:
            WebClient.print("<hr>System is Wind DISABLED");
            break;
          case 3:
            WebClient.print("<hr>System is Rain DISABLED");
            break;
        }
      }
      //menu area
      WebClient.print("<hr>");
      WebClient.print("<table><tr><td><button onclick=location.href='manual.html'>MANUAL</button><td>");
      WebClient.print("<td><button  onclick=location.href='schedule.html'>SCHEDULE</button></td></tr></table>");
      //Footer
      WebFooter();
      WebClient.stop();
    } else if (isRequest == 2) { //manual.html
      WebHeader();
      WebClient.print("<H1>Manual Control</H1><table>");
      for (int i = 0; i < 6; i++) {
        if (manual[i]) {
          sprintf(buf,"<tr><td><button  onclick=location.href='manual.cgi?zone=%d'>ZONE %d</button></td><td><span style=background:#00ff00;> &nbsp &nbsp </span><font>&nbsp ON</font></td></tr>",i+1,i+1);
        } else {
          sprintf(buf,"<tr><td><button  onclick=location.href='manual.cgi?zone=%d'>ZONE %d</button></td><td><span style=background:#cccccc;> &nbsp &nbsp </span><font>&nbsp OFF</font></td></tr>",i+1,i+1);
        }
        WebClient.print(buf);
      }
      WebClient.print("</table>");
      //menu area
      WebClient.print("<hr>");
      WebClient.print("<table><tr><td><button onclick=location.href='status.html'>STATUS</button><td>");
      WebClient.print("<td><button  onclick=location.href='schedule.html'>SCHEDULE</button></td></tr></table>");
      WebFooter();
      WebClient.stop();
    } else if (isRequest == 3){ //schedule.html
      WebHeader();
      WebClient.print("<H1>Schedule</H1><form action=schedule.cgi method=post>");
      //enable operation
      if (Parameters.isOperational) {
        WebClient.print("<input type=checkbox name=isOperational value=1 checked>Enable Operation<br>");
      } else {
        WebClient.print("<input type=checkbox name=isOperational value=1 >Enable Operation<br>");
      }
      //zone times
      WebClient.print("<hr><table><tr><td><b>Zone</b></td><td><b>Prog A</b></td><td><b>Prog B</b></td></tr>");
      for (int i = 0; i < 6; i++) {
        sprintf(buf,"<tr><td>%d</td><td><input type=text size=3 value=%d name=A_minutes%d></td><td><input type=text size=3 value=%d name=B_minutes%d></td></tr>",i+1,Parameters.A_minutes[i],i+1,Parameters.B_minutes[i],i+1);
        WebClient.print(buf);
      }
      WebClient.print("</table>Enter zone duration in minutes<hr>");
      //start times
      WebClient.print("<table><tr><td><b>Day</b></td><td><b>Start A</b></td><td><b>Start B</b></td></tr>");
      for (int i = 0; i < 7; i++) {
        sprintf(buf,"<tr><td>%s</td><td><input type=text size=5 value=%s name=A_hhmm%d></td><td><input type=text size=5 value=%s name=B_hhmm%d></td></tr>",dowString[i],Parameters.A_hhmm[i],i+1,Parameters.B_hhmm[i],i+1);
        WebClient.print(buf);
      }
      WebClient.print("</table>Enter time as 24hr HHMM or off<hr>");
      //weather options
      WebClient.print("<b>Weather Options</b><br>");
      if (Parameters.isRain) {
        WebClient.print("<input type=checkbox name=isRain value=1 checked>Disable start if it has rained<br>");
      } else {
        WebClient.print("<input type=checkbox name=isRain value=1 >Disable start if it has rained<br>");
      }
      if (Parameters.isWind) {
        WebClient.print("<input type=checkbox name=isWind value=1 checked>Disable start if it is windy<br>");
      } else {
        WebClient.print("<input type=checkbox name=isWind value=1 >Disable start if it is windy<br>");
      }
      sprintf(buf,"<input type=text size=3 value=%d name=wind_mph>Wind MPH to disable at<br>",Parameters.wind_mph);
      WebClient.print(buf);
      sprintf(buf,"<input type=text size=3 value=%d name=A_temp>Temperature to enable A program<br>",Parameters.A_temp);
      WebClient.print(buf);
      sprintf(buf,"<input type=text size=3 value=%d name=B_temp>Temperature to enable B program<br>",Parameters.B_temp);
      WebClient.print(buf);
      //submit button
      WebClient.print("<hr><input type=submit value='Submit Changes'></form>");
      //menu area
      WebClient.print("<hr>");
      WebClient.print("<table><tr><td><button onclick=location.href='status.html'>STATUS</button><td>");
      WebClient.print("<td><button  onclick=location.href='manual.html'>MANUAL</button></td></tr></table>");
      WebFooter();
      WebClient.stop();
    } else if (isRequest == 4) {//weather.html
      WebHeader();
      WebClient.print("<H1>Weather Response</H1>Data response from api.wunderground.com</div>");
      WebClient.print("<pre>");
      WebClient.print(WeatherBuf);
      WebClient.print("</pre><button onclick=location.href='status.html'>STATUS</button></body></html>");
      WebClient.stop();
    }
  }
}

// Get web server requests
void WebTask(void) {
  switch (WebState) {
    case 0: // connect to server
      if (!WebClient.connected()) {
        WebClient = server.available();
      } else {
        memset(LANBuf,0,sizeof(LANBuf));
        LANBufCount = 0;
        ContentLength = 0;
        ContentStart = 0;
        WebState = 1;
      }
      break;
    case 1: // get returned data
      if (WebClient.connected()) {
        if (WebClient.available()) { //get each data segment
          char buf[256];
          int n = WebClient.read((uint8_t*)buf,sizeof(buf));
          if ((LANBufCount + n) <= sizeof(LANBuf)) { //prevent array overflow
            memcpy(LANBuf+LANBufCount,buf,n);
            LANBufCount += n;
          }
          if (ContentLength == 0) {
            char *p = strstr(LANBuf,"Content-Length:");
            if (p) {
              ContentLength = atoi(p+15);
            }
          }
          if (ContentStart == 0) {
            char *p = strstr(LANBuf,"\r\n\r\n");
            if (p) {
              ContentStart = p + 4 - LANBuf;
            }
          }
        } else if (LANBufCount >= (ContentLength + ContentStart) && LANBufCount > 0) {
          WebState = 2;
        }
      } else {
        WebState = 0;
      }
      break;
    case 2: // parse data
      ParseWeb();
      WebState = 0;
      LANBufCount = 0;
      ContentStart = 0;
      ContentLength = 0;
      break;
  }
}

// Get web form response
void ParseForm(void) {
  char buf[32], *p;
  p = strstr(LANBuf,"isOperational=");
  if (p) {
    Parameters.isOperational = true;
  } else {
    Parameters.isOperational = false;
  }
  for (int i = 0; i < 6; i++) {
    sprintf(buf,"A_minutes%d=",i+1);
    p = strstr(LANBuf,buf);
    if (p) {
      p += strlen(buf);
      Parameters.A_minutes[i] = atoi(p);
    }
  }
  for (int i = 0; i < 6; i++) {
    sprintf(buf,"B_minutes%d=",i+1);
    p = strstr(LANBuf,buf);
    if (p) {
      p += strlen(buf);
      Parameters.B_minutes[i] = atoi(p);
    }
  }
  for (int i = 0; i < 7; i++) {
    sprintf(buf,"A_hhmm%d=",i+1);
    p = strstr(LANBuf,buf);
    if (p) {
      p += strlen(buf);
      char *end = strchr(p,'&');
      int n = 4;
      if (end) {
        n = min(end - p,4);
      }
      memset(Parameters.A_hhmm[i],0,6);
      strncpy(Parameters.A_hhmm[i],p,n);
    }
  }
  for (int i = 0; i < 7; i++) {
    sprintf(buf,"B_hhmm%d=",i+1);
    p = strstr(LANBuf,buf);
    if (p) {
      p += strlen(buf);
      char *end = strchr(p,'&');
      int n = 4;
      if (end) {
        n = min(end - p,4);
      }
      memset(Parameters.B_hhmm[i],0,6);
      strncpy(Parameters.B_hhmm[i],p,n);
    }
  }
  p = strstr(LANBuf,"isRain=");
  if (p) {
    Parameters.isRain = true;
  } else {
    Parameters.isRain = false;
  }
  p = strstr(LANBuf,"isWind=");
  if (p) {
    Parameters.isWind = true;
  } else {
    Parameters.isWind = false;
  }
  p = strstr(LANBuf,"wind_mph=");
  if (p) {
    p += 9;
    Parameters.wind_mph = atoi(p);
  }
  p = strstr(LANBuf,"A_temp=");
  if (p) {
    p += 7;
    Parameters.A_temp = atoi(p);
  }
  p = strstr(LANBuf,"B_temp=");
  if (p) {
    p += 7;
    Parameters.B_temp = atoi(p);
  }
  EEPROM.put(0, Parameters);
}

//look for zone control command
void ParseManual(void) {
  char *p = strstr(LANBuf,"?zone=");
  if (p) {
    int x = atoi(p+6) - 1;
    if (x >= 0 && x <= 5) {
      for (int i = 0; i <= 5; i++) {//check all 6 zones
        if (i == x) {
          manual[i] = !manual[i];
        } else {
          manual[i] = false;
        }
      }
    }
  }
}

Credits

183193 102389716507771 4133659 n rhhfeodpxv
Martin Smaha

Designing hardware and software since 1975.

Contact

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

Particle Photon Based Security System with Alerting
Intermediate
  • 1,345
  • 20

Full instructions

Be alerted to any door or window opening and closing in your house on your smart phone.

Happy Plant Notifier
Intermediate
  • 290
  • 5

Full instructions

Get a text or call if your plant is getting too dry, along with a nice LCD display giving information regarding your plant's health.

My Guardian for the Workshop
Intermediate
  • 196
  • 4

Full instructions

Device will send emails if the door or window has been opened, and will monitor the temperature and humidity of the place.

Smart and Safe Outdoor Plant Watering System
Intermediate
  • 2,526
  • 18

Monitors soil moisture and weather forecasts to give plants just the water they need. Safe garden hose hookup for season long watering.

Christmas Gift Box
Intermediate
  • 3,684
  • 595

Full instructions

Christmas Gift Box plays music and sends an email when it is opened.

Carbon Fiber Vacuum Chamber
Intermediate
  • 3,035
  • 94

Full instructions

Our project is a carbon fiber vacuum chamber that is monitored by multiple particle photons and various sensors.

Add projectSign up / Login