ESP8266 weather station using Arduino IDE

Today we are making really cool weather station using ESP8266 (NodeMCU) and DHT11, rain sensor. In this project, we’ll learn how to make a DIY fully featured weather station using an ESP8266 and sensors. There are two types of weather station, one which is having own sensors and second type of weather station is where we pull data from the weather station servers. In this tutorial we are looking at both types of weather stations.

NodeMCU Weather Station

Components Required

  1. ESP8266 (NodeMCU)
  2. DHT11
  3. Connecting Wires
  4. 0.96″ OLED Display (Required for second type weather station)

Introduction

What is Weather Station ?

A weather station is a device that collects data related to the weather and environment using many different sensors. Weather stations are also called weather centers, personal weather stations, professional weather stations, home weather station, weather forecaster and forecasters.

Weather stations sensors may include a thermometer (LM35 or DHT11) to take temperature readings, a barometer (BMP180) to measure the pressure in the atmosphere, as well as other sensors to measure rain, wind, humidity and more. Weather stations range from simple analog technology to digital technology. Some even connect to your computer or the internet, so the data collected can be analyzed using weather station software.

Types of Sensors used for Weather Station

The following is a list of measurement devices that are used in weather stations:

Thermometer (LM35) – A thermometer measures temperature. You can measure both the temperature indoors and outdoors, record highs and lows, show trend arrows to indicate temperature rising or falling, and even predict short-term future temperature ranges.

You can read this example code for LM35 using ESP8266

LM35 Temperature Sensor

Hygrometer (DHT11)- A hygrometer measures relative humidity. Relative humidity is the quantity or percentage of water vapor (water in gas form) in the air. Humidity influences environmental factors and calculations like precipitation, fog, dew point and heat index. In addition, maintaining proper humidity indoors has implications for your health and home.

You can read ESP8266 Reading data from DHT-11

DHT11 Pinout for ESP8266

Barometer (BMP180) – A barometer measures atmospheric pressure. A barometer can help to forecast upcoming weather based on the changes it measures in the atmospheric pressure. You can create history chart or a pressure trend arrow so you can easily track changes, like a pressure drop. ESP8266 DHT11 Charts example is given here.

BMP180 Barometric Pressure/Temperature/Altitude Sensor
BMP180 Barometric Pressure/Temperature/Altitude Sensor

Anemometer – An anemometer measures how fast the wind is blowing, or wind speed. ESP8266 weather stations can display wind speed in MPH, KPH or knots, and record current, peak and average wind speed readings.

NodeMCU Anemometer Wind Speed Sensor
Anemometer Wind Speed Sensor

Wind Vane – A wind vane, or weather vane, is an instrument that determines which direction the wind is blowing. To display wind direction you can use rotary encoder or magnetic compass sensor with ESP8266.

Wind Vane Arduino
Wind Vane

Rain Gauge – A rain gauge measures rainfall or liquid precipitation. This weather stations include rainfall alerts to notify you when a rain event has begun, or to alert you of potential flood conditions.

Rain Sensor Module
Rain Sensor Module

ESP8266 Based Weather Station

In first part we make weather station using DHT-11 and Rain Sensor. So this data is sent to client (web browser). In this we make web server on ESP8266. If you are looking for much advanced weather station using BMP180, DHT11 and Rain sensor read here.

Circuit Diagram of Weather Station

Connect rain sensor to A0 Pin of NodeMCU through a voltage divider, ESP8266 is having 1V input on ADC. Connect DHT11 to D5 of NodeMCU.

ESP8266 Weather Station Circuit Diagram

NodeMCU Code for Weather Station

Program is divided in two parts. first part contains ESP8266 WiFi and Hardware related functions i.e. our main.ino file. Second part is HTML and user interface GUI. it is index.h file.

before uploading program to ESP. Update your SSID and Password in code.

main.ino

/* 
 * ESP8266 NodeMCU DHT11 and Rain Sensor - Weather Station Example
 * https://circuits4you.com
 * 
 * Referances
 * https://circuits4you.com/2017/12/31/nodemcu-pinout/
 * https://circuits4you.com/2019/01/25/esp8266-dht11-humidity-temperature-data-logging/
 * 
 */

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
 
#include "index.h" //Our HTML webpage contents with javascripts
#include "DHTesp.h"  //DHT11 Library for ESP
  
#define LED 2        //On board LED
#define DHTpin 14    //D5 of NodeMCU is GPIO14

DHTesp dht;

//SSID and Password of your WiFi router
const char* ssid = "Your-SSID";
const char* password = "Your-Password";
 
ESP8266WebServer server(80); //Server on port 80
 
