Embedded System Project 9: Mini Weather Station

Fernaldi Fauzie
8 min readApr 1, 2021

Hi! Before, we already made an ESP32 web server to turn on/off LEDs. Now, we will try to create a web server with the ESP32 to display readings from the BMP280 sensor module. As we know from previous projects, the BMP280 sensor can be used to measures temperature, pressure, and approximate altitude. In other words, we can easily build a mini weather station and monitor the measurements using the ESP32 web server.

My Mini Weather Station

As usual, we will use Arduino IDE to compile and upload the code to the ESP32. For this story/project, I’m using this randomnerdtutorials post as a reference.

Parts and Components Required:

  • ESP32 Development Board
  • BMP280 sensor module
  • Breadboard
  • Jumper wires
  • Any device that as a browser on the local network

Additional Information

Before we continue, make sure that you already installed Adafruit Unified Sensor from Library Manager in Arduino IDE. I already have mine installed because I need it for my previous project. To access the Library Manager, go to Sketch > Include Library > Manage Libraries.

Library Manager in Arduino IDE

Oh, right! If you guys are still not familiar with external sensors especially BMP280, I recommend checking my previous project about BMP280! Also, if you guys want to know more about I2C, consider visiting my previous project about OLED displays! There are some brief explanations about I2C.

Schematic Diagram

We will use the same schematic diagram with my project 4 which is about the external sensor (BMP280), which means, ESP32 3v3 connected to BMP280 VCC, ESP32 GND connected to BMP280 GND, GPIO 21 connected to BMP280 SCL, and GPIO22 connected to BMP280 SDA.

Schematic diagram from https://www.circuitschools.com/interfacing-bmp280-with-esp-32-on-i2c-with-errors-and-solutions/

Although I will use BMP280, BMP180 also can work.

Schematic Diagram Version 2

So I tried to make my own schematic diagram. Unfortunately, I couldn’t find the exact sensor I used which is BMP280 that has 6 pins (as there are only 4 pins in the schematic diagram). But there is no serious matter with it because in general, they are the same as I only use 4 from 6 pins which all of them are available in that sensor in the schematic diagram.

Table in HTML

Because we will try to display BMP280 sensor readings on a web page, it’s better to make the table in HTML to make it more comfortable to be seen. In short, these are the way to make the table in HTML:

  • To create table, use <table> and </table> tags
  • To create table row, use <tr> and </tr> tags
  • To create the table heading, use <th> and </th> tags
  • To create table cell, use <tc> and </tc> tags

To create a table for our readings with BMP280, we can use the following HTML code:

<table>
<tr>
<th>MEASUREMENT</th>
<th>VALUE</th>
</tr>
<tr>
<td>Temp. Celsius</td>
<td>--- *C</td>
</tr>
<tr>
<td>Temp. Fahrenheit</td>
<td>--- *F</td>
</tr>
<tr>
<td>Pressure</td>
<td>--- hPa</td>
</tr>
<tr>
<td>Approx. Altitude</td>
<td>--- meters</td></tr>
</table>

For now, this is how it looked like. But don’t worry! We will make it better!

Arduino IDE Code

Now, you may use the following code to get the sensor readings, but as usual, don’t forget to change the SSID and password before uploading! This code references Rui Santos, but I modified it because I used the BMP280 sensor module instead of the BME280 sensor module. I also modified the web server style.

