Smart Sprinkler System

Hi Josh,

Its possible you still need to select the right board within the IDE. Go to <Tools<Board and make sure you select the Mega2560. Hope this fixes the issue.

I doubt this specific app and device handler can be ported, but the controller can be. Below is the photon code which uses HTTP GET/POST commands. You can interface with it via any method that can send GET/POST so its not limited to the smarththings interface.

// This #include statement was automatically added by the Particle IDE.
#include "MCP23S17/MCP23S17.h"
MCP mcpchip(0, A2);    // create an object at address 0, SPI slave select on pin A2

#define nRelays 8
#define nValves 8
#define relayMode 0 //0 for sequentual, 1 for conccurrent

// Define Onbaord LED for testing
int led = D7; 

//Time Variables
double start_time=0;
double end_time=0;
double curr_time=0;
int wifi_rssi = 0;

//Status Strings
char currLEDState[250];
char rtrnStr[250];
char statusStr[250];

char v_state[250];
//char wifi_rssi[25];

//Relay Arrays
int r_cmd[nRelays];
int r_queue[nRelays];
int r_times[nRelays];
int r_curr = 0;

//Valve Variables
int v_cmd[nValves];
int v_queue[nValves];
int v_queue_cnt=0;
int v_times[nValves];
int v_curr = 0;
bool v_start = false;
double v_time_end = 0;
int onPump = 0;


void setup() 
{
  //Register Spark function
  Particle.function("setCmd", parseCmd);
  Particle.variable("currTime", &curr_time, DOUBLE);
  Particle.variable("wifiStrength", &wifi_rssi, INT); 
  
  Particle.variable("currLEDState", currLEDState, STRING);
  Particle.variable("rtrnStr", rtrnStr, STRING);
  Particle.variable("statusStr", statusStr, STRING);
  Particle.variable("v_state", v_state, STRING);
  
  //Set all pins as output
  mcpchip.pinMode(0x0000);

  
  //Sync Time
  Particle.syncTime();
  Time.zone(-7);
  
  // Initialize D0 pin as an output
  pinMode(led, OUTPUT);
}

void loop() {
    sprinkler_controller();
    status();
    //sprintf(strBuffer,"Time:%5.0f",getTime());
}

void status()
{
    //sprintf(wifi_rssi,"%03d dB\nRSSI", WiFi.RSSI());
    wifi_rssi= WiFi.RSSI();
    sprintf(v_state,"");
    for (int valve=1;valve<nValves+1;valve++)
    {
        if(valve==v_curr)
        {
            sprintf(v_state,"%son%d",v_state,valve);
        } else {
            if(v_in_q(valve) == 1)
            {
                sprintf(v_state,"%sq%d",v_state,valve);
            } else {
                sprintf(v_state,"%soff%d",v_state,valve);
            }
        }
        
        if (valve < nValves)
            sprintf(v_state,"%s,",v_state);
    }
    
    if (onPump == 1) 
    {
        sprintf(v_state,"%s,onPump",v_state);
    } else {
        sprintf(v_state,"%s,offPump",v_state);
    }
    
    
    
}

void sprinkler_controller()
{
    //Handle Single Sprinkler On
   if(isVcmd() > 0 && v_queue_cnt == 0) 
   {
       v_curr = isVcmd();
       sprintf(rtrnStr,"v%d: On",v_curr);
   }

    //Handle Queued Sprinklers
   if(isVcmd() > 0 && v_queue_cnt > 0 ) 
   {
       v_curr = v_queue[0];
       
       if(!v_start)
       {
            v_start=true;
            v_time_end = (getTime()+60*v_times[v_curr-1]);
            if (v_time_end >= 86400)
                v_time_end=0;
                

           //Send Command to Start Valve
       } else {
           
           sprintf(rtrnStr,"v%d: On, C:%5.0f, E:%5.0f ,%5.0f s left",v_curr,getTime(),v_time_end,v_time_end-getTime());
           
           if(getTime()>=v_time_end) 
           {
               //Send Command to Stop Current Valve
               sprintf(rtrnStr,"v%d:Stopped",v_curr);
               //Reset Variables
               v_start = false;
               v_queue_cnt--;
               v_queue_cl_n_shift();
               v_cmd[v_curr-1]=0;
               v_curr = 0;
           }
       }
   }
   
   if(isVcmd()==0)
   {
       //Send Command to Turn off All Valves and reset parameters for good measure
       sprintf(rtrnStr,"All Off");
       mcpchip.wordWrite(MCP_GPIOA, ~0x00);
       
       v_curr = 0;
   } else {
       mcpchip.wordWrite(MCP_GPIOA, ~( (0x01 << v_curr-1) | (onPump << nValves-1) ));
   }

   
}




