Learn how to plot sensor readings (temperature, humidity, and pressure) on a web server using the ESP32 or ESP8266 with Arduino IDE. The ESP will host a web page with three real time charts that have new readings added every 30 seconds.

Project Overview
In this tutorial we’ll build an asynchronous web server using the ESPAsyncWebServer library.
The HTML to build the web page will be stored on the ESP32 or ESP8266 Filesystem (SPIFFS). To learn more about building a web server using SPIFFS, you can refer to the next tutorials:
- ESP32 Web Server using SPIFFS (SPI Flash File System)
- ESP8266 Web Server using SPIFFS (SPI Flash File System)
We’ll display temperature, humidity and pressure readings from a BME280 sensor on a chart, but you can modify this project to display sensor readings from any other sensor. To learn more about the BME280, read our guides:
- ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)
- ESP8266 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)
To build the charts, we’ll use the Highcharts library. We’ll create three charts: temperature, humidity and pressure over time. The charts display a maximum of 40 data points, and a new reading is added every 30 seconds, but you change these values in your code.
Prerequisites
Make sure you check all the prerequisites in this section before continuing with the project in order to compile the code.
1. Install ESP Board in Arduino IDE
We’ll program the ESP32 and ESP8266 using Arduino IDE. So, you must have the ESP32 or ESP8266 add-on installed. Follow one of the next tutorials to install the ESP add-on:
- Installing ESP32 Board in Arduino IDE (Windows, Mac OS X, Linux)
- Installing ESP8266 Board in Arduino IDE (Windows, Mac OS X, Linux)
2. Filesystem Uploader Plugin
To upload the HTML file to the ESP32 and ESP8266 flash memory, we’ll use a plugin for Arduino IDE: Filesystem uploader. Follow one of the next tutorials to install the filesystem uploader depending on the board you’re using:
- ESP32: Install FileSystem Uploader Plugin in Arduino IDE
- ESP8266: Install FileSystem Uploader Plugin in Arduino IDE
3. Installing Libraries
To build the asynchronous web server, you need to install the following libraries.
- ESP32: you need to install the ESPAsyncWebServer and the AsyncTCP libraries.
- ESP8266: you need to install the ESPAsyncWebServer and the ESPAsyncTCP libraries.
These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation folder.
To get readings from the BME280 sensor module you need to have the next libraries installed:
You can install these libraries through the Arduino Library Manager.
Parts Required

To follow this tutorial you need the following parts:
- ESP32 or ESP8266 (read ESP32 vs ESP8266)
- BME280 sensor
- Breadboard
- Jumper wires
Schematic Diagram
The BME280 sensor module we’re using communicates via I2C communication protocol, so you need to connect it to the ESP32 or ESP8266 I2C pins.
BME280 wiring to ESP32
BME280 | ESP32 |
SCK (SCL Pin) | GPIO 22 |
SDI (SDA pin) | GPIO 21 |
So, assemble your circuit as shown in the next schematic diagram.

Recommended reading: ESP32 Pinout Reference Guide
BME280 wiring to ESP8266
BME280 | ESP8266 |
SCK (SCL Pin) | GPIO 5 |
SDI (SDA pin) | GPIO 4 |
Assemble your circuit as in the next schematic diagram if you’re using an ESP8266 board.

Recommended reading: ESP8266 Pinout Reference Guide
Organizing your Files
To build the web server you need two different files. The Arduino sketch and the HTML file. The HTML file should be saved inside a folder called data inside the Arduino sketch folder, as shown below:

