What options are available for Spark automated rain barrel project?

From a sensor/actuator standpoint, what is required beyond the following:

  1. Outflow control (proportional or binary ?)
  2. Depth/volume detection (proportional or set-point ? If set-point, how many ?)

@AndyW it seems like those would be the required pieces. From briefly looking around it seems like ruben’s valves make the most sense as it appears that it takes a certain amount of pressure for some solenoids to work. So it would seem that those valves or maybe a 3 way automated valve if you wanted two watering zones.

For depth obviously the bare minimum would be a float switch to keep the pump from running dry. This should probably be a minimum requirement as most pumps do not like being run dry. I like the ultra sonic idea and have seen a couple projects using it for this very thing. I saw one today that was available at radio shack for 20 bucks.

The nice things is no matter how manny barrels you have, if you have the oriented horizontally you only need one sensor for depth.

Also consider getting in touch with your local water conservation group. Franklin county has a rain barrel program which helped cheapen the cost of getting a rain barrel.

The nice thing about the pumps that i mentioned above is that they have an internal pressure switch. So they will pump to 35 psi the shut off until it drops to 20 psi then kick back on. Minimizing the energy used and gives (seemingly) adequate pressure. They are used for RVs and Boats and should be available second hand.

@wildhans1 i plan on using my barrels to collect cooling water from chilling my wort. i collect the very hottest and add pbw to clean my HLT MT and BK. The rest will go into the barrels for later use.

@Shadoe607 Indeed, the other valves, although less expensive and smaller aren’t ideal for when there is no pressure (such as here).
Speaking of pressure, I tried the Attwood Tsunami pump today to see how much head it can lift and the answer is not so much. What is nice is that it is inexpensive and runs off of a simple H-bridge motor shield (of which I have many laying around). This would be suitable to pump into a garden through a perforated hose but I’m not so sure about uphill. Tell me more about the pump you are using…oh, just saw it (SureFlow or Jetflo. I’ll have a look).

As far as level sensing goes, the ultrasonics that are cheap are not weatherized and will fail hard (but hey, $10). I’ve been using the MaxBotix 7389, which is excellent but very expensive. Alternatively, I found out that the Milonetech e-tape is now available in a tall version for a 55 gallon drum…my tests of a shorter version showed it lasting a long long time. Still, getting the thing to hang straight is a bit of a trick. I’d rather not do this again:

Maybe I’ll give the owner a quick email to see if it has been used for rain barrels before.

As far as stopping the pump, I was just going to stop the pump when the measurement device (e.g. e-tape) sensed that the water depth is not that great.

A note regarding the local water authorities. Some municipalities do not allow you to collect your rainwater so check with them before installing a rain barrel. (one must cover one’s bum)

@wildhans1 I am not sure if you are speaking to @Shadoe607 or me but I don’t have any plans for a dedicated shield. I would like to use the solar power shield and either wire up an H-bridge for the motor (if it is used) or just use the shield-shield. I’m in fact just thinking like @AndyW for the moment and controlling the valves before I throw a motor on there. The valves would be binary controlled. I would like depth to be proportional to be a little bit smart about how much to drain (although, admittedly, most rain barrels are drastically undersized for most roofs…resulting in the need to have nearly all the capacity available for many storms…at least here in Cincinnati).

