ESP8266 Weather Forecaster

In this post you’re going to build a weather forecaster with an ESP8266.

This project was written by Emmanuel Odunlade and edited by Rui Santos.

I hate being out on a rainy day without an umbrella and I’m pretty sure it’s the same for everyone else.

Often, the weather widget on our smartphones displays the weather forecast but when you’re in a hurry you forget to pay attention to it.

So, it would be nice to have a device hanging at the back of the door which reminds you to leave your house with an umbrella on a day where it might rain. This device tells you the weather forecast with a change in the LED color. This device wouldn’t have an alarm neither a screen, just a few LEDs that would fit naturally into your environment.

Parts required

Here’s a complete list of the components you need for this project (click the links below to find the best price at Maker Advisor):

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Open Weather Map

This project is based on the Open Weather Map API, so it’s necessary to sign up on their platform and obtain an API key before we get down to putting together the schematics and writing the code.

OpenWeatherMap’s free plan provides everything you need for thins example. To use the API you need an API key, known as the APIID. To get an APIID:

  1. Open a browser and go to OpenWeatherMap
  2. Press the Sign up button and create a free account
  3. Once your account is created, you’ll be presented with a dashboard that contains several tabs (see figure below)
  4. Select the API Keys tab and copy your unique Key

This is a unique key you need to pull information from the site. Copy and paste this key somewhere, you’ll need it in a moment.

To pull information on weather in your chosen location, enter the following URL with the sections in curly brackets replaced with your chosen location information and your unique API key:

http://api.openweathermap.org/data/2.5/forecast?q={your city},{your country code}&APPID={your API Key}&mode=json&units=metric&cnt=2

Replace {your city} with the city you want data for, {your country code} with the country code for that city, and {your API key} with your unique API key we found previously. For example, our API URL for the town of Porto in Portugal, after replacing with the details, would be:

http://api.openweathermap.org/data/2.5/forecast?q=Porto,PT&APPID=801d2603e9f2e1c70e042e4------&mode=json&units=metric&cnt=2

Note: more information on using the API to get weather information is available here.

Copy your URL into your browser and it should give you a bunch of information that corresponds to your local weather forecast information.

Keep your unique API key safe and we can then move to the code section.

Installing the ArduinoJson library

For this project you need to install the ArduinoJson library in your Arduino IDE that allows you to Decode and Encode JSON with Arduino or ESP8266. Follow these next steps:

  1. Click here to download the ArduinoJson. You should have a .zip folder in your Downloads folder
  2. Unzip the .zip folder and you should get ArduinoJson-master folder
  3. Rename your folder from ArduinoJson-master to ArduinoJson
  4. Move the ArduinoJson folder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Uploading code

Having the ESP8266 add-on for the Arduino IDE installed (how to Install the ESP8266 Board in Arduino IDE), go to Tools and select “NodeMCU (ESP-12E Module)”.

Copy the following code to your Arduino IDE and upload it to your ESP8266 board.

Go through the code to add your SSID, password, city, country code and Open Weather Map API key.

 /*
  * Author: Emmanuel Odunlade 
  * Complete Project Details https://randomnerdtutorials.com
  */
  
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>

// Replace with your SSID and password details
char ssid[] = "REPLACE_WITH_YOUR_SSID";        
char pass[] = "REPLACE_WITH_YOUR_PASSWORD";   

WiFiClient client;

// Open Weather Map API server name
const char server[] = "api.openweathermap.org";

// Replace the next line to match your city and 2 letter country code
String nameOfCity = "REPLACE_WITH_YOUR_CITY,REPLACE_WITH_YOUR_COUNTRY_CODE"; 
// How your nameOfCity variable would look like for Lagos on Nigeria
//String nameOfCity = "Lagos,NG"; 

// Replace the next line with your API Key
String apiKey = "REPLACE_WITH_YOUR_API_KEY"; 

String text;

int jsonend = 0;
boolean startJson = false;
int status = WL_IDLE_STATUS;

int rainLed = 2;  // Indicates rain
int clearLed = 3; // Indicates clear sky or sunny
int snowLed = 4;  // Indicates snow
int hailLed = 5;  // Indicates hail

#define JSON_BUFF_DIMENSION 2500

unsigned long lastConnectionTime = 10 * 60 * 1000;     // last time you connected to the server, in milliseconds
const unsigned long postInterval = 10 * 60 * 1000;  // posting interval of 10 minutes  (10L * 1000L; 10 seconds delay for testing)