//===============================================================
// This routine is executed when you open its IP in browser
//===============================================================
void handleRoot() {
 String s = MAIN_page; //Read HTML contents
 server.send(200, "text/html", s); //Send web page
}

float humidity, temperature;

void handleADC() {
 int rain = analogRead(A0);
 //Ref 1: https://circuits4you.com/2019/01/11/nodemcu-esp8266-arduino-json-parsing-example/
 //Ref 2: https://circuits4you.com/2019/01/25/arduino-how-to-put-quotation-marks-in-a-string/
 
 //Create JSON data
 String data = "{\"Rain\":\""+String(rain)+"\", \"Temperature\":\""+ String(temperature) +"\", \"Humidity\":\""+ String(humidity) +"\"}";
 
 digitalWrite(LED,!digitalRead(LED)); //Toggle LED on data request ajax
 server.send(200, "text/plane", data); //Send ADC value, temperature and humidity JSON to client ajax request

 //Get Humidity temperatue data after request is complete
 //Give enough time to handle client to avoid problems
  delay(dht.getMinimumSamplingPeriod());

  humidity = dht.getHumidity();
  temperature = dht.getTemperature();

  Serial.print("H:");
  Serial.println(humidity);
  Serial.print("T:");
  Serial.println(temperature); //dht.toFahrenheit(temperature));
  Serial.print("R:");
  Serial.println(rain);
}

//==============================================================
//                  SETUP
//==============================================================
void setup()
{
  Serial.begin(115200);
  Serial.println();

  //Ref 3: https://circuits4you.com/2019/01/25/interfacing-dht11-with-nodemcu-example/
  // Autodetect is not working reliable, don't use the following line
  // dht.setup(17);

  // use this instead: 
  dht.setup(DHTpin, DHTesp::DHT11); //for DHT11 Connect DHT sensor to GPIO 17
  //dht.setup(DHTpin, DHTesp::DHT22); //for DHT22 Connect DHT sensor to GPIO 17

  WiFi.begin(ssid, password);     //Connect to your WiFi router
  Serial.println("");
 
  //Onboard LED port Direction output
  pinMode(LED,OUTPUT); 
  
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  //If connection successful show IP address in serial monitor
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());  //IP address assigned to your ESP
 
  server.on("/", handleRoot);      //Which routine to handle at root location. This is display page
  server.on("/readADC", handleADC); //This page is called by java Script AJAX
 
  server.begin();                  //Start server
  Serial.println("HTTP server started");
}

//==============================================================
//                     LOOP
//==============================================================
void loop()
{
  server.handleClient();          //Handle client requests
}

index.h

