ESP8266 Wifi Weather Station + Display

Hi I have a project in mind which shows the weather data in the Blynk app and on a type of display connected to my Wemos. The project will have a BME280 Sensor and a display that will show basic information such as temperature,Pressure and Humidity values which are obtained from the sensor. I would also like to get a forecast of the next day from the Open Weather App. I have created an account and got an API key. If possible I would like to add an led strip that will light up in colour according to the weather forecast. Example if the weather will be sunny the led strip will turn yellow. The blynk app can also be used to view the information from anywhere. Has anyone ever done anything like this project?

Did you start building it?
All the bits and pieces can be found here:

  1. Blynk examples in the library
  2. Search on this forum
  3. Google
  4. Numerous YouTube videos
  5. Hackster.io
  6. Instructables
  7. Help.blynk.cc
  8. Docs.blynk.cc
1 Like

Hi I have mofified a code I found that outputs the data on an LCD and I sucsessfully intergrated blynk. The code does work but I think I overcomplicated things. For the code to work I did not use the Webhook widget. Can this widget be used to simplify my code? I tried the webhook widget example and I got a respons in the serial monitor sucessfuly. Now can some one explain to me how I can translate the data from the webhook widget and get weather updates on the blynk app?

This is the code I was referring to.

Thanks in advance.

void getWeatherData() //client function to send/receive GET request data.
        {
          if (client.connect(servername, 80)) {  //starts client connection, checks for connection
            client.println("GET /data/2.5/weather?id=" + CityID + "&units=metric&APPID=" + APIKEY);
            client.println("Host: api.openweathermap.org");
            client.println("User-Agent: ArduinoWiFi/1.1");
            client.println("Connection: close");
            client.println();
          }
          else {
            Serial.println("connection failed"); //error message if no client connect
            Serial.println();
          }

          while (client.connected() && !client.available()) delay(1); //waits for data
          while (client.connected() || client.available()) { //connected or data available
            char c = client.read(); //gets byte from ethernet buffer
            result = result + c;
          }

          client.stop(); //stop client
          result.replace('[', ' ');
          result.replace(']', ' ');
         // Serial.println(result);

          char jsonArray [result.length() + 1];
          result.toCharArray(jsonArray, sizeof(jsonArray));
          jsonArray[result.length() + 1] = '\0';

          StaticJsonBuffer<1024> json_buf;
          JsonObject &root = json_buf.parseObject(jsonArray);
          if (!root.success())
          {
            Serial.println("parseObject() failed");
          }

          String location = root["name"];
          String country = root["sys"]["country"];
          float temperature = root["main"]["temp"];
          float humidity = root["main"]["humidity"];
          String weather = root["weather"]["main"];
          String description = root["weather"]["description"];
          float pressure = root["main"]["pressure"];
           float temperature_min = root["main"]["temp_min"]; 
           float temperature_max = root["main"]["temp_max"];

          weatherDescription = description;
          weatherLocation = location;
          Country = country;
          Temperature = temperature;
          Humidity = humidity;
          Pressure = pressure;
          Weather = weather;
          temp_min = temperature_min;
          temp_max = temperature_max;
        }