void setup() {
  pinMode(clearLed, OUTPUT);
  pinMode(rainLed, OUTPUT);
  pinMode(snowLed, OUTPUT);
  pinMode(hailLed, OUTPUT);
  Serial.begin(9600);
  
  text.reserve(JSON_BUFF_DIMENSION);
  
  WiFi.begin(ssid,pass);
  Serial.println("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi Connected");
  printWiFiStatus();
}

void loop() { 
  //OWM requires 10mins between request intervals
  //check if 10mins has passed then conect again and pull
  if (millis() - lastConnectionTime > postInterval) {
    // note the time that the connection was made:
    lastConnectionTime = millis();
    makehttpRequest();
  }
}

// print Wifi status
void printWiFiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

// to request data from OWM
void makehttpRequest() {
  // close any connection before send a new request to allow client make connection to server
  client.stop();

  // if there's a successful connection:
  if (client.connect(server, 80)) {
    // Serial.println("connecting...");
    // send the HTTP PUT request:
    client.println("GET /data/2.5/forecast?q=" + nameOfCity + "&APPID=" + apiKey + "&mode=json&units=metric&cnt=2 HTTP/1.1");
    client.println("Host: api.openweathermap.org");
    client.println("User-Agent: ArduinoWiFi/1.1");
    client.println("Connection: close");
    client.println();
    
    unsigned long timeout = millis();
    while (client.available() == 0) {
      if (millis() - timeout > 5000) {
        Serial.println(">>> Client Timeout !");
        client.stop();
        return;
      }
    }
    
    char c = 0;
    while (client.available()) {
      c = client.read();
      // since json contains equal number of open and close curly brackets, this means we can determine when a json is completely received  by counting
      // the open and close occurences,
      //Serial.print(c);
      if (c == '{') {
        startJson = true;         // set startJson true to indicate json message has started
        jsonend++;
      }
      if (c == '}') {
        jsonend--;
      }
      if (startJson == true) {
        text += c;
      }
      // if jsonend = 0 then we have have received equal number of curly braces 
      if (jsonend == 0 && startJson == true) {
        parseJson(text.c_str());  // parse c string text in parseJson function
        text = "";                // clear text string for the next time
        startJson = false;        // set startJson to false to indicate that a new message has not yet started
      }
    }
  }
  else {
    // if no connction was made:
    Serial.println("connection failed");
    return;
  }
}

//to parse json data recieved from OWM
void parseJson(const char * jsonString) {
  //StaticJsonBuffer<4000> jsonBuffer;
  const size_t bufferSize = 2*JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(2) + 4*JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(2) + 3*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 2*JSON_OBJECT_SIZE(7) + 2*JSON_OBJECT_SIZE(8) + 720;
  DynamicJsonBuffer jsonBuffer(bufferSize);

  // FIND FIELDS IN JSON TREE
  JsonObject& root = jsonBuffer.parseObject(jsonString);
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }

  JsonArray& list = root["list"];
  JsonObject& nowT = list[0];
  JsonObject& later = list[1];

  // including temperature and humidity for those who may wish to hack it in
  
  String city = root["city"]["name"];
  
  float tempNow = nowT["main"]["temp"];
  float humidityNow = nowT["main"]["humidity"];
  String weatherNow = nowT["weather"][0]["description"];

  float tempLater = later["main"]["temp"];
  float humidityLater = later["main"]["humidity"];
  String weatherLater = later["weather"][0]["description"];

  // checking for four main weather possibilities
  diffDataAction(weatherNow, weatherLater, "clear");
  diffDataAction(weatherNow, weatherLater, "rain");
  diffDataAction(weatherNow, weatherLater, "snow");
  diffDataAction(weatherNow, weatherLater, "hail");
  
  Serial.println();
}