int parseCmd(String data) {
    int relay = 0;
    int valve = 0;
    
    //Led Control
    String ledState = tryExtractString(data, "<ledState>", "</ledState>");
    
    
    //Relay Control
    //Return the Relay State
    String r_State = tryExtractString(data, "<r_State>", "</r_State>");
    //Direct Relay Control
    String r_On = tryExtractString(data, "<r_On>", "</r_On>");
    //Direct Relay Control
    String r_Off = tryExtractString(data, "<r_Off>", "</r_Off>");    
    //Timed Relay Control
    String r_OnFor = tryExtractString(data, "<r_OnFor>", "</r_OnFor>");
    
    
    //Water Valve Control
    //Return the Valve State
    String v_State = tryExtractString(data, "<v_State>", "</v_State>");
    //Direct Valve Control
    String v_On = tryExtractString(data, "<v_On>", "</v_On>");
    //Direct Valve Control
    String v_Off = tryExtractString(data, "<v_Off>", "</v_Off>");    
    
    //Pump Control  
    String v_Pump = tryExtractString(data, "<v_Pump>", "</v_Pump>");

    //Timed Relay Control
    String v_OnFor = tryExtractString(data, "<v_OnFor>", "</v_OnFor>");    
    //Advancing valves
    String v_Advance = tryExtractString(data, "<v_Advance>", "</v_Advance>");
    
    
    //Led Control
    if (ledState != NULL) {
        if (ledState == "ON") {
            digitalWrite(led, 1);
            sprintf(currLEDState,"State is: on");
        }
        else {
            digitalWrite(led, 0);
            sprintf(currLEDState,"State is: off");
        }
        curr_time = getTime();
    }
    
    //Relays Functions
    //Set the status of the relay in the string
    if (r_State != NULL) {
        relay = atoi(r_State);
        if (relay != r_curr && r_cmd[relay-1]==1)
            sprintf(statusStr,"R%d: In Queue",relay);

        if (relay != r_curr && r_cmd[relay-1]==0)
            sprintf(statusStr,"R%d: Off",relay);

        if (relay = r_curr && r_cmd[relay-1]==1)
            sprintf(statusStr,"R%d: On",relay);

        if (relay = r_curr && r_cmd[relay-1]==0)
            sprintf(statusStr,"R%d: Off",relay);
    }
    //Turn relay on
    if (r_On != NULL) {
        relay = atoi(r_On);
        r_cmd[relay-1] = 1;
    }
    //Turn relay off
    if (r_Off != NULL) {
        relay = atoi(r_Off);
        if (relay > 0)
            r_cmd[relay-1] = 0;
        else
            memset(v_cmd,0,nValves);
    }
    //Turn relay on for a set time period
    if (r_OnFor != NULL) {
        const char* stringArgs = r_OnFor.c_str();
        char* myCopy = strtok(strdup(stringArgs), ",");
        relay = atoi(myCopy);
        if (relay > 0) {
            myCopy = strtok(NULL, ","); 
            r_cmd[relay-1] = 1;
            r_times[relay-1] = atoi(myCopy);
        }
    } 
    
    //Valve Functions
    //Set the status of the valve in the string
    if (v_State != NULL) {
        valve = atoi(v_State);
        if (valve != v_curr && v_cmd[valve-1]==1)
        {
            sprintf(statusStr,"V%d: In Queue",valve);
            sprintf(v_state,"q%d",valve);
        }
        if (valve != v_curr && v_cmd[valve-1]==0)
        {
            sprintf(statusStr,"V%d: Off",valve);
            sprintf(v_state,"off%d",valve);
        }
        if (valve = v_curr && v_cmd[valve-1]==1)
        {
            sprintf(statusStr,"V%d: On",valve);
            sprintf(v_state,"on%d",valve);
        }
        if (valve = v_curr && v_cmd[valve-1]==0)
        {
            sprintf(statusStr,"V%d: Off",valve);
            sprintf(v_state,"off%d",valve);
        }

    }
    //Turn specific valve on -- Turn any others off if on, and kill queue
    if (v_On != NULL) {
        valve = atoi(v_On);
        v_clr();
        v_cmd[valve-1]=1;
        digitalWrite(led, 1);
    }
    //Turn specific valve off
    if (v_Off != NULL) {
        valve = atoi(v_Off);
        if (valve == 0)
        {
            //Reset
            v_clr();
        } else {
            v_cmd[valve-1] = 0;
        }
        digitalWrite(led, 0);
    }
    //Turn valve on for a set time period -- Use queues when called for various valves
    if (v_OnFor != NULL) {
        const char* stringArgs = v_OnFor.c_str();
        char* myCopy = strtok(strdup(stringArgs), ",");
        valve = atoi(myCopy);
        if (valve > 0) {
            myCopy = strtok(NULL, ",");
            
            //Use Queues
            if(!v_in_q(valve))
            {
                if (v_queue_cnt==0)
                    v_clr();
                    
                v_queue_cnt++;
                
                if (v_queue_cnt < nValves+1)
                {
                    v_queue[v_queue_cnt-1]=valve;
                    v_cmd[valve-1] = 1;
                    v_times[valve-1] = atoi(myCopy);
                    
                    sprintf(statusStr,"V%d: Added to Queue, Position %d (%d,%d)",valve,v_queue_cnt,isVcmd(),v_in_q(valve));
                    
                } else {
                    sprintf(statusStr,"V%d: Queue Full",valve);  
                }                
            } else {
                sprintf(statusStr,"V%d: Already in Queue, Time Updated",valve);  
                v_times[valve-1] = atoi(myCopy);
            }

        }
    }     
    
    //Shift Valves
    if (v_Advance != NULL)
    {
        //Determine wether in queue mode or single
        if(v_queue_cnt==0)
        {
            if (isVcmd() > 0) 
            {
                valve = isVcmd();
                if (valve == nValves)
                {
                    valve = 1;
                    valve_tgl_on(valve); 
                } else {
                    valve++;
                    valve_tgl_on(valve);
                }

            }
            sprintf(statusStr,"Advancing to V%d",valve); 
        } else {
            //Trick it by setting a new end time
            v_time_end=getTime();
            if(v_queue[1] > 0)
                sprintf(statusStr,"Advancing to V%d",v_queue[1]); 
            else
                sprintf(statusStr,"No More In Queue"); 
        }
        
    }
    
    if (v_Pump != NULL)
    {
        onPump = atoi(v_Pump);
    }
    
return 1;
}