const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
<title>circuits4you.com</title>
</head>
<style>
@import url(https://fonts.googleapis.com/css?family=Montserrat);
@import url(https://fonts.googleapis.com/css?family=Advent+Pro:400,200);
*{margin: 0;padding: 0;}

body{
  background:#544947;
  font-family:Montserrat,Arial,sans-serif;
}
h2{
  font-size:14px;
}
.widget{
  box-shadow:0 40px 10px 5px rgba(0,0,0,0.4);
  margin:100px auto;
  height: 330px;
  position: relative;
  width: 500px;
}

.upper{
  border-radius:5px 5px 0 0;
  background:#f5f5f5;
  height:200px;
  padding:20px;
}

.date{
  font-size:40px;
}
.year{
  font-size:30px;
  color:#c1c1c1;
}
.place{
  color:#222;
  font-size:40px;
}
.lower{
  background:#00A8A9;
  border-radius:0 0 5px 5px;
  font-family:'Advent Pro';
  font-weight:200;
  height:130px;
  width:100%;
}
.clock{
  background:#00A8A9;
  border-radius:100%;
  box-shadow:0 0 0 15px #f5f5f5,0 10px 10px 5px rgba(0,0,0,0.3);
  height:150px;
  position:absolute;
  right:25px;
  top:-35px;
  width:150px;
}

.hour{
  background:#f5f5f5;
  height:50px;
  left:50%;  
  position: absolute;
  top:25px;
  width:4px;
}

.min{
  background:#f5f5f5;
  height:65px;
  left:50%;  
  position: absolute;
  top:10px;
  transform:rotate(100deg);
  width:4px;
}

.min,.hour{
  border-radius:5px;
  transform-origin:bottom center;
  transition:all .5s linear;
}

.infos{
  list-style:none;
}
.info{
  color:#fff;
  float:left;
  height:100%;
  padding-top:10px;
  text-align:center;
  width:25%;
}
.info span{
  display: inline-block;
  font-size:40px;
  margin-top:20px;
}
.weather p {
  font-size:20px;padding:10px 0;
}
.anim{animation:fade .8s linear;}

@keyframes fade{
  0%{opacity:0;}
  100%{opacity:1;}
}

a{
 text-align: center;
 text-decoration: none;
 color: white;
 font-size: 15px;
 font-weight: 500;
}
</style>
<body>


<div class="widget"> 
  <div class="clock">
    <div class="min" id="min"></div>
    <div class="hour" id="hour"></div>
  </div>
  <div class="upper">
    <div class="date" id="date">21 March</div>
    <div class="year">Temperature</div>
    <div class="place update" id="temperature">23 &deg;C</div>
  </div>
  <div style="text-align: center;"><a href="https://circuits4you.com" style="align:center">circuits4you.com</a></div>
  <div class="lower">    
    <ul class="infos">
      <li class="info temp">
        <h2 class="title">TEMPERATURE</h2>
        <span class='update' id="temp">21 &deg;C</span>
      </li>
      <li class="info weather">
        <h2 class="title">WEATHER</h2>
        <p class="main update">Sunny</p>
      </li>
      <li class="info wind">
        <h2 class="title">RAIN</h2>
        <span class='update' id="rain">0%</span>
      </li>
      <li class="info humidity">
        <h2 class="title">HUMIDITY</h2>
        <span class='update' id="humidity">23%</span>
      </li>
    </ul>
  </div>
</div>

<script>
setInterval(drawClock, 2000);
    
function drawClock(){
    var now = new Date();
    var hour = now.getHours();
    var minute = now.getMinutes();
    var second = now.getSeconds();
    
    //Date
    var options = {year: 'numeric', month: 'long', day: 'numeric' };
	var today  = new Date();
    document.getElementById("date").innerHTML = today.toLocaleDateString("en-US", options);
    
    //hour
    var hourAngle = (360*(hour/12))+((360/12)*(minute/60));
    var minAngle = 360*(minute/60);
    document.getElementById("hour").style.transform = "rotate("+(hourAngle)+"deg)";
    //minute
    document.getElementById("min").style.transform = "rotate("+(minAngle)+"deg)";

    //Get Humidity Temperature and Rain Data
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
      var txt = this.responseText;
      var obj = JSON.parse(txt); //Ref: https://www.w3schools.com/js/js_json_parse.asp
        document.getElementById("rain").innerHTML = obj.Rain + "%";
        document.getElementById("temperature").innerHTML = Math.round(obj.Temperature) + "&deg;C";
        document.getElementById("temp").innerHTML = Math.round(obj.Temperature) + "&deg;C";
        document.getElementById("humidity").innerHTML = Math.round(obj.Humidity) + "%";
      }  
    };
   xhttp.open("GET", "readADC", true); //Handle readADC server on ESP8266
   xhttp.send();
}
</script>
</body>
</html>
)=====";

Results and Testing

After uploading open serial monitor and get the ip address.

Open IP address in Web Browser.  After opening it in web browser, you will see that on board blue LED will Blink. this led toggles when it receives http request from web browser.

In program we are sending ajax request to update the page without refreshing it. You can read more on it here.

Weather Station Server

How Weather Station program works ?

Now lets see the complete working of weather station program and circuit.

Complete project is consists of Hardware and Software part. First we discuss hardware part.

Weather Station Hardware

Hardware part is simple we are using only two sensors DHT11 and Rain Sensor. You can add more sensors like BMP180 barometric pressure sensor.

Rain sensor analog output is connected to Analog input of ESP8266 through a voltage divider. which drops the output of rain sensor to suit ESP8266 analog input maximum range.

DHT11 gives us humidity and temperature readings. It is digital sensor gives output in serial stream of data. For this sensor interfacing we are using DHTesp library which takes care of all communication with DHT11 sensor. DHT11 is connected to NodeMCU pin D5 which is GPIO14.

Weather Station Software Working

Software consists of following parts.

  1. Connecting to WiFi
  2. Server Creation
  3. Getting Data from Sensors
  4. Sending GUI to user
  5. Updating Sensors values on web page

Connecting to WiFi

Connection to you wifi is made by ESP8266 using below code. First we define wifi ssid and password.

Then we try to connect with wifi using WiFi.begin(ssid, password) and wait until connection successful using while (WiFi.status() != WL_CONNECTED).

Once we connect to wifi successfully we print IP address assigned by you WiFi Router on Serial monitor using WiFi.localIP() function.

Next part is to create web server on ESP8266. for this we are including web server library  #include <ESP8266WebServer.h>

