This is a pretty cool project! I’d like to help somehow if possible. Level sensor: the ultrasonic HC-SR04 is CHEAP and well documented in the arduino world.
Water temp sensors would be helpful for something. Perhaps homebrewers could use it as a heat sink.
Do you have plans to make a dedicated shield for the project?
@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.
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.
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.
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
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. ?
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
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.
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));
}