Full code below

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

    /* Comment this out to disable prints and save space */
    #define BLYNK_PRINT Serial


    //#include <SPI.h>
    #include <ArduinoJson.h>
    #include <ESP8266WiFi.h>
    #include <BlynkSimpleEsp8266.h>

    int count = 0;

    String APIKEY = "7c072c58b4b3433eaf828794f0e6fafa";
    String CityID = "2562620"; //rabat malta


    // You should get Auth Token in the Blynk App.
    // Go to the Project Settings (nut icon).
    char auth[] = "74fXXXXXXXXXXXXXXXXXXXXXXXXXf1";

    // Your WiFi credentials.
    // Set password to "" for open networks.
    char ssid[] = "CaXXXXXXFi";
    char pass[] = "caXXXXXXeri";

    int  counter = 60;

    String weatherDescription = "";
    String weatherLocation = "";
    String Country;
    String Weather;
    float Temperature;
    float Humidity;
    float Pressure;
    float temp_min;
    float temp_max;

    BlynkTimer timer;
    WidgetLCD lcd(V4);

    WiFiClient client;
    char servername[] = "api.openweathermap.org"; // remote server we will connect to
    String result;

    void testing () {
      Blynk.virtualWrite(V6, Temperature);
      Blynk.virtualWrite(V2, Humidity);
      Blynk.virtualWrite(V0, Pressure);
      Blynk.virtualWrite(V5, count);
      Blynk.virtualWrite(V7, temp_min);
      Blynk.virtualWrite(V8, temp_max);
      //Blynk.virtualWrite(V0, Country);

    lcd.print(12,0, Country);
    lcd.print(0,0, weatherLocation);
    lcd.print(0,1, Weather);
    //lcd.print(0,1, weatherDescription);
      
      count++;
    }

    void setup()
    {
      // Debug console
      Serial.begin(9600);

      Blynk.begin(auth, ssid, pass);
      WiFi.begin(ssid, pass);
    lcd.clear();
      timer.setInterval(1000L, testing);
    }

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

    void getdata() {
      if (counter == 60) //Get new data every 10 minutes
      {
        counter = 0;
        //  displayGettingData();
        timer.setTimeout(1000, getWeatherData);
      }
      else
      {
        counter++;
        // displayWeather(weatherLocation, weatherDescription);
        // timer.setTimeout(5000, displayConditions);
        timer.setTimeout(5000, getdata);
      }
    }


    void getWeatherData() //client function to send/receive GET request data.
    {
      if (client.connect(servername, 80)) {  //starts client connection, checks for connection
        client.println("GET /data/2.5/weather?id=" + CityID + "&units=metric&APPID=" + APIKEY);
        client.println("Host: api.openweathermap.org");
        client.println("User-Agent: ArduinoWiFi/1.1");
        client.println("Connection: close");
        client.println();
      }
      else {
        Serial.println("connection failed"); //error message if no client connect
        Serial.println();
      }

      while (client.connected() && !client.available()) delay(1); //waits for data
      while (client.connected() || client.available()) { //connected or data available
        char c = client.read(); //gets byte from ethernet buffer
        result = result + c;
      }

      client.stop(); //stop client
      result.replace('[', ' ');
      result.replace(']', ' ');
     // Serial.println(result);

      char jsonArray [result.length() + 1];
      result.toCharArray(jsonArray, sizeof(jsonArray));
      jsonArray[result.length() + 1] = '\0';

      StaticJsonBuffer<1024> json_buf;
      JsonObject &root = json_buf.parseObject(jsonArray);
      if (!root.success())
      {
        Serial.println("parseObject() failed");
      }

      String location = root["name"];
      String country = root["sys"]["country"];
      float temperature = root["main"]["temp"];
      float humidity = root["main"]["humidity"];
      String weather = root["weather"]["main"];
      String description = root["weather"]["description"];
      float pressure = root["main"]["pressure"];
       float temperature_min = root["main"]["temp_min"]; 
       float temperature_max = root["main"]["temp_max"];

      weatherDescription = description;
      weatherLocation = location;
      Country = country;
      Temperature = temperature;
      Humidity = humidity;
      Pressure = pressure;
      Weather = weather;
      temp_min = temperature_min;
      temp_max = temperature_max;
    }

@claytoncamilleri100 for weather data and the Blynk webhook widget you need the ArduinoJson parser.

See some of these examples:

https://www.arduino.cc/en/Tutorial.WiFi101WeatherAudioNotifier


http://zeflo.com/2014/esp8266-weather-display/

And the definitive ESP8266 weather station https://github.com/squix78/esp8266-weather-station

It does? I think timers must be declared within the setup()?! Instead of getdata() (can’t really understand how it works :smiley: ) in the main loop() I would create a separate timer for the getWeatherData() function.

They can be started (declared) at any time (like say a repeating 10 minute countdown before dropping a water bomb on your head after you hit the snooze button that activates that timer function :stuck_out_tongue_winking_eye: ), and will start running regularly from that time on (depending on timer type), but since most want a timer to start at boot, the setup is the normal place to put it.