Then we create server instance using below function. Web uses default port 80, so we defined it as port 80.

ESP8266WebServer server(80); //Server on port 80

After this in setup() we create client request handling functions, one for displaying GUI and another which sends the sensor data to user using AJAX and JSON. AJAX is part of web page. You can read more on AJAX here and JSON parsing here.

When user enters ESP8266 IP it requests root page. That request is served by handleRoot() function. It sends web page (HTML) to user (web browser).Once the web page is loaded it contains javascript with AJAX code which sends data request to ESP8266 webserver at /readADC. If you enter IP with readADC request as shown below. ESP sends sensor data in JSON format.

This readADC request is handled by handleADC() function. as shown below. Handled ADC sends data first to web client and after that it reads DHT11 sensor to prevent data lags. In this you can clearly see data variable holds the JSON of sensor values. Many escape characters are uses as we are using double quotes in JSON. You can read more on how to use double quotes inside the string without compilation errors.

Now let’s move to web page i.e. HTML and Java script code part. Which is present in index.h file. In java script we access HTML elements using id’s.

Ajax java script part of the web page. We call drawClock() function at every two seconds. using setInterval(drawClock, 2000); function.

In drawClock() Function we have AJAX part which sends the GET request to the ESP at  readADC. when web browser (java program) receives it response text i.e. JSON. it calls onreadystatechange function. in that we confirm that the request is OK (200). if it is OK then we get the responseText i.e. JSON and then using javascript json parsing JSON.parse(txt); we get all our sensor data in json objects. This json object data is then updated in web page dynamically (without page refresh) using document.getElementById(“id of html element“).innerHTML = sensor data.

This is how all works. GUI is created using CSS and HTML. which is not covered here as its more web development related topic.

ESP8266 Based Weather Station using  server data

In this we are not using any sensor but we get weather data from local weather station server. To display it we need a display. This type of application is like you have small device on table which request weather data to weather stations and continuously updates on small display. Advantage of this type of weather station is you get weather forecast.

Before you start read more on OLED interfacing with ESP8266.

Weather Station Connections

In this we have only OLED 0.96″ Display and NodeMCU.

NodeMCU OLED Weather Station

Arduino IDE Code for Weather Station using NodeMCU

Before we start coding we need few libraries to installed.

  1. OLED See Example
  2. JSON See Example

Read this for more on how to send HTTPS request ?

Before uploading code get your location lat lon. you can search in google and replace it

String Link = “/api/current?lat=18.53&lon=73.86“;

Then enter your wifi SSID and Password then upload the code.

/*
 *  ESP8266 Weather Station Data Rading
 *  -Manoj R. Thkuar
 *  https://circuits4you.com
 *  
 *  Referances
 *  https://circuits4you.com/2019/01/09/nodemcu-esp8266-oled-display-code-example/
 *  https://circuits4you.com/2019/01/11/nodemcu-esp8266-arduino-json-parsing-example/
 *  https://circuits4you.com/2019/01/10/esp8266-nodemcu-https-secured-get-request/
 *  https://codepen.io/sgestrella/pen/GPjeYP
 *  
 */

#include <ESP8266WiFi.h>
#include <WiFiClient.h> 
#include <WiFiClientSecure.h> 
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
//#include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`

const char* wifiName = "Your-SSID";
const char* wifiPass = "Your-Password";

//Web Server address to read from 
const char *host = "fcc-weather-api.glitch.me";

//Enter you lat long here
String Link = "/api/current?lat=18.53&lon=73.86";
const int httpsPort = 443;  //HTTPS= 443 and HTTP = 80
 
//SHA1 finger print of certificate use web browser to view and copy
const char fingerprint[] PROGMEM = "8E 68 78 DD 47 8F 03 BF 8B 51 DB 62 FE 5A CB 6E E9 20 82 A1";

// Initialize the OLED display using Wire library
SSD1306Wire  display(0x3c, D2, D1);  //D2=SDK  D1=SCK  As per labeling on NodeMCU

