Reading a tipping bucket rain gauge

Because we have limit in library as I said before.

If you need to send 100 req/sec you have to. But I don’t know your needs.

I thought I had explained that in my first post. However from your answers it is clear that my rain gauge probably won’t work with Blynk but I will keep trying :slight_smile:

Why is that?

The gauge doesn’t work reliably with the recommended interval so your advice was to change my hardware. I take that to mean that my rain gauge probably won’t work with Blynk.

Because this is hardware issue. You can’t send 100 req/sec from your hardware. So it is logical that gague can’t show 100 req/sec as you don’t send 100 req/sec :slight_smile:.

I think the problem here is that I’m talking about data you try send to blynk and you are talking about reading rain gauge value. If you’ll separate this functions it would be easier to follow.

Set your polling timer as short as you need it to be - how about 1ms? 100us? But don’t send the rain-gauge triggers; count them.

Create a Blynk-update timer and set to say, 3 seconds. When ever it fires, send and clear the trigger count.

Adjust the times to suit your application and the flooding-limits of Blynk.

1 Like

You don’t have to send values with this resolution. Have 2 functions: one detects events and counts them, second updates the data in the cloud and in the app.

Think like an engineer :wink:

1 Like

And debounce your reed switch. I’ve been using reed switches for the past year for a couple different projects (driving very small 1:87 scale cars for one …) and I’ve found out you gonna need at least a simple debounce circuit with a capacitor and one or two resistors. An added diode would makes things even more reliable. Connect the whole bunch to a setup with interrupts and you are good to go.

Why are you even using a timer?

This hall sensor isnt sending a tonne of data…the bucket takes a few seconds to a few minutes to fill up?

void setup(){
  int prevButtonState = HIGH; // not sure if you have NO/NC type sensor but assuming its NC
rainAmount = .3; // put this here to make the function a teeny bit faster
}

