In this tutorial you will learn the basics of how to build your own internet weather station using an ESP32.
We will download weather data from OpenWeather, parse JSON data using ArduinoJson to extract temperature, humidity, location and weather description and display this information on an OLED.
Let’s get started!
Required Parts
Below you will find the parts required for this project. For this project, I am using an older ESP32 board (ESP32 lite), which has been deprecated but you can still get it. That’s the one listed below. There is a successor model with improved specs, which you can find here. But any other ESP32, ESP8266 or Arduino with Wi-Fi will work as well.
ESP32 lite
USB Data Cable
Dupont Wire Set
Breadboard
OLED Display
Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.
OpenWeather
For our internet weather station we obviously will need to download weather data from the internet. There various ways we could do this but the easiest is to use OpenWeather.
OpenWeather is a service that provides current weather data and forecasts through an API that developers can use in their projects. They have a free plan that allows you to make 1000 API calls per day. That is an API call every 60*60*24/1000 = 86.4 seconds and more than sufficient, since updating the weather every 5 or 10 minutes is plenty fast.
Furthermore, even just the free plan provides information about Current Weather, 3-hour Forecast 5 days, Basic weather maps, Air Pollution and Geocoding. More than enough for most hobby application.
Creating an OpenWeather account
Before you can use any of the OpenWeather APIs, you need an API-key and for that you need an account. To create an account go to the sign-up page and enter your details there.
Create OpenWeather API-key
Once you have an account and are signed in go to the api-key creation page and create an API-key. The API-key is that long string “sdfd87fakeby6apikeysf4z” that you see in the screenshot below. Your key will look different to mine, and the one shown is obviously a fake key ; )
You can give the key a name but you don’t have to. Names are useful if you have multiple applications that use different keys and you want to keep them separate.
Download OpenWeather weather data
With your API-key you can now retrieve weather data from the various APIs by constructing the relevant URL. For instance, if you go to the Current Weather API, the documentation tells you that the URL should look like this:
https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
So, you would need to know the latitude and longitude of your location, and your API-key and replace the placeholders in curly brackets ({lat}
, {lon}
, {API key}
) by the actual values. That works but figuring out the latitude and longitude for various cities your interested in, is a bit cumbersome.
Luckily, there is the Geocoding API API that allows us to get the weather data more easily be providing a string with the city name and country code. The URL pattern looks like this:
https://api.openweathermap.org/data/2.5/weather?q={city name},{country code}&appid={API key}
For instance, to get the weather in Melbourne, Australia (AU), where I live, you would write:
http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&APPID=sdfd87fakeby6apikeysf4z
If you paste this URL into the search bar of your browser you will see the following output. Make sure to check the “Pretty print” box at the top.
If you look at the temperature you will notice a crazily high value. In the example above the reported temperature value is 283.38. This is, because per default OpenWeather returns temperatures in Kelvin. If you want metric (Celsius) or imperial (Fahrenheit) units, you can specify this in the URL.
For instance, to get the temperatures in Celsius you need to use:
https://api.openweathermap.org/data/2.5/weather?q={city name},{country code}&appid={API key}&units=metric
and for Fahrenheit you would write units=imperial
. For more information have a look at the OpenWeather documentation for units of measurement.
That is all you need to know to get started with OpenWeather. But I recommend that you browse the other APIs mentioned above. There is a lot of cool data you can use to make your weather station really useful.
Parsing JSON data with ArduinoJson
Per default OpenWeather returns data in JSON format. JSON is a structured text format we need to parse to extract the data we want. That is quite difficult if you implement this yourself and we therefore use the excellent ArduinoJson library, instead.
JSON document
But first let’s have a look at what information we would like to extract from weather data. The screenshot below shows the complete data I get when asking OpenWeather for the weather in Melbourne.
For our simple internet weather station, I am going to extract four pieces of information. The short description of the weather under weather -> main
("Clouds"
), the current temperature under main -> temp
(10.29
), the current relative humidity under main -> humidity
(82
), and the name of the location under name
("Melbourne"
).
Extracting data with ArduinoJson
A code snippet using ArduinoJson to extract these four weather values would look like follows:
StaticJsonDocument<4096> doc; String json = http.getString(); deserializeJson(doc, json); const char* name = doc["name"]; const char* weather = doc["weather"][0]["main"]; float temp = doc["main"]["temp"]; int hum = doc["main"]["humidity"];
First, you need to reserve some memory for the JSON data to load into. The line StaticJsonDocument<4096> doc;
reserves 4KB of memory. If you download more weather data, such as weather forecasts, you may need to increase this value.
Next we download the weather data and write (deserialize) it to the document. The code snippet does not show the HTTP client we are using for the download but you will find it in the full code below.
String json = http.getString(); deserializeJson(doc, json);
If the JSON data returned from OpenWeather would by syntactically incorrect (missing brackets, …), the deserialization will fill fail.
Deserialization simply means the plain text of the json
string is parsed and converted into a data structure that allows us to access the field and values of the JSON document doc
more easily. For more detailed information on deserialization have a look at the excellent ArduinoJson tutorial on deserialization.
The following four lines of code extract the information we want from the doc
that contains the deserialized json
string.
const char* name = doc["name"]; const char* weather = doc["weather"][0]["main"]; float temp = doc["main"]["temp"]; int hum = doc["main"]["humidity"];
The first line extracts the name of the weather location ("Melbourne"
), which is stored under doc["name"]
. Since this is stored as a string (see quotation marks), we must store it in a const char* name
variable.
The short weather description ("Clouds"
) is stored under doc["weather"][0]["main"]
. Note that under doc["weather"]
, we have a list []
and we want the first (and only) element of that list. We therefore need to write doc["weather"][0]
. Then we get the description under main
, and the complete path is doc["weather"][0]["main"]
. Again this returns a string and needs to be stored as a const char* weather
.
The current temperature is a floating point value (10.29), which is located under doc["main"]["temp"]
. Since, it is a floating point value it needs to be stored in a float
variable.
Finally, we extract the current humidity (82
) from doc["main"]["humidity"]
and store it in a variable of type int
.
You need to be very careful to match the data types of the values to the data types used in the JSON document and to use the correct paths to extract those values. Otherwise, you will end up with memory errors that are very hard to debug. If you want to extract different values start slowly and add new values step by step, to know when something went wrong.
Connecting the ESP32 and OLED
Connecting the ESP32 to the OLED is very simple since the OLED has an I2C interface. First connect ground (G) of the ESP32 to GND of the OLED. Next connect 3V of the ESP32 to VCC of the OLED.
Then we need to connect the SCL and SDA. Any pin will work but the default I2C pins on the ESP32-lite are 23 for SCL and 19 for SDA. So we connect the corresponding pins of the OLED to those ones. The picture above shows the complete wiring. If you have difficulties with this, have a look at the tutorial How to Interface the SSD1306 I2C OLED Graphic Display With Arduino
If you don’t know what the default I2C pins for your ESP32 are, don’t worry. It is easy to find out. Just run the following code and your ESP32 will tell you.
void print(const char* name, int pin) { Serial.print(name); Serial.println(pin); } void setup() { Serial.begin(9600); } void loop() { print("SDA: ", SDA); print("SCL: ", SCL); delay(5000); }
For more information read our post on how to Find I2C and SPI default pins. Alternatively, you can define the pins you want use. The code in the following sections shows you how.
Code for the Internet Weather Station
In this section we are going to write the code to download weather data from OpenWeather, extract location, temperature, humidity and weather description from the data and display them on an OLED. Have a look at the complete code below first and then we will discuss its details.
#include "WiFi.h" #include "HTTPClient.h" #include "ArduinoJson.h" #include "Adafruit_SSD1306.h" #define WIFI_SSID "YOURSSID" #define WIFI_PASSWORD "YOURPASSWORD" #define URL "http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&APPID=YOURAPIKEY&units=metric" HTTPClient http; Adafruit_SSD1306 oled(128, 64, &Wire, -1); StaticJsonDocument<4096> doc; void downloadWeather() { http.begin(URL); if (http.GET() > 0) { String json = http.getString(); auto err = deserializeJson(doc, json); if (err) { Serial.printf("Deserializion error: %s\n", err.f_str()); } } else { Serial.println("Could not get data!"); } http.end(); } void displayWeather() { const char* name = doc["name"]; const char* weather = doc["weather"][0]["main"]; float temp = doc["main"]["temp"]; int hum = doc["main"]["humidity"]; oled.clearDisplay(); oled.setCursor(0, 0); oled.printf(" %s\n", name); oled.printf(" %s\n", weather); oled.printf(" T: %.1f C\n", temp); oled.printf(" H: %d %%\n", hum); oled.display(); } void oled_init() { Wire.begin(19, 23); // sda, scl oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); oled.clearDisplay(); oled.setTextSize(2); oled.setTextColor(WHITE); } void wifi_init() { WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) delay(500); } void setup(void) { Serial.begin(115200); oled_init(); wifi_init(); } void loop() { downloadWeather(); displayWeather(); delay(10*60*1000); // 10min }
Libraries
The code starts by including the required libraries. WiFi.h
for WiFi access, HTTPClient.h
for downloading data via HTTP, ArduinoJson.h
to parse the downloaded data and Adafruit_SSD1306.h
to display the data on the OLED.
WiFi.h
and HTTPClient.h
are standard libraries but ArduinoJson.h
and Adafruit_SSD1306.h
you will have to install via the Library Manager.
Constants
Next we define some constants. Specifically our WiFi credentials and the URL to the OpenWeather API, where we download our weather data from. You can change the URL to get weather data for a different location or in a different unit, but don’t forget to use the your API-key.
#define WIFI_SSID "YOURSSID" #define WIFI_PASSWORD "YOURPASSWORD" #define URL "http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&APPID=YOURAPIKEY&units=metric"
Have a look at the API documentation for Current Weather, which shows you all the different ways you can construct a URL. Also check-out the Geocoding API, which simplifies the usage of locations.
Objects
For the remainder of the code we will need three objects. An HTTPClient
that allows us to download data from a website, a StaticJsonDocument
to store the deserialized data and an oled
object to display the data on the OLED.
HTTPClient http; Adafruit_SSD1306 oled(128, 64, &Wire, -1); StaticJsonDocument<4096> doc;
For the OLED you have to specify the dimensions, in my case this 128×64 pixels, and for the JSON document we need to reserve the maximum amount of memory we potentially need. I generously reserved 4kB (4069 Bytes), which is sufficient for the current weather data. If you download the weather forecast, which is bigger, you may need to reserve more space.
downloadWeather
The downloadWeather
function downloads the weather data from OpenWeather via http.GET()
, deserializes it and stores it in the StaticJsonDocument
via deserializeJson
().
void downloadWeather() { http.begin(URL); if (http.GET() > 0) { String json = http.getString(); auto err = deserializeJson(doc, json); if (err) { Serial.printf("Deserializion error: %s\n", err.f_str()); } } else { Serial.println("Could not get data!"); } http.end(); }
If anything goes wrong, we print an error message to the Serial Monitor.
displayWeather
To display the weather we first extract the four pieces of weather information from the doc
as described above. Then we clear the display, set the cursor and simply print the location name, the short description of the weather, the current temperature and the relative humidity.
void displayWeather() { const char* name = doc["name"]; const char* weather = doc["weather"][0]["main"]; float temp = doc["main"]["temp"]; int hum = doc["main"]["humidity"]; oled.clearDisplay(); oled.setCursor(0, 0); oled.printf(" %s\n", name); oled.printf(" %s\n", weather); oled.printf(" T: %.1f C\n", temp); oled.printf(" H: %d %%\n", hum); oled.display(); }
On the OLED the weather information will be displayed as shown in the picture below.
oledInit
Before we can use the OLED we have to initialize it. Most importantly, we need to specify the pins used for the I2C interface (SDA, SCL) and the I2C address of the display.
If you use the default I2C pins for your microcontroller, you don’t have to call Wire.begin(), otherwise you must.
The I2C address of the OLED I am using is 0x3C, which is common, but yours might have a different one. Have a look at the tutorial How to Interface the SSD1306 I2C OLED Graphic Display With Arduino, if you have difficulties.
void oled_init() { Wire.begin(19, 23); // sda, scl oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); oled.clearDisplay(); oled.setTextSize(2); oled.setTextColor(WHITE); }
Apart from that, we just clear the display and set the text size and color.
wifiInit
Similarly to the OLED we also need to initialize the WiFi connection. Make sure you are using the correct ssid and password, otherwise the WiFi client will not connect.
void wifi_init() { WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) delay(500); }
setup
With the helper functions above the setup
function has become very simple. We just initialize the serial communication, the OLED and the WiFi and that’s it.
void setup(void) { Serial.begin(115200); oled_init(); wifi_init(); }
loop
The loop function is also simple. We download the weather data, display it on the OLED and then wait for 10 minutes before repeating the process.
void loop() { downloadWeather(); displayWeather(); delay(10*60*1000); // 10min }
When implement and testing make sure that you have a sufficiently large delay when downloading the weather data. If you exceed more than 1000 calls per day you will be blocked for that day.
Otherwise, that’s it! Now you have a nice little internet weather station!
Conclusion
In this tutorial you learned how to build a internet weather station. While it is very basic, it is a great starting point for a much more advanced weather station.
Explore the other APIs OpenWeather provides and see what data you want to extract. The data returned by OpenWeather even includes icon ids you can use to display weather icons.
You may want to move on to a bigger display than the tiny OLED we used here. Some of the Elecrow displays with an integrated ESP32 and touch screen might be a good choice. See the CrowPanel 2.8″ ESP32 Display : Easy Setup Guide on how to use one of those.
In addition to the internet weather you could also collect local weather data using temperature, humidity and air quality sensors.
Finally, actively reacting to changing weather conditions and switching on sprinklers, closing windows or activating air-conditioner are fun projects.
Enjoy ; )
Stefan is a professional software developer and researcher. He has worked in robotics, bioinformatics, image/audio processing and education at Siemens, IBM and Google. He specializes in AI and machine learning and has a keen interest in DIY projects involving Arduino and 3D printing.