void v_clr()
{
    for(int i=0;i<nValves;i++)
    {
        v_cmd[i]=0;
        v_queue[i]=0;
        v_queue_cnt = 0;
        v_curr = 0;
        v_start = false;
    }
}

int isVcmd(void)
{
    int valve = 0;
    for(int i = 0; i < nValves; i++)
    {
        if(v_cmd[i] > 0)
        {
            valve = i+1;
            break;
        }
            
    }    
return valve;    
}


int v_in_q(int valve)
{
    int check_fl = 0;
    for(int i = 0; i < nValves; i++)
    {
        if(v_queue[i] == valve)
            check_fl = 1;
    }    
return check_fl;
}

void v_queue_cl_n_shift()
{
    for (int i=1; i < nValves; i++) 
    {
        v_queue[i-1]=v_queue[i];
    }
    v_queue[nValves-1] = 0;
}

void valve_tgl_on(int valve)
{
    /*
    if (v_curr > 0)
        sprintf(v_state,"on%d,off%d",valve,v_curr);
    else
        sprintf(v_state,"on%d",valve);
        */
    for(int i = 0; i < nValves; i++)
    {
        if(valve == i+1)
            v_cmd[i]=1;
        else
            v_cmd[i]=0;
    }
    
}


//Get the current time in seconds
double getTime() 
{
    return (Time.hour()*3600+Time.minute()*60+Time.second());
}

// Returns any text found between a start and end string inside 'str'
// example: startfooend  -> returns foo
String tryExtractString(String str, const char* start, const char* end) {
    if (str == NULL) {
        return NULL;
    }

    int idx = str.indexOf(start);
    if (idx < 0) {
        return NULL;
    }

    int endIdx = str.indexOf(end);
    if (endIdx < 0) {
        return NULL;
    }

    return str.substring(idx + strlen(start), endIdx);
}

So Since my system is working great now I wonder if we can use the same type of system to integrate into a security alarm system. I have a system that is no longer working but I have all these motion sensor and door/window sensors that go back to the main system box. If I was able to trip a relay from those sensors back to the Ardino controller rewrite some code for this and change a few pictures…Just wondering if this is possible or if there is something out there that does this. I really don’t want to interface into the security system since I don’t use it. Just want to trip a few relays when the sensor go off and report back to smarthings

