Weather station bugs: rainin to reset after 60 min and other...

Hi All,

So been working on this weather station for the past few weeks.

I have a few things I need help with. First, the I need the rainin to reset 60 minutes after the last rain fall event. The windgustmph needs to reset every ten min. Secondly, their is a huge amount of global variables (85% of the dynamic variables). I want to move some to the PROGMEM not sure how to accomplish this and then to recall the variable again. So I somebody can help me with an example I will appreciate it. Thank you

int DEBUG = 1;      

#include <Wire.h>
#include <SoftwareSerial.h>
#include <SparkFunESP8266WiFi.h>
#include "DHT.h" 
#include <Adafruit_BMP085.h>
#include "RTClib.h"         

#define DHTTYPE DHT11 
#define DHTPIN 4   // DHT 11

// analog I/O pins
const byte LIGHT = A0;
// digital I/O pins
const byte STAT1 = 7;
const byte RAIN = 2;
const int anemometer = 3;

// Constants
char SERVER[] = "rtupdate.wunderground.com";// Realtime update server - RapidFire
char WEBPAGE [] = "GET /weatherstation/updateweatherstation.php?";
char ID [] = "wuId";
char PASSWORD [] = "pw";
const char mySSID[] = "ssid";
const char myPSK[] = "pw";

// Global Variables
ESP8266Client client;
RTC_DS3231 rtc;                       
DHT dht(DHTPIN, DHTTYPE);              
Adafruit_BMP085 bmp;                    
unsigned int connections = 0;           
unsigned int timeout = 30000;        

volatile float rainHour[60]; //60 floating numbers to keep track of 60 minutes of rain
float rainin = 0; // [rain inches over the past hour)] -- the accumulated rainfall in the past 60 min
volatile float dailyrainin = 0; // [rain mm so far today in local time]
volatile unsigned long raintime, rainlast, raininterval, rain;

long lastSecond; //The millis counter to see when a second rolls by
byte seconds; //When it hits 60, increase the current minute
byte second_2m; //Keeps track of the "wind speed/dir avg" over last 2 minutes array of data
byte minutes; //Keeps track of where we are in various arrays of data
byte minute_10m; //Keeps track of where we are in wind gust/dir 

unsigned int counter = 0;         // pulse count for wind sensor 
unsigned int RPM = 0;             // Revolutions per minute for wind sensor
unsigned int winddir = 0.0;       // Wind direction for anemometer
float windspeedmph = 0/ 0.445;    // Wind speed (m/s) for wind sensor
float windgustmph = 0;            // Max wind speed for wind sensor
const float pi = 3.14159265;      //RPM Calcs
const unsigned long period = 2500;
const int radio = 70;  

