Building a Connected Arduino-Based Weather Station
2020-12-16 | By Maker.io Staff
License: See Original Project
This article will explain how to build a simple (yet fully functional and feature-rich) weather station for your home, using only a connected Arduino compatible board and a sensor. When the project is done, users can then conveniently use any computer or other device in their home network to check the weather forecast and current sensor readings.
BOM
Adafruit Feather Huzzah ESP8266
Setting up the Arduino IDE
Before connecting any external components, make sure that the Feather board can find the home Wi-Fi network and establish a connection. For that, first install the board in the Arduino IDE. Start by opening up the IDE’s preferences window:
(Image source: Screenshot taken by the author.)
In the preferences window, add the following link (http://arduino.esp8266.com/stable/package_esp8266com_index.json) to the Additional Board Manager URLs.
(Image source: Screenshot taken by the author.)
If there are already other URLs here, separate URLs with commas. Once done, click ‘OK’ and open the board manager in the Arduino IDE’s main window.
(Image source: Screenshot taken by the author.)
In the board manager, search for ‘ESP8266’ and install the following package:
(Image source: Screenshot taken by the author.)
Wait for the installation process to finish. Once it’s done, configure the Huzzah board as follows:
(Image source: Screenshot taken by the author.)
Make sure to select the correct port, too. Note that it’s possible to use any ESP8266 board for this project.
Connecting the ESP8266 to a Wi-Fi network
It’s now time to connect the ESP8266 to a Wi-Fi network! For that, use the ESP8266WiFi library that was installed in the last step. The library has standard functions for establishing a connection to a wireless network. Therefore, the code to get your Huzzah board up and running is quite short:
#include <ESP8266WiFi.h>
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
void setup()
{
Serial.begin(9600);
Serial.print("Connecting");
// Connect to your local network
WiFi.begin(ssid, password);
// Wait until the connection got established
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(250);
}
Serial.println("");
Serial.print("Connected to network: ");
Serial.println(ssid);
}
void loop()
{}
When uploading this sketch, users should see something similar to this in the serial monitor:
(Image source: Screenshot taken by the author.)
If the board can’t establish a connection, check whether you have entered the SSID and password correctly. If you can’t upload the code, look at the Huzzah’s manual. Note that uploading code to this board can take quite a bit longer than sending a sketch to a regular Arduino.
Connecting the BMP280 Sensor
Now that code can be uploaded from the Arduino IDE to the ESP8266, it’s time to connect the sensor breakout board to the Feather Huzzah. As mentioned earlier, this sensor uses I2C to communicate with the MCU. As a result, there are only four connections needed:
Learn more with the Digi-Key Scheme-It link here.
Note that you can use either serial communication or I2C to interface with this particular breakout board. Consult the datasheet for further details.
Next, you must download two libraries from the official Adafruit GitHub repository and import them in the Arduino IDE:
To download them, click the green ‘Clone or download’ button and download the libraries as zip archives.
(Image source: Screenshot taken by the author.)
Then, extract both folders and move them to your Arduino IDE’s libraries folder.
(Image source: Screenshot taken by the author.)
This allows the libraries to communicate with the sensor. The minimal example below can be used to test whether your sensor works as expected:
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
const float altitude = 175.0f;
Adafruit_BME280 sensor;
void setup()
{
Serial.begin(9600);
// Initialize the sensor
// You might have to change the I2C address
// In my case, the address is 0x76
// Consult your sensor's datasheet for further details
bool e = !sensor.begin(0x76);
if(e)
Serial.println("Sensor error!");
else
{
Serial.print("Temperature: ");
Serial.println(readSensor(0));
Serial.print("Humidity: ");
Serial.println(readSensor(1));
Serial.print("Atmospheric Pressure: ");
Serial.println(readSensor(2));
}
}
float readSensor(int value)
{
if(value == 0)
return sensor.readTemperature();
else if(value == 1)
return sensor.readHumidity();
else
{
float p = sensor.seaLevelForAltitude(altitude, sensor.readPressure());
return (p * 0.01f);
}
}
void loop()
{}
When uploading and running the program, the serial monitor should display some legitimate values:
(Image source: Screenshot taken by the author.)
Note that the temperature is in degrees Celsius by default. To convert to Fahrenheit, use a custom function:
float celsiusToFahrenheit(float c)
{
return (c * 9 / 5) 32;
}
Hosting a Web Server on the Feather Huzzah Board
This project uses the WiFiServer class of the previously installed ESP8266 library to create a TCP socket that listens on port 80 of the ESP8266. A client can send a request to the Feather Huzzah board and it will respond with some data. In this case, that’s a simple HTML website.
#include <ESP8266WiFi.h>
const char* ssid = "YOUR_SSID";
const char* password = "PASSWORD";
WiFiServer server(80);
void setup()
{
// Start the serial connection
Serial.begin(9600);
Serial.print("Connecting");
// Connect to your local network
WiFi.begin(ssid, password);
// Wait until the connection got established
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(250);
}
Serial.println("");
Serial.print("Connected to network: ");
Serial.println(ssid);
// Start the server
// A client will be able connect to this server
server.begin();
Serial.print("Server started with address: ");
Serial.print("http://");
Serial.print(WiFi.localIP());
Serial.println("/");
}
void loop()
{
// Check for incoming connections
WiFiClient client = server.available();
// Only continue if a client made a request
if (!client)
return;
// Wait for the client to send data
while(!client.available())
delay(5);
// Read the first line of the HTTP request
// which will contain something like
// METHOD /requested_url HTTP_VERSION
// However, for the sake of simplicity this device will
// respond to GET requests so that they can be sent with
// any web browser.
String request = client.readStringUntil('\r');
client.flush();
// Return a response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
// The HTTP response body is separated from the header by an empty line
// (actually a line containing \r\n, but this will work)
client.println("");
// Return the response body (an html page)
client.println("<!DOCTYPE html>");
client.println("<html>");
client.println("<head>");
client.println("<title>Adafruit Feather Huzzah</title>");
client.println("</head>");
client.println("<body style='padding:10px;'>");
client.println("<h1>Hello world!</h1>");
client.println("This is a self-hosted webserver on an Adafruit Feather Huzzah!");
client.println("</body>");
}
The comments above take up most of the space in this simple code example. The setup method essentially does the same thing as before.
However, there is an additional single call that starts the server. In the loop, the code checks whether there are any unfulfilled client requests — this could be a user visiting the website in a web browser. If there are, the code parses them. The server then sends some data back to the client, which is a simple HTML website in this case:
(Image source: Screenshot taken by the author.)
Bringing it All Together for the Weather Station
With these two key principles - reading the values from a sensor and serving a website with static content - it’s easy to imagine combining the two to serve a website that displays the measured values.
(Image source: Screenshot taken by the author.)
Look at the download here to see a JavaScript code that renders the temperature history graph on the website.
Getting the Weather Forecast Data
Another application for this project, however, involves fetching the weather forecast data for your region and processing it to display it on the website. A free plan of the OpenWeatherMap API lets you request a JSON object that contains the weather forecast for that day.
To access the information, users will need an API key to be conveniently stored in a variable in the Arduino sketch:
const char* api_key = "4c28b...";
A library called ArduinoJson parses the information that the API delivers as a JSON encoded string.
(Image source: Screenshot taken by the author.)
Once done, all of this can be conveniently imported. Users who haven’t worked with this library, or JSON in general, should take a look at the library’s magnificent documentation. It discusses everything you need to know, and it also offers a handy tool to generate code. This tool allowed for the creation of the following method for fetching and parsing the weather forecast:
#include <ArduinoJson.h>
/* Other variables */
const char* api_key = "4c28b...";
// Longitude and Latitude will be used for retrieving the correct weather forecast
const float longitude = 16.373819f;
const float latitude = 48.208176f;
const char* city = "Vienna";
// Change between °C and °F
bool metric = true;
// The most recent weather forecast values
String forecastIconURL;
String forecastData;
void setup()
{ /* … */ }
void loop()
{ /* … */ }
void readForecastData()
{
// Skip this method if a sensor error occurred earlier
if(error == 0)
{
// Build the URL for requesting the forecast data
/* Omitted in this example */
String url =”...”;
Serial.print("Fetching weather forecast data from: ");
Serial.println(url);
if (WiFi.status() == WL_CONNECTED)
{
// Here, the Arduino acts as an HTTP Client and requests the data as JSON
HTTPClient http;
http.useHTTP10(true);
http.begin(url);
int res = http.GET();
// The request was correctly fulfilled
if (res > 0)
{
// Get the request response payload
String payload = http.getString();
// Calculate the necessary dynamic memory size and create a JSON document with that size
const size_t capacity = JSON_ARRAY_SIZE(1) JSON_OBJECT_SIZE(1) 2*JSON_OBJECT_SIZE(2) JSON_OBJECT_SIZE(4) JSON_OBJECT_SIZE(5) JSON_OBJECT_SIZE(6) JSON_OBJECT_SIZE(13) 290;
DynamicJsonDocument doc(capacity);
// Parse the JSON
DeserializationError err = deserializeJson(doc, payload);
// No parsing errors occurred
if(err == DeserializationError::Ok)
{
JsonObject weather_0 = doc["weather"][0];
JsonObject main = doc["main"];
JsonObject sys = doc["sys"];
// Read the values of certain fields from the JSON document
const char* weather_0_main = weather_0["main"];
const char* weather_0_icon = weather_0["icon"];
const char* weather_0_description = weather_0["description"];
const char* sys_country = sys["country"];
float main_temp_min = main["temp_min"];
float main_temp_max = main["temp_max"];
float main_temp = main["temp"];
float main_feels_like = main["feels_like"];
int main_pressure = main["pressure"];
int main_humidity = main["humidity"];
// Create the forecast strings
/* Omitted in this example */
}
else
{
Serial.println("Error reading weather data!");
}
}
// Close the connection
http.end();
}
else
{
Serial.println("Error fetching weather forecast data! WiFi connection closed!");
}
}
}
Where to take it from here
When you upload this code to the Feather board, you'll end up with a fully functional weather station which can measure the current temperature, humidity, and atmospheric pressure. Furthermore, it fetches a detailed weather forecast for that day. Some other ideas include adding an LCD screen and putting everything in a waterproof case together with a rechargeable battery. You could also add event warnings and send out emails, for example, before a major storm.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum