This project shows how to build a web server with the ESP8266 NodeMCU board to plot sensor readings in charts with multiple series. As an example, we’ll plot sensor readings from four different DS18B20 temperature sensors on the same chart. You can modify the project to plot any other data. To build the charts, we’ll use the Highcharts JavaScript library.
We have a similar tutorial for the ESP32 board:
Project Overview
This project will build a web server with the ESP8266 NodeMCU board that displays temperature readings from four DS18B20 temperature sensors on the same chart—chart with multiple series. The chart displays a maximum of 40 data points for each series, and new readings are added every 30 seconds. You can change these values in your code.
DS18B20 Temperature Sensor
The DS18B20 temperature sensor is a one-wire digital temperature sensor. This means that it just requires one data line to communicate with your microcontroller.
Each sensor has a unique 64-bit serial number, which means you can connect multiple sensors to the same GPIO—as we’ll do in this tutorial. Learn more about the DS18B20 temperature sensor:
Server-Sent Events
The readings are updated automatically on the web page using Server-Sent Events (SSE).
To learn more about SSE with the ESP8266, you can read:
Files Saved on the Filesystem (LittleFS)
To keep our project better organized and easier to understand, we’ll save the HTML, CSS, and JavaScript files to build the web page on the board’s filesystem (LittleFS).
Learn more about using LittleFS with the ESP8266, you can read:
Prerequisites
Make sure you check all the prerequisites in this section before continuing with the project.
1. Install ESP8266 Board in Arduino IDE
We’ll program the ESP8266 using Arduino IDE. So, you must have the ESP8266 add-on installed. Follow the next tutorial if you haven’t already:
If you want to use VS Code with the PlatformIO extension, follow the next tutorial instead to learn how to program the ESP8266:
2. Filesystem Uploader Plugin
To upload the HTML, CSS, and JavaScript files to the ESP8266 flash memory (LittleFS), we’ll use a plugin for Arduino IDE: LittleFS Filesystem uploader. Follow the next tutorial to install the filesystem uploader plugin:
If you’re using VS Code with the PlatformIO extension, read the following tutorial to learn how to upload files to the filesystem:
3. Installing Libraries
To build this project, you need to install the following libraries:
- OneWire (by Paul Stoffregen) (Arduino Library Manager);
- DallasTemperature (Arduino Library Manager);
- Arduino_JSON library by Arduino version 0.1.0 (Arduino Library Manager)
- ESPAsyncWebServer (.zip folder);
- ESPAsyncTCP (.zip folder).
You can install the first two libraries using the Arduino Library Manager. Go to Sketch > Include Library > Manage Libraries and search for the library name.
The ESPAsyncWebServer and AsynTCP libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch > Include Library > Add .zip Library and select the libraries you’ve just downloaded.
Installing Libraries (VS Code + PlatformIO)
If you’re programming the ESP8266 using PlatformIO, you should add the following lines on the platformio.ini file to include the libraries (also change the Serial Monitor speed to 115200):
monitor_speed = 115200
lib_deps = ESP Async WebServer
arduino-libraries/Arduino_JSON @ 0.1.0
milesburton/DallasTemperature@^3.9.1
paulstoffregen/OneWire@^2.3.5
Parts Required
To follow this tutorial you need the following parts:
- ESP8266 (read Best ESP8266 development boards)
- 4x DS18B20 temperature sensor (one or multiple sensors) – waterproof version
- 4.7k Ohm resistor
- Jumper wires
- Breadboard
If you don’t have four DS18B20 sensors, you can use three or two. Alternatively, you can also use other sensors (you need to modify the code) or data from any other source (for example, sensor readings received via MQTT, ESP-NOW, or random values—to experiment with this project…)
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Schematic Diagram
Wire four DS18B20 sensors to your board.
Recommended reading: ESP8266 Pinout Reference: Which GPIO pins should you use?
Getting the DS18B20 Sensors’ Addresses
Each DS18B20 temperature sensor has an assigned serial number. First, you need to find that number to label each sensor accordingly. You need to do this so that later you know from which sensor you’re reading the temperature.
Upload the following code to the ESP8266. Make sure you have the right board and COM port selected.
/*
* Rui Santos
* Complete Project Details https://randomnerdtutorials.com
*/
#include <OneWire.h>
// Based on the OneWire library example
OneWire ds(4); //data wire connected to GPIO 4
void setup(void) {
Serial.begin(115200);
}
void loop(void) {
byte i;
byte addr[8];
if (!ds.search(addr)) {
Serial.println(" No more addresses.");
Serial.println();
ds.reset_search();
delay(250);
return;
}
Serial.print(" ROM =");
for (i = 0; i < 8; i++) {
Serial.write(' ');
Serial.print(addr[i], HEX);
}
}
Wire just one sensor at a time to find its address (or successively add a new sensor) so that you’re able to identify each one by its address. Then, you can add a physical label to each sensor.
Open the Serial Monitor at a baud rate of 115200, press the on-board RST/EN button and you should get something as follows (but with different addresses):
Untick the “Autoscroll” option so that you’re able to copy the addresses. In our case, we’ve got the following addresses:
- Sensor 1: 28 FF A0 11 33 17 3 96
- Sensor 2: 28 FF B4 6 33 17 3 4B
- Sensor 3: 28 FF 11 28 33 18 1 6B
- Sensor 4: 28 FF 43 F5 32 18 2 A8
Organizing Your Files
To keep the project organized and make it easier to understand, we’ll create four files to build the web server:
- Arduino sketch that handles the web server;
- index.html: to define the content of the web page;
- sytle.css: to style the web page;
- script.js: to program the behavior of the web page—handle web server responses, events, create the chart, etc.
You should save the HTML, CSS, and JavaScript files inside a folder called data inside the Arduino sketch folder, as shown in the previous diagram. We’ll upload these files to the ESP8266 filesystem (LittleFS).
You can download all project files:
HTML File
Copy the following to the index.html file.
<!-- Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-plot-readings-charts-multiple/ -->
<!DOCTYPE html>
<html>
<head>
<title>ESP IOT DASHBOARD</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://code.highcharts.com/highcharts.js"></script>
</head>
<body>
<div class="topnav">
<h1>ESP WEB SERVER CHARTS</h1>
</div>
<div class="content">
<div class="card-grid">
<div class="card">
<p class="card-title">Temperature Chart</p>
<div id="chart-temperature" class="chart-container"></div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
The HTML file for this project is very simple. It includes the JavaScript Highcharts library in the head of the HTML file:
<script src="https://code.highcharts.com/highcharts.js"></script>
There is a <div> section with the id chart-temperature where we’ll render our chart later on.
<div id="chart-temperature" class="chart-container"></div>
CSS File
Copy the following styles to your style.css file.
/* Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-plot-readings-charts-multiple/ */
html {
font-family: Arial, Helvetica, sans-serif;
display: inline-block;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
p {
font-size: 1.4rem;
}
.topnav {
overflow: hidden;
background-color: #0A1128;
}
body {
margin: 0;
}
.content {
padding: 5%;
}
.card-grid {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.card {
background-color: white;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
}
.card-title {
font-size: 1.2rem;
font-weight: bold;
color: #034078
}
.chart-container {
padding-right: 5%;
padding-left: 5%;
}
JavaScript File (creating the charts)
Copy the following to the script.js file. Here’s a list of what this code does:
- initializing the event source protocol;
- adding an event listener for the new_readings event;
- creating the chart;
- getting the latest sensor readings from the new_readings event and plot them in the chart;
- making an HTTP GET request for the current sensor readings when you access the web page for the first time.
// Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-plot-readings-charts-multiple/
// Get current sensor readings when the page loads
window.addEventListener('load', getReadings);
// Create Temperature Chart
var chartT = new Highcharts.Chart({
chart:{
renderTo:'chart-temperature'
},
series: [
{
name: 'Temperature #1',
type: 'line',
color: '#101D42',
marker: {
symbol: 'circle',
radius: 3,
fillColor: '#101D42',
}
},
{
name: 'Temperature #2',
type: 'line',
color: '#00A6A6',
marker: {
symbol: 'square',
radius: 3,
fillColor: '#00A6A6',
}
},
{
name: 'Temperature #3',
type: 'line',
color: '#8B2635',
marker: {
symbol: 'triangle',
radius: 3,
fillColor: '#8B2635',
}
},
{
name: 'Temperature #4',
type: 'line',
color: '#71B48D',
marker: {
symbol: 'triangle-down',
radius: 3,
fillColor: '#71B48D',
}
},
],
title: {
text: undefined
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: {
text: 'Temperature Celsius Degrees'
}
},
credits: {
enabled: false
}
});
//Plot temperature in the temperature chart
function plotTemperature(jsonValue) {
var keys = Object.keys(jsonValue);
console.log(keys);
console.log(keys.length);
for (var i = 0; i < keys.length; i++){
var x = (new Date()).getTime();
console.log(x);
const key = keys[i];
var y = Number(jsonValue[key]);
console.log(y);
if(chartT.series[i].data.length > 40) {
chartT.series[i].addPoint([x, y], true, true, true);
} else {
chartT.series[i].addPoint([x, y], true, false, true);
}
}
}
// Function to get current readings on the webpage when it loads for the first time
function getReadings(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(this.responseText);
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
if (!!window.EventSource) {
var source = new EventSource('/events');
source.addEventListener('open', function(e) {
console.log("Events Connected");
}, false);
source.addEventListener('error', function(e) {
if (e.target.readyState != EventSource.OPEN) {
console.log("Events Disconnected");
}
}, false);
source.addEventListener('message', function(e) {
console.log("message", e.data);
}, false);
source.addEventListener('new_readings', function(e) {
console.log("new_readings", e.data);
var myObj = JSON.parse(e.data);
console.log(myObj);
plotTemperature(myObj);
}, false);
}
Get Readings
When you access the web page for the first time, we’ll request the server to get the current sensor readings. Otherwise, we would have to wait for new sensor readings to arrive (via Server-Sent Events), which can take some time depending on the interval that you set on the server.
Add an event listener that calls the getReadings function when the web page loads.
// Get current sensor readings when the page loads
window.addEventListener('load', getReadings);
The window object represents an open window in a browser. The addEventListener() method sets up a function to be called when a certain event happens. In this case, we’ll call the getReadings function when the page loads (‘load’) to get the current sensor readings.
Now, let’s take a look at the getReadings function. We’ll send an HTTP request so that the ESP knows it needs to send new readings. When the ESP receives this request, it will reset the timer to send a new event with the readings.
Create a new XMLHttpRequest object. Then, send a GET request to the server on the /readings URL using the open() and send() methods.
function getReadings() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/readings", true);
xhr.send();
}
When we send that request, the ESP will send a response. So, we need to handle what happens when we receive the response. We’ll use the onreadystatechange property that defines a function to be executed when the readyState property changes. The readyState property holds the status of the XMLHttpRequest. The response of the request is ready when the readyState is 4, and the status is 200.
- readyState = 4 means that the request finished and the response is ready;
- status = 200 means “OK”
In our case, we’ll just log the ESP response (an “OK” message). So, the request should look something like this:
function getReadings(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(this.responseText);
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
Creating the Chart
The following lines create the charts with multiple series.
// Create Temperature Chart
var chartT = new Highcharts.Chart({
chart:{
renderTo:'chart-temperature'
},
series: [
{
name: 'Temperature #1',
type: 'line',
color: '#101D42',
marker: {
symbol: 'circle',
radius: 3,
fillColor: '#101D42',
}
},
{
name: 'Temperature #2',
type: 'line',
color: '#00A6A6',
marker: {
symbol: 'square',
radius: 3,
fillColor: '#00A6A6',
}
},
{
name: 'Temperature #3',
type: 'line',
color: '#8B2635',
marker: {
symbol: 'triangle',
radius: 3,
fillColor: '#8B2635',
}
},
{
name: 'Temperature #4',
type: 'line',
color: '#71B48D',
marker: {
symbol: 'triangle-down',
radius: 3,
fillColor: '#71B48D',
}
},
],
title: {
text: undefined
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: {
text: 'Temperature Celsius Degrees'
}
},
credits: {
enabled: false
}
});
To create a new chart, use the new Highcharts.Chart() method and pass as argument the chart properties.
var chartT = new Highcharts.Chart({
In the next line, define where you want to put the chart. In our example, we want to place it in the HTML element with the chart-temperature id—see the HTML file section.
chart:{
renderTo:'chart-temperature'
},
Then, define the options for the series. The following lines create the first series:
series: [
{
name: 'Temperature #1',
type: 'line',
color: '#101D42',
marker: {
symbol: 'circle',
radius: 3,
fillColor: '#101D42',
}
The name property defines the series name. The type property defines the type of chart—in this case, we want to build a line chart. The color refers to the color of the line—you can change it to whatever color you desire.
Next, define the marker properties. You can choose from several default symbols—square, circle, diamond, triangle, triangle-down. You can also create your own symbols. The radius refers to the size of the marker, and the fillColor refers to the color of the marker. There are other properties you can use to customize the marker—learn more.
marker: {
symbol: 'circle',
radius: 3,
fillColor: '#101D42',
}
Creating the other series is similar, but we’ve chosen different names, markers and colors.
There are many other options you can use to customize your series—check the documentation about plotOptions.
You can also define the chart title—in this case, as we’ve already defined a title for the chart in a heading of the HTML file, we will not set the title here. The title is displayed by default, so we must set it to undefined.
title: {
text: undefined
},
Define the properties for the X axis—this is the axis where we’ll display data and time. Check more options to customize the X axis.
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
We set the title for the y axis. See all available properties for the y axis.
yAxis: {
title: {
text: 'Temperature Celsius Degrees'
}
}
Time Zone
If, for some reason, after building the project, the charts are not showing the right time zone, add the following lines to the JavaScript file after the second line:
Highcharts.setOptions({
time: {
timezoneOffset: -60 //Add your time zone offset here in seconds
}
});
The charts will show the time in UTC. If you want it to display in your timezone, you must set the useUTC parameter (which is a time parameter) as false:
time:{
useUTC: false
},
So, add that when creating the chart as follows:
var chart = new Highcharts.Chart({
time:{
useUTC: false
},
(…)
To learn more about this property, check this link on the documentation: https://api.highcharts.com/highcharts/time.useUTC
Finally, set the credits option to false to hide the credits of the Highcharts library.
credits: {
enabled: false
}
Plot Temperatures
We’ve created the plotTemperature() function that accepts as an argument a JSON object with the temperature readings we want to plot.
//Plot temperature in the temperature chart
function plotTemperature(jsonValue) {
var keys = Object.keys(jsonValue);
console.log(keys);
console.log(keys.length);
for (var i = 0; i < keys.length; i++){
var x = (new Date()).getTime();
console.log(x);
const key = keys[i];
var y = Number(jsonValue[key]);
console.log(y);
if(chartT.series[i].data.length > 40) {
chartT.series[i].addPoint([x, y], true, true, true);
} else {
chartT.series[i].addPoint([x, y], true, false, true);
}
}
}
First, we get the keys of our JSON object and save them on the keys variable. This allows us to go through all the keys in the object.
var keys = Object.keys(jsonValue);
The keys variable will be an array with all the keys in the JSON object. In our case:
["sensor1", "sensor2", "sensor3", "sensor4"]
This works if you have a JSON object with a different number of keys or with different keys. Then, we’ll go through all the keys (keys.length()) to plot each of its value in the chart.
The x value for the chart is the timestamp.
var x = (new Date()).getTime()
The key variable holds the current key in the loop. The first time we go through the loop, the key variable is “sensor1”.
const key = keys[i];
Then, we get the value of the key (jsonValue[key]) and save it as a number in the y variable.
Our chart has multiple series (index starts at 0). We can access the first series in the
temperature chart using: chartT.series[0], which corresponds to chartT.series[i] the first time we go through the loop.
First, we check the series data length:
- If the series has more than 40 points: append and shift a new point;
- Or if the series has less than 40 points: append a new point.
To add a new point use the addPoint() method that accepts the following arguments:
- The value to be plotted. If it is a single number, a point with that y value is
appended to the series. If it is an array, it will be interpreted as x and y values. In our case, we pass an array with the x and y values; - Redraw option (boolean): set to true to redraw the chart after the point is added.
- Shift option (boolean): If true, a point is shifted off the start of the series as one is appended to the end. When the chart length is bigger than 40, we set the shift option to true.
- withEvent option (boolean): Used internally to fire the series addPoint event—learn more here.
So, to add a point to the chart, we use the next lines:
if(chartT.series[i].data.length > 40) {
chartT.series[i].addPoint([x, y], true, true, true);
} else {
chartT.series[i].addPoint([x, y], true, false, true);
}
Handle events
Plot the readings on the charts when the client receives the readings on the new_readings event
Create a new EventSource object and specify the URL of the page sending the updates. In our case, it’s /events.
if (!!window.EventSource) {
var source = new EventSource('/events');
Once you’ve instantiated an event source, you can start listening for messages from the server with addEventListener().
These are the default event listeners, as shown here in the AsyncWebServer documentation.
source.addEventListener('open', function(e) {
console.log("Events Connected");
}, false);
source.addEventListener('error', function(e) {
if (e.target.readyState != EventSource.OPEN) {
console.log("Events Disconnected");
}
}, false);
source.addEventListener('message', function(e) {
console.log("message", e.data);
}, false);
Then, add the event listener for new_readings.
source.addEventListener('new_readings', function(e) {
When new readings are available, the ESP8266 sends an event (new_readings) to the client. The following lines handle what happens when the browser receives that event.
source.addEventListener('new_readings', function(e) {
console.log("new_readings", e.data);
var myObj = JSON.parse(e.data);
console.log(myObj);
plotTemperature(myObj);
}, false);
Basically, print the new readings on the browser console, convert the data into a JSON object and plot the readings on the chart by calling the plotTemperature() function.
Arduino Sketch
Copy the following code to your Arduino IDE or to the main.cpp file if you’re using PlatformIO.
/*********
Rui Santos
Complete instructions at https://RandomNerdTutorials.com/esp8266-nodemcu-plot-readings-charts-multiple/
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.
*********/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <Arduino_JSON.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// 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);
// Create an Event Source on /events
AsyncEventSource events("/events");
// Json Variable to hold sensor readings
JSONVar readings;
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;
// GPIO where the DS18B20 sensors are connected to
const int oneWireBus = 4;
// Setup a oneWire instance to communicate with OneWire devices (DS18B20)
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
// Address of each sensor
DeviceAddress sensor3 = { 0x28, 0xFF, 0xA0, 0x11, 0x33, 0x17, 0x3, 0x96 };
DeviceAddress sensor1 = { 0x28, 0xFF, 0xB4, 0x6, 0x33, 0x17, 0x3, 0x4B };
DeviceAddress sensor2 = { 0x28, 0xFF, 0x43, 0xF5, 0x32, 0x18, 0x2, 0xA8 };
DeviceAddress sensor4 = { 0x28, 0xFF, 0x11, 0x28, 0x33, 0x18, 0x1, 0x6B };
// Get Sensor Readings and return JSON object
String getSensorReadings(){
sensors.requestTemperatures();
readings["sensor1"] = String(sensors.getTempC(sensor1));
readings["sensor2"] = String(sensors.getTempC(sensor2));
readings["sensor3"] = String(sensors.getTempC(sensor3));
readings["sensor4"] = String(sensors.getTempC(sensor4));
String jsonString = JSON.stringify(readings);
return jsonString;
}
// Initialize LittleFS
void initLittleFS() {
if (!LittleFS.begin()) {
Serial.println("An error has occurred while mounting LittleFS");
}
else{
Serial.println("LittleFS mounted successfully");
}
}
// Initialize WiFi
void initWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());
}
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
initWiFi();
initLittleFS();
// Web Server Root URL
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html");
});
server.serveStatic("/", LittleFS, "/");
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
lastTime = lastTime + timerDelay;
request->send(200, "text/plain", "OK!");
});
events.onConnect([](AsyncEventSourceClient *client){
if(client->lastId()){
Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
}
// send event with message "hello!", id current millis
// and set reconnect delay to 1 second
client->send("hello!", NULL, millis(), 10000);
});
server.addHandler(&events);
// Start server
server.begin();
}
void loop() {
if ((millis() - lastTime) > timerDelay) {
// Send Events to the client with the Sensor Readings Every 10 seconds
events.send("ping",NULL,millis());
events.send(getSensorReadings().c_str(),"new_readings" ,millis());
lastTime = millis();
}
}
How the code works
Let’s take a look at the code and see how it works to send readings to the client using server-sent events.
Including Libraries
The OneWire and DallasTemperature libraries are needed to interface with the DS18B20 temperature sensors.
#include <OneWire.h>
#include <DallasTemperature.h>
The ESP8266WiFi, ESPAsyncWebServer and ESPsyncTCP libraries are used to create the web server.
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
The HTML, CSS, and JavaScript files to build the web page are saved on the ESP8266 filesystem (LittleFS). So, we also need to include the LitteFS library.
#include "LittleFS.h"
You also need to include the Arduino_JSON library to make it easier to handle JSON strings.
#include <Arduino_JSON.h>
Network Credentials
Insert your network credentials in the following variables, so that the ESP8266 can connect to your local network using Wi-Fi.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
AsyncWebServer and AsyncEventSource
Create an AsyncWebServer object on port 80.
AsyncWebServer server(80);
The following line creates a new event source on /events.
AsyncEventSource events("/events");
Declaring Variables
The readings variable is a JSON variable to hold the sensor readings in JSON format.
JSONVar readings;
The lastTime and the timerDelay variables will be used to update sensor readings every X number of seconds. As an example, we’ll get new sensor readings every 30 seconds (30000 milliseconds). You can change that delay time in the timerDelay variable.
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;
DS18B20 Sensors
The DS18B20 temperature sensors are connected to GPIO 4 (D2).
// GPIO where the DS18B20 sensors are connected to
const int oneWireBus = 4;
Setup a oneWire instance to communicate with OneWire devices (DS18B20):
OneWire oneWire(oneWireBus);
Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
Insert the addresses of your DS18B20 Sensors in the following lines (check this section if you don’t have the addresses of your sensors):
// Address of each sensor
DeviceAddress sensor3 = { 0x28, 0xFF, 0xA0, 0x11, 0x33, 0x17, 0x3, 0x96 };
DeviceAddress sensor1 = { 0x28, 0xFF, 0xB4, 0x6, 0x33, 0x17, 0x3, 0x4B };
DeviceAddress sensor2 = { 0x28, 0xFF, 0x43, 0xF5, 0x32, 0x18, 0x2, 0xA8 };
DeviceAddress sensor4 = { 0x28, 0xFF, 0x11, 0x28, 0x33, 0x18, 0x1, 0x6B };
Get DS18B20 Readings
To get readings from the DS18B20 temperature sensors, first, you need to call the requesTemperatures() method on the sensors object. Then, use the getTempC() function and pass as argument the address of the sensor you want to get the temperature—this gets the temperature in celsius degrees.
Note: if you want to get the temperature in Fahrenheit degrees, use the getTemF() function instead.
Finally, save the readings in a JSON string (jsonString variable) and return that variable.
// Get Sensor Readings and return JSON object
String getSensorReadings(){
sensors.requestTemperatures();
readings["sensor1"] = String(sensors.getTempC(sensor1));
readings["sensor2"] = String(sensors.getTempC(sensor2));
readings["sensor3"] = String(sensors.getTempC(sensor3));
readings["sensor4"] = String(sensors.getTempC(sensor4));
String jsonString = JSON.stringify(readings);
return jsonString;
}
Initialize LittleFS
The initLittleFS() function initializes the LittleFS filesystem:
void initLittleFS() {
if (!LittleFS.begin()) {
Serial.println("An error has occurred while mounting LittleFS");
}
else{
Serial.println("LittleFS mounted successfully");
}
}
Intialize WiFi
The initWiFi() function initializes Wi-Fi and prints the IP address on the Serial Monitor.
// Initialize WiFi
void initWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());
}
setup()
In the setup(), initialize the Serial Monitor, Wi-Fi and filesystem.
Serial.begin(115200);
initWiFi();
initLittleFS();
Handle Requests
When you access the ESP8266 IP address on the root / URL, send the text that is stored on the index.html file to build the web page.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html");
});
Serve the other static files requested by the client (style.css and script.js).
server.serveStatic("/", LittleFS, "/");
When you receive a request on the /readings URL, reset the timer—this way, the if statement in the loop() will be true and the ESP8266 will send new readings via SSE.
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
lastTime = lastTime + timerDelay;
request->send(200, "text/plain", "OK!");
});
We send a response with OK, so that we know on the client-side that the ESP8266 received the request.
Server Event Source
Set up the event source on the server.
events.onConnect([](AsyncEventSourceClient *client){
if(client->lastId()){
Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
}
// send event with message "hello!", id current millis
// and set reconnect delay to 1 second
client->send("hello!", NULL, millis(), 10000);
});
server.addHandler(&events);
Finally, start the server.
server.begin();
loop()
In the loop(), send events to the browser with the newest sensor readings to update the web page every 30 seconds.
if ((millis() - lastTime) > timerDelay) {
// Send Events to the client with the Sensor Readings Every 10 seconds
events.send("ping",NULL,millis());
events.send(getSensorReadings().c_str(),"new_readings" ,millis());
lastTime = millis();
}
Use the send() method on the events object and pass as an argument the content you want to send and the name of the event. In this case, we want to send the JSON string returned by the getSensorReadings() function. The name of the events is new_readings.
Uploading Code and Files
After inserting your network credentials, save the code. Go to Sketch > Show Sketch Folder, and create a folder called data.
Inside that folder you should save the HTML, CSS and JavaScript files.
Then, upload the code to your ESP8266 board. Make sure you have the right board and COM port selected. Also, make sure you’ve added your networks credentials and the sensors’ addresses to the code.
After uploading the code, you need to upload the files. Go to Tools > ESP8266 LittleFS Data Upload and wait for the files to be uploaded.
When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the ESP8266 EN/RST button, and it should print the ESP8266 IP address.
Demonstration
Open your browser and type the ESP8266 IP address. You should get access to the web page that shows the sensor readings. Wait some time until it gathers some data points.
You can select a point to see its value and timestamp.
Wrapping Up
In this tutorial you’ve learned how to create charts with multiple series to display temperature from multiple DS18B20 sensors. You can modify this project to create as many charts and series as you want and plot data from any other sensors or sources.
You might also like reading: ESP32/ESP8266 Plot Sensor Readings in Real Time Charts – Web Server
Learn more about the ESP8266 with our resources:
- Build Web Servers with ESP32 and ESP8266 eBook
- Home Automation using ESP8266
- More ESP8266 NodeMCU Projects and Guides…
Thank you for reading.
Rui,
I have been working on a project that monitors the Voltages (Alternator, Solar panel and Battery) around an auxiliary battery in my vehicle, so cannot access the Internet and use HighCharts.
I’ve found RGraph (https://www.rgraph.net/demos/index.html), which seems to run Ok from an SD card but there are so many options it’s getting confusing. Have you any recommendations on a simpler way to produce a graph?
Dave
Hi Dave.
Unfortunately, I never experimented with that library.
The best to get started is to try to experiment first with the Demos provided: https://www.rgraph.net/demos/bar-line-switch-effect.html
Regards,
Sara
I think that that’s the first time that I’ve heard some say “There’s too many options!”…!
@Rui – download the offline highcharts archives for the graphs, modify the adress of the online chart source from on the index file (https://code.highcharts.com/highcharts.js) into your sd-card path folder (I don’t remember the exact path – look it up on google) and take the “highcharts.js” file from the archive and put it on your sd-card root folder. Basically you move the *.js file from the online source to your local sd-card source and specify the new location of the file for the index file to use.
Alex,
Thanks I’ll do that. I now noticed some docs.
https://www.highcharts.com/docs/getting-started/installation
Dave
Hi Rui and Sara. This is a nice tutorial. I would like to combine this with a ESP32 and with AP mode and also a faster refresh of let’s say 1 second. So not connected with the internet. Would this be possible ?
I want to will the same code work if i had lets say a dh11 and a MAX30102 sensors connected , as both of the sensors will produce data at different rates and are indeed 2 different sensors .
Nice projet, i’ve tried and succeed to all the first part, i diid not try the trend graph part, but i’m wondering if it will be possible to send data from the dallas to am excel or access or other db, for in a second time paly with recovered data-
Hi.
You can save the data on a google sheet, for example:
– https://randomnerdtutorials.com/esp32-esp8266-publish-sensor-readings-to-google-sheets/
You can save the data on a microSD card, and then load it on the web page:
– https://randomnerdtutorials.com/esp32-microsd-card-arduino/
Or use a MySQL database:
– https://randomnerdtutorials.com/esp32-esp8266-mysql-database-php/
I hope this helps.
Regards,
Sara
Thanks I’ll tryto follow your suggestions
thanks
Hi to All,
thanks for such great tutorials.
Please can you give some hints for showing at last 120 last samples when client connects, and continue to plotting as usual.
Regards,
Hi.
Save the readings on a file on the filesystem to a maximum of 120 readings in JSON format.
When the web page loads make a request to get the readings.
We have a similar project in our “Build Web Server with ESP32/ESP8266” eBook. At the moment, we don’t have any similar tutorial published here.
Regards,
Sara
very nice project but my ESP8266 forgets web files after turn off …
Hi.
do you get any errors or can you provide more information?
Regards,
Sara
Hi,
no errors ,
after turning off the NOD8266 and again on , my mobile can not find the web page.
but after uploading the files ( from DATA folder ) everything works fine.
It remembers the sketch but forgets the files
Thank you for help,
Kris Palka
Love the project. Perfectly explained. Is it possible to add a relay with defined temperatures to be used as a thermostat?
Something like this https://randomnerdtutorials.com/esp32-esp8266-thermostat-web-server/
But with a graph of the temperatures.
Thanks a lot in advance
Hey great stuff guys! I’ve set this up with 10 sensors in 2 locations and everything is working great but for the life of me I cant get the time zone adjustment to work. I need a -21600 offset and copied the code etc. but to no avail.
this is what I’m using:
// Create Temperature Chart
Highcharts.setOptions({
time: {
timezoneOffset: -21600 //Add your time zone offset here in seconds
}
});
var chartT = new Highcharts.Chart({
its in the script.js file unless its in the wrong location?
Hi.
the charts will show the time in UTC. If you want it to display in your timezone, you must set the useUTC parameter (which is a time parameter) as false:
time:{
useUTC: false
},
So, add that when creating the chart as follows:
var chart = new Highcharts.Chart({
time:{
useUTC: false
},
(…)
To learn more about this property, check this link on the documentation:
https://api.highcharts.com/highcharts/time.useUTC
I hope this helps.
Regards,
Sara
Thanks Sara! That worked perfectly! I struggled with it for a couple weeks, lol. Keep up the great work!
Another possibility is to set the actual Timezone, like
time:{
timezone: ‘Europe/Amsterdam’
}
Hi Sarah, I’m improving my setup. Using your tutorial for the whatsapp bot to send me a message when two temperatures get to close, say less than 5 degrees. That would signal that my burner is off. Is there a place I could put an if,then,else to test the temps even if the webpage is not open? Or should it go in the loop and just keep the webpage open all the time? Thanks for your help! You guys have so many great projects and tutorials!
Hello, Sara and Rui.
I tried to find one of your projects where the 8266 displayed a simple graph using SVG.
Can you give me the link, please?
Thank you very much and congratulations for your excellent work.
Hi.
Is it this one https://randomnerdtutorials.com/esp32-esp8266-plot-chart-web-server/?
Can you give more details about that project.
We have several with charts.
Regards,
Sara
Thank you very much, Sara, for your kind response. I’ve searched your website a lot and haven’t seen the tutorial I’m referring to. Maybe I’m confused, but it seems to me that it is a web server that displays a simple graph using SVG, without using filesSystem or Charts.js but rather http://www.w3.org/2000/svg.
I have developed the idea but I am looking for the source for reference.
Very grateful for the excellent tutorials.
Thank you and happy new year
Hi.
Unfortunately, we don’t have any projects using that method.
Regards,
Sara
Hallo,
mit der Arduino IDE 2.2.1 kann ich den Ordner daten nicht hochladen.
Gruss Ulrich
Hello,
With the Arduino IDE 2.2.1 I cannot upload the data folder.
Greetings Ulrich
Hi.
The app to upload the data folder is only compatible with Arduino 1.8.x
Regards
Sara
I’ve been working with this sketch for a little while now. It works fine when you simply load the sketch to an ESP8266 and connect to it via a web browser.
However, clicking the Refresh button on the web browser while the chart is running causes the ESP8266 to throw an exception and restart.
Has anyone else seen this behavior?
Never mind – seems to have been a memory configuration setup problem that I now can’t repeat. Please delete these comments?