Close

Software and Libraries

A project log for Solar Powered WiFi Weather Station V3.0

ESP32 based Open Source Weather Station

Debasish DuttaDebasish Dutta 10/07/2021 at 14:540 Comments

To use the ESP32 board with the Arduino library, you'll have to use the Arduino IDE with ESP32 board support. If you haven't already done that yet, you can easily install ESP32 Board support to your Arduino IDE by following this tutorialby Sparkfun.

Install Libraries:

Before uploading the code install the following libraries :

1. ESP32

2.Blynk

3. BME280

4. Adafruit_SI1145_Library

5. BH1750

6. One Wire

7. Dallas Temperature

Arduino Test Code:

//======================================================================================//
//                                                                                      //
//                 Solar WiFi Weather Station V3.0 Firmware                             //
//                                                                                      //
//             Developed by Debasish Dutta, Last Update: 30.03.2021                     //                 
//                                                                                      //
//======================================================================================// 
    
    #include <BME280I2C.h>
    #include "Adafruit_SI1145.h" 
    #include <BH1750.h>     
    #include <DallasTemperature.h> 
    #include <OneWire.h>   
    #include "Wire.h"    
    #include <WiFi.h>      
    #include <BlynkSimpleEsp32.h>   
    #include "esp_deep_sleep.h" //Library needed for ESP32 Sleep Functions  

  //=================== Pin assignment definitions ==========================================
 
    #define WIND_SPD_PIN 14
    #define RAIN_PIN     25
    #define WIND_DIR_PIN 35
    #define VOLT_PIN     33 
    #define TEMP_PIN 4  // DS18B20 hooked up to GPIO pin 4

  //=======================================================================================
  
    WiFiClient client;
    BME280I2C bme;
    Adafruit_SI1145 uv = Adafruit_SI1145();
    BH1750 lightMeter(0x23);    
    OneWire oneWire(TEMP_PIN);
    DallasTemperature sensors(&oneWire);
    
  //=========================Declaring Variables and Constants ================================== 

  
  // Variables used in reading temp,pressure and humidity (BME280)
    float temperature, humidity, pressure;
    
 // Variables used in reading UV Index (Si1145)    
    float UVindex;  
    
 // Variables used in reading Lux Level( BH1750 ) 
    float lux;

 // Variables used in calculating the windspeed 
    volatile unsigned long timeSinceLastTick = 0;
    volatile unsigned long lastTick = 0;    
    float windSpeed;
    
 // Variables used in calculating the wind direction 
    int vin; 
    String windDir = "";
        
 // Variables and constants used in tracking rainfall
    #define S_IN_DAY   86400
    #define S_IN_HR     3600
    #define NO_RAIN_SAMPLES 2000    
    volatile long rainTickList[NO_RAIN_SAMPLES];
    volatile int rainTickIndex = 0;
    volatile int rainTicks = 0;
    int rainLastDay = 0;
    int rainLastHour = 0;
    int rainLastHourStart = 0;
    int rainLastDayStart = 0;
    long secsClock = 0;

 // Variables used in calculating the battery voltage 
    float batteryVolt;   
    float Vout = 0.00;
    float Vin = 0.00;
    float R1 = 27000.00; // resistance of R1 (27K) // You can also use 33K 
    float R2 = 100000.00; // resistance of R2 (100K) 
    int val = 0;     

//=========================Deep Sleep Time ================================================ 

 //const int UpdateInterval = 1 * 60 * 1000000;  // e.g. 0.33 * 60 * 1000000; // Sleep time  
 //const int UpdateInterval = 15 * 60 * 1000000;  // e.g. 15 * 60 * 1000000; // // Example for a 15-Min update interval 15-mins x 60-secs * 10000

 //========================= Enable Blynk or Thingspeak ===================================
  
 // configuration control constant for use of either Blynk or Thingspeak
 //const String App = "BLYNK";         //  alternative is line below
   const String App = "Thingspeak"; //  alternative is line above   

 //========================= Variables for wifi server setup =============================
     
  // Your WiFi credentials.
  // Set password to "" for open networks.
  char ssid[] = "XXXX"; // WiFi Router ssid
  char pass[] = "XXXX"; // WiFi Router password
  
  // copy it from the mail received from Blynk
  char auth[] = "XXXX"; 
  
  // Thingspeak Write API
  const char* server = "api.thingspeak.com";
  const char* api_key = "XXXX"; // API write key 
  
