Arduino Rain Gauge - Tipping Bucket

I live in a location where rain can be highly localized. There can be an inch of rain just 1/4 mile from my house. Using weather underground, while very accurate across the region, tends to over/under report for my home. So, I am finishing up my Tipping Bucket rain gauge for Vera with Arduino.

It features the following:

-Creates two devices on your Vera, a Sensor and an information panel
-Sensor Device allows you to set the rain threshold in mm and set the interval window up to 5 days - you select hours up to 120
-Custom Information Panel Device displays the last 24, 48, 72, 96 and 120 hours total rain and is stored in Variable1 through Variable5
-Configuration changes to the settings in the Sensor device automatically updates the Sensor
-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)
-LED power/status indicator flashes every time rocker tips.

If you are interested to follow my final progress, take a look at the Rain Gauge post on MySensors.org.

Once I calibrate the sensor and make sure I develop a process to calibrate any bucket mechanism, I’ll post the sketch.

I’ll also update the final cost to build, but should be less than $45 including the custom tipping bucket assembly (3D printed) and electronics.

I will also get the final device on youtube…

Awesome, I’m excited to build one! Thanks for all your great work with this.

thanks.

here is the sketch, without the calibration part. I need to finish the physical device and need a part or two.

note during debug I sped up time, so you have to fix the length of an hour; change oneHour to 3600000UL

You can review if you like. Any opportunity to improve is appreciated.

/*
 Arduino Tipping Bucket Rain Gauge
 
 June 15, 2014
 
 Version 1.90b   
 
 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 Jim (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", "0.90b"); 
  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++;
    Serial.println(eepromIndex);
    if (eepromIndex > EEPROM_BUFFER + BUFFER_LENGTH) eepromIndex = EEPROM_BUFFER;
    EEPROM.write(eepromIndex, 0xFF);
    //
    for (int i = 118; 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 >=4)//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;
     }
   }
}

Hi BulldogLowell,
I’m in Florida too (central) and I’m looking for a solution to sense the rain. Your idea sound great, but I’m not as advanced as you, could you explain in more details how to build it? I’m a complete stranger to programming and arduino, do you think I could accomplish to build one?
Thank you :wink:

[quote=“lefrancais, post:4, topic:181608”]Hi BulldogLowell,
I’m in Florida too (central) and I’m looking for a solution to sense the rain. Your idea sound great, but I’m not as advanced as you, could you explain in more details how to build it? I’m a complete stranger to programming and arduino, do you think I could accomplish to build one?
Thank you ;)[/quote]

This is comprised of the MySensors.org arduino-based controller and in my case a home-made rain sensor.

If you are already involved with the arduino approach, it will be easy to either build a sensor, or you can buy one.

Let me know if you are already looking seriously at MySensors or already using it.

I am going to do a full write-up on the MySensors website when I am done. I have just been delayed with other things right now. I am sure I will have it done before the dry season!!!

Thank you for your answer BulldogLowell,

I’m not involved with arduino (yet), I’m about to get the open sprinkler for raspberry pi (first time getting involved with rasp pi).
Then to get it working correctly I need a rain sensor like you build.
I don’t mind getting into arduino but I’m afraid my capabilities my be limited.
I will be staying tuned to your updates!
Thank you

[quote=“lefrancais, post:6, topic:181608”]Thank you for your answer BulldogLowell,

I’m not involved with arduino (yet), I’m about to get the open sprinkler for raspberry pi (first time getting involved with rasp pi).
Then to get it working correctly I need a rain sensor like you build.
I don’t mind getting into arduino but I’m afraid my capabilities my be limited.
I will be staying tuned to your updates!
Thank you[/quote]

there is always NetAtmo, for which there is a nice plugin already working and the folks here that use it really seem to like it.

To me it was not adding value with exterior atmospheric conditions; I can get that from the public internet and the Weather Underground app excels there.

I already know what the temperature and humidity are in my house, having three thermostats in three separate zones.

No one has ever suffocated to death in my house from CO2 poisoning so I am probably OK not monitoring that. :wink:

For me, the addition of the rain gauge is the only thing that added something with real utility. My bugaboo was the price for the base unit plus the rain gauge (over $250), so I went DIY, or rather DImyself.

I should be done in a few weeks and will certainly let you know!

I completely agree with you the NetAtmo look nice but I don’t need all those features, just the rain gauge. I will never be able to convince my wife to buy it!!!

Any update on this project?

It’s complete… and working awesome at my house :slight_smile:

Check out this post on MySenors for more info: Rain Gauge | MySensors - Create your own Connected Home Experience