//=======================================================================
//                               SETUP
//=======================================================================
void setup() {
  
  Serial.begin(115200);
  delay(1000);
  Serial.println();  

  Serial.println("Initializing OLED Display");
  display.init();

  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  
  Serial.print("Connecting to ");
  Serial.println(wifiName);

  WiFi.begin(wifiName, wifiPass);
  
  display.clear();
  display.setFont(ArialMT_Plain_16);
  display.drawString(0, 10, "Connecting WiFi");
  display.display();
    
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  display.clear();
  display.setFont(ArialMT_Plain_16);
  display.drawString(0, 10, "Connected");
  display.display();

  
  
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());   //You can get IP address assigned to ESP
}
//=======================================================================
//                               LOOP
//=======================================================================
void loop() {

  WiFiClientSecure httpsClient;    //Declare object of class WiFiClient
 
  Serial.println(host);
 
  Serial.printf("Using fingerprint '%s'\n", fingerprint);
  httpsClient.setFingerprint(fingerprint);
  httpsClient.setTimeout(15000); // 15 Seconds
  delay(1000);
  
  Serial.print("HTTPS Connecting");
  int r=0; //retry counter
  while((!httpsClient.connect(host, httpsPort)) && (r < 30)){
      delay(100);
      Serial.print(".");
      r++;
  }
  if(r==30) {
    Serial.println("Connection failed");
  }
  else {
    Serial.println("Connected to web");
  }

  //Get weather data
  Serial.print("requesting URL: ");
  Serial.println("GET " + Link + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +               
               "Connection: close\r\n\r\n");
 
  httpsClient.print(String("GET ") + Link + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +               
               "Connection: close\r\n\r\n");
 
  Serial.println("request sent");
                  
  while (httpsClient.connected()) {
    String line = httpsClient.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
 
  Serial.println("reply was:");
  Serial.println("==========");
  String payload;
  while(httpsClient.available()){
    payload = httpsClient.readStringUntil('\n');  //Read Line by Line
    Serial.println(payload); //Print response
  }
    int str_len = payload.length() + 1; 
    Serial.println(str_len);  
    // Prepare the character array (the buffer) 
    char json[str_len];
     
    // Copy it over 
    payload.toCharArray(json, str_len);
  
  StaticJsonDocument<1000> doc;  //Refere to calculate size https://arduinojson.org/v6/assistant/

  deserializeJson(doc, json);

  float coord_lon = doc["coord"]["lon"]; // 73.86
  float coord_lat = doc["coord"]["lat"]; // 18.53
  
  JsonObject weather_0 = doc["weather"][0];
  int weather_0_id = weather_0["id"]; // 800
  const char* weather_0_main = weather_0["main"]; // "Clear"
  const char* weather_0_description = weather_0["description"]; // "clear sky"
  const char* weather_0_icon = weather_0["icon"]; // "https://cdn.glitch.com/6e8889e5-7a72-48f0-a061-863548450de5%2F01d.png?1499366022009"
  
  const char* base = doc["base"]; // "stations"
  
  JsonObject main = doc["main"];
  float main_temp = main["temp"]; // 34.44
  float main_pressure = main["pressure"]; // 1010.6
  float main_humidity = main["humidity"]; // 22
  float main_temp_min = main["temp_min"]; // 34.44
  float main_temp_max = main["temp_max"]; // 34.44
  float main_sea_level = main["sea_level"]; // 1010.6
  float main_grnd_level = main["grnd_level"]; // 937.58
  
  float wind_speed = doc["wind"]["speed"]; // 2.43
  float wind_deg = doc["wind"]["deg"]; // 287.5
  
  int clouds_all = doc["clouds"]["all"]; // 0
  
  long dt = doc["dt"]; // 1553248419
  
  JsonObject sys = doc["sys"];
  float sys_message = sys["message"]; // 0.0031
  const char* sys_country = sys["country"]; // "IN"
  long sys_sunrise = sys["sunrise"]; // 1553216850
  long sys_sunset = sys["sunset"]; // 1553260540
  
  long id = doc["id"]; // 6943660
  const char* location = doc["name"]; // "Shivaji Nagar"
  int cod = doc["cod"]; // 200

  Serial.println(main_temp);
  Serial.println(main_pressure);
  Serial.println(main_humidity);
  Serial.println(location);

  display.clear();
  display.setFont(ArialMT_Plain_16);  
  display.drawString(0, 0, "T:");
  display.drawString(15, 0, String(main_temp));
  display.drawString(0, 15, "P:");
  display.drawString(15, 15, String(main_pressure));
  display.drawString(0, 30, "H:");
  display.drawString(15, 30, String(main_humidity));
  display.drawString(0, 45, "L:");
  display.drawString(15, 45, location);
  display.display();


  delay(5000);  //GET Data at every 5 seconds
}

Results and Testing

You can get lot of information about weather. I have taken it all information in variable but only location, temperature, humidity and pressure is displayed on display. I accidentally dropped OLED and it is showing corrupted text. So graphics and other things are pending.

But finally I can say that this is the simplest example on internet. Here I am using fcc weather APIs. All other examples on internet use openWeather and some other paid services require registrations. You don’t have to register to any website. simply put lat long and it works.

Read More

 

Leave a Reply