Thx, good to know!

But I still can’t wrap my head around the getdata() in this particular example. Since getdata() is in the loop(), it’s getting called very often and each time (except when the counter hits 60) it tells the program to run a setTimeout.

From the docs:

For more information on timer usage, please see: Arduino Playground - HomePage

From Arduino playground:

Call function f once after d milliseconds. The callback function must be declared as void f(). After f has been called, the interval is deleted, therefore the value timerId is no longer valid.

So it basically tells it to run getdata() (one time) again after 5 seconds while the counter isn’t 60. Seems like a very strange implementation of the timer function. Unless I’m missing something!?

Opps, I hadn’t actually paid attention to the OP script…

Me neither… it is overly complicated and inaccurate… getdata() is called hundreds of times a second from the void loop() thus that count to 60 happens in seconds, not 10 minutes… and thus is running multiple timeouts that each get generated long before the first is close to finishing and thus each one running the functions repeatedly multiples of times quicker then they should… :face_with_raised_eyebrow:

Not to mention the final timeout which basically reruns the loop it is already in…

'duinoception :rofl:

To quote someone else in a similar matter, it’s kinda disappointing when people don’t read the attached code or manage to format it correctly :wink: :stuck_out_tongue: :fist_left:

It’s a serious loop in the loop that’s looping - with timers! :rofl:

Yes, my bad… I do scan every single post :face_with_monocle: and happened to have an answer on your particular question… but I wasn’t otherwise engaging this topic indepth at the time… I promise I will try to pay better attention :stuck_out_tongue_winking_eye:

1 Like

Hi I changed the data sorce from Open Weather Data to Underground Weather Data I found it gives more accurate readings. I have attached the current code but I am still working on getting more data outputs to the app. Let me know if I can the u change anything in my code to make it better.

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <ArduinoJson.h>

char auth[] = "7hfsdjhvdsvhsvh\uhfvukhvfdukvbk";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "chdhhos";
char pass[] = "12345678";

BlynkTimer timer;
WidgetLCD lcd(V4);

int counter = 1;

// Time entries that didn't like being removed.
#define DELAY_NORMAL    (10)
#define DELAY_ERROR     (10)

#define WUNDERGROUND "api.wunderground.com"

// HTTP request
const char WUNDERGROUND_REQ[] =
  "GET /api/56c7a193553ac6d0/conditions/q/Malta/Rabat.json HTTP/1.1\r\n"
  "User-Agent: ESP8266/0.1\r\n"
  "Accept: */*\r\n"
  "Host: " WUNDERGROUND "\r\n"
  "Connection: close\r\n"
  "\r\n";

String weather_desc1 = "";
String  Wind = "";
String  Full_name = "start";


float temp_c;
float humidity;
float pressure;
float wind_spd;


void setup()
{
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass);

  lcd.clear();

  while (Blynk.connect() == false) {
    // Wait until connected
  }
  timer.setInterval(5000L, sendWU); // 5 minutes between Wunderground API calls.\300000L
  timer.setInterval(1000L, sendWUtoBlynk); // 1.5 minutes between API data updates to Blynk./90000L
  timer.setInterval(1000L, debuging); // 1.5 minutes between API data updates to Blynk.
}

static char respBuf[4096];

bool showWeather(char *json);

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

void sendWUtoBlynk()
{
  Blynk.virtualWrite(V6, temp_c); // Write to Blynk app virtual pin 12
  //Blynk.virtualWrite(V6, temp_c); // Write to Blynk app virtual pin 12
  Blynk.virtualWrite(V2, humidity); // Write to Blynk app virtual pin 12
  Blynk.virtualWrite(V0, pressure); // Write to Blynk app virtual pin 12
  Blynk.virtualWrite(V1, wind_spd); // Write to Blynk app virtual pin 12
  lcd.print(0,0, Full_name);
  lcd.print(0,1, weather_desc1);
 
  
  counter++;
}

void debuging () {
  Blynk.virtualWrite(V5, counter); // Write to Blynk app virtual pin 12
}