void rainIRQ()
{
  raintime = millis(); // grab current time
  raininterval = raintime - rainlast; // calculate interval between this and last event

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

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

void setup(void){
  //Turn everything on
  int status;
  Serial.begin(9600);
  Wire.begin();    //For I2C
  rtc.begin();     //Hardware rtc
  bmp.begin();     //Pressure sensor
  dht.begin();     //Humidity Sensor
  pinMode(RAIN, INPUT);
  attachInterrupt(0, rainIRQ, FALLING);
  pinMode(anemometer, INPUT); //Anemometer input(spins like mad when the wind blows).
  digitalWrite(anemometer, HIGH); // set this high so when it detects a change it records it via INT(0)
  interrupts();
  seconds = 0;
  lastSecond = millis();

  
  // Turn the Wifi on
  Serial.print(F("\nInitializing...v1"));
   status = esp8266.begin();
  if (status <= 0)
  {
    Serial.println(F("Unable to communicate with shield. Looping"));
    while(1) ;
  }
  
  esp8266.setMode(ESP8266_MODE_STA); // Set WiFi mode to station
  if (esp8266.status() <= 0) 
  {
    if (esp8266.connect(mySSID, myPSK) < 0)
    {
      Serial.println(F("Error connecting"));
      while (1) ;
    }  
}
}//End of Setup

//Loop
void loop(void){  
  //Lets see what time the RTC is set at! -- If RTC is used
  DateTime now = rtc.now();
  //Get sensor data
  float tempc = bmp.readTemperature(); //Can read temp from bmp or dht sensors
  float temp2f = dht.readTemperature(true);
  float tempf =  (tempc * 9.0)/ 5.0 + 32.0; //was dht.readTemperature, need to convert native C to F
  float humidity = dht.readHumidity(); 
  float baromin = bmp.readPressure()* 0.0002953;// Calc for converting Pa to inHg (wunderground)
  float dewptf = (dewPoint(tempf, dht.readHumidity())); //Dew point calc(wunderground) //replaced dht.readtemp with converted temp

if ((now.hour()==23)  && (now.minute()==59) && (now.second() < 10 )){
    dailyrainin = 0;
    rainin = 0;
    windgustmph = 0;
}
//Keep track of which minute it is  
   if(millis() - lastSecond >= 1000)
 {
    digitalWrite(STAT1, HIGH); //Blink stat LED
    lastSecond += 1000;
    if(++seconds > 59)
    {
      seconds = 0;
      //Roll over 60 seconds then update the arrays for rain
      if(++minutes > 59) minutes = 0;
      if(++minute_10m > 9) minute_10m = 0;         
      rainHour[minutes] = 0; //Zero out this minute's rainfall amount
    }
  }

  // max wind speed holder(wunderground)
  if (windspeedmph > windgustmph) { //
    windgustmph = windspeedmph;
  }
  
  //Total rainfall for the day is calculated within the interrupt
  //Calculate amount of rainfall for the last 60 minutes 
  rainin = 0;
  for(int i = 0 ; i < 60 ; i++)
  rainin += rainHour[i];

  windvelocity();
  RPMcalc();
  WindSpeed();
  
  if (DEBUG) {   
  // Debug, or you can sit up all night watching it.
  Serial.println("+++++++++++++++++++++++++");
  /*
  //If you are using Real Time Clock (RTC)
  Serial.println("RTC TIME ");
  Serial.print("&dateutc=");
  Serial.print(now.year());
  Serial.print("-");
  Serial.print(now.month());
  Serial.print("-");
  Serial.print(now.day());
  Serial.println("+");
  Serial.print(now.hour());
  Serial.print(":");
  Serial.print(now.minute());
  Serial.print(":");
  Serial.println(now.second());
  */
  Serial.print("temp= ");
  Serial.print(tempf);
  Serial.println(" *F");
//  Serial.print("temp2f= ");
//  Serial.print(temp2f);
//  Serial.println(" *F");    
  Serial.print("windspeedmph=");
  Serial.println(windspeedmph);
  Serial.print("windgustmph=");
  Serial.println(windgustmph);
  Serial.print("windgustmph_10m=");
  Serial.println(windgustmph);//[mph past 10 minutes wind gust mph
  Serial.print("baro= ");
  Serial.print(baromin);
  Serial.println(" inHg");
  Serial.print("dew point= ");
  Serial.println(dewptf);
  Serial.print("humidity= ");
  Serial.println(humidity);
  Serial.print("rain= ");
  Serial.println(rainin);
  Serial.print("daily= ");
  Serial.println(dailyrainin);
  }//End debug loop
 
 //Send data to Weather Underground
 if (client.connect(SERVER, 80)) { 
    if (DEBUG) {    
      Serial.println(F("Sending DATA "));
      }
    // Ship it!
    client.print(WEBPAGE); 
    client.print("ID=");
    client.print(ID);
    client.print("&PASSWORD=");
    client.print(PASSWORD);
    client.print("&dateutc=");
    client.print("now");    //can use instead of RTC if sending in real time
//removed some to make code shorter
    client.print("&softwaretype=Arduino%20UNO%20version1&action=updateraw&realtime=1&rtfreq=2.5");//Rapid Fire
    client.println();
       
    if (DEBUG) {   
      Serial.println F(("Upload complete"));
      }
      
   }//End send loop 
    else {
      if (DEBUG) { Serial.println(F("Connection failed")); }
      return;
      }
    
    delay(2500); // --If plugged in send every 2.5 seconds  
    }//End loop

double dewPoint(double tempf, double humidity)
{
  double A0= 373.15/(273.15 + tempf);
  double SUM = -7.90298 * (A0-1);
  SUM += 5.02808 * log10(A0);
  SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1) ;
  SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1) ;
  SUM += log10(1013.246);
  double VP = pow(10, SUM-3) * humidity;
  double T = log(VP/0.61078);   
  return (241.88 * T) / (17.558-T);
}