//representing the data
void diffDataAction(String nowT, String later, String weatherType) {
  int indexNow = nowT.indexOf(weatherType);
  int indexLater = later.indexOf(weatherType);
  // if weather type = rain, if the current weather does not contain the weather type and the later message does, send notification
  if (weatherType == "rain") { 
    if (indexNow == -1 && indexLater != -1) {
      digitalWrite(rainLed,HIGH);
      digitalWrite(clearLed,LOW);
      digitalWrite(snowLed,LOW);
      digitalWrite(hailLed,LOW);
      Serial.println("Oh no! It is going to " + weatherType + " later! Predicted " + later);
    }
  }
  // for snow
  else if (weatherType == "snow") {
    if (indexNow == -1 && indexLater != -1) {
      digitalWrite(snowLed,HIGH);
      digitalWrite(clearLed,LOW);
      digitalWrite(rainLed,LOW);
      digitalWrite(hailLed,LOW);
      Serial.println("Oh no! It is going to " + weatherType + " later! Predicted " + later);
    }
    
  }
  // can't remember last time I saw hail anywhere but just in case
  else if (weatherType == "hail") { 
   if (indexNow == -1 && indexLater != -1) {
      digitalWrite(hailLed,HIGH);
      digitalWrite(clearLed,LOW);
      digitalWrite(rainLed,LOW);
      digitalWrite(snowLed,LOW);
      Serial.println("Oh no! It is going to " + weatherType + " later! Predicted " + later);
   }

  }
  // for clear sky, if the current weather does not contain the word clear and the later message does, send notification that it will be sunny later
  else {
    if (indexNow == -1 && indexLater != -1) {
      Serial.println("It is going to be sunny later! Predicted " + later);
      digitalWrite(clearLed,HIGH);
      digitalWrite(rainLed,LOW);
      digitalWrite(snowLed,LOW);
      digitalWrite(hailLed,LOW);
    }
  }
}

View raw code

Schematics

Wire your circuit accordingly to the schematic below:

Demonstration

The next figure describes what each LED indicates: (D2) Rain, (D3) Clear sky, (D4) Snow and (D5) Hail:

Here’s a photo of the final circuit:

Wrapping up

Nowadays, the goal of computing is to create a natural user interface between man and computers. We are looking to things like AR (Augmented Reality) to make that reality, but the truth is we are all getting tired of constantly looking at our phones and PCs for the tiniest piece of information.

I think having this project hanged somewhere around the house where you can easily know the weather on your way out is extremely useful.

To take this project further, I may throw in a 3D printed enclosure with a battery back up to make this look more neat. I will certainly share with you when that is done.

Thanks for reading!

You might also like reading our Home Automation using ESP8266 eBook.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