//========================= Setup Function ================================================ 

  void setup() 
    {
      Serial.begin(115200);
      delay(25);
      Serial.println("\nWeather station powered on.\n");
      Wire.begin(); 
      sensors.begin(); 
  //  Wire.begin(22, 21); // for BH1750  
      bme.begin(); // 0x76 is the address of the BME280 module
      uv.begin(0x60);  // 0x60 is the address of the GY1145 module     
      wifi_connect(); 
    
      // Wind speed sensor setup. The windspeed is calculated according to the number
      //  of ticks per second. Timestamps are captured in the interrupt, and then converted
      //  into mph. 
      pinMode(WIND_SPD_PIN, INPUT);     // Wind speed sensor
      attachInterrupt(digitalPinToInterrupt(WIND_SPD_PIN), windTick, FALLING);
    
      // Rain sesnor setup. Rainfall is tracked by ticks per second, and timestamps of
      //  ticks are tracked so rainfall can be "aged" (i.e., rain per hour, per day, etc)
      pinMode(RAIN_PIN, INPUT);     // Rain sensor
      attachInterrupt(digitalPinToInterrupt(RAIN_PIN), rainTick, FALLING);
      // Zero out the timestamp array.
      for (int i = 0; i < NO_RAIN_SAMPLES; i++) rainTickList[i] = 0;      
     
     // ESP32 Deep SLeep Mode
     // esp_deep_sleep_enable_timer_wakeup(UpdateInterval);
     // Serial.println("Going to sleep now...");
     // esp_deep_sleep_start();     
    }
//================================ Loop Function ==============================================  

      void loop() 
    {
      
     Read_Sensors_Data();  // Read all the Sensors 
    // printdata();          // Print all the sensors data on the serial monitor
     Send_Data();          // Upload all the sensors data on the Internet ( Blynk App or Thingspeak )

    }

//============================ Connect to WiFi Network =========================================

void wifi_connect()
{
  if (App == "BLYNK")  // for posting datas to Blynk App
  { 
    Blynk.begin(auth, ssid, pass);
  } 
  else if (App == "Thingspeak")  // for posting datas to Thingspeak website
  {
     WiFi.begin(ssid, pass);
     
     while (WiFi.status() != WL_CONNECTED) 
     {
            delay(500);
          //  Serial.print(".");
     }
     Serial.println("");
     Serial.println("WiFi connected");  
  } 
  else 
  {
    WiFi.begin(ssid, pass);
    Serial.print(App);
    Serial.println(" is not a valid application");
  }
  
}

//===================================================================================================

//           Read Sensors Data ( BME280, Si1145,BH1750, Bat. Voltage, Wind Sensors, Rain Gauge )