//Wind Speed Trigger/timer
void windvelocity(){
  windspeedmph = 0;
  counter = 0;
  attachInterrupt(1, addcount, FALLING);//anemometer
  unsigned long millis();                     
  long startTime = millis();
  while(millis() < startTime + period) {
  }
}
//RPM calc needed for wind speed calc
void RPMcalc(){
  RPM=((counter*1)*60)/(period/1000);  // Calculate revolutions per minute (RPM)
}
void WindSpeed(){
  windspeedmph = ((2 * pi * radio * RPM)/60) / 1000;
}
void addcount(){
  counter++;
}

I want to move some to the PROGMEM

You can't have variables in PROGMEM. You can have them in EEPROM, but EEPROM features a limited number of reads and writes.

You already have written code to time and reset measurements. What goes wrong, and why can't you just fix the problem?

The rainin value don't reset after 60 minutes.

The whole program seems to run for a very long time, 17 seconds between serial prints. I don't know if this is affecting the whole timing issue. I only have one delay(2500).

This is the 1st time I'm doing coding so not that experienced and not sure where to look for the problem.

Regards,
Tinus

This is a rather complicated program for a first programming attempt.

The best approach is to make sure that all the individual parts work correctly (read the rain gauge, etc.) before putting everything together. It is easier to cut out unneeded code than to write new code.

Also, you can put "print" statements at key points to check that the values make sense.

Aside: the following will work "properly" ONLY when tempf = -40 (you are calling a function that expects degrees C, with degrees F):

double dewPoint(double tempf, double humidity)
{
  double A0= 373.15/(273.15 + tempf);
...

The denominator converts Celsius to Kelvin.

Thank you. I have corrected the F to C.

Was pondering to change the calculation to a more basic calculation. Maybe this will speed up the calculation time of the Arduino. Slightly less acurate but a far more simpler calculation.

your problem is that:
lastSecond += 1000;
and:
delay(2500);
conflict with each other.
if you delay 2500 the increment of lastSecond
will always be at least 1500+ milliseconds short if
only increasing by 1000.
I'm not sure what I'd advise here but if you think about it,
these two pieces don't make sense.
You might want to keep track of when 2500 milliseconds
takes place, similar to how you are doing seconds.
Do note that your seconds counter will not work right
either because the code in the loop ( besides the delay(2500) )
also takes some non-zero time to execute.
Dwight

HI Dwight and jremington,

Thank you for your willingness to help.

I had that delay it then to sync with the Wunderground sever. Their rapid server works on a 2.5 sec interval. I since removed it.

I found the big slow down, it is the if (client.connect(SERVER, 80)) { .

It takes about 14 seconds to send the data. If I uncomment it then the process happens very quick and the rainin seems to reset every 60 min.

So any ideas how to get the client to send the data quicker?

Regards,
Me

14 seconds seems reasonable to me. That is the external world, not the Arduino.

Ignore any data collected during the connection period.

Have a variable of when to reset:

unsigned long WhenToReset = mille() + 60000;

Every time you reset,

WhenToReset = WhenToReset + 60000;

to check doing reset

if ( mills() - WhenToReset > 0 ) { reset }

Or something similar.
Dwight

Thank you. Will give it a go.

Got it fixed. So when the gust of rainfall event occurs, then the "time" gets recorded, do the calculation if > the interval then it resets.

No more delay due to the "lag" from print.client. So far it keeps well. So will see with a longer test.

Thanks for all the inputs.

if ((now.hour()==23)  && (now.minute()==59) && (now.second() > 45 )){
    dailyrainin = 0;
    rainin = 0;
    windgustmph = 0;
    windgustmph_10m = 0;
}
  unsigned long currentMillis = millis();
//Keep track of which minute it is  
   if (currentMillis - windgust_10mtime >= gust10min) {
   
   windgustmph_10m = 0; 
   }
  
if (currentMillis - raintime >= rainItime) {
     
    rainin  = 0;
    }

    //Check to see if this is a gust for the minute

  if(windspeedmph > windgustmph_10m)
    {
       windgustmph_10m = windspeedmph;
       windgust_10mtime = millis();
      
     }

Use:

if ((now.hour()==23) && (now.minute()==59) && (now.second() > 45 )){

with care.
If your code loop can take more than 15 seconds, the event can be
missed.
The code requires at least a comment that the loop() can never exceed
15 seconds.
It might be safer to keep a running total of seconds with the unsigned
integer and look for the difference like I posted.
That would insure a much larger window, just in case.
Dwight