ESP32 Online Weather Station

 










The Weather Display System is an IoT-based project designed to fetch and display real-time weather information and a 2-day weather forecast for a specific location (London, UK, in this case) on a TFT display. The system connects to the internet via WiFi, retrieves data from the OpenWeatherMap API, and presents it in a user-friendly format. It shows current weather conditions (temperature, humidity, feels-like temperature, dew point, visibility, and weather description), a forecast for the next two days, and the current local time. This project is ideal for hobbyists, students, or anyone interested in building a compact weather station using affordable hardware and open-source tools.

The system updates the weather data every 5 minutes to provide fresh information while adhering to API usage limits, and it updates the time every second for real-time clock functionality. The code is written in C++ for Arduino-compatible microcontrollers, making it versatile and easy to adapt.


Libraries Used

The project relies on the following Arduino libraries, each serving a specific purpose:

  1. WiFi.h
    • Purpose: Manages WiFi connectivity for ESP32 or ESP8266 boards.
    • Role: Establishes a connection to the specified WiFi network (SSID: "Malik", Password: "pines124") to enable internet access for API requests.
  2. HTTPClient.h
    • Purpose: Handles HTTP requests to external servers.
    • Role: Sends GET requests to the OpenWeatherMap API to fetch current weather and forecast data.
  3. ArduinoJson.h
    • Purpose: Parses JSON data received from the API.
    • Role: Extracts specific weather parameters (e.g., temperature, humidity, visibility) from the API’s JSON responses for processing and display.
  4. TFT_eSPI.h
    • Purpose: Controls TFT displays using the SPI interface.
    • Role: Drives the TFT display to show weather data, forecast, and time in a graphical format with customizable colors and text sizes.
  5. SPI.h
    • Purpose: Provides the Serial Peripheral Interface (SPI) protocol.
    • Role: Facilitates communication between the microcontroller and the TFT display.
  6. time.h
    • Purpose: Manages time-related functions.
    • Role: Synchronizes the system clock with an NTP server and displays the local time in Pakistan (UTC+5).

Hardware Required

The project requires the following hardware components:

  1. Microcontroller:
    • Example: ESP32 or ESP8266 (WiFi-enabled boards).
    • Purpose: Acts as the brain of the system, handling WiFi connectivity, API requests, data processing, and display control.
    • Requirements: Must have sufficient memory and pins to interface with the TFT display.
  2. TFT Display:
    • Example: ILI9341-based TFT display (e.g., 2.8" or 3.2" with 240x320 resolution).
    • Purpose: Displays the weather information and time graphically.
    • Connection: Connected to the microcontroller via SPI pins.
  3. Power Supply:
    • Example: USB power (5V) or battery with appropriate voltage regulator.
    • Purpose: Powers the microcontroller and display.
  4. Internet Connection:
    • Requirement: A stable WiFi network for fetching weather data from the OpenWeatherMap API.

How It Works

The Weather Display System operates as follows:

  1. Initialization:
    • On startup, the microcontroller initializes the TFT display, connects to the specified WiFi network, and sets the local time using an NTP server (adjusted for Pakistan’s UTC+5 timezone).
  2. Weather Data Retrieval:
    • The system uses the HTTPClient library to send GET requests to two OpenWeatherMap API endpoints:
      • Current Weather: http://api.openweathermap.org/data/2.5/weather for real-time data (temperature, humidity, feels-like, visibility, etc.).
      • Forecast: http://api.openweathermap.org/data/2.5/forecast for the next 2 days’ weather (sampled at 24-hour and 48-hour intervals).
    • The API responses are in JSON format, parsed using ArduinoJson to extract relevant data.
  3. Data Processing:
    • Current weather parameters like temperature, humidity, and visibility are directly retrieved from the API.
    • The dew point is calculated locally using the Magnus formula based on temperature and humidity.
    • Forecast data is sampled from the 3-hour interval forecast to approximate daily predictions.
  4. Display Updates:
    • The TFT_eSPI library renders the data on the TFT display:
      • Current Weather: Displayed in white (e.g., temperature, humidity, feels-like, dew point, visibility).
      • 2-Day Forecast: Displayed in cyan (temperature and weather description for the next two days).
      • Time: Displayed in yellow, updated every second.
    • The screen is refreshed to avoid overlapping text.
  5. Update Timing:
    • Weather Data: Updated every 5 minutes (300,000 ms) to fetch fresh data while respecting API limits.
    • Time: Updated every second (1,000 ms) for a real-time clock.
  6. Loop:
    • The system continuously checks the elapsed time using millis() and triggers updates accordingly, ensuring a smooth and automated operation.