//==================================================================================================


   void Read_Sensors_Data()
{
   // Reading BME280 sensor
    bme.read(pressure, temperature, humidity, BME280::TempUnit_Celsius, BME280::PresUnit_Pa); 


 //***************************************************************************

 // Reading DS18B20 sensor
     sensors.requestTemperatures(); 


 //***************************************************************************
  // Reading GY1145 UV sensor

    UVindex = uv.readUV();
   // the index is multiplied by 100 so to get the
   // integer index, divide by 100!
      UVindex /= 100.0;     
  //**********************************************************************************
 /*// Reading BH1750 sensor  
  lux = lightMeter.readLightLevel();
 
  */
//**********************************************************************************
// Reading Battery Level in %
   val = analogRead(VOLT_PIN);//reads the analog input
   Vout = (val * 3.3 ) / 4095.0; // formula for calculating voltage out 
   batteryVolt = Vout * ( R2+R1) / R2 ; // formula for calculating voltage in   
  

//**********************************************************************************
// Read Weather Meters Datas ( Wind Speed, Rain Fall and Wind Direction )

      static unsigned long outLoopTimer = 0;
      static unsigned long wundergroundUpdateTimer = 0;
      static unsigned long clockTimer = 0;
      static unsigned long tempMSClock = 0;
    
      // Create a seconds clock based on the millis() count. We use this
      //  to track rainfall by the second. We've done this because the millis()
      //  count overflows eventually, in a way that makes tracking time stamps
      //  very difficult.
      tempMSClock += millis() - clockTimer;
      clockTimer = millis();
      while (tempMSClock >= 1000)
      {
        secsClock++;
        tempMSClock -= 1000;
      }
      
      // This is a once-per-second timer that calculates and prints off various
      //  values from the sensors attached to the system.
      if (millis() - outLoopTimer >= 2000)
      {
        outLoopTimer = millis();
        // Windspeed calculation, in mph. timeSinceLastTick gets updated by an
        //  interrupt when ticks come in from the wind speed sensor.
        if (timeSinceLastTick != 0) windSpeed = 1000.0/timeSinceLastTick;

       // Calculate the wind direction and display it as a string.
        windDirCalc();       
     
     rainLastHour = 0;
     rainLastDay = 0;
  // If there are any captured rain sensor ticks...
     if (rainTicks > 0)
     {
      // Start at the end of the list. rainTickIndex will always be one greater
     //  than the number of captured samples.
      int i = rainTickIndex-1;
    
    // Iterate over the list and count up the number of samples that have been
    //  captured with time stamps in the last hour.
       while ((rainTickList[i] >= secsClock - S_IN_HR) && rainTickList[i] != 0)
          {
            i--;
            if (i < 0) i = NO_RAIN_SAMPLES-1;
            rainLastHour++;
          }
    
          // Repeat the process, this time over days.
          i = rainTickIndex-1;
          while ((rainTickList[i] >= secsClock - S_IN_DAY) && rainTickList[i] != 0)
          {
            i--;
            if (i < 0) i = NO_RAIN_SAMPLES-1;
            rainLastDay++;
          }
          rainLastDayStart = i;
        }
      }  
   }

   
     
   // Keep track of when the last tick came in on the wind sensor.
    void windTick(void)
    {
      timeSinceLastTick = millis() - lastTick;
      lastTick = millis();
    }
    
    // Capture timestamp of when the rain sensor got tripped.
    void rainTick(void)
    {
      rainTickList[rainTickIndex++] = secsClock;
      if (rainTickIndex == NO_RAIN_SAMPLES) rainTickIndex = 0;
      rainTicks++;
    }
    
// reading wind direction
    void windDirCalc()
    {
      
  vin = analogRead(WIND_DIR_PIN);

  if      (vin < 150) windDir="202.5";
  else if (vin < 300) windDir = "180";
  else if (vin < 400) windDir = "247.5";
  else if (vin < 600) windDir = "225";
  else if (vin < 900) windDir = "292.5";
  else if (vin < 1100) windDir = "270";
  else if (vin < 1500) windDir = "112.5";
  else if (vin < 1700) windDir = "135";
  else if (vin < 2250) windDir = "337.5";
  else if (vin < 2350) windDir = "315";
  else if (vin < 2700) windDir = "67.5";
  else if (vin < 3000) windDir = "90";
  else if (vin < 3200) windDir = "22.5";
  else if (vin < 3400) windDir = "45";
  else if (vin < 4000) windDir = "0";
  else windDir = "0";  
  }

 //======================  Print Data on Serial Monitor  ===============================================

  void printdata(){
   Serial.print("Air temperature [°C]: "); Serial.println(temperature);
   Serial.print("Humidity [%]: "); Serial.println(int(humidity));
   Serial.print("Barometric pressure [hPa]: "); Serial.println(pressure / 100);   
   Serial.print("UV: "); Serial.println(UVindex);   
 //  Serial.print("Light: ");  Serial.print(lux);  Serial.println(" lx");    
   Serial.print("Windspeed: "); Serial.print(windSpeed*2.4); Serial.println(" mph");     
   Serial.print("Wind dir: ");  Serial.print("  "); Serial.println(windDir);   
   Serial.print("Rainfall last hour: "); Serial.println(float(rainLastHour)*0.011, 3);
 //  Serial.print("Rainfall last day: ");  Serial.println(float(rainLastDay)*0.011, 3);
 //  Serial.print("Rainfall to date: ");   Serial.println(float(rainTicks)*0.011, 3);    
   Serial.print("Battery Level: "); Serial.println(batteryVolt);  
   Serial.print("Temperature in C: ");   Serial.println(sensors.getTempCByIndex(0));    //print the temperature in Celsius  
   Serial.print("Temperature in F: "); Serial.println((sensors.getTempCByIndex(0) * 9.0) / 5.0 + 32.0); //print the temperature in Fahrenheit  

  }
   