41 thoughts on “ESP8266 Weather Forecaster”

  1. Nice fun project. I was able to power my project using two AAA batteries wired into the breadboard and using the Sleep Function of NodeMCU to save battery power. I also changed the Weather code to access by US Zip code. It works great!

    Reply
  2. Didn’t work for me. The json didn’t get very far into the weather data before it would stop (new line problem maybe?). May want to look at the code again, depending on how old it is. (I was also using a D1 mini – maybe that had something to do with it?)

    Reply
    • Hi Jim. Sorry about that! The board shouldn’t make any difference (it should run/work with all ESP8266 boards). I’m not sure if there was a recent change to the API. Did you double-check your credentials and country code?
      Regards,
      Rui

      Reply
  3. Hello, very nice project! That was what I am looking for to control a relais for my heating to bloc the heat up of the boiler when tomorrow I will have sun for solar heating.
    BUT, it didn’t work on my NodeMCU ESP8266WiFi. I only got incomplete parsed streams, and nothing worked further.
    After hours of reading in the web I found this page: github.com/ThingPulse/esp8266-weather-station/issues/131
    and let me say, this was the solution! Add a simple delay(0) or (1) after receiving each letter of the string and now it works! Great!
    BR,
    ULi

    Reply
  4. I suspect this is just what I need, but I get the message:

    C:\Users\Hussey\AppData\Local\Temp\arduino_modified_sketch_430472\HelloServer.ino: In function ‘void parseJson(const char*)’:

    HelloServer:149:3: error: ‘DynamicJsonBuffer’ was not declared in this scope

    DynamicJsonBuffer jsonBuffer(bufferSize);”HelloServer:149:3: error: ‘DynamicJsonBuffer’ was not declared in this scope”

    when compile. I saw that the library has been updated to use JsonDocument, but updating made no difference. Trying a previous release of the library didn’t help either.

    I suspect there’s a code error, but I am not (yet) sophisticated to recognize and correct it.

    Reply
    • Hi Mark.
      It seems that the Library was updated. So, apparently you are running the newer version and our code uses the old version.
      Check the following links to solve your problem:
      arduinojson.org/v5/faq/error-jsonbuffer-was-not-declared-in-this-scope/?utm_source=github&utm_medium=issues
      github.com/bblanchon/ArduinoJson/issues/756
      Let me know if it helped.

      Reply
  5. I have added an icstation white 0.91 inch oled to display all sorts of weather conditions and it works great. A bit tricky but also great for debugging. Need assistance?

    Reply
  6. Hello,
    I have made an own project with ESP-12 and an ePaper to get some weather data, referring to your Weather Forcaster. This works fine with OpenWeatherMap-Api,
    but the weather forcast data from these api are not the best. I have tried weather.tuxnet24.de without success. I get connection errors with some other URL’s too, only api.openweathermap.org works without errors. Of course, I have made some experiments with the GET-string and an associeted item with the server[] declaration.

    I would be very grateful, if you have an idea, what could be wrong.

    Best Regards
    Claus

    Reply
    • Hi Claus.
      I’ve never used weather.tuxnet24.de. So, it is difficult to understand what might be wrong.
      What errors exactly are you getting?
      Regards,
      Sara 🙂

      Reply
  7. I dont want to annoy you, but the code is horrible and can be shrinked to 1/4 of the size.
    GET response is read with client.readStringUntil(‘\n’) no need to check the braces. After receiption is can be parsed with a few lines. Besides it doesnt work anymore because they changed from http to https connection. Therefore WiFiClientSecure client must be used. Otherwise no connection. I will rework the whole code in the next few days 🙂

    Reply
    • Hi Christian.
      Thanks for noticing.
      This project was built in 2017 and we haven’t made updates to this project since then.
      Later, you can share your code with us if you want.
      Regards,
      Sara

      Reply
  8. Ok, much works is done now and it works fine. but I also changed the language to my mantive language in the comments. Only variables etc are english.

    Reply
  9. Ah!! Really wanted to try this but the link to the Json library doesn’t work ): Not sure what I’m meant to do instead

    Reply
  10. Hi, I really like this project. Thank you for putting this together. I wonder rather than the individual LEDS if a Neopixel 24 LED can be integrated. I think it would look really cool and the options of displaying the weather will be more and a wall hanging of it will be really cool.

    Interested in thoughts and code if anyone chooses to go down the path.

    Cheers

    Reply
    • Hi.
      That’s a great suggestion.
      Unfortunately, we don’t have any tutorial with the ESP8266, Arduino IDE and an LED strip 🙁

      Reply
    • Thank you for this! However it seems to pull data every second, can I make it do it perhaps once a minute or so?

      Reply
    • Thank you Michael. You fixed it all !!!

      Works great now to adjust it for all those new status codes. Thinking of testing ranges of weather codes instead.

      Reply
    • Michael,
      Given that you gave your reply in 2019, I guess that is still for the ArduinJson version 5 library.
      I think that for the ArduinoJson library, the entite parse section can be replaced by the following:

      DynamicJsonDocument jsonDocument(capacity);
      // Parse JSON
      DeserializationError error = deserializeJson(jsonDocument, json);

      // Check for parsing errors
      if (error) {
      Serial.print(“Error parsing JSON: “);
      Serial.println(error.c_str());
      return;
      }

      // Access the “id” value within the first element of the “weather” array

      int list_1_weather_0_id = jsonDocument[“list”][0][“weather”][0][“id”];

      I think the reading of the JSON can be simplified too (as was suggested before) by
      c=client.readStringUntil(‘\n’);
      but I have not tried that

      Reply
  11. I also made some changes to the code as of May 31st, 2019, this time utilizing an LCD panel to display current data. Some of Michael’s code has been adopted into the .ino as well (credits are in the file).

    Here’s the link to the .ino and a Readme:
    drive.google.com/open?id=1AAvgl6gUthBjS3o_J4tR6pVA2QbkHGS3

    Reply
  12. I know this is already an old post, but I stumbled upon it as I needed a quick API call to OWM. As stated in one of the comments, this code could be a bit simpler with rtegard to the http request as well as with the json parser.
    Foolish as I am, I first tried ‘converting’ the code to ArduinoJson6, but decided it would be simpler to just start from scratch.
    It is a quick and simple (quick and dirty) code that pulls the items I need and that works with ArduinoJson version 6. Though I do not need it, I added the ‘weather id” that the code in this article relies upon to light op the LED’s. So if that is all one needs, it is simple to add the LEDs in the program.
    The http request in my program uses London, GB as place to sample, so when using it one would need to change it to another location, also one needs to add one’s own apiKey as well as SSID and PW.
    Location and apikey all need to be put directly in the request url. I did not bother to build it from variables as I was in a hurry…and do not need it.
    The program can be found here: https://gitlab.com/diy_bloke/openweathermap/

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.