void rain(){
  int buttonState = digitalRead(rainGauge);
  
  if(buttonState == LOW && prevButtonState == HIGH) // it just went passed the sensor and tipped the bucket so now do one action only so you dont cause a flood error on Blynk
  { 
      prevButtonState = LOW;
      totalAmount += rainAmount; //running total of rain that has fallen
      Blynk.virtualWrite(V13,totalAmount); // send rain amount to Blynk app
  }

if(buttonState == HIGH && prevButtonState == LOW) { //reset for next bucket tip or send a secondary virtWrite or slow serial commands
     
     prevButtonState = HIGH;
     Serial.print("this is the total rain that has fallen ");
      Serial.print(totalAmount);
      Serial.println(" mm");
}


void loop(){
  rain(); // add this in to the loop
}

This will probably go wrong if you do it like this. That’s why he’s using a timer.

You either use a timer or use an interrupt with debouncing.

Could you explain why? Maybe my understanding of the way the hardware works is incorrect.

Shamelessly added in from the original WIMP station - more on that here for reference: https://github.com/sparkfun/Wimp_Weather_Station

This was one of the few sections of code that i hardly touched, it just works - you may need to read up on the available interrupts on the ESP8266’es.

here is the code i use for my rain bucket - I use an Arduino to process the data and an ESP to send it via MQTT to blynk, but there is no reason this will not work directly on the ESP.

 >    //Interrupt routines (these are called by the hardware interrupts, not by the main code)
>     //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>     void rainIRQ()
>     // Count rain gauge bucket tips as they occur
>     // Activated by the magnet and reed switch in the rain gauge, attached to input D2
>     {
>       raintime = millis(); // grab current time
>       raininterval = raintime - rainlast; // calculate interval between this and last event

>       if (raininterval > 10) // ignore switch-bounce glitches less than 10mS after initial edge
>       {
>         dailyrainin += 0.2794; //Each dump is 0.011" of water
>         rainHour[minutes] += 0.011; //Increase this minute's amount of rain

>         rainlast = raintime; // set up for next event
>       }
>     }

The int is called later in the code as follows:

attachInterrupt(0, rainIRQ, FALLING);

Only reason i moved this off the ESP was trying to keep track of windspeed, DHT22, BMP085, rain bucket and RTC was too much for it to handle.

2 Likes

Because you will read the state of the button too fast. It will get executed thousands of times per second. This will most likely hang up your hardware.

Even a basic board like an UNO will be able to digitalRead() in the loop without hanging.

In my garden system, I have a similar reed sensor on my wind meter and that is spinnin at 300rpm! And my ESP has no problem reading it via my method above and doesn’t cause flood errors due to the prevButtonState flag.

I would still take a different approach, but then again, I al ready had some really frustrating moments because of buffer overflows, server floods and what not.

Besides all that, it’s good to take a look at what you are doing and find an optimal situation for the project you are doing. Pushing the hardware to it’s limit for a simple reading is, in my book, not a very good idea and when you expand the project you may get (like me) frustrating and weird results, not working stuff and general annoyance :slight_smile:

Fair enough. :slight_smile:

I’ll post up my garden system once its done. Currently has no problems reading wind, water flow and 3x moisture sensors via this in-the-loop method on an ESP-Dev board.
Maybe you will see signs of future issues that I dont :smiley:

Always good to share stuff and have someone else take a look at it. It usually leads to refreshing new insights :smiley:

Thanks everyone for your input, I now have my program reading the pulses from the rain gauge and updating in Blynk app reliably. It seems that using an interrupt is the way to go and a separate function to send the totals to Blynk. Thanks @Pavel for that idea and thanks to everyone that contributed in this thread.
Below is my code in its entirety, its a bit rough but I post it here for anyone that may like to have a play and improve on it.
Regards

/**************************************************************
 * Blynk is a platform with iOS and Android apps to control
 * Arduino, Raspberry Pi and the likes over the Internet.
 * You can easily build graphic interfaces for all your
 * projects by simply dragging and dropping widgets.
 *
 *   Downloads, docs, tutorials: http://www.blynk.cc
 *   Blynk community:            http://community.blynk.cc
 *   Social networks:            http://www.fb.com/blynkapp
 *                               http://twitter.com/blynk_app
 *
 * Blynk library is licensed under MIT license
 * This example code is in public domain.
 *
 **************************************************************
 * This example runs directly on ESP8266 chip.
 *
 * You need to install this for ESP8266 development:
 *   https://github.com/esp8266/Arduino
 *
 * Please be sure to select the right ESP8266 module
 * in the Tools -> Board menu!
 *
 * Change WiFi ssid, pass, and Blynk auth token to run :)
 *
 **************************************************************/

#define BLYNK_PRINT Serial    // Comment this out to disable prints and save space
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <SimpleTimer.h>
#include <Wire.h>
#include <BaroSensor.h>
#include "DHT.h"
#include <RCSwitch.h>
#define DHTPIN 10
#define DHTTYPE DHT22

DHT dht(DHTPIN,DHTTYPE,11);
RCSwitch mySwitch = RCSwitch();
WidgetLCD lcd(V11);
const int rainGauge = 12;   //Pin for rain gauge which has a resolution of .15mm
const int ledPin = 4;
float rainAmount = .3;     //variable to hold .3mm when bucket tips
float totalAmount;          //variable to hold total amount of rain that has fallen
long rssi;  //radio signal strength indicator
String mymin = " minutes";

char auth[] = "XXXXXXXXXXXXX";
char ssid[] = "NotTelling";  // Your WiFi credentials.
char pass[] = "NorHere";     // Set password to "" for open networks.

SimpleTimer timer;


void setup()
{
  attachInterrupt(rainGauge, rain, RISING); //interrupt to catch pulses sent from rain gauge
  Serial.begin(9600); 
  Wire.begin(2,14);  //I2C pins for barometer
  BaroSensor.begin();
  dht.begin();
  mySwitch.enableTransmit(0); // Transmitter is connected to ESP8266 Pin #0
  Blynk.begin(auth, ssid, pass);
  while (Blynk.connect() == false) { 
  // Wait until connected
  }
    pinMode(ledPin, OUTPUT);
    pinMode(rainGauge, INPUT_PULLUP);
    timer.setInterval(10000L, sendAllData); // set interval and function to call
    timer.setInterval(1000L, sendUptime);   // set interval and function to call
    timer.setInterval(1000L,rainupDate);    // set interval (1sec) to read rain gauge totals and send to Blynk app
}

void rainupDate(){
  Blynk.virtualWrite(V13,totalAmount); // send rain amount to Blynk app
}

void sendAllData(){  // this function sends all sensor data to Blynk app
    float temp = BaroSensor.getTemperature();
    int newtemp = ((temp + 0.05) * 10);
    temp = (newtemp / 10.0);   //returns temperature to 1 decimal place
    float pr = BaroSensor.getPressure();
    float p = pr + 50;        //adjust barometer value for height above sea level
    float h = dht.readHumidity();
    int lightReading = analogRead(A0);
    int light = map(lightReading,0,1023,0,100);   //Set the light reading to a pecentage of 100 with a 100% being full sun
    if (isnan(h)) 
      {
        Serial.println("Failed to read from DHT sensor!");
        return;
      }

    if(!BaroSensor.isOK()) 
      {
        Serial.print("Sensor not Found/OK. Error: "); 
        Serial.println(BaroSensor.getError());
        BaroSensor.begin(); // Try to reinitialise the sensor if we can
      }
  else 
  {
      Serial.print(" Pressure: ");
      Serial.print(p);
      Serial.print(" hPa\t");
      Serial.print("Temperature: ");
      Serial.print(temp);
      Serial.print("'C");
      Serial.print("\t");
      Serial.print("Humidity: ");
      Serial.print(h);
      Serial.print("%\t");
      Serial.print("Light ");
      Serial.println(light);
      
      Blynk.virtualWrite(V0,temp);
      Blynk.virtualWrite(V1,h);
      Blynk.virtualWrite(V2,p);
      Blynk.virtualWrite(V3,light);
  }
}

void sendUptime()
  {
    // This function sends Arduino up time every 1 second to Virtual Pin (V4)
    // In the app, Widget's reading frequency should be set to PUSH
    // You can send anything with any interval using this construction
    // Don't send more that 10 values per second
    Blynk.virtualWrite(V4, millis() / 1000);
    lcd.print(0,0,"Time connected");
    lcd.print(0,1, millis()/1000/60 + mymin);
  }

  void rain(){
      int buttonState = digitalRead(rainGauge);
      if(buttonState==0){  //it is raining
      digitalWrite (ledPin,HIGH);   //LED for visual feedback that pulses from rain gauge are being received
      totalAmount += rainAmount; //add up the total amount of rain that has fallen and put it in variable totalAmount
      Serial.print("this is the total rain that has fallen ");  //Print out to serial monitor
      Serial.print(totalAmount); 
      Serial.println(" mm");    
      digitalWrite(ledPin,LOW);
    }
  }

BLYNK_WRITE(V12){  // this function returnes WiFi signal strength
  rssi = WiFi.RSSI();
  int pinData = param.asInt(); 
  if(pinData==1)
  {
    lcd.clear();
    lcd.print(0 ,0,"Wifi Strength");   
    lcd.print(0 ,1,rssi);
  }
 }

 bool isFirstConnect = true; // Keep this flag not to re-sync on every reconnection

  // This function will run every time Blynk connection is established
  BLYNK_CONNECTED() 
  {
    if (isFirstConnect) 
    {
      // Request Blynk server to re-send latest values for all pins
//      Blynk.syncAll();
      // You can also update an individual Virtual pin like this:
      Blynk.syncVirtual(V0);
      Blynk.syncVirtual(V1);
      Blynk.syncVirtual(V2);
      Blynk.syncVirtual(V3);
      Blynk.syncVirtual(V4);
      Blynk.syncVirtual(V11);
      Blynk.syncVirtual(V13);
      isFirstConnect = false;
    }
  }
  

void loop()
{
  Blynk.run();
  timer.run();
}

/*Remote control of switches
-------------------------------------------------*/

  BLYNK_WRITE(V5){
    mySwitch.send(14331050,24); //turn on switch 3A
  }
  BLYNK_WRITE(V6){
    mySwitch.send(14331042,24); //turn off switch 3A
  }
  BLYNK_WRITE(V7){
    mySwitch.send(14331052,24); //turn on switch 2A
  }
  BLYNK_WRITE(V8){
    mySwitch.send(14331044,24); //turn off switch 2A
  }
  BLYNK_WRITE(V9){
    mySwitch.send(354206,24);  //turn on switch 1B
  }
  BLYNK_WRITE(V10){
    mySwitch.send(354198,24); //turn off switch 1B
  }
  

I could have put the Blynk.virtual Write from the rainUpdate function into the sendAllData function and then deleted rainUpdate and the corresponding timer. That would then mean one less timer and function for the program to worry about but I will leave that for another day.

2 Likes

thank you - I learned a bit from this thread :slight_smile:

EDIT New discussion about timers moved to: