WiFi Weather Station
Create a connected weather display that shows current conditions and forecasts from online weather APIs.

Required Components:
WiFi Weather Station
Introduction
Build a connected weather station that displays real-time weather data and forecasts using your MatriXDeck! This project demonstrates how to use the ESP32's WiFi capabilities to fetch data from online weather APIs and display it in an attractive, easy-to-read format on the LED matrix.
This project will teach you about:
- Making HTTP requests to web APIs
- Parsing JSON data
- Creating custom weather icons and animations
- Building an intuitive user interface on a small display
Materials Needed
- MatriXDeck V1
- WiFi internet connection
- OpenWeatherMap API key (free tier)
- USB-C cable for power (or use the battery)
Project Overview
We'll create a weather station with the following features:
- Current temperature, humidity, and conditions
- Daily forecast for the next few days
- Weather condition animations (rain, snow, sun, clouds)
- Automatic location detection based on IP (or customizable location)
- Cycling display between different weather information
Step-by-Step Instructions
1. Setting Up WiFi and API Key
First, let's set up the project with WiFi connectivity and our API key:
#include <MatriXDeck.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
MatriXDeck matrix;
// WiFi credentials
const char* ssid = "YourWiFiName";
const char* password = "YourWiFiPassword";
// OpenWeatherMap API settings
const char* apiKey = "your_openweathermap_api_key";
const char* city = "London"; // Default city
const char* countryCode = "uk"; // Default country code
String units = "metric"; // Use "imperial" for Fahrenheit
// Time between API updates (10 minutes = 600000 ms)
// Free OpenWeatherMap account allows 60 calls per minute
const unsigned long updateInterval = 600000;
unsigned long lastUpdateTime = 0;
// Weather data variables
float temperature, feelsLike, minTemp, maxTemp;
int humidity, pressure;
String weatherMain, weatherDescription;
String weatherIcon;
2. Connecting to WiFi and Setting Up
Next, let's implement the setup function:
void setup() {
matrix.begin();
// Display connecting message
matrix.clear();
matrix.print("WiFi", 8, 3, GREEN);
matrix.print("...", 12, 11, GREEN);
matrix.update();
// Connect to WiFi
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
attempts++;
}
if (WiFi.status() != WL_CONNECTED) {
// WiFi connection failed
matrix.clear();
matrix.print("WiFi", 2, 3, RED);
matrix.print("Failed", 2, 11, RED);
matrix.update();
delay(3000);
} else {
// WiFi connected successfully
matrix.clear();
matrix.print("Connected!", 0, 8, GREEN);
matrix.update();
delay(2000);
// Set up time (needed for HTTPS connections)
configTime(0, 0, "pool.ntp.org");
// Get initial weather data
getWeatherData();
}
}
3. Getting Weather Data from API
Now, let's implement the function to fetch weather data:
void getWeatherData() {
// Display loading message
matrix.clear();
matrix.print("Loading", 0, 4, YELLOW);
matrix.print("Weather", 0, 12, YELLOW);
matrix.update();
// Make sure WiFi is still connected
if (WiFi.status() != WL_CONNECTED) {
matrix.clear();
matrix.print("WiFi", 2, 3, RED);
matrix.print("Error", 2, 11, RED);
matrix.update();
return;
}
// Create HTTP client
HTTPClient http;
// Build the API URL (current weather data)
String url = "http://api.openweathermap.org/data/2.5/weather?q=";
url += String(city) + "," + String(countryCode);
url += "&units=" + units;
url += "&appid=" + String(apiKey);
// Start the request
http.begin(url);
int httpCode = http.GET();
if (httpCode > 0) {
// Successful response
if (httpCode == HTTP_CODE_OK) {
// Parse JSON response
String payload = http.getString();
// Use ArduinoJson to parse the response
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, payload);
if (!error) {
// Extract weather data
temperature = doc["main"]["temp"];
feelsLike = doc["main"]["feels_like"];
minTemp = doc["main"]["temp_min"];
maxTemp = doc["main"]["temp_max"];
humidity = doc["main"]["humidity"];
pressure = doc["main"]["pressure"];
weatherMain = doc["weather"][0]["main"].as<String>();
weatherDescription = doc["weather"][0]["description"].as<String>();
weatherIcon = doc["weather"][0]["icon"].as<String>();
// Update successful, store timestamp
lastUpdateTime = millis();
} else {
// JSON parsing error
matrix.clear();
matrix.print("JSON", 8, 3, RED);
matrix.print("Error", 6, 11, RED);
matrix.update();
delay(2000);
}
}
} else {
// HTTP error
matrix.clear();
matrix.print("API", 10, 3, RED);
matrix.print("Error", 6, 11, RED);
matrix.update();
delay(2000);
}
http.end();
}
4. Displaying Weather Information
Let's create functions to display the weather data in an attractive format:
// Variables for display state
int displayMode = 0; // 0 = current, 1 = details, 2 = forecast
unsigned long lastDisplayChange = 0;
const unsigned long displayChangeInterval = 5000; // Change display every 5 seconds
void loop() {
// Check if it's time to update the weather data
if (millis() - lastUpdateTime >= updateInterval) {
getWeatherData();
}
// Cycle through display modes
if (millis() - lastDisplayChange >= displayChangeInterval) {
displayMode = (displayMode + 1) % 3;
lastDisplayChange = millis();
}
// Display appropriate content based on mode
matrix.clear();
switch (displayMode) {
case 0:
displayCurrentWeather();
break;
case 1:
displayWeatherDetails();
break;
case 2:
displayForecastAnimation();
break;
}
matrix.update();
delay(100);
}
void displayCurrentWeather() {
// Draw temperature in large font
String tempString = String((int)round(temperature)) + ((units == "metric") ? "C" : "F");
matrix.print(tempString, 1, 8, YELLOW);
// Draw appropriate weather icon
drawWeatherIcon(22, 8);
}
void displayWeatherDetails() {
// Show humidity and feels like temperature
matrix.print("HUM:", 0, 2, GREEN);
matrix.print(String(humidity) + "%", 0, 9, YELLOW);
matrix.print("FEEL:", 20, 2, GREEN);
matrix.print(String((int)round(feelsLike)), 22, 9, YELLOW);
}
void displayForecastAnimation() {
// Display weather condition animation
// This will vary based on weather conditions
static int frame = 0;
if (weatherMain == "Rain") {
drawRainAnimation(frame);
} else if (weatherMain == "Snow") {
drawSnowAnimation(frame);
} else if (weatherMain == "Clear") {
drawSunAnimation(frame);
} else if (weatherMain == "Clouds") {
drawCloudAnimation(frame);
} else {
// Default animation for other weather types
matrix.print(weatherMain, 0, 4, GREEN);
}
frame = (frame + 1) % 8; // 8 frames of animation
}
5. Creating Weather Icons and Animations
Now let's implement the functions to draw weather icons and animations:
void drawWeatherIcon(int x, int y) {
// Draw different icons based on weather condition
if (weatherMain == "Clear") {
// Sun icon
matrix.drawCircle(x, y, 3, YELLOW);
matrix.setPixel(x, y-5, YELLOW); // Top ray
matrix.setPixel(x, y+5, YELLOW); // Bottom ray
matrix.setPixel(x-5, y, YELLOW); // Left ray
matrix.setPixel(x+5, y, YELLOW); // Right ray
matrix.setPixel(x-4, y-4, YELLOW); // Top-left ray
matrix.setPixel(x+4, y+4, YELLOW); // Bottom-right ray
matrix.setPixel(x-4, y+4, YELLOW); // Bottom-left ray
matrix.setPixel(x+4, y-4, YELLOW); // Top-right ray
}
else if (weatherMain == "Clouds") {
// Cloud icon
matrix.fillRect(x-3, y, 7, 3, GREEN);
matrix.fillRect(x-1, y-1, 3, 1, GREEN);
}
else if (weatherMain == "Rain") {
// Rain icon
matrix.fillRect(x-3, y-2, 7, 2, GREEN); // Cloud
matrix.setPixel(x-2, y+1, YELLOW); // Rain drop
matrix.setPixel(x, y+2, YELLOW); // Rain drop
matrix.setPixel(x+2, y+1, YELLOW); // Rain drop
}
else if (weatherMain == "Snow") {
// Snow icon
matrix.fillRect(x-3, y-2, 7, 2, GREEN); // Cloud
matrix.setPixel(x-2, y+1, GREEN); // Snowflake
matrix.setPixel(x, y+2, GREEN); // Snowflake
matrix.setPixel(x+2, y+1, GREEN); // Snowflake
}
else if (weatherMain == "Thunderstorm") {
// Thunderstorm icon
matrix.fillRect(x-3, y-2, 7, 2, GREEN); // Cloud
matrix.setPixel(x, y+1, YELLOW); // Lightning
matrix.setPixel(x, y+2, YELLOW); // Lightning
matrix.setPixel(x+1, y+3, YELLOW); // Lightning
}
else {
// Generic icon for other conditions
matrix.fillRect(x-2, y-1, 5, 3, GREEN);
}
}
void drawRainAnimation(int frame) {
// Draw cloud
matrix.fillRect(8, 2, 16, 3, GREEN);
// Draw rain drops at different positions based on frame
for (int i = 0; i < 6; i++) {
int xPos = 10 + i*3;
int yPos = 6 + ((i + frame) % 4) * 2;
if (yPos < 15) { // Only draw if within screen bounds
matrix.setPixel(xPos, yPos, YELLOW);
}
}
// Add temperature at bottom
String tempString = String((int)round(temperature));
matrix.print(tempString + ((units == "metric") ? "C" : "F"), 1, 1, YELLOW);
}
void drawSnowAnimation(int frame) {
// Draw cloud
matrix.fillRect(8, 2, 16, 3, GREEN);
// Draw snowflakes at different positions based on frame
for (int i = 0; i < 5; i++) {
int xPos = 8 + i*4;
int yPos = 6 + ((i + frame) % 4) * 2;
if (yPos < 15) { // Only draw if within screen bounds
// Draw a small cross for each snowflake
matrix.setPixel(xPos, yPos, GREEN);
matrix.setPixel(xPos-1, yPos, GREEN);
matrix.setPixel(xPos+1, yPos, GREEN);
matrix.setPixel(xPos, yPos-1, GREEN);
matrix.setPixel(xPos, yPos+1, GREEN);
}
}
// Add temperature at bottom
String tempString = String((int)round(temperature));
matrix.print(tempString + ((units == "metric") ? "C" : "F"), 1, 1, YELLOW);
}
void drawSunAnimation(int frame) {
// Draw the sun
int centerX = 16;
int centerY = 8;
int radius = 4;
// Draw the sun center
matrix.drawCircle(centerX, centerY, radius, YELLOW);
matrix.setPixel(centerX, centerY, YELLOW);
// Draw sun rays that "pulse" based on frame
int rayLength = 2 + (frame % 2);
// Draw 8 rays
for (int i = 0; i < 8; i++) {
float angle = i * PI / 4.0;
int x1 = centerX + cos(angle) * (radius + 1);
int y1 = centerY + sin(angle) * (radius + 1);
int x2 = centerX + cos(angle) * (radius + rayLength);
int y2 = centerY + sin(angle) * (radius + rayLength);
matrix.drawLine(x1, y1, x2, y2, YELLOW);
}
// Add temperature at bottom
String tempString = String((int)round(temperature));
matrix.print(tempString + ((units == "metric") ? "C" : "F"), 1, 1, YELLOW);
}
void drawCloudAnimation(int frame) {
// Draw moving clouds
int offset = frame % 8;
// Draw multiple clouds that move across the screen
matrix.fillRect(offset, 4, 8, 3, GREEN);
matrix.fillRect(offset+2, 3, 4, 1, GREEN);
matrix.fillRect(offset+16, 6, 8, 3, GREEN);
matrix.fillRect(offset+18, 5, 4, 1, GREEN);
// Add temperature at bottom
String tempString = String((int)round(temperature));
matrix.print(tempString + ((units == "metric") ? "C" : "F"), 1, 1, YELLOW);
}
Complete Code
The complete code combines all these elements and adds more features like button controls to change cities and forecast display options. You can find the full code in our GitHub repository.
API Setup Guide
To use the OpenWeatherMap API, you'll need to:
- Go to OpenWeatherMap and create a free account
- Navigate to the API Keys section and copy your API key
- Replace
your_openweathermap_api_key
in the code with your actual API key - Update the city and country code to your preferred location
Extending the Project
Here are some ideas to enhance your weather station:
- Multi-City Support: Add the ability to cycle through multiple cities
- Weather Alerts: Add special displays for weather alerts and warnings
- Historical Data: Track and display temperature trends over time
- Sensor Integration: Add the Sensor Pack to display local temperature alongside online data
- Voice Announcements: Use the speaker to announce weather conditions
Troubleshooting
Not receiving weather data
- Verify your API key is correct
- Check that your WiFi connection is stable
- Ensure the city name and country code are valid
Data not updating
- The free OpenWeatherMap API has rate limits; make sure you're not exceeding them
- Check for error messages on the display
Display issues
- Adjust the display layout if text appears cut off
- For very long city names, consider abbreviating or scrolling text
Conclusion
Congratulations! You've created a WiFi-connected weather station with your MatriXDeck. This project demonstrates how to connect to web APIs, process JSON data, and create an intuitive visual interface.
By creating this weather station, you've developed skills that can be applied to many other IoT projects. Your MatriXDeck can connect to various online services, making it a versatile display for all kinds of real-time data.
Consider expanding this project by adding other data sources or creating a dashboard that cycles through weather, news headlines, stock prices, and more!
Share your weather station customizations with us on our Discord community or tag us on social media with #MatriXDeckWeather!