Rain Guage
-
-
Nice, I wish I had the time and skills to do something like this too!
-
Thanks. It was actually pretty straightforward except for trying to remember how to get the Center of Gravity. In the end, the triangle method worked! Besides, you can just use this and it will take you no time
Attached is the working print. I cannot believe that a guy 15km away did the print for $16 (plus tax). He printed it in under an hour. It is smallish, about 100mm long by 100mm high by 25mm wide assembled.
I still have to mount a magnet to make sure it tips, but looking good. Check out the YouTube...
Photo:
Tipping Bucket Rain Guage – 00:14
— Jim B
-
@BulldogLowell Can't say much except: wow
-
Wow...Impressed!
Like Marcel..i thought why not just use weather service? but before i read your reply i already thought of the reason...fun!Whats more impressive..and just freaking awesome to be able to email a design file to someone...jump in the car and go and pickup a physical object!!
How did you find the printer guy?
-
I used 3dhubs.com.
They connect you with local folks who have the machines and want to make money with them. Really awesome,
I had to quote a few before I found the right price, though.
Check out the website, and put your location in. It will show you all of the local folks that do it.
(Once I know that this works, I'll post the geometry or a link to it in case anyone wants to make one. I just want to make sure it works with he magnet first.)
-
Thank you again!
- For the idea ( and when you think it is ready the files
- For the suggestion of 3dhubs.com I will certainly use it to make some money with my upcoming printer...
-
This is awesome! I was also wanting to do this. You beat me to it again (and I'm ok with that)
I live in Colorado and we also have very localized rain. I would like a rain meter so I know when and how often I need to irrigate. I was also thinking it would be useful to use VeraAlerts to announce if a window is open and it's raining.
How much rain water does it take to tip? From the video it seems like it would need quite a bit to tip. Would it be good to make the buckets(?) a little smaller so it would be more precise? Keep in mind I have no idea how big it is or how these things work in general so I could be way off with the question.
Great job though! Keep up the great work.
-
@petewill said:
This is awesome! I was also wanting to do this. You beat me to it again (and I'm ok with that)
I live in Colorado and we also have very localized rain. I would like a rain meter so I know when and how often I need to irrigate. I was also thinking it would be useful to use VeraAlerts to announce if a window is open and it's raining.
How much rain water does it take to tip? From the video it seems like it would need quite a bit to tip. Would it be good to make the buckets(?) a little smaller so it would be more precise? Keep in mind I have no idea how big it is or how these things work in general so I could be way off with the question.
Great job though! Keep up the great work.
I designed it to about 8ml, I have to calibrate it because of the meniscus, which looks substantial. I can tweak it by raising/lowing the allen cap screws.
there are 25 cubic inches of water per inch of rain in an 8in gauge. So considering that there are 16.38ml per cubic inch, that gets us to approx 50 tips per inch of rain or 0.02in (0.5mm) resolution. I believe for me that's OK, particularly here i s SoFla where we get a lot of rain.
I see your point regarding precision but you can easily go smaller. I wonder if I can make a funnel that will actually drop the water to the bottom if we only received 0.5mm of rain. I'll have to apply something to the funnel to lower the surface tension of the droplets, I think. The other consideration is offsetting the mass of the magnet, which is small but dense.
So unless you get enough water to fall down and tip it, it won't likely record less than say 1mm of rain.
-
Neodymium magnet in and works fine. I had to clean out the countersink hole with a drill to get it in and used E6000 glue to hold it.
Now onto the hall effect sensor, wiring it up and of course the sketch.
The 123D Design file is also attached if anyone wants to make one, play with the design, make fun of it, etc. The .stl file is 13Mb, I can email that if you want.
If you want a print, let me know. I also have a few magnets and sensors, so I can send what you need for the project.
I think I'm going to design a housing for the electronics too. I'm not sure I can find the right project box for this (I really just want to print more parts
-
Isn't it enough space in the foot for the electronics?
Or perhaps make foot a little bigger in v2.0 to fit it there
-
right!
While I want to keep the height low, I can modify the foot to include the whole of the electronica for sure. a radius of 4in provides a nice area for a lot of stuff.
plus, I was thinking about having the antenna at the bottom of the device...
good idea, thanks! It is better that we know that the tipping bucket will work (volumetrically) at this point.
time to get onto the rest of the physical design, I guess.
-
@marceltrapman said:
- For the suggestion of 3dhubs.com I will certainly use it to make some money with my upcoming printer...
Don't forget my "one free part referral" fee!!!
-
@BulldogLowell I have read about techniques to include the magnet while printing.
You can do that by designing a hole, stop the printer when the hole is about to be covered, insert the magnet and continue printing.I will think about you once the printer is there (a couple of months from now)...
-
So, I managed to finish a sketch for the Rain Gauge.
It creates two devices:
- an information panel that displays last 24hours rain and the current rate of rain. along with 5 buckets of time so that you may see up to 5 days total rainfall.
It stores 24, 48, 72, 96 and 120 hrs rain in the Variable1-Variable5 respectively.
I customized the Rain Sensor to look more like a rain sensor and be more descriptive on the front panel, using the image from the moisture sensor, which I thought looked better. - a binary sensor that you can set. Put in the hours you wish to evaluate (1-120) and the rainfall you desire to trip the device. They are entered as Variable1 and Variable 2.
images attached, take a look and comments are appreciated.
I would like to do more with the presentation of the sensors on the vera UI... I'll do that after I get this all together, if I can.
- an information panel that displays last 24hours rain and the current rate of rain. along with 5 buckets of time so that you may see up to 5 days total rainfall.
-
@BulldogLowell I also busy building an rain gauge. How is your one performing? Now did you set this up in the arduino? How did you do the calculation?
Regards
Francois
-
yes is working still, I mentioned how I did the math above...
-
@BulldogLowell do you mind sharing the sketch I not seeing in this forum or on the Build pages of MySensor.org
-
Sure, but I have that running at my 'other' house and it is on older version of MySensors (haven't upgraded there yet
I'll take a whack at updating if you like... it needs to get done anyway... here it is:
/* Arduino Tipping Bucket Rain Gauge June 15, 2014 Version 1.00b Arduino Tipping Bucket Rain Gauge Utilizing a tipping bucket sensor, your Vera home automation controller and the MySensors.org gateway you can measure and sense local rain. This sketch will create two devices on your Vera controller. One will display your total precipitation for the last 24, 48, 72, 96 and 120 hours. The other, a sensor that changes state if there is recent rain (up to last 120 hours) above a threshold. Both these settings are user definable. This sketch features the following: * Allows you to set the rain threshold in mm * Allows you to determine the interval window up to 120 hours. * Displays the last 24, 48, 72, 96 and 120 hours total rain in Variable1 through Variable5 of the Rain Sensor device * Configuration changes to Sensor device updated every 3 hours. * SHould run on any Arduino * Will retain Tripped/Not Tripped status and data in a power outage, saving small ammount of data to EEPROM (Circular Buffer to maximize life of EEBPROM * There is a unique setup requirement necessary in order to properly present the Vera device variables. The details are outlined in the sketch below. * LED status indicator by BulldogLowell@gmail.com for free public use */ #include <SPI.h> #include <EEPROM.h> #include <RF24.h> #include <Sensor.h> // #define STATE_LOCATION 513 // stay away from EEPROM used with Sensor.h #define EEPROM_BUFFER 514 // location of the EEPROM circular buffer #define BUFFER_LENGTH 121 // Sensor gw; boolean metric = false; // int eepromIndex; int tipSensorPin = 3; int ledPin = 5; //PWM capable required unsigned long dataMillis; unsigned long serialInterval = 10000UL; const unsigned long oneHour = 60000UL; unsigned long lastTipTime; unsigned long lastBucketInterval; unsigned long startMillis; int dayBuckets [5] [2] = { { V_VAR1, 24 }, { V_VAR2, 48 }, { V_VAR3, 72 }, { V_VAR4, 96 }, { V_VAR5, 120}, }; volatile byte rainBucket [120] ; // 5 days of data const unsigned long calibrateFactor = 1UL; //Calibration in milimeters of rain per single tip unsigned long rainRate = 0; float currentRain = 0; boolean wasTipped = false; boolean updateVera; int rainCount; volatile int tipBuffer = 0; byte rainWindow = 72;//default rain window in hours int rainSensorThreshold = 15;//default rain sensor sensitivity in mm byte hourCount = 24; byte state; byte oldState = -1; unsigned long ledPulseTime = 500UL; unsigned long pulseStart; // void setup() { Serial.begin(115200); gw.begin(); // pinMode(tipSensorPin, OUTPUT); attachInterrupt (1, sensorTipped, CHANGE); pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); gw.sendSketchInfo("Rain Guage", "1.00b"); gw.sendSensorPresentation(1, S_RAIN); gw.sendSensorPresentation(2, S_MOTION); Serial.println(F("Sensor Presentation Complete")); pinMode(tipSensorPin, INPUT); // state = EEPROM.read(STATE_LOCATION); for (int i = 0; i < BUFFER_LENGTH; i++) { byte locator = EEPROM.read(EEPROM_BUFFER + i); if (locator == 0xFF) { eepromIndex = EEPROM_BUFFER + i; loadRainArray(eepromIndex); Serial.println(eepromIndex); break; } } // gw.sendVariable(2, V_TRIPPED, state); dataMillis = millis(); startMillis = millis(); lastTipTime = millis() - oneHour; // uncomment the following block the first time you include this device. The delays // are necessary in order to create the five Variable buckets in the correct order. // Once you create them, you can comment out these lines and re-upload to your arduino //------------------------------------------------------ /* for (int j = 0; j < 5; j++) { delay(2000); gw.sendVariable(1, dayBuckets[j] [0], 1); } delay(2000); gw.sendVariable(2,V_VAR1, 1); delay(2000); gw.sendVariable(2,V_VAR2, 1); */ //------------------------------------------------------ rainWindow = atoi(gw.getStatus(2, V_VAR1)); delay(2000); rainSensorThreshold = atoi(gw.getStatus(2, V_VAR2)); gw.sendVariable(1, V_RAINRATE, 0); Serial.print(F("Radio Done")); analogWrite(ledPin, 20); } // void loop() { unsigned long measure = 0; // Check to see if we need to show sensor tripped in this block for (int i = 0; i < rainWindow; i++) { measure = measure + rainBucket [i]; } measure >= rainSensorThreshold ? state = 1: state = 0; if (millis() - pulseStart < ledPulseTime) { analogWrite(ledPin, 255); } else { analogWrite(ledPin, 20); } if (state != oldState) { gw.sendVariable(2, V_TRIPPED, state); EEPROM.write(STATE_LOCATION, state); oldState = state; } // if (millis() - lastTipTime >= oneHour)// timeout for rain rate { if (rainRate != 0) { rainRate = 0; gw.sendVariable(1, V_RAINRATE, rainRate); } } if (updateVera) { gw.sendVariable(1, V_RAINRATE, rainRate); updateVera = false; } // if ( (millis() - dataMillis) >= serialInterval)//Comment back in this block to enable Serial prints { for (int i = 24; i <= 120; i=i+24) { updateSerialData(i); } dataMillis = millis(); } // if (tipBuffer > 0) { Serial.println(F("Sensor Tipped")); rainBucket [0] ++; pulseStart = millis(); if (rainBucket [0] > 253) rainBucket[0] = 253; // odd occurance but prevent overflow int dayTotal = 0; for (int i = 0; i < 24; i++) { dayTotal = dayTotal + rainBucket [i]; } gw.sendVariable(1, V_RAIN, dayTotal); unsigned long tipDelay = millis() - lastTipTime; if (tipDelay <= oneHour) { rainRate = ((oneHour) / tipDelay); gw.sendVariable(1, V_RAINRATE, rainRate); Serial.print(F("RainRate= ")); Serial.println(rainRate); } lastTipTime = millis(); updateVera = true; tipBuffer--; } if ( (millis()- startMillis) >= oneHour) { Serial.println("one hour elapsed"); //EEPROM write last value EEPROM.write(eepromIndex, rainBucket[0]); eepromIndex++; if (eepromIndex > EEPROM_BUFFER + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER; Serial.println(eepromIndex); EEPROM.write(eepromIndex, 0xFF); // for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values { rainBucket [i + 1] = rainBucket [i]; } rainBucket[0] = 0; gw.sendVariable(1, V_RAIN, tipCounter(24));// send 24hr tips startMillis = millis(); for (int j = 0; j < 5; j++) { int total = 0; for (int i = 0; i < dayBuckets[j][1]; i++) { total = total + rainBucket [i]; } gw.sendVariable( 1, dayBuckets[j] [0], total); } hourCount++; if (hourCount >=3)//8 times daily update the Sensor variables { rainWindow = atoi(gw.getStatus(2, V_VAR1)); if (rainWindow < 6) { rainWindow = 6; gw.sendVariable( 2, V_VAR1, rainWindow); } if (rainWindow > 120) { rainWindow = 120; gw.sendVariable( 2, V_VAR2, rainWindow); } rainSensorThreshold = atoi(gw.getStatus(2, V_VAR2)); delay(2000); // if (rainSensorThreshold < 1) { rainSensorThreshold = 1; gw.sendVariable( 2, V_VAR2, rainSensorThreshold); } if (rainSensorThreshold > 1000) { rainSensorThreshold = 1000; gw.sendVariable( 2, V_VAR2, rainSensorThreshold); } // hourCount = 0; } } } // void sensorTipped() { static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { tipBuffer++; } last_interrupt_time = interrupt_time; } // unsigned long tipCounter(int x) { int tipCount = 0; for ( int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } return tipCount; } // void updateSerialData(int x) { Serial.print(F("Tips last ")); Serial.print(x); Serial.print(F("hours: ")); int tipCount = 0; for (int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } Serial.println(tipCount); } void loadRainArray(int value) { for (int i = 0; i < BUFFER_LENGTH - 1; i++) { value--; Serial.println(value); if (value < EEPROM_BUFFER) { value = EEPROM_BUFFER + BUFFER_LENGTH; } byte rainValue = EEPROM.read(value); Serial.println(rainValue); if (rainValue < 255) { rainBucket[i] = rainValue; } else { rainBucket [i] = 0; } } }
-
Hey Francois
Thanks for a nice projekt. I am thinking on building one myself. But is it possible to log the rain for everyday and writing the data to a csv file so I can use the data in Excel on Windows.
Hope that you can help my.
Tom
-
Tom,
Are you wishing to
- log to an SD card
- collect data controller side (e.g. Vera or other controller)
- transmit data directly to a server
- something else....?
-
@BulldogLowell said:
#define STATE_LOCATION 513 // stay away from EEPROM used with Sensor.h #define EEPROM_BUFFER 514 // location of the EEPROM circular buffer #define BUFFER_LENGTH 121
are we still required to steer clear of MySensor's EEPROM in addresses 0-512?
-
I use a vera light with a USB key where the data can be stored. It vill be better if I the date was on my dropbox og sendt to my NAS.
But I have not fount a way to do it.
Tom
-
are we still required to steer clear of MySensor's EEPROM in addresses 0-512?
Use the saveState()/loadState() function in the MySensors library
-
Hi!!
I was searching for a long time in internet any place where someone offers a design of this mechanism of rain gauge. Finally I've found you!. I really interested in having one, but I don't have a 3D (not jet!). Could you provide me one print? Let me know how is the process to get one, please ! (I am in Spain!)
Thank you
-
I actually used www.3dhubs.com which I believe is a Dutch company. I looked, they have service in Spain!
the service connects you with owners of 3D printers who make money using their personal machines.
-
@nostradamux I had one printed using @BulldogLowell design and thru 3Dhubs.com the build was good and I used abs also for material. My cost in USA was approx. $17 usd and took a day to print and receive, looks good.
However I have not gotten far with project due to other issues unrealated just with I had more time and experience.But this group is most helpful for newbies!
Good luck
-
@BulldogLowell could you convert your 123D file to STL so that the 3DHub could be used please ?
this is how to do it:
http://sitesupport.123dapp.com/entries/20540436-How-to-export-an-STL-file-for-3D-printing-in-123Dthanks,
-
exceeds max file size, so here is a link:
https://drive.google.com/file/d/0B3KGTJHUgpw1MVpaYnUyUDUwN0E/view?usp=sharing
-
@BulldogLowell Did you ever get around to updating the sketch to work with version 1.4.1? I'm finally getting around to making a rain gauge and I didn't want to recreate the wheel if you already made it and I missed it somewhere.
Thanks,
Pete
-
Sorry for the lag time, I've been on Easter vacation with the family!
Yes, I did update it, but I don't have my laptop with me here. I'll post it when I return home, early next week.
-
@BulldogLowell Great, thanks! You are a way better coder than me and I was struggling to understand the code. I did learn quite a bit about interrupts when I was trying to figure it out though.
-
@BulldogLowell Any luck finding the sketch? Sorry to bug you, I was just hoping to get this project completed soon so I can work on the irrigation controller next. It's almost time to start watering the lawn/plants... Thanks!
-
@petewill
I am embarrassed to tell you that after updating my arduino software a while back, I cannot locate a compilable sketch!I have Time Machine, but still can't find it... and it is running on my rain guage!!!
Here is a version that I have (which is titled RainGuage_V1.4.1.ino in my directory) which i found but cannot compile, it looks like a first save without edit?
I need to completely update Arduino, the version and mySensors but please look at this:
/* Arduino Tipping Bucket Rain Gauge June 15, 2014 Version 1.00b Arduino Tipping Bucket Rain Gauge Utilizing a tipping bucket sensor, your Vera home automation controller and the MySensors.org gateway you can measure and sense local rain. This sketch will create two devices on your Vera controller. One will display your total precipitation for the last 24, 48, 72, 96 and 120 hours. The other, a sensor that changes state if there is recent rain (up to last 120 hours) above a threshold. Both these settings are user definable. This sketch features the following: * Allows you to set the rain threshold in mm * Allows you to determine the interval window up to 120 hours. * Displays the last 24, 48, 72, 96 and 120 hours total rain in Variable1 through Variable5 of the Rain Sensor device * Configuration changes to Sensor device updated every 3 hours. * SHould run on any Arduino * Will retain Tripped/Not Tripped status and data in a power outage, saving small ammount of data to EEPROM (Circular Buffer to maximize life of EEBPROM * There is a unique setup requirement necessary in order to properly present the Vera device variables. The details are outlined in the sketch below. * LED status indicator by BulldogLowell@gmail.com for free public use */ #include <SPI.h> #include <EEPROM.h> #include <RF24.h> #include <Sensor.h> // #define STATE_LOCATION 513 // stay away from EEPROM used with Sensor.h #define EEPROM_BUFFER 514 // location of the EEPROM circular buffer #define BUFFER_LENGTH 121 // Sensor gw; boolean metric = false; // int eepromIndex; int tipSensorPin = 3; int ledPin = 5; //PWM capable required unsigned long dataMillis; unsigned long serialInterval = 10000UL; const unsigned long oneHour = 60000UL; unsigned long lastTipTime; unsigned long lastBucketInterval; unsigned long startMillis; int dayBuckets [5] [2] = { { V_VAR1, 24 }, { V_VAR2, 48 }, { V_VAR3, 72 }, { V_VAR4, 96 }, { V_VAR5, 120}, }; volatile byte rainBucket [120] ; // 5 days of data const unsigned long calibrateFactor = 1UL; //Calibration in milimeters of rain per single tip unsigned long rainRate = 0; float currentRain = 0; boolean wasTipped = false; boolean updateVera; int rainCount; volatile int tipBuffer = 0; byte rainWindow = 72;//default rain window in hours int rainSensorThreshold = 15;//default rain sensor sensitivity in mm byte hourCount = 24; byte state; byte oldState = -1; unsigned long ledPulseTime = 500UL; unsigned long pulseStart; // void setup() { Serial.begin(115200); gw.begin(); // pinMode(tipSensorPin, OUTPUT); attachInterrupt (1, sensorTipped, CHANGE); pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); gw.sendSketchInfo("Rain Guage", "1.00b"); gw.sendSensorPresentation(1, S_RAIN); gw.sendSensorPresentation(2, S_MOTION); Serial.println(F("Sensor Presentation Complete")); pinMode(tipSensorPin, INPUT); // state = EEPROM.read(STATE_LOCATION); for (int i = 0; i < BUFFER_LENGTH; i++) { byte locator = EEPROM.read(EEPROM_BUFFER + i); if (locator == 0xFF) { eepromIndex = EEPROM_BUFFER + i; loadRainArray(eepromIndex); Serial.println(eepromIndex); break; } } // gw.sendVariable(2, V_TRIPPED, state); dataMillis = millis(); startMillis = millis(); lastTipTime = millis() - oneHour; // uncomment the following block the first time you include this device. The delays // are necessary in order to create the five Variable buckets in the correct order. // Once you create them, you can comment out these lines and re-upload to your arduino //------------------------------------------------------ /* for (int j = 0; j < 5; j++) { delay(2000); gw.sendVariable(1, dayBuckets[j] [0], 1); } delay(2000); gw.sendVariable(2,V_VAR1, 1); delay(2000); gw.sendVariable(2,V_VAR2, 1); */ //------------------------------------------------------ rainWindow = atoi(gw.getStatus(2, V_VAR1)); delay(2000); rainSensorThreshold = atoi(gw.getStatus(2, V_VAR2)); gw.sendVariable(1, V_RAINRATE, 0); Serial.print(F("Radio Done")); analogWrite(ledPin, 20); } // void loop() { unsigned long measure = 0; // Check to see if we need to show sensor tripped in this block for (int i = 0; i < rainWindow; i++) { measure = measure + rainBucket [i]; } measure >= rainSensorThreshold ? state = 1: state = 0; if (millis() - pulseStart < ledPulseTime) { analogWrite(ledPin, 255); } else { analogWrite(ledPin, 20); } if (state != oldState) { gw.sendVariable(2, V_TRIPPED, state); EEPROM.write(STATE_LOCATION, state); oldState = state; } // if (millis() - lastTipTime >= oneHour)// timeout for rain rate { if (rainRate != 0) { rainRate = 0; gw.sendVariable(1, V_RAINRATE, rainRate); } } if (updateVera) { gw.sendVariable(1, V_RAINRATE, rainRate); updateVera = false; } // if ( (millis() - dataMillis) >= serialInterval)//Comment back in this block to enable Serial prints { for (int i = 24; i <= 120; i=i+24) { updateSerialData(i); } dataMillis = millis(); } // if (tipBuffer > 0) { Serial.println(F("Sensor Tipped")); rainBucket [0] ++; pulseStart = millis(); if (rainBucket [0] > 253) rainBucket[0] = 253; // odd occurance but prevent overflow int dayTotal = 0; for (int i = 0; i < 24; i++) { dayTotal = dayTotal + rainBucket [i]; } gw.sendVariable(1, V_RAIN, dayTotal); unsigned long tipDelay = millis() - lastTipTime; if (tipDelay <= oneHour) { rainRate = ((oneHour) / tipDelay); gw.sendVariable(1, V_RAINRATE, rainRate); Serial.print(F("RainRate= ")); Serial.println(rainRate); } lastTipTime = millis(); updateVera = true; tipBuffer--; } if ( (millis()- startMillis) >= oneHour) { Serial.println("one hour elapsed"); //EEPROM write last value EEPROM.write(eepromIndex, rainBucket[0]); eepromIndex++; if (eepromIndex > EEPROM_BUFFER + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER; Serial.println(eepromIndex); EEPROM.write(eepromIndex, 0xFF); // for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values { rainBucket [i + 1] = rainBucket [i]; } rainBucket[0] = 0; gw.sendVariable(1, V_RAIN, tipCounter(24));// send 24hr tips startMillis = millis(); for (int j = 0; j < 5; j++) { int total = 0; for (int i = 0; i < dayBuckets[j][1]; i++) { total = total + rainBucket [i]; } gw.sendVariable( 1, dayBuckets[j] [0], total); } hourCount++; if (hourCount >=3)//8 times daily update the Sensor variables { rainWindow = atoi(gw.getStatus(2, V_VAR1)); if (rainWindow < 6) { rainWindow = 6; gw.sendVariable( 2, V_VAR1, rainWindow); } if (rainWindow > 120) { rainWindow = 120; gw.sendVariable( 2, V_VAR2, rainWindow); } rainSensorThreshold = atoi(gw.getStatus(2, V_VAR2)); delay(2000); // if (rainSensorThreshold < 1) { rainSensorThreshold = 1; gw.sendVariable( 2, V_VAR2, rainSensorThreshold); } if (rainSensorThreshold > 1000) { rainSensorThreshold = 1000; gw.sendVariable( 2, V_VAR2, rainSensorThreshold); } // hourCount = 0; } } } // void sensorTipped() { static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { tipBuffer++; } last_interrupt_time = interrupt_time; } // unsigned long tipCounter(int x) { int tipCount = 0; for ( int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } return tipCount; } // void updateSerialData(int x) { Serial.print(F("Tips last ")); Serial.print(x); Serial.print(F("hours: ")); int tipCount = 0; for (int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } Serial.println(tipCount); } void loadRainArray(int value) { for (int i = 0; i < BUFFER_LENGTH - 1; i++) { value--; Serial.println(value); if (value < EEPROM_BUFFER) { value = EEPROM_BUFFER + BUFFER_LENGTH; } byte rainValue = EEPROM.read(value); Serial.println(rainValue); if (rainValue < 255) { rainBucket[i] = rainValue; } else { rainBucket [i] = 0; } } }
I will convert this this weekend.
-
Her is an untested go at it, and a very down and dirty update... I will test it probably Wednesday night, when I am back home. Let me know if you can!
/* Arduino Tipping Bucket Rain Gauge April 26, 2015 Version 1.01b for MySensors version 1.4.1 Arduino Tipping Bucket Rain Gauge Utilizing a tipping bucket sensor, your Vera home automation controller and the MySensors.org gateway you can measure and sense local rain. This sketch will create two devices on your Vera controller. One will display your total precipitation for the last 24, 48, 72, 96 and 120 hours. The other, a sensor that changes state if there is recent rain (up to last 120 hours) above a threshold. Both these settings are user definable. This sketch features the following: * Allows you to set the rain threshold in mm * Allows you to determine the interval window up to 120 hours. * Displays the last 24, 48, 72, 96 and 120 hours total rain in Variable1 through Variable5 of the Rain Sensor device * Configuration changes to Sensor device updated every 3 hours. * SHould run on any Arduino * Will retain Tripped/Not Tripped status and data in a power outage, saving small ammount of data to EEPROM (Circular Buffer to maximize life of EEPROM) * There is a unique setup requirement necessary in order to properly present the Vera device variables. The details are outlined in the sketch below. * LED status indicator by BulldogLowell@gmail.com for free public use */ #include <SPI.h> #include <MySensor.h> #include <EEPROM.h> #define RADIO_ID 99 #define STATE_LOCATION 513 // stay away from EEPROM used with Sensor.h #define EEPROM_BUFFER 514 // location of the EEPROM circular buffer #define BUFFER_LENGTH 121 // buffer plus the current hour // MySensor gw; // MyMessage msgRainRate(1, V_RAINRATE); MyMessage msgRain(1, V_RAIN); MyMessage msgRainVAR1(1, V_VAR1); MyMessage msgRainVAR2(1, V_VAR2); MyMessage msgRainVAR3(1, V_VAR3); MyMessage msgRainVAR4(1, V_VAR4); MyMessage msgRainVAR5(1, V_VAR5); MyMessage msgTripped(2, S_MOTION); MyMessage msgTrippedVar1(2, V_VAR1); MyMessage msgTrippedVar2(2, V_VAR2); // boolean metric = false; // int eepromIndex; int tipSensorPin = 3; int ledPin = 5; //PWM capable required unsigned long dataMillis; unsigned long serialInterval = 10000UL; const unsigned long oneHour = 60000UL; unsigned long lastTipTime; unsigned long lastBucketInterval; unsigned long startMillis; int dayBuckets [5] [2] = { { V_VAR1, 24 }, { V_VAR2, 48 }, { V_VAR3, 72 }, { V_VAR4, 96 }, { V_VAR5, 120}, }; volatile byte rainBucket [120] ; // 5 days of data const unsigned long calibrateFactor = 1UL; //Calibration in milimeters of rain per single tip unsigned long rainRate = 0; float currentRain = 0; boolean wasTipped = false; boolean updateVera; int rainCount; volatile int tipBuffer = 0; byte rainWindow = 72; //default rain window in hours int rainSensorThreshold = 15; //default rain sensor sensitivity in mm byte hourCount = 24; byte state; byte oldState = -1; // void setup() { Serial.begin(115200); gw.begin(getVariables, RADIO_ID); // pinMode(tipSensorPin, OUTPUT); attachInterrupt (1, sensorTipped, CHANGE); pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); gw.sendSketchInfo("Rain Guage", "1.01b"); gw.present(1, S_RAIN); gw.present(2, S_MOTION); Serial.println(F("Sensor Presentation Complete")); pinMode(tipSensorPin, INPUT); // state = EEPROM.read(STATE_LOCATION); //retreive prior state from EEPROM gw.send(msgTripped.set(state), false); //send state to Vera delay(250); // recharge the capacitor // boolean isDataOnEeprom = false; for (int i = 0; i < BUFFER_LENGTH; i++) { byte locator = EEPROM.read(EEPROM_BUFFER + i); if (locator == 0xFF) // found the EEPROM circular buffer index { eepromIndex = EEPROM_BUFFER + i; loadRainArray(eepromIndex); isDataOnEeprom = true; Serial.println(eepromIndex); break; } } // if (!isDataOnEeprom) { // load all zeroes to EEPROM? // I gotta check this out!!! } // // reset the timers dataMillis = millis(); startMillis = millis(); lastTipTime = millis() - oneHour; gw.request(2, V_VAR1); delay(250); gw.request(2, V_VAR2); delay(250); Serial.print(F("Radio Done")); analogWrite(ledPin, 20); } // void loop() { gw.process(); pulseLED(); // // let's constantly check to see if the rain in the past rainWindow hours is greater than rainSensorThreshold // int measure = 0; // Check to see if we need to show sensor tripped in this block for (int i = 0; i < rainWindow; i++) { measure += rainBucket [i]; } state = (measure >= rainSensorThreshold); if (state != oldState) { gw.send(msgTripped.set(state), false); delay(250); EEPROM.write(STATE_LOCATION, state); oldState = state; } // // Now lets reset the rainRate to zero if no tips in the last hour // if (millis() - lastTipTime >= oneHour)// timeout for rain rate { if (rainRate != 0) { rainRate = 0; gw.send(msgRainRate.set(0)); delay(250); } } // if tipped,send updates to VERA // if (updateVera) { gw.send(msgRainRate.set(rainRate)); delay(250); updateVera = false; } /* if ( (millis() - dataMillis) >= serialInterval)//Comment back in this block to enable Serial prints { for (int i = 24; i <= 120; i = i + 24) { updateSerialData(i); } dataMillis = millis(); } */ if (tipBuffer > 0) { Serial.println(F("Sensor Tipped")); rainBucket [0] ++; // if (rainBucket [0] > 253) rainBucket[0] = 253; // odd occurance but prevent overflow int dayTotal = 0; for (int i = 0; i < 24; i++) { dayTotal = dayTotal + rainBucket [i]; } gw.send(msgRain.set(dayTotal)); delay(250); unsigned long tipDelay = millis() - lastTipTime; if (tipDelay <= oneHour) { rainRate = ((oneHour) / tipDelay); gw.send(msgRainRate.set(rainRate)); Serial.print(F("RainRate= ")); Serial.println(rainRate); } lastTipTime = millis(); updateVera = true; tipBuffer--; } if (millis() - startMillis >= oneHour) { Serial.println("one hour elapsed"); //EEPROM write last value EEPROM.write(eepromIndex, rainBucket[0]); eepromIndex++; if (eepromIndex > EEPROM_BUFFER + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER; Serial.println(eepromIndex); EEPROM.write(eepromIndex, 0xFF); // for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values { rainBucket [i + 1] = rainBucket [i]; } rainBucket[0] = 0; gw.send(msgRain.set(tipCounter(24)));// send 24hr tips delay(250); transmitRainData(); // send all of the 5 buckets of data to controller startMillis = millis(); } } // void sensorTipped() { static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { tipBuffer++; } last_interrupt_time = interrupt_time; } // int tipCounter(int hours) { int tipCount = 0; for ( int i = 0; i < hours; i++) { tipCount = tipCount + rainBucket [i]; } return tipCount; } // void updateSerialData(int x) { Serial.print(F("Tips last ")); Serial.print(x); Serial.print(F("hours: ")); int tipCount = 0; for (int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } Serial.println(tipCount); } void loadRainArray(int value) { for (int i = 0; i < BUFFER_LENGTH - 1; i++) { value--; Serial.println(value); if (value < EEPROM_BUFFER) { value = EEPROM_BUFFER + BUFFER_LENGTH; } byte rainValue = EEPROM.read(value); Serial.println(rainValue); if (rainValue < 255) { rainBucket[i] = rainValue; } else { rainBucket [i] = 0; } } } void transmitRainData(void) { int rainUpdateTotal = 0; for (int i = 0; i < 24; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR1.set(rainUpdateTotal)); delay(250); for (int i = 24; i < 48; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR2.set(rainUpdateTotal)); delay(250); for (int i = 48; i < 72; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR3.set(rainUpdateTotal)); delay(250); for (int i = 72; i < 96; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR4.set(rainUpdateTotal)); delay(250); for (int i = 96; i < 120; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR5.set(rainUpdateTotal)); delay(250); } void getVariables(const MyMessage &message) { if (message.sensor == 1) { // nothing to do here } else if (message.sensor == 2) { if (message.type == V_VAR1) { rainWindow = atoi(message.data); if (rainWindow > 120) { rainWindow = 120; } else if (rainWindow < 6) { rainWindow = 6; } if (rainWindow != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar1.set(rainWindow)); } } else if (message.type == V_VAR2) { rainSensorThreshold = atoi(message.data); if (rainSensorThreshold > 1000) { rainSensorThreshold = 1000; } else if (rainSensorThreshold < 1) { rainSensorThreshold = 1; } if (rainSensorThreshold != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar2.set(rainSensorThreshold)); } } } } void pulseLED(void) { static boolean ledState = true; static unsigned long pulseStart = millis(); if (millis() - pulseStart < 500UL) { digitalWrite(ledPin, !ledState); pulseStart = millis(); } }
-
@BulldogLowell Awesome, thank you very much! I know what it's like to lose code that way. I have done the same thing myself...
Yes, I will test this. I'll do my best to get it tested tonight. I'm combining it with some other sensors (light and temp/humidity) so once I get everything merged I'll be able to upload it.
I'll let you know what I find.
Thanks again!
Pete
-
Ok, I made the necessary changes for my application and I had a couple of questions along the way.
-
I have never worked with eeprom so I don't pretend to know anything about it (it's now on my list of things to research though). Anyway, I noticed above hek recommended using the "saveState()/loadState() function in the MySensors library". Is that necessary in your code or are we ok as is? It seems to be functioning ok for me when I compile it so maybe not...
-
I would like to trigger my rain sensor as soon as the first bucket has tipped (I have scenes that tell me to close the windows etc. when it's raining). Is changing the rainSensorThreshold value to 1 an appropriate way to do that or should I create another child node for my purposes? Maybe I should use rainRate in my PLEG instead?
-
My tipping bucket was pulled from an existing rain gauge and according to my calculations (which may be off) each bucket is .7mm of rain. I don't know much about unsigned long variables but I'm wondering if I need to change "const unsigned long calibrateFactor" to a float or something?
Thanks for all your help!!
Pete
-
-
Jim,
I uploaded the code but it didn't seem to work for me. Do I need to upload some additional files to Vera? When I added the nodes a lot of my devices stopped displaying correctly (not just the newly added ones). They look like this:
When I deleted the rain gauge nodes everything came back:
As I was looking at the code I came up with some additional questions:
-
There is a dayBuckets array but it doesn't seem to do anything in the code?
-
I also couldn't find "const unsigned long calibrateFactor" used anywhere.
I fully admit I'm not a very good with code and I could be missing something. I was just trying to find how the variables were working together and being calculated.
Thanks for all your help with this! I could not do it without you.
Pete
-
-
@petewill The reason your MySensor plugin brakes when you add the rain gauge is that the device_files are missing. I don't know if there are any device files for the rain gauge since they are not included in the github branch for the vera plugin.
You could check under your rain gauge which device_file it is trying to use maybe you can search for it online and hope it exists.
Worst case is that you need to create them your self.
-
yeah, you need the implemention files:
D_RainSensor1.xml:
<?xml version="1.0"?> <root xmlns="urn:schemas-upnp-org:device-1-0"> <specVersion> <major>1</major> <minor>0</minor> </specVersion> <device> <deviceType>urn:schemas-micasaverde-com:device:RainSensor:1</deviceType> <Category_Num>12</Category_Num> <staticJson>D_RainSensor1.json</staticJson> <serviceList> <service> <serviceType>urn:schemas-upnp-org:service:RainSensor:1</serviceType> <serviceId>urn:upnp-org:serviceId:RainSensor1</serviceId> <SCPDURL>S_RainSensor1.xml</SCPDURL> </service> <service> <serviceType>urn:schemas-micasaverde-com:service:HaDevice:1</serviceType> <serviceId>urn:micasaverde-com:serviceId:HaDevice1</serviceId> <SCPDURL>S_HaDevice1.xml</SCPDURL> </service> </serviceList> </device> </root>
and
D_RainSensor1.json
{ "flashicon": "icons/Humidity_Sensor.swf", "imgIconBody": "", "imgIconDimmable": "", "imgIconTurnable": "", "imgIconMin": "", "imgIconMax": "", "halloIconsDir": "pics\/hallo", "inScene": "0", "DisplayStatus": {}, "doc_url": { "doc_language": 1, "doc_manual": 1, "doc_version": 1, "doc_platform": 0, "doc_page": "sensors" }, "Tabs": [ { "Label": { "lang_tag": "tabname_control", "text": "Information" }, "Position": "0", "TabType": "flash", "SceneGroup": [ { "id": "1", "top": "1.5", "left": "0", "x": "2", "y": "2" } ], "ControlGroup": [ { "id": "1", "scenegroup": "1", "type": "info" }, { "id": "2", "scenegroup": "1", "type": "info" } ], "Control": [ { "ControlGroup":"1", "ControlPair": "1", "ControlHeader": "1", "top": "1", "left": "0", "x": "1.25", "ControlType": "label", "Label": { "lang_tag": "rain_rate", "text": "Rate of Rain" }, "Display": { "Top": 20, "Left": 50, "Width": 75, "Height": 20 } }, { "ControlGroup":"1", "ControlPair": "1", "ControlHeader": "1", "top": "1", "left": "1.25", "x": "0.75", "ControlType": "variable", "Display": { "Service": "urn:upnp-org:serviceId:RainSensor1", "Variable": "CurrentRain", "Top": 20, "Left": 150, "Width": 25, "Height": 20 } }, { "ControlType": "label", "Label": { "lang_tag": "total_rain_unit", "text": "mm/hr" }, "Display": { "Top": 20, "Left": 200, "Width": 25, "Height": 20 } }, { "ControlGroup":"2", "ControlPair": "2", "ControlHeader": "1", "top": "0", "left": "0", "x": "1.25", "ControlType": "label", "Label": { "lang_tag": "total_rain", "text": "24hrs Total:", }, "Display": { "Top": 55, "Left": 50, "Width": 75, "Height": 20 } }, { "ControlGroup":"2", "ControlPair": "2", "ControlHeader": "1", "top": "0", "left": "1.25", "x": "0.75", "ControlType": "variable", "Display": { "Service": "urn:upnp-org:serviceId:RainSensor1", "Variable": "CurrentTRain", "Top": 55, "Left": 150, "Width": 25, "Height": 20 } }, { "ControlType": "label", "Label": { "lang_tag": "rain_rate_unit", "text": "mm" }, "Display": { "Top": 55, "Left": 200, "Width": 25, "Height": 20 } }, { "ControlGroup":"3", "ControlPair": "3", "ControlHeader": "1", "top": "0", "left": "0", "x": "1.25", "ControlType": "label", "Label": { "lang_tag": "rain_forty", "text": "48hrs Total:" }, "Display": { "Top": 80, "Left": 50, "Width": 75, "Height": 20 } }, { "ControlGroup":"3", "ControlPair": "3", "ControlHeader": "1", "top": "0", "left": "1.25", "x": "0.75", "ControlType": "variable", "Display": { "Service": "urn:upnp-org:serviceId:VContainer1", "Variable": "Variable2", "Top": 80, "Left": 150, "Width": 25, "Height": 20 } }, { "ControlType": "label", "Label": { "lang_tag": "rain_unit", "text": "mm" }, "Display": { "Top": 80, "Left": 200, "Width": 25, "Height": 20 } }, { "ControlGroup":"4", "ControlPair": "4", "ControlHeader": "1", "top": "0", "left": "0", "x": "1.25", "ControlType": "label", "Label": { "lang_tag": "rain_seventy", "text": "72hrs Total:" }, "Display": { "Top": 105, "Left": 50, "Width": 75, "Height": 20 } }, { "ControlGroup":"4", "ControlPair": "4", "ControlHeader": "1", "top": "0", "left": "1.25", "x": "0.75", "ControlType": "variable", "Display": { "Service": "urn:upnp-org:serviceId:VContainer1", "Variable": "Variable3", "Top": 105, "Left": 150, "Width": 25, "Height": 20 } }, { "ControlType": "label", "Label": { "lang_tag": "rain_unit1", "text": "mm" }, "Display": { "Top": 105, "Left": 200, "Width": 25, "Height": 20 } }, { "ControlGroup":"5", "ControlPair": "5", "ControlHeader": "1", "top": "0", "left": "0", "x": "1.25", "ControlType": "label", "Label": { "lang_tag": "rain_forty", "text": "96hrs Total:" }, "Display": { "Top": 130, "Left": 50, "Width": 75, "Height": 20 } }, { "ControlGroup":"5", "ControlPair": "5", "ControlHeader": "1", "top": "0", "left": "1.25", "x": "0.75", "ControlType": "variable", "Display": { "Service": "urn:upnp-org:serviceId:VContainer1", "Variable": "Variable4", "Top": 130, "Left": 150, "Width": 25, "Height": 20 } }, { "ControlType": "label", "Label": { "lang_tag": "rain_unit2", "text": "mm" }, "Display": { "Top": 130, "Left": 200, "Width": 25, "Height": 20 } }, { "ControlGroup":"6", "ControlPair": "6", "ControlHeader": "1", "top": "0", "left": "0", "x": "1.25", "ControlType": "label", "Label": { "lang_tag": "rain_hundred", "text": "120hrs Total:" }, "Display": { "Top": 155, "Left": 50, "Width": 75, "Height": 20 } }, { "ControlGroup":"6", "ControlPair": "6", "ControlHeader": "1", "top": "0", "left": "1.25", "x": "0.75", "ControlType": "variable", "Display": { "Service": "urn:upnp-org:serviceId:VContainer1", "Variable": "Variable5", "Top": 155, "Left": 150, "Width": 25, "Height": 20 } }, { "ControlType": "label", "Label": { "lang_tag": "rain_unit3", "text": "mm" }, "Display": { "Top": 155, "Left": 200, "Width": 25, "Height": 20 } } ] }, { "Label": { "lang_tag": "advanced", "text": "Advanced" }, "Position": "1", "TabType": "javascript", "ScriptName": "shared.js", "Function": "advanced_device" }, { "Label": { "lang_tag": "logs", "text": "Logs" }, "Position": "2", "TabType": "javascript", "ScriptName": "shared.js", "Function": "device_logs" }, { "Label": { "lang_tag": "notifications", "text": "Notifications" }, "Position": "3", "TabType": "javascript", "ScriptName": "shared.js", "Function": "device_notifications" } ], "eventList2": [ { "id": 1, "label": { "lang_tag": "total_rain_goes_above", "text": "Total rain goes above" }, "serviceId": "urn:upnp-org:serviceId:RainSensor1", "norepeat": "1", "argumentList": [ { "id": 1, "dataType": "r8", "name": "CurrentTRain", "comparisson": ">", "prefix": { "lang_tag": "total_rain", "text": "Total rain" }, "suffix": { "lang_tag": "total_rain_unit", "text": "mm" }, "HumanFriendlyText": { "lang_tag": "hft_total_rain_goes_above", "text": "Total rain for _DEVICE_NAME_ goes above _ARGUMENT_VALUE_" } } ] }, { "id": 2, "label": { "lang_tag": "total_rain_goes_below", "text": "Total rain goes below" }, "serviceId": "urn:upnp-org:serviceId:RainSensor1", "norepeat": "1", "argumentList": [ { "id": 1, "dataType": "r8", "name": "CurrentTRain", "comparisson": "<", "prefix": { "lang_tag": "total_rain", "text": "Total rain" }, "suffix": { "lang_tag": "total_rain_unit", "text": "mm" }, "HumanFriendlyText": { "lang_tag": "hft_total_rain_goes_below", "text": "Total rain for _DEVICE_NAME_ goes below _ARGUMENT_VALUE_" } } ] }, { "id": 3, "label": { "lang_tag": "rain_rate_goes_above", "text": "Current rain goes above" }, "serviceId": "urn:upnp-org:serviceId:RainSensor1", "norepeat": "1", "argumentList": [ { "id": 1, "dataType": "r8", "name": "CurrentRain", "comparisson": ">", "prefix": { "lang_tag": "rain_rate", "text": "Current rain" }, "suffix": { "lang_tag": "rain_rate_unit", "text": "mm/hr" }, "HumanFriendlyText": { "lang_tag": "hft_rain_rate_goes_above", "text": "Current rain for _DEVICE_NAME_ goes above _ARGUMENT_VALUE_" } } ] }, { "id": 4, "label": { "lang_tag": "rain_rate_goes_below", "text": "Current rain goes below" }, "serviceId": "urn:upnp-org:serviceId:RainSensor1", "norepeat": "1", "argumentList": [ { "id": 1, "dataType": "r8", "name": "CurrentRain", "comparisson": "<", "prefix": { "lang_tag": "rain_rate", "text": "Current rain" }, "suffix": { "lang_tag": "rain_rate_unit", "text": "mm/hr" }, "HumanFriendlyText": { "lang_tag": "hft_rain_rate_goes_below", "text": "Current rain for _DEVICE_NAME_ goes below _ARGUMENT_VALUE_" } } ] }, { "id": 5, "label": { "lang_tag": "battery_level_goes_below", "text": "Battery level goes below" }, "serviceId": "urn:micasaverde-com:serviceId:HaDevice1", "argumentList": [ { "id": 1, "prefix": { "lang_tag": "Level", "text": "Level" }, "dataType": "i4", "name": "BatteryLevel", "comparisson": "<", "suffix": { "lang_tag": "percent_sign", "text": "%" }, "HumanFriendlyText": { "lang_tag": "hft_battery_level_goes_below", "text": "Battery level for _DEVICE_NAME_ go below _ARGUMENT_VALUE_%" } } ] } ], "DeviceType": "urn:schemas-micasaverde-com:device:RainSensor:1" }
S_Rainsensor1.xml:
<?xml version="1.0"?> <scpd xmlns="urn:schemas-upnp-org:service-1-0"> <specVersion> <major>1</major> <minor>0</minor> </specVersion> <serviceStateTable> <stateVariable> <name>CurrentTRain</name> <sendEventsAttribute>yes</sendEventsAttribute> <dataType>r8</dataType> <shortCode>total_rain</shortCode> </stateVariable> <stateVariable> <name>CurrentRain</name> <sendEventsAttribute>yes</sendEventsAttribute> <dataType>r8</dataType> <shortCode>rain_rate</shortCode> </stateVariable> </serviceStateTable> <actionList> <action> <name>GetCurrentTRain</name> <argumentList> <argument> <name>RainValue</name> <direction>out</direction> <relatedStateVariable>CurrentTRain</relatedStateVariable> </argument> </argumentList> </action> <action> <name>GetCurrentRain</name> <argumentList> <argument> <name>RainValue</name> <direction>out</direction> <relatedStateVariable>CurrentRain</relatedStateVariable> </argument> </argumentList> </action> </actionList> </scpd>
-
@petewill said:
As I was looking at the code I came up with some additional questions:
There is a dayBuckets array but it doesn't seem to do anything in the code?
I also couldn't find "const unsigned long calibrateFactor" used anywhere.
dayBuckets[] was used in the old code and since I designed 1mm of rain per tip, I never had to implement calibration.
-
@petewill said:
I have never worked with eeprom so I don't pretend to know anything about it (it's now on my list of things to research though). Anyway, I noticed above hek recommended using the "saveState()/loadState() function in the MySensors library". Is that necessary in your code or are we ok as is? It seems to be functioning ok for me when I compile it so maybe not...
I developed the code before hek integrated EEPROM in his libraries. This uses a circular buffer, so it would take me some time to reprogram for hek's library, so I just kept using the arduino EEPROM.h library I used to develop the original code.
@petewill said:
changing the rainSensorThreshold value to 1 an appropriate way to do that
yes, I hardcoded boundaries, which you will will allow a minimum of 1 (one)
@petewill said:
My tipping bucket was pulled from an existing rain gauge and according to my calculations (which may be off) each bucket is .7mm of rain. I don't know much about unsigned long variables but I'm wondering if I need to change "const unsigned long calibrateFactor" to a float or something?
Sorry, I never put a bucket calibration into my code, I lucked out with the 1mm per tip working.
-
@BulldogLowell Thanks for the responses! Ok, I suspected I was missing some implementation files. Thanks for them. Did you create them from scratch? If so, nice!
I will take a look at the code to see if I can implement the calebrateFactor. I will most likely be back with more questions. Thanks again.
-
@BulldogLowell
I have been doing lots of research and working to understand the code so it will work with the calibrateFactor and I have another question... Why is the "tipSensorPin" set to OUTPUT then INPUT? Does that have to do with the interrupt?pinMode(tipSensorPin, OUTPUT); attachInterrupt (1, sensorTipped, CHANGE); //* should this be RISING instead?? pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); gw.sendSketchInfo(SN, SV); gw.present(CHILD_ID_RAIN, S_RAIN); gw.present(CHILD_ID_TRIPPED_RAIN, S_MOTION); Serial.println(F("Sensor Presentation Complete")); pinMode(tipSensorPin, INPUT);
Thanks again for your help! I'll post the full code back here when I have it working in case anyone else wants to use a different size tipping bucket.
-
@petewill said:
Why is the "tipSensorPin" set to OUTPUT then INPUT? Does that have to do with the interrupt?
it looks a lot like a bonehead error in the code!!
funny how the brain works, I never noticed that...
-
@BulldogLowell said:
it looks a lot like a bonehead error in the code!!
Ok, thanks. I make those constantly...
I have been testing the code for the last couple of days and I think this is working but another set of eyes would be good. I added functionality to set the "calibrateFactor" to a decimal. I also updated the save/load EEPROM to work with the MySensors standard. It seems to be working in my testing but I'm no programmer.
I also have two questions:
-
Is it necessary to save/load "state" to/from EEPROM? It seems that from my testing the state is set when "measure" is calculated in this line: state = (measure >= rainSensorThreshold);. It seems that measure is evaluated when the sensor first loads and rainBucket [] is loaded from EEPROM. Hopefully that makes sense what I'm asking.
-
I'm still trying to understand interrupts but I'm wondering if changing to "FALLING" would be more appropriate? Maybe it doesn't matter but I thought I'd check. Here is the line I'm referring to: attachInterrupt (1, sensorTipped, FALLING);
Here is the code if you wanted to take a look:
/* Arduino Tipping Bucket Rain Gauge April 26, 2015 Version 1.01b for MySensors version 1.4.1 Arduino Tipping Bucket Rain Gauge Utilizing a tipping bucket sensor, your Vera home automation controller and the MySensors.org gateway you can measure and sense local rain. This sketch will create two devices on your Vera controller. One will display your total precipitation for the last 24, 48, 72, 96 and 120 hours. The other, a sensor that changes state if there is recent rain (up to last 120 hours) above a threshold. Both these settings are user definable. This sketch features the following: * Allows you to set the rain threshold in mm * Allows you to determine the interval window up to 120 hours. * Displays the last 24, 48, 72, 96 and 120 hours total rain in Variable1 through Variable5 of the Rain Sensor device * Configuration changes to Sensor device updated every 3 hours. * SHould run on any Arduino * Will retain Tripped/Not Tripped status and data in a power outage, saving small ammount of data to EEPROM (Circular Buffer to maximize life of EEPROM) * There is a unique setup requirement necessary in order to properly present the Vera device variables. The details are outlined in the sketch below. * LED status indicator by BulldogLowell@gmail.com for free public use */ #include <SPI.h> #include <MySensor.h> //*No longer need the EEPROM.h? //#include <EEPROM.h> #define NODE_ID 24 #define SN "Rain Gauge" #define SV "1.01b" #define CHILD_ID_RAIN 3 #define CHILD_ID_TRIPPED_RAIN 4 #define STATE_LOCATION 0 // location to save state to EEPROM #define EEPROM_BUFFER 1 // location of the EEPROM circular buffer #define BUFFER_LENGTH 121 // buffer plus the current hour // MySensor gw; // MyMessage msgRainRate(CHILD_ID_RAIN, V_RAINRATE); MyMessage msgRain(CHILD_ID_RAIN, V_RAIN); MyMessage msgRainVAR1(CHILD_ID_RAIN, V_VAR1); MyMessage msgRainVAR2(CHILD_ID_RAIN, V_VAR2); MyMessage msgRainVAR3(CHILD_ID_RAIN, V_VAR3); MyMessage msgRainVAR4(CHILD_ID_RAIN, V_VAR4); MyMessage msgRainVAR5(CHILD_ID_RAIN, V_VAR5); MyMessage msgTripped(CHILD_ID_TRIPPED_RAIN, V_TRIPPED); MyMessage msgTrippedVar1(CHILD_ID_TRIPPED_RAIN, V_VAR1); MyMessage msgTrippedVar2(CHILD_ID_TRIPPED_RAIN, V_VAR2); // boolean metric = false; // int eepromIndex; int tipSensorPin = 3; //Do not change (needed for interrupt) int ledPin = 5; //PWM capable required unsigned long dataMillis; unsigned long serialInterval = 10000UL; const unsigned long oneHour = 3600000UL; unsigned long lastTipTime; unsigned long lastBucketInterval; unsigned long startMillis; float rainBucket [120] ; // 5 days of data //*120 hours = 5 days float calibrateFactor = .7; //Calibration in milimeters of rain per single tip. Note: Limit to one decimal place or data may be truncated when saving to eeprom. float rainRate = 0; volatile int tipBuffer = 0; byte rainWindow = 72; //default rain window in hours int rainSensorThreshold = 10; //default rain sensor sensitivity in mm. Will be overwritten with msgTrippedVar2. byte state = 0; byte oldState = -1; // void setup() { gw.begin(getVariables, NODE_ID); pinMode(tipSensorPin, INPUT); // attachInterrupt (1, sensorTipped, CHANGE); //* should this be FALLING instead?? attachInterrupt (1, sensorTipped, FALLING); pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); digitalWrite(tipSensorPin, HIGH); //ADDED. Activate internal pull-up gw.sendSketchInfo(SN, SV); gw.present(CHILD_ID_RAIN, S_RAIN); gw.present(CHILD_ID_TRIPPED_RAIN, S_MOTION); // Serial.println(F("Sensor Presentation Complete")); state = gw.loadState(STATE_LOCATION); //retreive prior state from EEPROM // Serial.print("Tripped State (from EEPROM): "); // Serial.println(state); gw.send(msgTripped.set(state==1?"1":"0")); delay(250); // recharge the capacitor // boolean isDataOnEeprom = false; for (int i = 0; i < BUFFER_LENGTH; i++) { byte locator = gw.loadState(EEPROM_BUFFER + i); //New code if (locator == 0xFF) // found the EEPROM circular buffer index { eepromIndex = EEPROM_BUFFER + i; //Now that we have the buffer index let's populate the rainBucket with data from eeprom loadRainArray(eepromIndex); isDataOnEeprom = true; // Serial.print("EEPROM Index "); // Serial.println(eepromIndex); break; } } // reset the timers dataMillis = millis(); startMillis = millis(); lastTipTime = millis() - oneHour; //will this work if millis() starts a 0?? gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR1); //Get rainWindow from controller (Vera) delay(250); gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR2); //Get rainSensorThreshold from controller (Vera) delay(250); // Serial.println("Radio Done"); // analogWrite(ledPin, 20); } // void loop() { gw.process(); pulseLED(); // // let's constantly check to see if the rain in the past rainWindow hours is greater than rainSensorThreshold // float measure = 0; // Check to see if we need to show sensor tripped in this block for (int i = 0; i < rainWindow; i++) { measure += rainBucket [i]; // Serial.print("measure value (total rainBucket within rainWindow): "); // Serial.println(measure); } state = (measure >= rainSensorThreshold); if (state != oldState) { gw.send(msgTripped.set(state==1?"1":"0")); delay(250); gw.saveState(STATE_LOCATION, state); //New Code // Serial.print("State Changed. Tripped State: "); // Serial.println(state); oldState = state; } // // Now lets reset the rainRate to zero if no tips in the last hour // if (millis() - lastTipTime >= oneHour)// timeout for rain rate { if (rainRate != 0) { rainRate = 0; gw.send(msgRainRate.set(0)); delay(250); } } //////////////////////////// ////Comment back in this block to enable Serial prints // // if ( (millis() - dataMillis) >= serialInterval) // { // for (int i = 24; i <= 120; i = i + 24) // { // updateSerialData(i); // } // dataMillis = millis(); // } // //////////////////////////// if (tipBuffer > 0) { // Serial.println("Sensor Tipped"); //******Added calibrateFactor calculations here to account for calibrateFactor being different than 1 rainBucket [0] += calibrateFactor; // Serial.print("rainBucket [0] value: "); // Serial.println(rainBucket [0]); if (rainBucket [0] * calibrateFactor > 253) rainBucket[0] = 253; // odd occurance but prevent overflow}} float dayTotal = 0; for (int i = 0; i < 24; i++) { dayTotal = dayTotal + rainBucket [i]; } // Serial.print("dayTotal value: "); // Serial.println(dayTotal); gw.send(msgRain.set(dayTotal,1)); delay(250); unsigned long tipDelay = millis() - lastTipTime; if (tipDelay <= oneHour) { rainRate = ((oneHour) / tipDelay) * calibrateFactor; //Is my math/logic correct here?? gw.send(msgRainRate.set(rainRate, 1)); // Serial.print("RainRate= "); // Serial.println(rainRate); } //If this is the first trip in an hour send .1 else { gw.send(msgRainRate.set(0.1, 1)); } lastTipTime = millis(); tipBuffer--; } if (millis() - startMillis >= oneHour) { // Serial.println("One hour elapsed."); //EEPROM write last value //Converting rainBucket to byte. Note: limited to one decimal place. //Can this math be simplified?? float convertRainBucket = rainBucket[0] * 10; if (convertRainBucket > 253) convertRainBucket = 253; // odd occurance but prevent overflow byte eepromRainBucket = (byte)convertRainBucket; gw.saveState(eepromIndex, eepromRainBucket); eepromIndex++; if (eepromIndex > EEPROM_BUFFER + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER; // Serial.print("Writing to EEPROM. Index: "); // Serial.println(eepromIndex); gw.saveState(eepromIndex, 0xFF); for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values { rainBucket [i + 1] = rainBucket [i]; } rainBucket[0] = 0; gw.send(msgRain.set(tipCounter(24),1));// send 24hr tips delay(250); transmitRainData(); // send all of the 5 buckets of data to controller startMillis = millis(); } } // void sensorTipped() { static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { tipBuffer++; } last_interrupt_time = interrupt_time; } // float tipCounter(int hours) { float tipCount = 0; for ( int i = 0; i < hours; i++) { tipCount = tipCount + rainBucket [i]; } return tipCount; } // void updateSerialData(int x) { Serial.print(F("Tips last ")); Serial.print(x); Serial.print(F(" hours: ")); float tipCount = 0; for (int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } Serial.println(tipCount); } void loadRainArray(int value) { for (int i = 0; i < BUFFER_LENGTH - 1; i++) { value--; Serial.print("EEPROM location: "); Serial.println(value); if (value < EEPROM_BUFFER) { value = EEPROM_BUFFER + BUFFER_LENGTH; } float rainValue = gw.loadState(value); if (rainValue < 255) { //Convert back to decimal value float decimalRainValue = rainValue/10; rainBucket[i] = decimalRainValue; } else { rainBucket [i] = 0; } // Serial.print("rainBucket[ value: "); // Serial.print(i); // Serial.print("] value: "); // Serial.println(rainBucket[i]); } } void transmitRainData(void) { float rainUpdateTotal = 0; for (int i = 0; i < 24; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR1.set(rainUpdateTotal,1)); delay(250); for (int i = 24; i < 48; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR2.set(rainUpdateTotal,1)); delay(250); for (int i = 48; i < 72; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR3.set(rainUpdateTotal,1)); delay(250); for (int i = 72; i < 96; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR4.set(rainUpdateTotal,1)); delay(250); for (int i = 96; i < 120; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR5.set(rainUpdateTotal,1)); delay(250); } void getVariables(const MyMessage &message) { if (message.sensor == CHILD_ID_RAIN) { // nothing to do here } else if (message.sensor == CHILD_ID_TRIPPED_RAIN) { if (message.type == V_VAR1) { rainWindow = atoi(message.data); if (rainWindow > 120) { rainWindow = 120; } else if (rainWindow < 6) { rainWindow = 6; } if (rainWindow != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar1.set(rainWindow)); } } else if (message.type == V_VAR2) { rainSensorThreshold = atoi(message.data); if (rainSensorThreshold > 1000) { rainSensorThreshold = 1000; } else if (rainSensorThreshold < 1) { rainSensorThreshold = 1; } if (rainSensorThreshold != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar2.set(rainSensorThreshold)); } } } } void pulseLED(void) { static boolean ledState = true; static unsigned long pulseStart = millis(); if (millis() - pulseStart < 500UL) { digitalWrite(ledPin, !ledState); pulseStart = millis(); } }
-
-
This starts to look really good. Maybe we should lift this into the examples on github?
Just wish someone could sell the tipping bucket hardware.
-
Pete, let me look it all over again and see if we can get the calibration working. I think the EEPROM part can be updated for @hek 's addition into the mySensors library.
@hek maybe we can do a 100% printed design for the collector/funnel and mechanism. and offer that on github as well.
-
@BulldogLowell said:
@hek maybe we can do a 100% printed design for the collector/funnel and mechanism. and offer that on github as well.
Good idea. We could probably just place the stl-file in the folder together with ino-file. Let me know when you feel ready.
-
@BulldogLowell said:
Pete, let me look it all over again and see if we can get the calibration working. I think the EEPROM part can be updated for @hek 's addition into the mySensors library.
Great! I think it should be working now (it did in my testing) but it could probably be cleaned up to work more efficiently.
@hek maybe we can do a 100% printed design for the collector/funnel and mechanism. and offer that on github as well.
That would be great! I was also hoping to make a step by step video of how I got mine all set up (with Jim's permission of course). That way pretty much any collector could be used when the calibration setting is changed. I was able to find one really cheap at a local store. The 3d print option would be really cool though.
@BulldogLowell one thing I forgot to mention in my post above is that I don't think the rainWindow and rainSensorThreshold are pulled from Vera except for when it's initially powered on. I thought I remember reading somewhere that it was designed to check every 3 hours. Maybe I'm remembering incorrectly though. I can make the change if you want but I wanted to check with you first.
-
@petewill said:
@BulldogLowell one thing I forgot to mention in my post above is that I don't think the rainWindow and rainSensorThreshold are pulled from Vera except for when it's initially powered on. I thought I remember reading somewhere that it was designed to check every 3 hours. Maybe I'm remembering incorrectly though. I can make the change if you want but I wanted to check with you first.
yeah, we need to add the:
gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR1); gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR2);
into a millis( ) timer, once an hour works, I'd think.
Try that out in here:
if (millis() - startMillis >= oneHour)
Once you get it done, let's take @hek 's idea and put it into github. I can clean it up, get Serial printing on a toggle and we can straighten out all the questions you had in the code.
Thanks for pushing this forward, it is good to get more eyes on it (remember, I may need to reinstall in my working sensor someday!)
@hek,
i think @korttoma has NetAtmo, maybe he will give us some rough dimensions for that size (the one I make was the standard eight inch diameter used by the US weather services, but a more portable version may be better). @korttoma?
Once I get that, I will work on a 3D printable design (for many to contribute I hope).
we also need to create image files for the Sensor, but for now, if you want to do the tutorial, I say go for it!
-
@petewill said:
I have been testing the code for the last couple of days and I think this is working but another set of eyes would be good. I added functionality to set the "calibrateFactor" to a decimal. I also updated the save/load EEPROM to work with the MySensors standard. It seems to be working in my testing but I'm no programmer.
Pete,
I had a whack of combining your desire to use floats, and my desire not to use floats!
Essentially we are storing in hundredths of a mm or hundredths of an inch, and dividing the values down when we transmit to Vera.
you set the calibration in hundredths of a unit per tip here:
#define CALIBRATE_FACTOR 5 // e.g. 5 is .05mm (or 5 hundredths of an inch if imperial) per tip
Take a look at this and let me know if you like it, note it is untested and I still have to test the circular buffer storing ints instead of bytes.
/* Arduino Tipping Bucket Rain Gauge April 26, 2015 Version 1.02b for MySensors version 1.4.1 Arduino Tipping Bucket Rain Gauge Utilizing a tipping bucket sensor, your Vera home automation controller and the MySensors.org gateway you can measure and sense local rain. This sketch will create two devices on your Vera controller. One will display your total precipitation for the last 24, 48, 72, 96 and 120 hours. The other, a sensor that changes state if there is recent rain (up to last 120 hours) above a threshold. Both these settings are user definable. This sketch features the following: * Allows you to set the rain threshold in mm * Allows you to determine the interval window up to 120 hours. * Displays the last 24, 48, 72, 96 and 120 hours total rain in Variable1 through Variable5 of the Rain Sensor device * Configuration changes to Sensor device updated every hour * SHould run on any Arduino * Will retain Tripped/Not Tripped status and data in a power outage, saving small ammount of data to EEPROM (Circular Buffer to maximize life of EEPROM) * There is a unique setup requirement necessary in order to properly present the Vera device variables. The details are outlined in the sketch below. * LED status indicator by @BulldogLowell and @PeteWill for free public use */ #include <SPI.h> #include <MySensor.h> #include <math.h> #define NODE_ID 24 #define SKETCH_NAME "Rain Gauge" #define SKETCH_VERSION "1.02b" #define DEBUG_ON // comment out this line to disable serial debug #define CHILD_ID_RAIN 3 #define CHILD_ID_TRIPPED_RAIN 4 #define STATE_LOCATION 0 // location to save state to EEPROM #define EEPROM_BUFFER 1 // location of the EEPROM circular buffer #define BUFFER_LENGTH 121 // buffer plus the current hour #define CALIBRATE_FACTOR 100 // e.g. 5 is .05mm (or 5 hundredths of an inch if imperial) per tip #ifdef DEBUG_ON #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #define SERIAL_START(x) Serial.begin(x) #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) #define SERIAL_START(x) #endif // MySensor gw; // MyMessage msgRainRate(CHILD_ID_RAIN, V_RAINRATE); MyMessage msgRain(CHILD_ID_RAIN, V_RAIN); MyMessage msgRainVAR1(CHILD_ID_RAIN, V_VAR1); MyMessage msgRainVAR2(CHILD_ID_RAIN, V_VAR2); MyMessage msgRainVAR3(CHILD_ID_RAIN, V_VAR3); MyMessage msgRainVAR4(CHILD_ID_RAIN, V_VAR4); MyMessage msgRainVAR5(CHILD_ID_RAIN, V_VAR5); // MyMessage msgTripped(CHILD_ID_TRIPPED_RAIN, V_TRIPPED); MyMessage msgTrippedVar1(CHILD_ID_TRIPPED_RAIN, V_VAR1); MyMessage msgTrippedVar2(CHILD_ID_TRIPPED_RAIN, V_VAR2); // boolean metric = true; // int eepromIndex; int tipSensorPin = 3; // Must be interrupt capable pin int ledPin = 5; // PWM capable pin required unsigned long dataMillis; unsigned long serialInterval = 10000UL; const unsigned long oneHour = 3600000UL; unsigned long lastTipTime; unsigned long lastBucketInterval; unsigned long startMillis; unsigned int rainBucket [120] ; /* 120 hours = 5 days of data */ unsigned int rainRate = 0; volatile int tipBuffer = 0; byte rainWindow = 72; //default rain window in hours int rainSensorThreshold = 50; //default rain sensor sensitivity in hundredths. Will be overwritten with msgTrippedVar2. byte state = 0; byte oldState = -1; // void setup() { SERIAL_START(115200); gw.begin(getVariables, NODE_ID); pinMode(tipSensorPin, INPUT_PULLUP); attachInterrupt (1, sensorTipped, FALLING); // depending on location of the hall effect sensor may need CHANGE pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); gw.sendSketchInfo(SKETCH_NAME, SKETCH_VERSION); gw.present(CHILD_ID_RAIN, S_RAIN); gw.present(CHILD_ID_TRIPPED_RAIN, S_MOTION); DEBUG_PRINTLN(F("Sensor Presentation Complete")); state = gw.loadState(STATE_LOCATION); //retreive prior state from EEPROM DEBUG_PRINT(F("Previous Tripped State (from EEPROM): ")); DEBUG_PRINTLN(state ? "Tripped" : "Not Tripped"); // gw.send(msgTripped.set(state)); delay(250); // recharge the capacitor // boolean isDataOnEeprom = false; for (int i = 0; i < BUFFER_LENGTH; i++) { byte locator = gw.loadState(EEPROM_BUFFER + 2 * i); //<<<<<<<<<<< if (locator == 0xFF) // found the EEPROM circular buffer index { eepromIndex = EEPROM_BUFFER + 2 * i; //Now that we have the buffer index let's populate the rainBucket with data from eeprom loadRainArray(eepromIndex); isDataOnEeprom = true; DEBUG_PRINT("EEPROM Index "); DEBUG_PRINTLN(eepromIndex); isDataOnEeprom = true; break; } } if (!isDataOnEeprom) // Added for the first time it is run on a new arduino { eepromIndex = 1; gw.saveState(eepromIndex, 0xFF); gw.saveState(eepromIndex + 1, 0x00); } // reset the timers dataMillis = millis(); startMillis = millis(); lastTipTime = millis() - oneHour; gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR1); //Get rainWindow from controller (Vera) delay(250); gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR2); //Get rainSensorThreshold from controller (Vera) delay(250); DEBUG_PRINTLN(F("Radio Setup Complete!")); } // void loop() { gw.process(); if (state) { prettyFade(); // breath if tripped } else { slowFlash(); // blink if not tripped } // // let's constantly check to see if the rain in the past rainWindow hours is greater than rainSensorThreshold // int measure = 0; // Check to see if we need to show sensor tripped in this block for (int i = 0; i < rainWindow; i++) { measure += rainBucket [i]; DEBUG_PRINT(F("measure value (total rainBucket within rainWindow): ")); DEBUG_PRINTLN(measure); } // state = (measure >= rainSensorThreshold); if (state != oldState) { gw.send(msgTripped.set(state)); delay(250); gw.saveState(STATE_LOCATION, state); //New Code DEBUG_PRINT(F("State Changed. Tripped State: ")); DEBUG_PRINTLN(state); oldState = state; } // // Now lets reset the rainRate to zero if no tips in the last hour // if (millis() - lastTipTime >= oneHour)// timeout for rain rate { if (rainRate != 0) { rainRate = 0; gw.send(msgRainRate.set(0)); delay(250); } } #ifdef DEBUG_ON if ( (millis() - dataMillis) >= serialInterval) { for (int i = 24; i <= 120; i = i + 24) { updateSerialData(i); } dataMillis = millis(); } #endif // if (tipBuffer > 0) { DEBUG_PRINTLN(F("Sensor Tipped")); DEBUG_PRINT(F("rainBucket [0] value: ")); DEBUG_PRINTLN(rainBucket [0]); // int dayTotal = 0; for (int i = 0; i < 24; i++) { dayTotal = dayTotal + rainBucket [i]; } // DEBUG_PRINT(F("dayTotal value: ")); DEBUG_PRINTLN(dayTotal); gw.send(msgRain.set(dayTotal, 1)); delay(250); // unsigned long tipDelay = millis() - lastTipTime; if (tipDelay <= oneHour) { rainRate = ((oneHour) / tipDelay); gw.send(msgRainRate.set(rainRate, 1)); DEBUG_PRINT(F("RainRate= ")); DEBUG_PRINTLN(rainRate); } // //If this is the first trip in an hour send .1 // else { gw.send(msgRainRate.set( (float) CALIBRATE_FACTOR / 100.0, 1));//<<<<<<<<<<<<<<calibrate? } lastTipTime = millis(); tipBuffer--; } // if (millis() - startMillis >= oneHour) { DEBUG_PRINTLN(F("One hour elapsed.")); //EEPROM write last value gw.saveState(eepromIndex, highByte(rainBucket[0])); gw.saveState(eepromIndex + 1, lowByte(rainBucket[0])); eepromIndex++; if (eepromIndex > EEPROM_BUFFER + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER; DEBUG_PRINT(F("Writing to EEPROM. Index: ")); DEBUG_PRINTLN(eepromIndex); gw.saveState(eepromIndex, 0xFF); gw.saveState(eepromIndex + 1, 0x00); // for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values { rainBucket [i + 1] = rainBucket [i]; } rainBucket[0] = 0; gw.send(msgRain.set(tipCounter(24), 1)); // send 24hr tips delay(250); transmitRainData(); // send all of the 5 buckets of data to controller delay(250); gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR1); delay(250); gw.request(CHILD_ID_TRIPPED_RAIN, V_VAR2); startMillis = millis(); } } // void sensorTipped() { static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { rainBucket[0] += CALIBRATE_FACTOR; // adds CALIBRATE_FACTOR hundredths of unit each tip } last_interrupt_time = interrupt_time; } // float tipCounter(int hours) { float tipCount = 0; for ( int i = 0; i < hours; i++) { tipCount = tipCount + rainBucket [i]; } return tipCount; } // void updateSerialData(int x) { DEBUG_PRINT(F("Tips last ")); DEBUG_PRINT(x); DEBUG_PRINTLN(F(" hours: ")); int tipCount = 0; for (int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } DEBUG_PRINTLN(tipCount); } void loadRainArray(int value) { for (int i = 0; i < BUFFER_LENGTH - 1; i++) { value--; DEBUG_PRINT("EEPROM location: "); DEBUG_PRINTLN(value); if (value < EEPROM_BUFFER) { value = EEPROM_BUFFER + BUFFER_LENGTH; } byte rainValueHigh = gw.loadState(value); byte rainValueLow = gw.loadState(value + 1); int rainValue = (rainValueHigh << 8) & rainValueLow; rainBucket[i] = rainValue; // DEBUG_PRINT(F("rainBucket[ value: ")); DEBUG_PRINT(i); DEBUG_PRINT(F("] value: ")); DEBUG_PRINTLN(rainBucket[i]); } } void transmitRainData(void) { int rainUpdateTotal = 0; for (int i = 0; i < 24; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR1.set(( float) rainUpdateTotal / 100.0 , 1)); delay(250); for (int i = 24; i < 48; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR2.set( (float) rainUpdateTotal / 100.0 , 1)); delay(250); for (int i = 48; i < 72; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR3.set( (float) rainUpdateTotal / 100.0 , 1)); delay(250); for (int i = 72; i < 96; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR4.set( (float) rainUpdateTotal / 100.0 , 1)); delay(250); for (int i = 96; i < 120; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR5.set( (float) rainUpdateTotal / 100.0 , 1)); delay(250); } void getVariables(const MyMessage &message) { if (message.sensor == CHILD_ID_RAIN) { // nothing to do here } else if (message.sensor == CHILD_ID_TRIPPED_RAIN) { if (message.type == V_VAR1) { rainWindow = atoi(message.data); if (rainWindow > 120) { rainWindow = 120; } else if (rainWindow < 1) { rainWindow = 1; } if (rainWindow != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar1.set(rainWindow)); } } else if (message.type == V_VAR2) { rainSensorThreshold = atoi(message.data); if (rainSensorThreshold > 10000) { rainSensorThreshold = 10000; } else if (rainSensorThreshold < 1) { rainSensorThreshold = 1; } if (rainSensorThreshold != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar2.set(rainSensorThreshold)); } } } } // void prettyFade(void) { float val = (exp(sin(millis() / 2000.0 * PI)) - 0.36787944) * 108.0; analogWrite(ledPin, val); } void slowFlash(void) { static boolean ledState = true; static unsigned long pulseStart = millis(); if (millis() - pulseStart < 100UL) { digitalWrite(ledPin, !ledState); pulseStart = millis(); } }
EDIT, modified some serial.print()s that needed editing for the debug toggle
-
@BulldogLowell
Thanks for the comments and sorry for the delayed response. It was a busy weekend and I didn't get much time in front of the computer.I had a whack of combining your desire to use floats, and my desire not to use floats!
Haha! I only used floats because I don't know any better. I'm curious, why the dislike of floats?
Once you get it done, let's take @hek 's idea and put it into github.
Is this something you would like me to do since you made it way better I'm happy to do it but I don't want to overstep. I consider this your handy work that I'm just happy to use.
Take a look at this and let me know if you like it, note it is untested and I still have to test the circular buffer storing ints instead of bytes.
Great! I will take a look some time this week. I mounted my gauge on the house today because we were supposed to get some rain and I wanted to see how it worked in real life. Unfortunately we didn't get any... Hopefully we will get some in the next couple of days before I pull it off to do some more testing.
we also need to create image files for the Sensor, but for now, if you want to do the tutorial, I say go for it!
I was thinking of going through how I made my gauge by using an existing rain gauge. I'd go over how to do the calculations for the tipping bucket calibration, uploading the files to Vera and how I added the MySensors stuff to the existing gauge. I will definitely mention the 3d printing option as well
Thanks for your help!.
-
@petewill said:
why the dislike of floats?
their four-byte size on arduino make for a huge eeprom storage buffer that we really don't need, we will never have negative rain and we'd never be interested in precision below 0.01 inches or milimeters.
@petewill said:
I mounted my gauge on the house today
terrific, glad to see it make its way into the real world!
-
@BulldogLowell said:
their four-byte size on arduino make for a huge eeprom storage buffer that we really don't need
Good to know! I still have a lot to learn about all this stuff. It's definitely fun.
terrific, glad to see it make its way into the real world!
Yes, me too! I'm happy to report it's working. It rained 6.4mm last night. SO COOL!
-
big thumbs up!
-
how about adding windspeed to the device?
-
@BulldogLowell said:
how about adding windspeed to the device?
Cool device. Haven't seen that one before.
-
@BulldogLowell Cool! Do you think it detects wind from all directions? Would this be better than the traditional spinning cup design?
-
-
electronics box:
-
@BulldogLowell this looks great once you complete it I will print it. Only problem our raining session is over till Dec. But I can start the build so long :).
-
Nice. But I'm a bit worried about water finding its way into the electronics. Would it be possible to make a cavity for the electronics box in the bottom plate?
-
@hek
recessing the electronics into the bottom would be tough in this design. I was taking advantage on the two parts to use the smooth plate of the printer, and then make a gasket or even use silicone. lemme think about it. I could add a couple of deflectors on the bottom plate to glue on around where the weep holes are...PS enough room for 2 C sized cells
bottom view:
-
@BulldogLowell Awesome!
I kind of wish I didn't have mine built already
I may have missed it but did you have any mounting holes (for the side of a building or pole) What is your vision for that?
It would also be cool to see some place to add a DHT22 in there. On mine I added one of those as well as a BH1750 for light. I'm not sure how long that will hold up though.
-
@petewill said:
It would also be cool to see some place to add a DHT22 in there. On mine I added one of those as well as a BH1750 for light. I'm not sure how long that will hold up though.
I'll try to fit that in, if I can, but I'm really focusing on sealing up the electronics. A skirt around the electronics housing may be enough...
I was thinking of a female 'flange-like' option for the unit to sit at the top of some galvanized pipe. It could be mounted that way on the side of a house, a fence or just a pole (back of your basketball backboard!). So, the yellow-brown part will be of two options to print.
-
@BulldogLowell said:
I was taking advantage on the two parts to use the smooth plate of the printer
Of course, didn't think that long.
-
-
If you add a little edge around the holes, the water cannt flow into the electronic box.
-
@BulldogLowell Awesome. I would love to get a 3d printer. Someday.
-
As it happens, I am sort of turning the code on its head for this update. Do you want to use accumulated rain over X days as the trigger?
I was thinking about storing the time on EEPROM and retrieving the stored rain values from VERA for each of the last 5 days after a power-up/restart. This saves from having to create too much EEPROM management, plus we can observe how much time has passed in the event of a power-off. That is, if the unit starts up, we test EEPROM to see what was the last day of rain recorded. If it hasn't been more than one, then we retrieve the values from VERA that were previously sent up. Sound OK to you?
I would save an epoch timestamp once each day at midnight when we total today's rain and cascade the values back a day.
-
@BulldogLowell said:
Do you want to use accumulated rain over X days as the trigger?
As opposed to hours? I don't have too strong of an opinion either way. I think whatever is easier works. If they are both the same then maybe hours so we have a little more granular control?
I was thinking about storing the time on EEPROM and retrieving the stored rain values from VERA for each of the last 5 days after a power-up/restart.
I like that it will simplify the EEPROM more and potentially prolong the life of the device (since it's not using as much EEPROM). One thought though, if there is a communication error between Vera and the rain gauge when it is first started is there a way to prevent the rain gauge from subsequently overwriting the values in Vera? I don't know too much about the "ack" portion of the communication but maybe that would make it so it's not an issue?
I'm excited to see the new code. Keep up the good work!
-
@petewill said:
As opposed to hours? I don't have too strong of an opinion either way. I think whatever is easier works. If they are both the same then maybe hours so we have a little more granular control?
that's where we were with the 120 hour framework, so let's leave that. I'm not worried about EEPROM life, you get 100,000 writes and that is your lifetime++ using the circular buffer. I did the math on it but can't recall the exact number.
So, we won't change EEPROM data, actually may need to add another 24hours. We just send to VERA the total of each of the past 5 days instead of the total accumulated rainfall over the past 24, 48, 72, 96 and 120 hours. This will be calendar days, we will verify by getting the time from VERA, and synchronize that a few times daily. the days will rollover at midnight, and the daily accumulation will be cascaded back accordingly.
trigger for the sensor will still be total accumulation over so many hours.
Yes?
-
@BulldogLowell That sounds GREAT to me! This is your baby though (and you have much more experience with this stuff) so I trust what direction you go.
-
here is some (untested) mods to update the 5 day rainfall history once per day at midnight. Take a look, let me know what you think (any problems issues). It turns out I was overthinking the problem and it really only needed minor mods...
/* Arduino Tipping Bucket Rain Gauge April 26, 2015 Version 1.4.1 alpha Arduino Tipping Bucket Rain Gauge Utilizing a tipping bucket sensor, your Vera home automation controller and the MySensors.org gateway you can measure and sense local rain. This sketch will create two devices on your Vera controller. One will display your total precipitation for the last 24, 48, 72, 96 and 120 hours. The other, a sensor that changes state if there is recent rain (up to last 120 hours) above a threshold. Both these settings are user definable. This sketch features the following: * Allows you to set the rain threshold in mm * Allows you to determine the interval window up to 120 hours. * Displays the last 5 days of rain in Variable1 through Variable5 of the Rain Sensor device * Configuration changes to Sensor device updated every hour * SHould run on any Arduino * Will retain Tripped/Not Tripped status and data in a power interruption, saving small ammount of data to EEPROM (Circular Buffer to maximize life of EEPROM) * LED status indicator by @BulldogLowell and @PeteWill for free public use */ #include <SPI.h> #include <MySensor.h> #include <math.h> #include <Time.h> #define NODE_ID 24 #define SKETCH_NAME "Rain Gauge" #define SKETCH_VERSION "1.4.1a" #define DWELL_TIME 125 // this allows for radio to come back to power after a transmission, ideally 0 #define DEBUG_ON // comment out this line to disable serial debug #define CHILD_ID_RAIN_LOG 3 // Keeps track of accumulated rainfall #define CHILD_ID_TRIPPED_INDICATOR 4 // Indicates Tripped when rain detected #define EEPROM_STATE_LOCATION 0 // location to save state to EEPROM #define EEPROM_BUFFER_LOCATION 1 // location of the EEPROM circular buffer #define BUFFER_LENGTH 121 #define CALIBRATE_FACTOR 100 // e.g. 5 is .05mm (or 5 hundredths of an inch if imperial) per tip #ifdef DEBUG_ON #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #define SERIAL_START(x) Serial.begin(x) #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) #define SERIAL_START(x) #endif // MySensor gw; // MyMessage msgRainRate(CHILD_ID_RAIN_LOG, V_RAINRATE); MyMessage msgRain(CHILD_ID_RAIN_LOG, V_RAIN); // MyMessage msgRainVAR1(CHILD_ID_RAIN_LOG, V_VAR1); MyMessage msgRainVAR2(CHILD_ID_RAIN_LOG, V_VAR2); MyMessage msgRainVAR3(CHILD_ID_RAIN_LOG, V_VAR3); MyMessage msgRainVAR4(CHILD_ID_RAIN_LOG, V_VAR4); MyMessage msgRainVAR5(CHILD_ID_RAIN_LOG, V_VAR5); // MyMessage msgTripped(CHILD_ID_TRIPPED_INDICATOR, V_TRIPPED); MyMessage msgTrippedVar1(CHILD_ID_TRIPPED_INDICATOR, V_VAR1); MyMessage msgTrippedVar2(CHILD_ID_TRIPPED_INDICATOR, V_VAR2); // boolean metric = true; int eepromIndex; int tipSensorPin = 3; // Must be interrupt capable pin int ledPin = 5; // PWM capable pin required unsigned long dataMillis; const unsigned long serialInterval = 10000UL; const unsigned long oneHour = 3600000UL; unsigned long lastTipTime; unsigned long startMillis; unsigned int rainBucket [24] ; /* 24 hours = 1 day of data */ unsigned int rainRate = 0; volatile int wasTippedBuffer = 0; byte rainWindow = 72; //default rain window in hours int rainSensorThreshold = 50; //default rain sensor sensitivity in hundredths. Will be overwritten with msgTrippedVar2. byte state = 0; byte oldState = -1; int lastRainRate = 0; int lastMeasure = 0; boolean gotTime = false; byte lastHour; void setup() { SERIAL_START(115200); // // Set up the IO pinMode(tipSensorPin, INPUT_PULLUP); attachInterrupt (1, sensorTipped, FALLING); // depending on location of the hall effect sensor may need CHANGE pinMode(ledPin, OUTPUT); digitalWrite(ledPin, HIGH); // //Let's get the controller talking to the Arduino gw.begin(getVariables, NODE_ID); gw.sendSketchInfo(SKETCH_NAME, SKETCH_VERSION); delay(DWELL_TIME); gw.present(CHILD_ID_RAIN_LOG, S_RAIN); delay(DWELL_TIME); gw.present(CHILD_ID_TRIPPED_INDICATOR, S_MOTION); delay(DWELL_TIME); DEBUG_PRINTLN(F("Sensor Presentation Complete")); state = gw.loadState(EEPROM_STATE_LOCATION); //retreive prior state from EEPROM DEBUG_PRINT(F("Previous Tripped State (from EEPROM): ")); DEBUG_PRINTLN(state ? "Tripped" : "Not Tripped"); // gw.send(msgTripped.set(state)); delay(DWELL_TIME); // //Sync time with the server, this will be called hourly in order to keep time from creeping with the crystal // while(timeStatus() == timeNotSet) { gw.process(); gw.requestTime(receiveTime); Serial.println("getting Time"); delay(1000); // call once per second Serial.print("."); } // //retrieve from EEPROM stored values on a power cycle. // boolean isDataOnEeprom = false; for (int i = 0; i < BUFFER_LENGTH; i++) { byte locator = gw.loadState(EEPROM_BUFFER_LOCATION + 2 * i); //<<<<<<<<<<< if (locator == 0xFF) // found the EEPROM circular buffer index { eepromIndex = EEPROM_BUFFER_LOCATION + 2 * i; //Now that we have the buffer index let's populate the rainBucket[] with data from eeprom loadRainArray(eepromIndex); isDataOnEeprom = true; DEBUG_PRINT("EEPROM Index "); DEBUG_PRINTLN(eepromIndex); isDataOnEeprom = true; break; } } if (!isDataOnEeprom) // Added for the first time it is run on a new arduino { eepromIndex = 1; gw.saveState(eepromIndex, 0xFF); // store the EEPROM index marker... gw.saveState(eepromIndex + 1, 0xFF); } dataMillis = millis(); startMillis = millis(); lastTipTime = millis() - oneHour; // //Get sensor time window and threshold from controller gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR1); delay(DWELL_TIME); gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR2); delay(DWELL_TIME); DEBUG_PRINTLN(F("Radio Setup Complete!")); } void loop() { gw.process(); if (state) { prettyFade(); // breathe if tripped } else { slowFlash(); // blink if not tripped } #ifdef DEBUG_ON // Serial Debug Block if ( (millis() - dataMillis) >= serialInterval) { for (int i = 24; i <= 120; i = i + 24) { updateSerialData(i); } dataMillis = millis(); } #endif // // let's constantly check to see if the rain in the past rainWindow hours is greater than rainSensorThreshold // int measure = 0; // Check to see if we need to show sensor tripped in this block for (int i = 0; i < rainWindow; i++) { measure += rainBucket [i]; if (measure != lastMeasure) { DEBUG_PRINT(F("measure value (total rainBucket within rainWindow): ")); DEBUG_PRINTLN(measure); lastMeasure = measure; } } // state = (measure >= rainSensorThreshold); if (state != oldState) { gw.send(msgTripped.set(state)); delay(DWELL_TIME); gw.saveState(EEPROM_STATE_LOCATION, state); //New Code DEBUG_PRINT(F("New Sensor State... Sensor: ")); DEBUG_PRINTLN(state? "Tripped" : "Not Tripped"); oldState = state; } // unsigned long tipDelay = millis() - lastTipTime; if (wasTippedBuffer) // if was tipped, then update the 24hour total and transmit to Vera { DEBUG_PRINTLN(F("Sensor Tipped")); DEBUG_PRINT(F("rainBucket [0] value: ")); DEBUG_PRINTLN(rainBucket [0]); // int dayTotal = 0; for (int i = 0; i < 24; i++) { dayTotal = dayTotal + rainBucket [i]; } // DEBUG_PRINT(F("dayTotal value: ")); DEBUG_PRINTLN(dayTotal); gw.send(msgRain.set(dayTotal, 1)); delay(DWELL_TIME); wasTippedBuffer--; rainRate = ((oneHour) / tipDelay); if (rainRate != lastRainRate) { gw.send(msgRainRate.set(rainRate, 1)); delay(DWELL_TIME); DEBUG_PRINT(F("RainRate= ")); DEBUG_PRINTLN(rainRate); lastRainRate = rainRate; } } if (tipDelay > oneHour) { rainRate = 0; gw.send(msgRainRate.set(rainRate, 1)); } // if (millis() - startMillis >= oneHour) { DEBUG_PRINTLN(F("One hour elapsed.")); for (int i = BUFFER_LENGTH - 1; i >= 0; i--)//cascade an hour of values back into the array { rainBucket [i + 1] = rainBucket [i]; } rainBucket[0] = 0; gw.send(msgRain.set(rainTotal(24), 1)); // send 24hr tips delay(DWELL_TIME); gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR1); delay(DWELL_TIME); gw.request(CHILD_ID_TRIPPED_INDICATOR, V_VAR2); delay(DWELL_TIME); gw.saveState(eepromIndex, highByte(rainBucket[0])); gw.saveState(eepromIndex + 1, lowByte(rainBucket[0])); eepromIndex++; if (eepromIndex > EEPROM_BUFFER_LOCATION + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER_LOCATION; DEBUG_PRINT(F("Writing to EEPROM. Index: ")); DEBUG_PRINTLN(eepromIndex); gw.saveState(eepromIndex, 0xFF); gw.saveState(eepromIndex + 1, 0xFF); gw.requestTime(receiveTime); // sync the time every hour delay(DWELL_TIME); startMillis = millis(); } if (hour() == 0 and lastHour == 23) { transmitRainData(); } lastHour = hour(); } void sensorTipped() { unsigned long thisTipTime = millis(); if (thisTipTime - lastTipTime > 100UL) { rainBucket[0] += CALIBRATE_FACTOR; // adds CALIBRATE_FACTOR hundredths of unit each tip wasTippedBuffer++; } lastTipTime = thisTipTime; } // int rainTotal(int hours) { int total = 0; for ( int i = 0; i < hours; i++) { total += rainBucket [i]; } return total; } void updateSerialData(int x) { DEBUG_PRINT(F("Tips last ")); DEBUG_PRINT(x); DEBUG_PRINTLN(F(" hours: ")); int tipCount = 0; for (int i = 0; i < x; i++) { tipCount = tipCount + rainBucket [i]; } DEBUG_PRINTLN(tipCount); } void loadRainArray(int value) // load stored rain array from EEPROM on powerup { for (int i = 0; i < BUFFER_LENGTH - 1; i++) { value--; DEBUG_PRINT("EEPROM location: "); DEBUG_PRINTLN(value); if (value < EEPROM_BUFFER_LOCATION) { value = EEPROM_BUFFER_LOCATION + BUFFER_LENGTH; } byte rainValueHigh = gw.loadState(value); byte rainValueLow = gw.loadState(value + 1); int rainValue = (rainValueHigh << 8) & rainValueLow; rainBucket[i] = rainValue; // DEBUG_PRINT(F("rainBucket[ value: ")); DEBUG_PRINT(i); DEBUG_PRINT(F("] value: ")); DEBUG_PRINTLN(rainBucket[i]); } } void transmitRainData(void) { int rainUpdateTotal = 0; for (int i = 0; i < 24; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR1.set(( float) rainUpdateTotal / 100.0 , 1)); delay(DWELL_TIME); rainUpdateTotal = 0; for (int i = 24; i < 48; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR2.set( (float) rainUpdateTotal / 100.0 , 1)); delay(DWELL_TIME); rainUpdateTotal = 0; for (int i = 48; i < 72; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR3.set( (float) rainUpdateTotal / 100.0 , 1)); delay(DWELL_TIME); rainUpdateTotal = 0; for (int i = 72; i < 96; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR4.set( (float) rainUpdateTotal / 100.0 , 1)); delay(DWELL_TIME); rainUpdateTotal = 0; for (int i = 96; i < 120; i++) { rainUpdateTotal += rainBucket[i]; } gw.send(msgRainVAR5.set( (float) rainUpdateTotal / 100.0 , 1)); delay(DWELL_TIME); } void getVariables(const MyMessage &message) { if (message.sensor == CHILD_ID_RAIN_LOG) { // nothing to do here } else if (message.sensor == CHILD_ID_TRIPPED_INDICATOR) { if (message.type == V_VAR1) { rainWindow = atoi(message.data); if (rainWindow > 120) { rainWindow = 120; } else if (rainWindow < 1) { rainWindow = 1; } if (rainWindow != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar1.set(rainWindow)); } } else if (message.type == V_VAR2) { rainSensorThreshold = atoi(message.data); if (rainSensorThreshold > 10000) { rainSensorThreshold = 10000; } else if (rainSensorThreshold < 1) { rainSensorThreshold = 1; } if (rainSensorThreshold != atoi(message.data)) // if I changed the value back inside the boundries, push that number back to Vera { gw.send(msgTrippedVar2.set(rainSensorThreshold)); } } } } void prettyFade(void) { float val = (exp(sin(millis() / 2000.0 * PI)) - 0.36787944) * 108.0; analogWrite(ledPin, val); } void slowFlash(void) { static boolean ledState = true; static unsigned long pulseStart = millis(); if (millis() - pulseStart < 100UL) { digitalWrite(ledPin, !ledState); pulseStart = millis(); } } void receiveTime(unsigned long time) { DEBUG_PRINTLN(F("Time received from controller...")); setTime(time); char theTime[26]; sprintf(theTime, "The current time is %d:%2d", hour(), minute()); DEBUG_PRINTLN(theTime); }
-
Wow, just wow man.... that is a thing of beauty!!
-
@ServiceXp
waiting for the 3D prints from China, I'll post when I get them!
-
I would love to see the .stl files for this.
It has turned into something really amazing imho.
-
@marceltrapman said:
I would love to see the .stl files for this.
It has turned into something really amazing imho.I'll post, but I wanted to print and check first...
@marceltrapman here you go...
Posted to GoogleDrive
-
@BulldogLowell this has been quite a project to watch unfold and develop into just the greatest project. I am learning more just following you programing wizards, than I learn surfing . Thanks for the project and the inside tracks on programming etc.
I have got my tripping gage back from my local 3d hub shop and the job looks good I hope to have it running hardware and soft before I return to and take it to Florida this fall.
-
A page for your new creation has been deployed here:
http://www.mysensors.org/build/rainPlease let me know if I missed something or spelling needs to be corrected.
-
@hek Looks great! Thanks!
-
@petewill - another outstanding tutorial video.
-
@blacey Thanks! Couldn't have done it without @BulldogLowell and @hek
-
Received my off the shelf tipping bucket sensor today. http://www.ebay.com/itm/331525977548?rmvSB=true
To my surprise it contained a dummy 2xAAA battery compartment. It might be possible to squeeze in a sensebender and nrf-radio (with some slight modification).
-
@hek Cool! Looks like you may even have some room on the other side if you can figure out a way to waterproof it?
-
@hek said:
Received my off the shelf tipping bucket sensor today. http://www.ebay.com/itm/331525977548?rmvSB=true
To my surprise it contained a dummy 2xAAA battery compartment. It might be possible to squeeze in a sensebender and nrf-radio (with some slight modification).
@hek
Did you ever wire this up with sensebender & radio squeezed in?
I have this same guage I want to use with sensebender
-
No, never had time to finish my rain gauge this summer.
But it should be doable. But the big pcb in there has to be modified, or even better is probably to move the reed switch directly to the sensebender.
-
@hek said:
No, never had time to finish my rain gauge this summer.
But it should be doable. But the big pcb in there has to be modified, or even better is probably to move the reed switch directly to the sensebender.
ok thanks
-
Hi.
i'm having some trouble whit my rain gauge...My rain gauge is a Aercus KW9015, using the code provided i'm getting rain count every hour whit no rain at all.
I don't know what to do to fix this.Over the rain gauge i have 4 connections.
1 - GND
2 - TX1 --- Temperature sensor
3 - TX2 --- Rain sensor
4 - VCCWhy i'm i getting reads of rain ever 1h when there is no rain ?
Can anyone help me whit this problem ?
-
@mrc-core
Are you actually getting a rain value or is it just sending 0? The code is designed to send an update to your gateway every hour with the total rain for the day whether there is rain or not.
-
Some times i do get value 0 but other times i get rain value a big rain value for example values above 10mm of rain.
The update is made every hour. Yesterday the rain value was 148mm when there was no rain at all. It doesn't rain for the last month.Ill try today reassembling the arduino, clear cd rom and flash again the rain gauge code.
But i don't understand why i get this values.
I even beleaved that the sensor was sendind data because off the wind... but it's not the problem.
-
how is it connected electronically?
-
At the rain gauge i have an arduino nano conneting PIN "d3" to TX2 over the rain gauge circuit, the arduino is powered at pins VIN and GND.
The rain gauge gets its power from the 3.3V over the arduino.im posting two images from the rain gauge circuit:
Starting to believe i had connect someting rong like for example had switched TX1 and TX2 at the rain gauge
-
Looking over the IMG_2 should i connect arduino pin d3 to T1 just above the reed switch?
Or i'm i correct using the TX2 connection.
-
No PULLUP or PULLDOWN resistor?
-
no i cant find any pullup or pulldown resistor.
You can see the images from de interior of the rain gauge hi have.
-
Which resistor do i pu between the 5v and the D3 pin ???
-
if you don't pull up/down the signal, you may get floating voltages and spurious interrupts occurring.
try to use the internal pullup
pinMode(yourInterruptPin, INPUT_PULLUP);
if that does not work try an external 10kOhm resistor to pull it up/down for your switch.
-
Going to put the 10kOhm resistor. the internall pullup did not work.
One question. the resistor i put it between de 5v and the D3 pin
Suggested Topics
-
Welcome
Announcements • • hek