//======================Upload Sensors data to Blynk App or Thingspeak =================================

void Send_Data()
{
// code block for uploading data to BLYNK App
  
  if (App == "BLYNK") { // choose application
   Blynk.virtualWrite(0,temperature );    // virtual pin 0 
   Blynk.virtualWrite(1, humidity ); // virtual pin 1
   Blynk.virtualWrite(2, pressure/100 );    // virtual pin 2
   Blynk.virtualWrite(3, UVindex);    // virtual pin 3
  // Blynk.virtualWrite(4, windSpeed*1.492 ); // virtual pin 4  
   Blynk.virtualWrite(4, windSpeed*2.4*4.5 ); // virtual pin 4 
   Blynk.virtualWrite(5, windDir);    // virtual pin 5 
   Blynk.virtualWrite(6, rainLastHour);    // virtual pin 6 
   Blynk.virtualWrite(7, batteryVolt);    // virtual pin 7 
   Blynk.virtualWrite(8, sensors.getTempCByIndex(0));    // virtual pin 8 
   delay(12*5000);
  } 
    
 // code block for uploading data to Thingspeak website
 
  else if (App == "Thingspeak") {
  // Send data to ThingSpeak 
  WiFiClient client;  
  if (client.connect(server,80)) {
  Serial.println("Connect to ThingSpeak - OK"); 
  Serial.println(""); 
  Serial.println("********************************************"); 
  String postStr = "";
  postStr+="GET /update?api_key=";
  postStr+=api_key;   
  postStr+="&field1=";
  postStr+=String(temperature);
  postStr+="&field2=";
  postStr+=String(humidity);
  postStr+="&field3=";
  postStr+=String(pressure/100);  
  postStr+="&field4=";
  postStr+=String(UVindex);  
  postStr+="&field5=";
  //postStr+=String(windSpeed*1.492); //speed in mph
  postStr+=String(windSpeed*2.4*4.5); //speed in Km/h
  postStr+="&field6=";
  postStr+=String(windDir);
  postStr+="&field7=";
  postStr+=String(float(rainTicks)*0.011, 3);  
  postStr+="&field8=";
  postStr+=String(batteryVolt);   
  postStr+="&field9=";
  postStr+=String(sensors.getTempCByIndex(0));   
  postStr+=" HTTP/1.1\r\nHost: a.c.d\r\nConnection: close\r\n\r\n";
  postStr+="";
  client.print(postStr);
  delay(5000);
 //*******************************************************************************

}
 while(client.available()){
    String line = client.readStringUntil('\r');
   // Serial.print(line);
  }
 }  
}
//=============================End of the Program ================    
  

Updated on 09.07.2021

My friend James is working hard to resolve the deep sleep issues, you may visit his GitHub repository for updates.

How to Install the Libraries?

You can read this tutorial by Sparkfun to install the Arduino libraries.

In my earlier version, there are two separate codes for Blynk and Thinspeak but in this version, we have written a single piece of code. The user has to only comment out a single line of code for Blynk or Thingspeak. For example, if you are using it for Blynk App, the code should be as below:

// configuration control constant for use of either Blynk or Thingspeak
// const String App = "BLYNK"; // alternative is line below
const String App = "Thingspeak"; 
// alternative is line above

Update On 19.08.2021 ( Deep Sleep Implementation by James )

James has written a new code for this project to implement deep sleep. You can find the code in his GitHub repository. The link is given below:

https://github.com/jhughes1010/weather

Steps to Install the New Software:

1. Download the code from the above GitHub link. Ensure you download from the MASTER branch. James is actively working on DEVELOP.

2. First, you have to unzip the downloaded code and then save it somewhere on your PC or Laptop. Remove the word master from the folder name, the final name shall be " weather "

3. Rename sec.h to secrets.h (this file contains WiFi details, API keys for internet data destinations, and configuration options ).

You can see the above images for your reference. The details on the software configuration are available here.

Credit: I would like to give special thanks to James who has worked hard to improve the software of this project.

Discussions