/*********
This code references Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/
// Load Wi-Fi library
#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_Sensor.h>
#define SEALEVELPRESSURE_HPA (1013.25)Adafruit_BMP280 bmp; // I2C
//Adafruit_BMP280 bmp(BMP_CS); // hardware SPI
//Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO, BMP_SCK);
// Replace with your network credentials
const char* ssid = "FF";
const char* password = "anrq8199";
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request
String header;
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
//status = bmp.begin();
if (!bmp.begin(0x76)) {
Serial.println("Could not find a valid BMP280 sensor, check wiring!");
while (1);
}
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
currentTime = millis();
previousTime = currentTime;
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();

// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the table
client.println("<style>body { text-align: center; font-family: \"courier\", Arial;}");
client.println("table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }");
client.println("th { padding: 12px; background-color: #00c767; color: white; }");
client.println("tr { border: 1px solid #ddd; padding: 12px; }");
client.println("tr:hover { background-color: #bcbcbc; }");
client.println("td { border: none; padding: 12px; }");
client.println(".sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px; }");
client.println("table, td, th {border: 1px solid black;}");

// Web Page Heading
client.println("</style></head><body><h1>ESP32 with BMP280</h1>");
client.println("<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>");
client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
client.println(bmp.readTemperature());
client.println(" *C</span></td></tr>");
client.println("<tr><td>Temp. Fahrenheit</td><td><span class=\"sensor\">");
client.println(1.8 * bmp.readTemperature() + 32);
client.println(" *F</span></td></tr>");
client.println("<tr><td>Pressure</td><td><span class=\"sensor\">");
client.println(bmp.readPressure() / 100.0F);
client.println(" hPa</span></td></tr>");
client.println("<tr><td>Approx. Altitude</td><td><span class=\"sensor\">");
client.println(bmp.readAltitude(SEALEVELPRESSURE_HPA));
client.println(" m</span></td></tr>");
client.println("</body></html>");

// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}

N.B. For this code, I used the average sea level pressure which is 1013.25, because I was having a hard time finding my average sea level pressure in my location, probably because I don’t live in the big city.

Result?

This is how the web server looked like:

Then, I tried to keep refreshing the web server (with Ctrl + R) while pressing the BMP280 sensor. Because the value of sensor readings is changing, it seems like I succeed!

My ESP32 Web Server to Show BMP280 Readings
Bonus GIF (Careful, might cause dizziness)

Code Explanation?

Alright! As usual, I would like to point out some important parts of the code! I will not explain some part of the code that had been explained before though, so make sure to check my previous project especially about the ESP32 web server!

In the setup(), we start a serial communication at a baud rate of 115200 for debugging purposes with the Serial.begin() function.

Serial.begin(115200);

In the loop(), the following code snippet sends the web page to display the BMP280 sensor readings in a table. In Arduino IDE, we use client.println() to send the web page to the client.

// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the table
client.println("<style>body { text-align: center; font-family: \"courier\", Arial;}");
client.println("table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }");
client.println("th { padding: 12px; background-color: #00c767; color: white; }");
client.println("tr { border: 1px solid #ddd; padding: 12px; }");
client.println("tr:hover { background-color: #bcbcbc; }");
client.println("td { border: none; padding: 12px; }");
client.println(".sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px; }");
client.println("table, td, th {border: 1px solid black;}");

// Web Page Heading
client.println("</style></head><body><h1>ESP32 with BMP280</h1>");
client.println("<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>");
client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
client.println(bmp.readTemperature());
client.println(" *C</span></td></tr>");
client.println("<tr><td>Temp. Fahrenheit</td><td><span class=\"sensor\">");
client.println(1.8 * bmp.readTemperature() + 32);
client.println(" *F</span></td></tr>");
client.println("<tr><td>Pressure</td><td><span class=\"sensor\">");
client.println(bmp.readPressure() / 100.0F);
client.println(" hPa</span></td></tr>");
client.println("<tr><td>Approx. Altitude</td><td><span class=\"sensor\">");
client.println(bmp.readAltitude(SEALEVELPRESSURE_HPA));
client.println(" m</span></td></tr>");
client.println("</body></html>");

Keep in mind that to display the sensor readings on the table, we just need to send them between the corresponding <td> and </td> tags. For example, to display the temperature in Celcius:

client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
client.println(bmp.readTemperature());
client.println(" *C</span></td></tr>");

Conclusion

In summary, we already learn about how to read temperature, pressure, and estimate altitude using the BMP280 sensor module. Then, we also learned how to build a web server that displays a table with sensor readings. Of course, this project can also be used to display data from other sensors easily.

I hope that this project can bring you guys inspiration and excitement to keep learning technology, especially about microprocessors. See you guys in the next project and don’t forget to have fun!

--

--