void sendWU()
{
  // Open socket to WU server port 80
  Serial.print(F("Connecting to "));
  Serial.println(WUNDERGROUND);

  // Use WiFiClient class to create TCP connections
  WiFiClient httpclient;
  const int httpPort = 80;
  if (!httpclient.connect(WUNDERGROUND, httpPort)) {
    Serial.println(F("connection failed"));
    delay(DELAY_ERROR);
    return;
  }

  // This will send the http request to the server
  Serial.print(WUNDERGROUND_REQ);
  httpclient.print(WUNDERGROUND_REQ);
  httpclient.flush();

  // Collect http response headers and content from Weather Underground
  // HTTP headers are discarded.
  // The content is formatted in JSON and is left in respBuf.
  int respLen = 0;
  bool skip_headers = true;
  while (httpclient.connected() || httpclient.available()) {
    if (skip_headers) {
      String aLine = httpclient.readStringUntil('\n');
      //Serial.println(aLine);
      // Blank line denotes end of headers
      if (aLine.length() <= 1) {
        skip_headers = false;
      }
    }
    else {
      int bytesIn;
      bytesIn = httpclient.read((uint8_t *)&respBuf[respLen], sizeof(respBuf) - respLen);
      Serial.print(F("bytesIn ")); Serial.println(bytesIn);
      if (bytesIn > 0) {
        respLen += bytesIn;
        if (respLen > sizeof(respBuf)) respLen = sizeof(respBuf);
      }
      else if (bytesIn < 0) {
        Serial.print(F("read error "));
        Serial.println(bytesIn);
      }
    }
    delay(1);
  }
  httpclient.stop();

  if (respLen >= sizeof(respBuf)) {
    Serial.print(F("respBuf overflow "));
    Serial.println(respLen);
    delay(DELAY_ERROR);
    return;
  }
  // Terminate the C string
  respBuf[respLen++] = '\0';
  Serial.print(F("respLen "));
  Serial.println(respLen);
  //Serial.println(respBuf);

  if (showWeather(respBuf)) {
    delay(DELAY_NORMAL);
  }
  else {
    delay(DELAY_ERROR);
  }
}

bool showWeather(char *json)
{
  StaticJsonBuffer<3 * 1024> jsonBuffer;

  // Skip characters until first '{' found
  // Ignore chunked length, if present
  char *jsonstart = strchr(json, '{');
  //Serial.print(F("jsonstart ")); Serial.println(jsonstart);
  if (jsonstart == NULL) {
    Serial.println(F("JSON data missing"));
    return false;
  }
  json = jsonstart;

  // Parse JSON
  JsonObject& root = jsonBuffer.parseObject(json);
  if (!root.success()) {
    Serial.println(F("jsonBuffer.parseObject() failed"));
    return false;
  }

  // Extract weather info from parsed JSON
  JsonObject& current = root["current_observation"];
  temp_c = current["temp_c"];
  pressure = current["pressure_mb"];
  humidity = current["relative_humidity"];
  wind_spd = current["wind_kph"];
  String weather_desc = current["weather"];
  String full_name = current["full"];
  //humidity = current["relative_humidity"];

weather_desc1 = weather_desc;
Full_name = full_name;

  return true;
}
2 Likes

That’s a very good start.

What I would change is the interval on your timers.

  1. For gathering weather data, so much reading is not needed. Intervals of 5 minutes or more are fine for that sort of application.

  2. I would not set the timer to exactly the same interval. E.g. I would set the 1000ms to 1200 and 1000. The execution of functions also take time, so it’s better to have them a bit off from each other, that makes for a smooth ride of timed functions and less change of disconnecting and other weird issues.

timer.setInterval(5000L, sendWU); // 5 minutes between Wunderground API calls.\300000L
  timer.setInterval(1000L, sendWUtoBlynk); // 1.5 minutes between API data updates to Blynk./90000L
  timer.setInterval(1000L, debuging); // 1.5 minutes between API data updates to Blynk.

Yes I did that for testing the code so I wouldn’t have to wait for the data when I boot the esp. I would change the times back. As you can see I commented out the delays.