Creating the HTML File
<!DOCTYPE HTML><html>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. -->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
https://code.highcharts.com/highcharts.js tags.
The following spinet creates the temperature chart. You define the chart id, you can set the title, the axis labels, etc…
var chartT = new Highcharts.Chart({
chart:{ renderTo : 'chart-temperature' },
title: { text: 'BME280 Temperature' },
series: [{
showInLegend: false,
data: []
}],
plotOptions: {
line: { animation: false,
dataLabels: { enabled: true }
},
series: { color: '#059e8a' }
},
xAxis: { type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: { text: 'Temperature (Celsius)' }
//title: { text: 'Temperature (Fahrenheit)' }
},
credits: { enabled: false }
});
Then, the setInvertal() function adds points to the charts. Every 30 seconds it makes a request to the /temperature URL to get the temperature readings from your ESP32 or ESP8266.
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = (new Date()).getTime(),
y = parseFloat(this.responseText);
//console.log(this.responseText);
if(chartT.series[0].data.length > 40) {
chartT.series[0].addPoint([x, y], true, true, true);
} else {
chartT.series[0].addPoint([x, y], true, false, true);
}
}
};
xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 30000 ) ;
The other graphics are created in a similar way. We make a request on the /humidity and /pressure URLs to get the humidity and pressure readings, respectively.
In the Arduino sketch, we should handle what happens when we receive those requests: we should send the corresponding sensor readings.
Arduino Sketch
. Then, you need to type your network credentials (SSID and password) to make it work.
/*********
Gnd_To_Vcc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
// Import required libraries
#ifdef ESP32
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#else
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#endif
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/
Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
String readBME280Temperature() {
// Read temperature as Celsius (the default)
float t = bme.readTemperature();
// Convert temperature to Fahrenheit
//t = 1.8 * t + 32;
if (isnan(t)) {
Serial.println("Failed to read from BME280 sensor!");
return "";
}
else {
Serial.println(t);
return String(t);
}
}
String readBME280Humidity() {
float h = bme.readHumidity();
if (isnan(h)) {
Serial.println("Failed to read from BME280 sensor!");
return "";
}
else {
Serial.println(h);
return String(h);
}
}
String readBME280Pressure() {
float p = bme.readPressure() / 100.0F;
if (isnan(p)) {
Serial.println("Failed to read from BME280 sensor!");
return "";
}
else {
Serial.println(p);
return String(p);
}
}
void setup(){
// Serial port for debugging purposes
Serial.begin(115200);
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
// Initialize SPIFFS
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP32 Local IP Address
Serial.println(WiFi.localIP());
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html");
});
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", readBME280Temperature().c_str());
});
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", readBME280Humidity().c_str());
});
server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", readBME280Pressure().c_str());
});
// Start server
server.begin();
}
void loop(){
}
How the code works
Let’s take a quick look at the code and see how it works.
Including libraries
First, include the necessary libraries. You include different libraries depending on the board you’re using. If you’re using an ESP32, the code loads the following libraries:
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
If you’re using an ESP8266, the code loads these libraries:
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
Create an instance to communicate with the BME280 sensor using I2C:
Adafruit_BME280 bme; // I2C
Insert your network credentials in the following variables:
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Create AsyncWebServer object on port 80:
AsyncWebServer server(80);
Read Temperature, Humidity and Pressure
Then, we create three functions readBME280Temperature(), readBME280Humidity() and readBME280Pressure(). These functions request the temperature, humidity and pressure from the BME280 sensor and return the readings as a String type.
String readBME280Temperature() {
// Read temperature as Celsius (the default)
float t = bme.readTemperature();
// Convert temperature to Fahrenheit
//t = 1.8 * t + 32;
if (isnan(t)) {
Serial.println("Failed to read from BME280 sensor!");
return "";
}
else {
Serial.println(t);
return String(t);
}
}
Init BME280
In the setup(), initialize the sensor:
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
Init SPIFFS
Initialize the filesystem (SPIFFS):
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
Connect to Wi-Fi
Connect to Wi-Fi and print the IP address in the Serial Monitor:
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP32 Local IP Address
Serial.println(WiFi.localIP());
Handle requests
Then, we need to handle what happens when the ESP receives a request.
When it receives a request on the root URL, we send the HTML text that is saved in SPIFFS under the index.html name:
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html");
});
When we receive a request on the /temperature, /humidity or /pressure URLs, call the functions that return the sensor readings.
For example, if we receive a request on the /temperature URL, we call the readBME280Temperature() function that returns the temperature.
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", readBME280Temperature().c_str());
});
The same happens for the other readings.
Finally, start the server:
server.begin();
Because this is an asynchronous web server we don’t need to write anything in the loop().
void loop(){
}
Uploading Code and Files
Save the code as ESP_Chart_Web_Server . Go to Sketch > Show Sketch Folder, and create a folder called data. Inside that folder you should save the HTML file created previously.
Now you need to upload the HTML file to the ESP32 or ESP8266 filesystem. Go to Tools > ESP32/ESP8266 Data Sketch Upload and wait for the files to be uploaded.

Then, upload the code to your board. Make sure you have the right board and COM port selected. Also, make sure you’ve inserted your networks credentials in the code.

When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the board “EN/RST” button, and it should print its IP address.

Demonstration
Open a browser on your local network and type the ESP32 or ESP8266 IP address. You should see three charts. A new data point is added every 30 seconds to a total of 40 points. New data keeps being displayed on the charts as long as you have your web browser tab open.
Here is an example of the humidity chart:

You can select each point to see the exact timestamp.

Wrapping Up
In this tutorial you’ve learned how to create charts to display data in your web server. You can modify this project to create as many charts as you want and using any other sensors.
Next, we recommend building a project that displays charts from data stored on your database. Here’s other tutorials that you might like:
- Visualize Your ESP32/ESP8266 Sensor Readings from Anywhere in the World
- ESP32/ESP8266 Insert Data into MySQL Database using PHP and Arduino IDE
Thank you for reading.
Hi, thank you for sharing the idea and code. However I am not able to create the index.html file. Seems like there is something missing. Or am I wrong?
LikeLike
Please, can you publish the index.html file?
LikeLike