1. Header Includes and Global Variables


#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <time.h>


// WiFi credentials

const char* ssid = "Malik";
const char* password = "pines124";


// OpenWeatherMap API details

String apiKey = "your api key here";
String city = "london";
String countryCode = "gb";   // for UK
String currentWeatherURL = "...";
String forecastURL = "...";


// Initialize TFT display

TFT_eSPI tft = TFT_eSPI();


unsigned long lastWeatherUpdate = 0;

unsigned long lastTimeUpdate = 0;






Purpose: Sets up the foundation of the program by including libraries and defining global variables.

  • How It Works:
    • Libraries: Import necessary functionality (WiFi, HTTP requests, JSON parsing, TFT control, SPI communication, and time management).
    • WiFi Credentials: ssid and password store the network details for WiFi connection.
    • API Details: apiKey, city, countryCode, currentWeatherURL, and forecastURL define the OpenWeatherMap API endpoints with parameters (metric units, location: london, UK).
    • TFT Object: tft is an instance of TFT_eSPI to control the display.
    • Timing Variables: lastWeatherUpdate and lastTimeUpdate track the last update times for weather and time, respectively, using milliseconds.








  • 2. Setup Function

    void setup() {
        Serial.begin(115200);
        tft.init();
        tft.setRotation(1);
        tft.fillScreen(TFT_BLACK);

        tft.setTextColor(TFT_WHITE, TFT_BLACK);
        tft.setTextSize(2);
        tft.setCursor(10, 10);
        tft.println("Connecting to WiFi...");
        WiFi.begin(ssid, password);

        while (WiFi.status() != WL_CONNECTED) {
            delay(500);
            Serial.print(".");
        }

        tft.fillScreen(TFT_BLACK);
        tft.setCursor(10, 10);
        tft.println("WiFi Connected!");

        configTime(18000, 0, "pool.ntp.org", "time.nist.gov");

        fetchCurrentWeather();
        fetchForecast();
    }





  • Purpose: Initializes hardware, connects to WiFi, sets up the time, and performs initial weather updates.
  • How It Works:
    • Serial: Starts serial communication at 115200 baud for debugging.
    • TFT Initialization: tft.init() sets up the display, setRotation(1) adjusts orientation, and fillScreen(TFT_BLACK) clears it.
    • WiFi Connection: Displays a "Connecting" message, then uses WiFi.begin() to connect to the network. Loops with a delay until connected, showing progress via Serial.
    • Success Message: Clears the screen and shows "WiFi Connected!" once the connection is established.
    • Time Setup: configTime(18000, 0, ...) sets the timezone to UTC+5 (18000 seconds) and syncs with NTP servers.
    • Initial Fetch: Calls fetchCurrentWeather() and fetchForecast() to get and display weather data immediately.




  • 3. Loop Function

     
    void loop() {
        unsigned long currentMillis = millis();

        if (currentMillis - lastTimeUpdate >= 1000) {
            displayTime();
            lastTimeUpdate = currentMillis;
        }

        if (currentMillis - lastWeatherUpdate >= 300000) {
            fetchCurrentWeather();
            fetchForecast();
            lastWeatherUpdate = currentMillis;
        }
    }





  • Purpose: Continuously checks for updates and triggers time and weather refreshes.
  • How It Works:
    • Timing: millis() gets the current runtime in milliseconds, stored in currentMillis.
    • Time Update: If 1 second (1000 ms) has passed since lastTimeUpdate, it calls displayTime() and updates the timestamp.
    • Weather Update: If 5 minutes (300000 ms) have passed since lastWeatherUpdate, it calls fetchCurrentWeather() and fetchForecast() to refresh weather data, then updates the timestamp.
    • Non-blocking: Uses time differences instead of delay() to keep the system responsive.




  • 4. Fetch Current Weather Function

    void fetchCurrentWeather() {
        if (WiFi.status() == WL_CONNECTED) {
            HTTPClient http;
            http.begin(currentWeatherURL);
            int httpResponseCode = http.GET();

            if (httpResponseCode == 200) {
                String payload = http.getString();
                Serial.println(payload);

                DynamicJsonDocument doc(2048);
                deserializeJson(doc, payload);

                String weather = doc["weather"][0]["description"];
                float temperature = doc["main"]["temp"];
                int humidity = doc["main"]["humidity"];
                float feelsLike = doc["main"]["feels_like"];
                float dewPoint = calculateDewPoint(temperature, humidity);
                int rawVisibility = doc["visibility"];
                int visibility = rawVisibility / 1000;

                displayCurrentWeather(weather, temperature, humidity, feelsLike, dewPoint, visibility);
            }
            http.end();
        }
    }

    • Purpose: Retrieves and processes current weather data from the API.
    • How It Works:
      • WiFi Check: Ensures the device is connected before proceeding.
      • HTTP Request: Initializes http with currentWeatherURL and sends a GET request.
      • Response Handling: If successful (code 200), gets the JSON response as a string.
      • JSON Parsing: Creates a 2048-byte DynamicJsonDocument, parses the payload, and extracts weather parameters.
      • Data Extraction: Pulls description, temperature, humidity, feels-like temp, and visibility; calculates dew point.
      • Display: Passes data to displayCurrentWeather() for rendering.
      • Cleanup: Closes the HTTP connection with http.end().

    5. Fetch Forecast Function

    void fetchForecast() {
        if (WiFi.status() == WL_CONNECTED) {
            HTTPClient http;
            http.begin(forecastURL);
            int httpResponseCode = http.GET();

            if (httpResponseCode == 200) {
                String payload = http.getString();
                DynamicJsonDocument doc(4096);
                deserializeJson(doc, payload);

                float tempDay1 = doc["list"][8]["main"]["temp"];
                String weatherDay1 = doc["list"][8]["weather"][0]["description"];
                float tempDay2 = doc["list"][16]["main"]["temp"];
                String weatherDay2 = doc["list"][16]["weather"][0]["description"];

                displayForecast(tempDay1, weatherDay1, tempDay2, weatherDay2);
            }
            http.end();
        }
    }



    • Purpose: Fetches and processes a 2-day weather forecast.
    • How It Works:
      • WiFi Check: Verifies connectivity.
      • HTTP Request: Sends a GET request to forecastURL (5-day/3-hour forecast).
      • Response Handling: If successful, parses the larger JSON (4096 bytes).
      • Forecast Sampling: Extracts data for ~24 hours (index 8) and ~48 hours (index 16) ahead, assuming 3-hour intervals.
      • Display: Passes temperature and weather description for both days to displayForecast().
      • Cleanup: Ends the HTTP connection.

    6. Dew Point Calculation Function


    float calculateDewPoint(float temp, float humidity) {
        float a = 17.27;
        float b = 237.7;
        float alpha = ((a * temp) / (b + temp)) + log(humidity / 100.0);
        return (b * alpha) / (a - alpha);
    }


    • Purpose: Computes the dew point using temperature and humidity.
    • How It Works:
      • Formula: Implements the Magnus approximation: Td = (b * α) / (a - α), where α = (a * T / (b + T)) + ln(RH/100).
      • Inputs: Takes temp (Celsius) and humidity (percentage).
      • Output: Returns the dew point temperature in Celsius.

    7. Display Current Weather Function

    void displayCurrentWeather(String weather, float temp, int humidity, float feelsLike, float dewPoint, int visibility) {
        tft.fillScreen(TFT_BLACK);
        tft.setTextColor(TFT_WHITE, TFT_BLACK);
        tft.setTextSize(2);

        int yPos = 10;
        // [Text output for weather, temp, feelsLike, humidity, dewPoint, visibility]
    }


    • Purpose: Renders current weather data on the TFT display.
    • How It Works:
      • Clear Screen: Wipes the display with black.
      • Text Setup: Sets white text on black background, size 2.
      • Positioning: Uses yPos to vertically space lines (incremented by 25).
      • Output: Prints each parameter with units (e.g., "Temp: 25.3 C") at specific coordinates.

    8. Display Forecast Function

    void displayForecast(float temp1, String weather1, float temp2, String weather2) {
        tft.setTextColor(TFT_CYAN, TFT_BLACK);
        tft.setTextSize(2);

        int yPos = 160;
        // [Text output for Day 1 and Day 2]
    }



  • Purpose: Shows the 2-day forecast on the display.
  • How It Works:
    • Text Setup: Uses cyan text, size 2.
    • Positioning: Starts at yPos = 160 (below current weather).
    • Output: Prints temperature and weather for Day 1 and Day 2.     



    9. Display Time Function


    void displayTime() {
        struct tm timeinfo;
        if (!getLocalTime(&timeinfo)) {
            Serial.println("Failed to obtain time");
            return;
        }

        char timeStr[10];
        strftime(timeStr, sizeof(timeStr), "%H:%M:%S", &timeinfo);

        tft.fillRect(10, 210, 150, 30, TFT_BLACK);
        tft.setTextColor(TFT_YELLOW, TFT_BLACK);
        tft.setTextSize(2);
        tft.setCursor(10, 210);
        tft.print("Time: ");
        tft.print(timeStr);
    }
    • Purpose: Updates and displays the current time.
    • How It Works:
      • Time Fetch: Gets local time into timeinfo using getLocalTime().
      • Formatting: Converts to "HH:MM:SS" format with strftime.
      • Display: Clears a rectangle at (10, 210), sets yellow text, and prints the time.

    Summary of Workflow

    • Setup: Initializes everything and connects to WiFi/time servers.
    • Loop: Manages timing for updates (time every second, weather every 5 minutes).
    • Data Fetch: Pulls weather/forecast data via API, processes it.
    • Display: Renders all information on the TFT in an organized layout.

    Each part works together to create a cohesive system that fetches, processes, and displays weather and time data efficiently.


        Weather API Used   OpenWeatherMapAPI




    In this project, we are using the OpenWeatherMap API to fetch weather data. Specifically, two endpoints from the OpenWeatherMap API are utilized to retrieve current weather conditions and forecast data. Below is a detailed explanation of the API and the endpoints used:


    Weather API Used: OpenWeatherMap API

    The OpenWeatherMap API is a widely-used, free, and reliable weather data service that provides current weather information, forecasts, and historical data for locations worldwide. It is accessible via HTTP requests and returns data in JSON format, making it suitable for integration with microcontrollers like the ESP32 or ESP8266. The API requires an API key for authentication, and in this project, the key "7b7ae3269ec6e4e3b2xxxxxxxxxxxxxx" is used.

    API Endpoints Used in the Project

    1. Current Weather Data Endpoint
      • URL: http://api.openweathermap.org/data/2.5/weather
      • Parameters:q=London,gb: Specifies the location (London, GB).
        • appid=7b7ae3269ec6e4e3bxxxxxxxxxxxxxxx: The API key for authentication.
    units=metric: Requests data in metric units (e.g., Celsius for temperature, meters for visibility).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   




     

         LCD connections with ESP32






  • SPI Communication: The ESP32 uses its VSPI peripheral to send commands and data to the ILI9341 via the MOSI and SCK pins. The CS pin selects the display, and the DC pin toggles between command mode (e.g., setting the cursor) and data mode (e.g., sending pixel data).
  • Reset: The RESET pin initializes the display when powered on or when tft.init() is called.
  • Display Control: The TFT_eSPI library abstracts these low-level details, allowing the code to focus on high-level functions like tft.fillScreen() and tft.print().
  • FULL CODES OF THIS PROJECT


    #include <WiFi.h>
    #include <HTTPClient.h>
    #include <ArduinoJson.h>
    #include <TFT_eSPI.h>
    #include <SPI.h>
    #include <time.h>

    // WiFi credentials
    const char* ssid = "1111";
    const char* password = "111";

    // OpenWeatherMap API details
    String apiKey = "your API key here";
    String city = "london";
    String countryCode = "GB";
    String currentWeatherURL = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "," + countryCode + "&appid=" + apiKey + "&units=metric";
    String forecastURL = "http://api.openweathermap.org/data/2.5/forecast?q=" + city + "," + countryCode + "&appid=" + apiKey + "&units=metric";

    // Initialize TFT display
    TFT_eSPI tft = TFT_eSPI();

    unsigned long lastWeatherUpdate = 0;
    unsigned long lastTimeUpdate = 0;

    void setup() {
        Serial.begin(115200);

        // Initialize display
        tft.init();
        tft.setRotation(1);
        tft.fillScreen(TFT_BLACK);

        // Connect to WiFi
        tft.setTextColor(TFT_WHITE, TFT_BLACK);
        tft.setTextSize(2);
        tft.setCursor(10, 10);
        tft.println("Connecting to WiFi...");
        WiFi.begin(ssid, password);

        while (WiFi.status() != WL_CONNECTED) {
            delay(500);
            Serial.print(".");
        }

        tft.fillScreen(TFT_BLACK);
        tft.setCursor(10, 10);
        tft.println("WiFi Connected!");

        // Set  Time Zone (UTC+5)
        configTime(18000, 0, "pool.ntp.org", "time.nist.gov");

        // Initial updates
        fetchCurrentWeather();
        fetchForecast();
    }

    void loop() {
        unsigned long currentMillis = millis();

        // Update time every second
        if (currentMillis - lastTimeUpdate >= 1000) {
            displayTime();
            lastTimeUpdate = currentMillis;
        }

        // Update weather every 5 minutes (300000 ms)
        if (currentMillis - lastWeatherUpdate >= 300000) {
            fetchCurrentWeather();
            fetchForecast();
            lastWeatherUpdate = currentMillis;
        }
    }

    // Function to fetch current weather data
    void fetchCurrentWeather() {
        if (WiFi.status() == WL_CONNECTED) {
            HTTPClient http;
            http.begin(currentWeatherURL);
            int httpResponseCode = http.GET();

            if (httpResponseCode == 200) {
                String payload = http.getString();
                Serial.println(payload);

                // Parse JSON
                DynamicJsonDocument doc(2048);
                deserializeJson(doc, payload);

                String weather = doc["weather"][0]["description"];
                float temperature = doc["main"]["temp"];
                int humidity = doc["main"]["humidity"];
                float feelsLike = doc["main"]["feels_like"];
                float dewPoint = calculateDewPoint(temperature, humidity);
                int rawVisibility = doc["visibility"]; // Get raw value first
                int visibility = rawVisibility / 1000; // Then convert to km

                displayCurrentWeather(weather, temperature, humidity, feelsLike, dewPoint, visibility);
            }
            http.end();
        }
    }

    // Function to fetch 2-day forecast
    void fetchForecast() {
        if (WiFi.status() == WL_CONNECTED) {
            HTTPClient http;
            http.begin(forecastURL);
            int httpResponseCode = http.GET();

            if (httpResponseCode == 200) {
                String payload = http.getString();
                DynamicJsonDocument doc(4096);
                deserializeJson(doc, payload);

                // Get tomorrow and day after tomorrow's forecast (taking 24h and 48h from now)
                float tempDay1 = doc["list"][8]["main"]["temp"];  // Approx 24h from now
                String weatherDay1 = doc["list"][8]["weather"][0]["description"];
                float tempDay2 = doc["list"][16]["main"]["temp"]; // Approx 48h from now
                String weatherDay2 = doc["list"][16]["weather"][0]["description"];

                displayForecast(tempDay1, weatherDay1, tempDay2, weatherDay2);
            }
            http.end();
        }
    }

    // Calculate dew point using temperature and humidity
    float calculateDewPoint(float temp, float humidity) {
        float a = 17.27;
        float b = 237.7;
        float alpha = ((a * temp) / (b + temp)) + log(humidity / 100.0);
        return (b * alpha) / (a - alpha);
    }

    // Display current weather
    void displayCurrentWeather(String weather, float temp, int humidity, float feelsLike, float dewPoint, int visibility) {
        tft.fillScreen(TFT_BLACK);
        tft.setTextColor(TFT_WHITE, TFT_BLACK);
        tft.setTextSize(2);

        int yPos = 10;
        tft.setCursor(10, yPos);
        tft.print("Weather: ");
        tft.println(weather);

        yPos += 25;
        tft.setCursor(10, yPos);
        tft.print("Temp: ");
        tft.print(temp);
        tft.println(" C");

        yPos += 25;
        tft.setCursor(10, yPos);
        tft.print("Feels Like: ");
        tft.print(feelsLike);
        tft.println(" C");

        yPos += 25;
        tft.setCursor(10, yPos);
        tft.print("Humidity: ");
        tft.print(humidity);
        tft.println("%");

        yPos += 25;
        tft.setCursor(10, yPos);
        tft.print("Dew Point: ");
        tft.print(dewPoint);
        tft.println(" C");

        yPos += 25;
        tft.setCursor(10, yPos);
        tft.print("Visibility: ");
        tft.print(visibility);
        tft.println(" km");
    }

    // Display 2-day forecast
    void displayForecast(float temp1, String weather1, float temp2, String weather2) {
        tft.setTextColor(TFT_CYAN, TFT_BLACK);
        tft.setTextSize(2);

        int yPos = 160;
        tft.setCursor(10, yPos);
        tft.print("Day 1: ");
        tft.print(temp1);
        tft.print("C, ");
        tft.println(weather1);

        yPos += 25;
        tft.setCursor(10, yPos);
        tft.print("Day 2: ");
        tft.print(temp2);
        tft.print("C, ");
        tft.println(weather2);
    }

    // Function to display time
    void displayTime() {
        struct tm timeinfo;
        if (!getLocalTime(&timeinfo)) {
            Serial.println("Failed to obtain time");
            return;
        }

        char timeStr[10];
        strftime(timeStr, sizeof(timeStr), "%H:%M:%S", &timeinfo);

        tft.fillRect(10, 210, 150, 30, TFT_BLACK);
        tft.setTextColor(TFT_YELLOW, TFT_BLACK);
        tft.setTextSize(2);
        tft.setCursor(10, 210);
        tft.print("Time: ");
        tft.print(timeStr);
    }






    Comments