I’m grokking @Dave’s library. I was also thinking of ripping into what they’ve done here (http://jflasher.github.io/spark-helper/) for the interface; then again, it’s old and I am curious to know if the spark team has something in alpha/beta that I can use without having to roll my own (I’ll probably just give up and do it…it has been some time since I’ve SSHd into my Bluehost :-/ )

For the moment, Dave’s / @mdma 's work looks interesting. This is the stuff that worries me the most.

I’ve used those little differential pressure sensors to measure water depth. The one I used was half the cost of getting on eof those Milonetech sensors and I had the added advantage of being able to measure outside the range of that sensor. The P1 port was connected to a length of silicon tubing that had a weight on the end. This was then dropped into the water tank. The P2 port was left open to correct for atmospheric changes. The resulting output from the sensor was then the water pressure which is easy to convert to a depth. It proved to be pretty accurate and stable readings.

Here’s a pressure sensor: http://www.digikey.com/product-detail/en/TBPMANN005PGUCV/480-5833-ND/4696873

0-5psi = 0-138 inches of water…

$12

That sensor is not differential so it would be affected by atmospheric pressure unless you have another sensor such as one of the little barometric sensors to correct for this.

The sensor measures gauge pressure so the atmospheric pressure is cancelled out. http://en.wikipedia.org/wiki/Pressure_measurement

Be careful when you see the word gauge pressure. It is either of 2 types.Vented or sealed. Vented means it has a pressure connection vented to atmosphere and normally consists of a porting into the house or a vent tube that is inside the cable that is connected to the gauge. Sealed means it is calibrated with a 1 atmosphere environment and then sealed. This means it will never be exact zero unless the atmospheric is the exact value that the sensor was calibrated at.

Saying that, these appear to have a small hole in the base for the atmospheric so as long as you don’t put it inside a sealed enclosure it will work. :slight_smile:

BUT… they are unamplified so require external instrumentation amplifier.

Better to go with one of the ones with the built in circuit and a 0-5V or 0-3V output. Plenty to choose from, if a little more expensive.

I am going to use this one in my monitoring system for a small project I am doing for a local water storage system. I have used this with a 16 bit ADC and I can measure to 1mm resolution of depth :smile:

http://sg.element14.com/jsp/search/productdetail.jsp?id=1735764&Ntt=1735764

Advantage is the output is I2C so even easier to connect to the Spark Core.

If the price is so low, perhaps by putting one outside the water at the top of the rainbarrel and another one at the bottom of the inside of the rainbarrel, you can cancel out the systematic error. ?

It might be cheap but you need to add in the cost of the instrumentation amplifier you need to build for them both :slight_smile:

True- they are vented or sealed. Most of the low pressure versions (<1bar) are vented though, correct?

The ASDX series are dry-only as far as I can see. Here are some liquid versions:

Seems like part # SSCDLNT005PGAA3 would be a good fit, but I can’t find stock anywhere. Here’s a 0-5psi, but 5v version: http://www.digikey.com/product-search/en?wt.z_cid=ref_hearst_0211_buynow&site=us&mpart=SSCDANT005PGAA5&v=480

And I2C: http://sensing.honeywell.com/index.php?ci_id=44280
a 3.3v version, but unfortunately 0-15psi so the accuracy would be low: http://eu.mouser.com/ProductDetail/Honeywell/SSCDANT015PG2A3/?qs=%2Fha2pyFaduhWQ5orEWpB5y2HjEdD8CttOAgPnp2Zleg2puN5SemASQ%3D%3D

So we have a 5v version with the correct range and a 3.3 version that’s over the correct range. Hmm… any other options?

The one I linked to is 0-5psi.

I believe you are correct as to all low pressure being vented. I would suspect this to be the case otherwise they would fluctuate wildy due to atmospheric changes and that would not be ideal :smile:

That one is dry media only though. Do you have a diaphragm for it?

I was using a silicon tube so there was only air in contact with the sensor. After over 1 year it still works.

Hi guys. I am resurrecting this project after some time. I am going back to the original idea of using data from wunderground. I need a place to pull the feed, parse the data, set up simple rules to operate (or not operate) the pump based upon those data. I remember @Dave (I think it was Dave) saying that I could setup my own server to do this. Is there anyone that either has a service I can use or (2) are there any detailed instructions on how to do so, or (3) can anyone do this for me?
Where I am stuck is figuring out how to translate the control logic from my flow diagram into a functioning web service.

I’d suggest taking a look at the IFTTT beta to see if that will fit what you’re looking for.

More about IFTTT: https://ifttt.com/wtf

1 Like

So I got setup with Dave’s wonderful webhooks and am doing everything on the spark core. I’d Love some help in learning how to release memory for arrays and strings as I go through the process. Now. This isn’t well commented and I deeply apologize for that but I’d like to know if you have some pointers (eh? eh?) about when and what commands I can use to free memory, especially after I copy things to char arrays and vectors. @Dave or @peekay123 ? You’ve been super helpful in the past. Anybody else? I’m just afraid to release something because I am using pointers a lot of places and want to empty memory of an array before clearing the pointer but I don’t want to clear the memory if I’ve passed the pointer. Honestly, pointers still confuse me to a great degree.
Also, something else may be incorrect in my code. Feel free to chastise

//Here we go. Logic totally integrated onboard
//***Before Startup

//Rainbarrel characteristics
//Int k = 1.3 // WAG correction for non/quasi-cylindrical rainbarrels
int tDrainage = (30 * 60) * 60 ; // half an hour to fully drain the tank using valve (seconds)
int tOFPump = (15 * 60) * 60 ; // Time it takes to drain the tank using an overflow pump
float radius = 11.5 / 12 ; //ft
float maxDist = 32 / 12 ; //ft // depth to water surface
float minDist = 4 / 12 ; //ft // dist to water surface
float Cpi = 3.1415 ;
float maxVolume = Cpi * maxDist * radius * radius;

// Select how to overflow – whether to overflow by opening a valve, by running the irrigation pump, or overflow pump
int useAutoIrrig = false ;
int useAutoValve = true;
int useDrainPump = false;
int useInletValve = false ;

//Select nighttime operation
int UseNightTime = true;

//Site characteristics
int rArea = 20 * 50 ; // ft^2 Enter roof area, in sq ft for 1 downspout (A ft * B ft)
//int zipcode = 45202 not needed now. Just directly calling web address

// Check vitals when awakening
// Initialize conditions // what units
int dist = digitalRead (1);

float Volume = Cpi * dist * radius * radius; // Initialize volume

// called once on startup
void setup() {

  // Get time
  Spark.syncTime(); // Syncs time with the cloud

//      if (dist < minDist) {
//        Serial.print("Water depth too high");
//         // open a valve here -- add code
//        int tEndHr = max((50 - Time.minute()), 5);
//        Spark.sleep(SLEEP_MODE_DEEP, int(tEndHr * 60));
//      }

  Serial.begin(115200);


  //Get current hour
  int currentHour = Time.hour();

  // Don't operate at night if boolean off
  if (!UseNightTime) {
    if (currentHour > 21) {
      int tToMidnight = 24 - currentHour;
      Spark.sleep(SLEEP_MODE_DEEP, int ((tToMidnight + 6.8) * 60 * 60)); // want to wake up a few minutes before 7 so I don't sleep for 50 min again
    }
    if (currentHour > 7) {
      Spark.sleep(SLEEP_MODE_DEEP, int ((6.8 - currentHour) * 60 * 60)); // same thing, wait until morning a little before 7
    }
  }


  // Lets listen for the hook response
  Spark.subscribe("hook-response/MYHOOKNAME", gotWeatherData, MY_DEVICES);

  // Lets give ourselves 10 seconds before we actually start the program.
  // That will just give us a chance to open the serial monitor before the program sends the request
  for (int i = 0; i < 10; i++) {
    Serial.println("waiting " + String(10 - i) + " seconds before we publish");
    delay(1000);
  }
}
// SETUP COMPLETE
// Now go into loop

// called forever really fast
void loop() {

  Serial.println("Loop setup");

  /* Try to keep system in relative sync so it samples about
  10 minutes before the hour, projecting for the next X hours.*/
  int currentMinute = Time.minute();
  if (currentMinute < 50) {
    Spark.sleep(SLEEP_MODE_DEEP, int(50 - currentMinute) * 60);
  }

  // Let's request the weather, but no more than once every 60 seconds.
  Serial.println("Requesting Weather!");

  // publish the event that will trigger our webhook
  Spark.publish("MyWebHook");

}

// This function will get called when weather data comes in
void gotWeatherData(const char *name, const char *data) {
  int counter = 0;

  char *copy = strdup(data);
  // Set up arrays to hold the values from the string
  char strArray[37][6] = {0};// = 0; // 36 or 37 different "words", each one varied length,6 char per reading is enough
  //  char strArray1[30][6] = {0};// = 0; // temperature
  char strArray2[37][6] = {0};// = 0; // QPF quantitative precip forecast
  char strArray3[37][6] = {0};// = 0; // POP prob of precip

  // get the first token
  const char s[2] = "~";
  char *token;

  token = strtok(copy, s);
  if (token) {
    int q = 0;
    while (token != NULL) // && counter < 15)
    {
      if (q == 0) {
        strncpy(strArray[counter], token, 5); // You've got to COPY the data pointed to
        q++;
      }
      if (q == 1) {
        strncpy(strArray2[counter], token, 5); // You've got to COPY the data pointed to
        q++;
      }
      if (q == 2) {
        strncpy(strArray3[counter++], token, 5); // You've got to COPY the data pointed to
        q = 0;
      }
      token = strtok(NULL, s);
    }
  }

  //releave memory for data

  int vHour[36] = {0};
  //vTemp [36] // we aren't doing the temperature check right now but will in the future
  float vQPF[36] = {0};
  float vPOP[36] = {0};

  int i = 0 ;
  for (int j = 0; j <= 35; i++) { // step through all 36 hours of data
    vHour [i] = int(strArray[i]);
    vQPF [i] = atof(strArray2[i]);
    vPOP [i] = atof(strArray3[i]);
    i++;
  }

  // free data    ????????????

  //    Multiply the chance of rainfall by the QPF to get proportinal rain
  float vConvolved[36] = {0};
  for (int i = 0; i <= 35; i++) {
    vConvolved[i] = vQPF[i] * vPOP[i] ;
  }

  //(also taking in uncertainty with hours from now)
  for (int i = 0; i <= 35; i++) {
    vConvolved[i] = vConvolved[i] * ((3 - i) / 3); // the X-i is a difference to weight hours farther away lees than close hours.
    //the /Y is just to create ratiometric multiplier
    // currently only using 3 hrs as a stopgap test
  }

  // free vQPF and vPOP ?????????????

  //    Use the equation that converts depth of rainfall to volume of runoff
  for (int i = 0; i <= 35; i++) {
    vConvolved[i] = (vConvolved[i] / 12) * rArea ;
  }

  operateSystem(vConvolved);
}

void operateSystem(float vConvPassed[36]) {

  // By ranking storms, we get an idea of what we can do and when.
  // Lots of cool stuff removed...not yet in use
    // Not worrying about freezing weather right now
  
  // Just looking 3 hours into the future right now. Not using the fancier logic
  //    Is available capacity enough to capture the entire volume of the next three hours?
  float threeHrVolume = vConvPassed[0] + vConvPassed[1] + vConvPassed[2] ;
  if (threeHrVolume < Volume) { // here volume is available capacity
      // not working with automated valve but will in future
  }

  else { //Not enough space
      if (useAutoValve) {
      digitalWrite(1, HIGH);
      delay (5);
      digitalWrite(1, LOW);
    }
    while ((Volume < threeHrVolume) & (dist < maxDist)) {
      if (useDrainPump) {
        analogWrite (2, 20);
      }
      else {
        // no pump, just wait
      }
      dist = digitalRead(1); ///// WHAT UNITS???
      Volume = Cpi * dist * radius * radius; // Initialize volume
      delay(5000);
    }
    analogWrite (2, 0); // just in case; safety
  }

  // Make sure everything is closed up
  if ((Volume > threeHrVolume) || (dist > maxDist)) {
    //digitalWrite(1, LOW);
    analogWrite (2, 0);
  }

  //Fancier logic that I am not implementing right now
  if (useAutoIrrig) {
    ////Not doing this right now because irrigation is not being used
    }

  int tEndHr = max((50 - Time.minute()), 5);
  Spark.sleep(SLEEP_MODE_DEEP, int(tEndHr * 60));
}
1 Like

Are you having a memory issue with your code?

can you explain what you are trying to do here in setup?

  int currentHour = Time.hour();

  // Don't operate at night if boolean off
  if (!UseNightTime) {
    if (currentHour > 21) {
      int tToMidnight = 24 - currentHour;
      Spark.sleep(SLEEP_MODE_DEEP, int ((tToMidnight + 6.8) * 60 * 60)); // want to wake up a few minutes before 7 so I don't sleep for 50 min again
    }
    if (currentHour > 7) {
      Spark.sleep(SLEEP_MODE_DEEP, int ((6.8 - currentHour) * 60 * 60)); // same thing, wait until morning a little before 7
    }
  }

it looks as if you are (providing the option of) selecting the ability avoid turning the system active when you powerup, I guess. Realizing that setup() only runs once, it is difficult to see what you want to do there.

FYI. Global variables (as you have created in the header) will permanently consume memory, whereas Local variables are released from the stack on a change of scope. So, keeping Global variables to a minimum is a good guideline for your programming.

Also these Global variables:

int useAutoIrrig = false ;
int useAutoValve = true;
int useDrainPump = false;
int useInletValve = false ;

each of these variables are reserving in the stack a four byte (32 bit) signed integer (int32_t) compared to something like this:

boolean useAutoIrrig = false ;
boolean useAutoValve = true;
boolean useDrainPump = false;
boolean useInletValve = false ;

which reserve the space for a single byte (8 bit) unsigned integer (uint8_t)

compacting your variables can help save memory… though in this example, it is not a huge amount!

I am not sure that this will give you what you want:

float minDist = 4 / 12;

since the right side is evaluated prior to the assignment. the expression (4/12) may be evaluated as an integer expression and could yeild the result of zero. However I am not sure if the compiler is smart enough to come up with your desired result which looks like you want it to be 0.33333.

you handled it correctly in other examples, and you can force the compiler to recognize that this is the division of floats by doing it this way:

float minDist = 4.0 / 12.0;
1 Like