Check out this project. Announcing the "ST_Anything" Arduino/ThingShield Project

1 Like

I stumbled across this cool project to build a Smartthings sprinkler system but unfortunately it appears the ThingShield has been discontinued. Does anyone know of an alternative to using the ThingShield which would work with the Arduino or maybe the Raspberry Pi?

Thanks.

Bunch of options out there. Here are two I like

https://z-uno.z-wave.me/

http://ziy.io/

woody_seiders:

Thanks for the reply, but those two devices are not available in the US. Also, the 2nd device (ZiY) apparently is on hold now.

I think it’s time for some enterprising person to make a post with all the Thing Shield alternatives in one place. Here are some links to get you started…


If you’re still looking for a ThingShield, I have one to sell. As well, I have the entire kit for this project. It worked great, however, I decided to go with the Rachio system.

Make me an offer for all of the items.

Included are the following:

  • Plastic box enclosure
  • Power for the Arduino
  • Arduino
  • SmartThing Shield
  • 8 channel relay
  • All of the wiring shown

Shoot me a private message and we can work out the details.

Mark

1 Like

Hi, I’m interested in the ThingShield. Please let me know how can I pay for it.

Thanks

Hey, Mark.

I’m curious, what made you decide to go with the Rachio system?

I’m also looking for an alternative. Nothing wrong with this sprinkler system, but everything else SmartThings is terrible and not worth the headache.

I do like the project but it seems like getting a SmartThings Shield for Arduino is almost impossible.
Is there some way to have an Arduino device integrated into ST ?

Me again, sorry, it’s just that I don’t want to abandon this project so easily.
So my question is, in case I decide to maintain this whole setup, can I just replace the ST Shield for Arduino with a WiFi Shield instead ?

Is there too much to customize in this project to make it to work with a WiFi shield instead?

Tks for your advice and help.

@diegoga,

Thanks for your interest. I haven’t recently scanned community posts to see if there is a general work around to the ThingShield that could be easily incorporated into this project.

One solution that I am aware of is to use a Particle Photon board, The good news is that @aacordon did some really cool work (apparently ahead of his time) to port this project onto a Photon board. You can search this thread to see his progress reports. Perhaps he can chime in on the current status and whether he has posted his code.

So far the photon has been working continuously since May.

I am using an IO expander to control the relays using a carrier pcb for the photon as shown in post: Smart Sprinkler System

The end product is in post: Smart Sprinkler System

I normally work out of bitbucket. Just moved the files to git.

-The *.groovy file is the ported Smarthings App. No changes were made to the scheduler.
-The relay-controller.ino is the file used with the particle photon.
-The scratchpad.py is just a python script i used to print out the syntax for the smart things app for n-many relays
-The spark_web.py was how i tested the string commands sent to the photon via python, the photon device_id and access_token need to be provided in lines 43/44

Have fun.

Maybe this will help keep your sprinkler system project alive and well? Should be an easy change of your sketch to support either the NodeMCU ESP8266 board with integrated WiFi, or an Arduino with a W5100 Ethernet shield (more wiring though!) Check out my new “SmartThings v2.0” library. It still supports the ThingShield, but adds some new options. My goal was to keep the basic ThingShield functions the same (i.e. run(), send(), the callback function) while adding support for some new methods of connecting to the ST Hub over Ethernet (hard wired or WiFi). The new “SmartThings v2.x” library could easily be expanded to support other network connectivity if desired.

SO the winter is over and I turned my system back on. I went and replaced all the emitters that the pack rats stole and tested the system. Manually everything worked great. Well over the last day it was warm enough to start the system through the scheduler. Well it did not want to go on through the scheduler. Tried two times and gave up. three days in a row. Manually works fine. Anything I need to check?

I really like the scheduling aspect of this. I however just have a single valve that controls my drip system in my garden. It is connected to a 24v power supply plugged into a z-wave outlet. Is there a way to incorporate my small scale need? I tried using the scheduler and used my switch as the selection for the relay, but that did not work. I appreciate any suggestions!

For one single valve an alternate solution to consider is to use a combination of ST_Anything and CoRE. This project uses the ST_Anything library, but ST_Anything has an example of different devices. You could reuse the Switch type and CoRE could be used to setup a simple schedule. You would lose functionality, but you could eventually add that back. (Switch is on/off by default, but that can be changed. Also, not sure if CoRE handles weather but it is VERY powerful and is something any ST power user should use.)
Just an alternate solution for you to consider.