ESP32 Plot Sensor Readings in Charts (Multiple Series)

This project shows how to build a web server with the ESP32 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.

ESP32 Plot Sensor Readings in Charts Multiple Series Arduino

We have a similar tutorial for the ESP8266 NodeMCU board:

Project Overview

This project will build a web server with the ESP32 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.

ESP32 Web Server Chart with Multiple Series ESP32

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.

DS18B20 Temperature Sensor one-wire digital sensor module

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).

Sensor Readings Multiple Series Server-Sent Events DS18B20

To learn more about SSE, you can read:

Files Saved on the Filesystem

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).

Prerequisites

Make sure you check all the prerequisites in this section before continuing with the project.

1. Install ESP32 Board in Arduino IDE

We’ll program the ESP32 using Arduino IDE. So, you must have the ESP32 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 ESP32:

2. Filesystem Uploader Plugin

To upload the HTML, CSS, and JavaScript files to the ESP32 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:

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 ESP32 using PlatformIO, you should add the following lines on the platformio.ini file to include the libraries and set the default filesystem to LittleFS (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
board_build.filesystem = littlefs

Parts Required

To follow this tutorial you need the following parts:

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.

ESP32 Multiple DS18B20 Sensors Diagram Circuit wiring

Recommended reading: ESP32 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 ESP32. 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);
  }
}

View raw code

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):

Getting onewire DS18B20 address Serial Monitor

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.
Organizing Your Files Arduino sketch index html css javascript

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 ESP32 filesystem (LittleFS).

You can download all project files:

HTML File

Copy the following to the index.html file.

<!-- Complete project details: https://randomnerdtutorials.com/esp32-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>

View raw code

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/esp32-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%;
}

View raw code

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/esp32-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) {
      var myObj = JSON.parse(this.responseText);
      console.log(myObj);
      plotTemperature(myObj);
    }
  };
  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);
}

View raw code

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. 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 with the required information. 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”

So, the request should look something like this:

function getStates(){
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      … DO WHATEVER YOU WANT WITH THE RESPONSE …
    }
  };
  xhr.open("GET", "/states", true);
  xhr.send();
}

The response sent by the ESP is the following text in JSON format.

{
  "sensor1" : "25",
  "sensor2" : "21",
  "sensor3" : "22",
  "sensor4" : "23"
}

We need to convert the JSON string into a JSON object using the parse() method. The result is saved on the myObj variable.

var myObj = JSON.parse(this.responseText);

The myObj varible is a JSON object that contains all the temperature readings. We want to plot those readings on the same chart. For that, we’ve created a function called plotTemperature() that plots the temperatures stored in a JSON object on a chart.

plotTemperature(myObj);

Here’s the complete getReadings() function.

function getReadings(){
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var myObj = JSON.parse(this.responseText);
      console.log(myObj);
      plotTemperature(myObj);
    }
  }; 
  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 minutes
  }
});

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 ESP32 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 & Sara Santos - Random Nerd Tutorials
  Complete instructions at https://RandomNerdTutorials.com/esp32-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 <WiFi.h>
#include <AsyncTCP.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){
    String json = getSensorReadings();
    request->send(200, "application/json", json);
    json = String();
  });

  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();
  }
}

View raw code

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 WiFi, ESPAsyncWebServer and AsyncTCP libraries are used to create the web server.

#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

The HTML, CSS, and JavaScript files to build the web page are saved on the ESP32 filesystem (LittleFS). So, we also need to include the LittleFS 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 ESP32 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.

// 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:

// Initialize LittleFS
void initLittleFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
    Serial.println("LittleFSmounted 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 ESP32 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, "/");

Send the JSON string with the current sensor readings when you receive a request on the /readings URL.

// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
  String json = getSensorReadings();
  request->send(200, "application/json", json);
  json = String();
});

The json variable holds the return from the getSensorReadings() function. To send a JSON string as response, the send() method accepts as first argument the response code (200), the second is the content type (“application/json”) and finally the content (json variable).

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.

Arduino IDE Open Sketch Folder to create data folder

Inside that folder you should save the HTML, CSS and JavaScript files.

Then, upload the code to your ESP32 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.

Arduino IDE 2 Upload Button

After uploading the code, you need to upload the files to the filesystem.

Press [Ctrl] + [Shift] + [P] on Windows or [] + [Shift] + [P] on MacOS to open the command palette. Search for the Upload LittleFS to Pico/ESP8266/ESP32 command and click on it.

If you don’t have this option is because you didn’t install the filesystem uploader plugin. Check this tutorial.

Upload LittleFS to Pico ESP8266 ESP32 Arduino IDE

Important: make sure the Serial Monitor is closed before uploading to the filesystem. Otherwise, the upload will fail.

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

Demonstration

Open your browser and type the ESP32 IP address. You should get access to the web page that shows the sensor readings. Wait some time until it gathers some data points.

ESP Web Server Charts demonstration temperature

You can select a point to see its value and timestamp.

ESP Web Server Charts demonstration temperature multiple series

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 ESP32 with our resources:

Thank you for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

75 thoughts on “ESP32 Plot Sensor Readings in Charts (Multiple Series)”

  1. Excelente post! Já uso o ds18b20 com esp8266 e Arduino vou tentar a parte que atribui o Bend do senso ao nome ” Sensor 1: ” …
    Parabéns!

    Carlos Bruni

    Reply
  2. Rui,
    Fantastic project. Wish that it was affordable for my budget. It is an excellent way for a novice to learn how to use the different programming tools. Keep up the good work. Just remember that a lot of your readers are not as skilled as you are. Don’t loose the young readers.
    George

    Reply
  3. Exactly what I’m wanting to do! Thanks!

    I bought your Webserver PDF awhile back and it provides everything you need to understand and build these web servers on the ESP32.

    Reply
  4. Rui,
    Clearly not a project for the first timer. It does show the second stage novis that adding complexity will take him down many paths to achieve a successful project. Everything one needs is out there to discover and master, and if it isn’t, to invent.
    A mechanical solution is finite, a software solution is infinite. We all have a lot more to learn.
    Keep up the good work.
    Pixel

    Reply
    • Hi.
      Thanks for your feedback.
      Indeed, this is a more advanced tutorial that combines many subjects already covered on the blog.
      We have other more entry-level projects for beginners.
      We also have an eBook exclusively dedicated to building web servers with the ESP32 and ESP8266 that shows step by step how to build a web server from a simple “Hello World” web server to more advanced projects similar to this one.
      https://randomnerdtutorials.com/build-web-servers-esp32-esp8266-ebook/
      Thanks for following our work.
      Regards,
      Sara

      Reply
  5. Rui,

    Seems quite advanced considering most of the people in our “Meet up Group” are way below your paygrade. Also, several of us are still looking for a tutorial that uses higher temperature devices like RTDs and Thermocouples with amplifiers line the 31865 and 31855. These devices offer great accuracy at temperatures like the DS18B20 but offer a much wider temperature range.

    Random Tutorial Student for about 1 year

    Reply
    • Hi Carl.
      Thanks for your comment.
      I’ll add sensors with wider temperature ranges to my list of to-do tutorials.
      Thanks for the feedback.
      Regards,
      Sara

      Reply
    • Hey Carl, (and anyone else still using Maxim 31855’s)
      Unless you have a large stock of 31855’s, you should seriously look at the 31856, also from Maxim. The 31856 can digitize nearly any thermocouple (there is not a seperate device for T’s, K’s, J’s, etc) and actually is internally coded with TC compensation curves, so that you actually get an output that truly represents the input temperature. (Unlike the 31855, which generates a linear approximation of the temperature)
      There are other advantages to the 31856 as well, not the least of is that it is actually cheaper than the 31855!

      Reply
  6. This is a masterpiece of work.

    Having all the sensors controlled by one ESP32 has its limitations; especially when it comes to temperature.
    What I am hoping for; is four ESP-01 scattered around the house to monitor the temperature in a “wider” area.

    I will be waiting and I know you will do it one day…..

    Reply
  7. Another excellent tutorial that covers a lot of ground. I think tutorials that raise the bar are an important part of your tutorial / project portfolio for those of us who have progressed with our Arduino / ESP learning. It is a bit like going to school, we start at nursery school and as we move on toward university the learning becomes progressively more challenging.
    Samir makes a good point and it is relatively easy to have sensors in multiple rooms and then using perhaps, MQTT, ESP-NOW or ESP mesh, to collect the data on one device. The device collecting the data could then be be configured as per this tutorial to send the data for charting. I think you have covered all the techniques I described in various tutorials so it is a case of integrating several projects into one.

    Reply
    • Hi Bob.
      I’m glad you enjoyed the tutorial.
      You’re right. We’ve covered those subjects in previous tutorials.
      You just need to search for a keyword in our search bar and find all related tutorials.
      Thanks for your feedback.
      Regards,
      Sara

      Reply
  8. Dear SARA,
    Very good tutorial.
    Don’t forget to install “Arduino_JSON” library.
    I went to connect to my web server and I see tempatures at the first time.
    Very good job.
    Best regrds from France

    Reply
    • Hi Rodrigo.
      Thanks for the feedback.
      I’ve added the Arduino_JSON to the list of needed libraries.
      Thanks for noticing.
      Regards,
      Sara

      Reply
      • Hi Sara, nice tutorials, I wonder which software you use to draw these flow charts, those pictures looks extremely good! Could you please tell me the name?

        Reply
  9. Hi Sara.
    Thanks for another great tutorial. It is as always a pleasure to read.

    If anyone are facing the issue , where the marker points disappear from time to time you can add this to the script “enabled Threshold: 0”

    series: [
    {
    name: ‘Temperature #1’,
    type: ‘line’,
    color: ‘#101D42’,
    marker: {
    symbol: ‘circle’,
    radius: 3,
    fillColor: ‘#101D42’,
    enabledThreshold: 0
    }
    },
    {
    name: ‘Temperature #2’,
    type: ‘line’,
    color: ‘#00A6A6’,
    marker: {
    symbol: ‘square’,
    radius: 3,
    fillColor: ‘#00A6A6’,
    enabledThreshold: 0
    }
    },
    {
    name: ‘Temperature #3’,
    type: ‘line’,
    color: ‘#8B2635’,
    marker: {
    symbol: ‘triangle’,
    radius: 3,
    fillColor: ‘#8B2635’,
    enabledThreshold: 0
    }
    },
    {
    name: ‘Temperature #4’,
    type: ‘line’,
    color: ‘#71B48D’,
    marker: {
    symbol: ‘triangle-down’,
    radius: 3,
    fillColor: ‘#71B48D’,
    enabledThreshold: 0
    }
    },
    ],

    Reply
  10. Hi again.
    It is also possible to have shared tooltips by moving the line
    var x = (new Date ()). getTime ();
    on top of the line
    for (var i = 0; i <keys.length; i ++) {
    as below
    var x = (new Date ()). getTime ();
    for (var i = 0; i <keys.length; i ++) {
    and then add these lines

    tooltip: {
    crosshairs: true,
    shared: true,
    xDateFormat: ‘% A,% e,% b,% H:% M:% S’
    },

    just before these lines
    xAxis: {
    type: ‘datetime’,
    dateTimeLabelFormats: {second: ‘% H:% M:% S’}
    },

    Reply
    • by the way if you want the exact same time interval between each point you have to move the line
      lastTime = millis ();

      just below the lines
      void loop () {
      if ((millis () – lastTime)> timerDelay) {

      as below
      void loop () {
      if ((millis () – lastTime)> timerDelay) {
      lastTime = millis ();
      // 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 ());

      }
      }

      Reply
  11. hi
    Just another input.
    It is possible to give each sensor its own scale with other units of measurement.
    for example if you want to add moisture meter to the project.

    It can be done like this.

    // Create Temperature Chart
    var chartT = new Highcharts.Chart({
    chart:{
    renderTo:’chart-temperature’
    },
    series: [
    {
    yAxis: 0,
    name: ‘Temperature #1’,
    type: ‘line’,
    color: ‘#101D42’,
    marker: {
    symbol: ‘circle’,
    radius: 3,
    fillColor: ‘#101D42’,
    }
    },
    {
    yAxis: 1,
    name: ‘Temperature #2’,
    type: ‘line’,
    color: ‘#00A6A6’,
    marker: {
    symbol: ‘square’,
    radius: 3,
    fillColor: ‘#00A6A6’,
    }
    },
    {
    yAxis: 2,
    name: ‘Temperature #3’,
    type: ‘line’,
    color: ‘#8B2635’,
    marker: {
    symbol: ‘triangle’,
    radius: 3,
    fillColor: ‘#8B2635’,
    }
    },
    {
    yAxis: 3,
    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: [
    {
    opposite: false,
    title: {
    enabled:true,
    text: ‘Sensor 1’,
    }
    },

    {
    opposite: false,
    title: {
    enabled:true,
    text: ‘Sensor 2’,
    }
    },

    {
    opposite: true,
    title: {
    enabled:true,
    text: ‘Sensor 3’,
    }
    },
    {
    opposite: true,
    title: {
    enabled:true,
    text: ‘Sensor 4’,
    }
    },

    ],
    credits: {
    enabled: false
    }
    });

    Reply
    • Hi.
      It can work with an ESP8266 with slight changes in the code.

      • The ESP8266 uses the ESP8266WiFi.h library.
      • Additionally, you might need to change the filesystem (for example, if you want to use LittleFS for the ESP8266 instead of SPIFFS).

      I hope this helps. We’ll release this project for the ESP8266 soon.
      Regards,
      Sara

      Reply
  12. Hi ppl, i could make it work replacing the dallas temp sensors, with a MPU6050.
    I got the readings on the web-browser, but i can see that the fastest refreshing time of
    the chart plots, is around one second.
    Is it possible to make it faster, around ten times per second may be?
    If case, what part of the code should i modify?
    Thanks!

    Reply
    • Hi.
      The readings are sent to the server using server-sent events every 30 seconds.
      You can change that by changing the value of the timerDelay variable. Insert your desired time interval in milliseconds.
      I hope this helps.
      Regards,
      Sara

      Reply
  13. I have not tried the project but the level of detail the article is written is amazing. Congratulations Sara and thanks for sharing.

    Reply
  14. Great keep up the good work.

    Please note one error in the TimeZone setting
    the setting is in MINUTES not seconds I used the following for New Zealand
    Highcharts.setOptions({
    time: {
    timezoneOffset: 720 //NZ Add your time zone offset here in minutes
    }
    });

    Reply
    • Hey Roy, (and anyone else still using Maxim 31855’s)
      Unless you have a large stock of 31855’s, you should seriously look at the 31856, also from Maxim. The 31856 can digitize nearly any thermocouple (there is not a seperate device for T’s, K’s, J’s, etc) and actually is internally coded with TC compensation curves, so that you actually get an output that truly represents the input temperature. (Unlike the 31855, which generates a linear approximation of the temperature)
      There are other advantages to the 31856 as well, not the least of is that it is actually cheaper than the 31855!

      Reply
  15. Hello, thank you for this great job, i have problrm, my be you can help me ??

    i am trying to print more than one chart on may page, one for température, and one for humidity, but the chart always print the first one on on the top of my javascript listners (temperature), even the plotTemperature() function is in the other Listner, like below :

    source.addEventListener(‘new_readingsTemp’, function(e) {
    console.log(“new_readingsTemp”, e.data);
    var myObj1 = JSON.parse(e.data);
    console.log(myObj1)
    }, false);
    }

    source.addEventListener(‘new_readingsHum’, function(e) {
    console.log(“new_readingsHum”, e.data);
    var myObj2 = JSON.parse(e.data);
    console.log(myObj2);
    plotTemperature(myObj2);
    }, false);
    }

    the values are correctley printed in the page for both, but the chart always show temperature ?

    Reply
  16. another great tutorial.
    I want to put my ESP32 into deep sleep for 30 mins, wake it up, get the readings and then sleep again. I need to save some battery power.
    How would I do that?

    Reply
  17. Hi Sara, Rui,
    thanks for all your very helpful tutorials. I’ve used them on a TTGO T1 Display to plot daily and weekly temperature charts for my campervan using the ESP-32 as an access point . I also found that you can download the highcharts code and load it SPIFFS so that the sketch can run without access to the internet; the ESP32 serves the charts to my smartphone once I’ve connected to the access point.
    The highcharts code can be downloaded from https://www.highcharts.com/blog/download/ and the extract the highcharts.js file from the code sub folder and copy it to your sketch data folder and then upload to SPIFFS.
    Couldn’t have done it without your excellent examples.
    Thank you, Martin.

    Reply
  18. hi,
    I want to convert this into a direct access as I will not have internet access were this is going to be located.
    I have tried but only getting the initial connection with the HTML page showing “ESP web server charts” and a box indicating “Temperature chart” but no chart. I guess the jason file is not connecting.
    /*
    * Keep fridge below 4c – set to 3c
    * Keep freezer -18 to -20c
    */
    /*********
    Rui Santos
    Complete instructions at https://RandomNerdTutorials.com/esp32-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 <WiFi.h>
    #include <AsyncTCP.h>
    #include <ESPAsyncWebServer.h>
    #include “SPIFFS.h”
    #include <Arduino_JSON.h>
    #include <OneWire.h>
    #include <DallasTemperature.h>

    // Replace with your network credentials
    const char* ssid = “ESP32-Access-Point”;
    const char* password = “123456789”;

    // 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 = 1000; //Set delay to multiples of 1000 Ms

    // 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 sensor1 = { 0x28, 0xCA, 0x35, 0x56, 0xB5, 0x1, 0x3C, 0x99 };
    DeviceAddress sensor2 = { 0x28, 0xBB, 0x12, 0x56, 0xB5, 0x1, 0x3C, 0x9F };
    DeviceAddress sensor3 = { 0x28, 0xFF, 0xA0, 0x11, 0x33, 0x17, 0x3, 0x96 };
    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 SPIFFS
    void initSPIFFS() {
    if (!SPIFFS.begin()) {
    Serial.println(“An error has occurred while mounting SPIFFS”);
    }
    else{
    Serial.println(“SPIFFS 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());
    */
    WiFi.softAP(ssid, password);

    IPAddress IP = WiFi.softAPIP();
    Serial.print(“AP IP address: “);
    Serial.println(IP);

    server.begin();
    }

    void setup() {
    // Serial port for debugging purposes
    Serial.begin(115200);
    initWiFi();
    initSPIFFS();

    // Web Server Root URL

    server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, “/index.html”, “text/html”);
    });

    server.serveStatic(“/”, SPIFFS, “/”);

    // Request for the latest sensor readings
    server.on(“/readings”, HTTP_GET, [](AsyncWebServerRequest *request){
    String json = getSensorReadings();
    request->send(200, “application/json”, json);
    json = String();
    });

    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();
    }
    }

    Reply
    • Hi.
      If you want to run this example in Access Point mode, you need to take into account that this example uses the Highcharts library.
      The Highcharts library is loaded from the internet when the ESP32 runs the web server. So, if you run it in access point mode (you don’t have access to the internet), you’ll need to save the Highcharts library locally on an SD card and then, load it from there when accessing the web page.
      Regards,
      Sara

      Reply
      • Hi Sara,
        you don’t need to use an SD card, the highcharts code can be downloaded from https://www.highcharts.com/blog/download/ and the extract the highcharts.js file from the code sub folder and copy it to your sketch data folder and then upload to SPIFFS. That way you don’t need an SD card and reader.
        I’ve learned a lot from your various tutorials which gave me a good grounding to explore further from.
        Thanks, Martin

        Reply
  19. Hi, great article. I have a question.
    How to save the readings from sensors and display the charts with values, when accesing the server, without waiting until it gathers data points?

    Reply
    • Hi.
      You need to save the data point on the filesystem or on a microSD card.
      Then, when loading the web page, it should request the file with all the readings.
      Regards,
      Sara

      Reply
  20. I am stuck on the SPIFFS function. Any idea what I am getting wrong?

    //
    Connecting to WiFi …..192.168.2.249
    E (3367) SPIFFS: mount failed, -10025
    An error has occurred while mounting SPIFFS

    INFO
    Chip is ESP32-D0WDQ6 (revision 1)

    Reply
  21. Great project! I was wondering however, is it possible to make the conncting lines between the data points curved as opossed to straight? This would make for a curvier looking graph and I was wondering if there was a way to go about doing this

    Reply
    • Hi.
      Yes.
      To make the lines curvy, you just need to add:

      type: ‘spline’
      to the chart properties.
      So, the first lines of your chart will be like this:

      var chartT = new Highcharts.Chart({chart:{
      renderTo : ‘chart-temperature’
      type: ‘spline’
      },
      ….
      I hope this helps.
      Regards,
      Sara

      Reply
  22. Hi,
    Is it possible to realize the whole thing in via an access point (AP)? And how fast can it be?
    My concrete project would be to display the serial plotter of the Arduino IDE via an AP on the smartphone and to observe the value of an analog input.

    Reply
  23. Will a 5k ohm resistor work for this project, or do I really need a 4.7k ohm resistor? I’m new to this electronics stuff and don’t know if close is good enough …

    Also, I’m using a ESP32, is SPIFFS going to get deprecated for ESP32 soon? Should I consider moving to LittleFS now as I build projects?

    Thank you.

    Reply
    • You can use any value between 2.2k and 22k. It is just a pull-up to allow a decent communication speed. A lower valve tolerates more speed, but draws more current. 4.7k or 5k is about the same thing.
      Bill

      Reply
      • Thank you that is very helpful. I was trying to decipher the datasheet and thought it would work. It shows 4.7k ohms in the schematic, but for input voltages from 3v to 5.5v. I have a limited supply of resistors I have a 5.1k (5k1) ohm resistor, which should work. I it doesn’t I can wire a couple 2ks in series.

        Reply
        • In a world a long long time ago, resistors were made with compressed carbon powder and it was very hard to control the resistance accurately. They drifted with time, temperature, and humidity. As a result, they were labeled in groups separated by 20%. values like 1.0, 2.2, 3.3, 4.7, 5.6, 6.3, 7.2, and 8.1 were calculated to be 20% apart. All of the antique engineers of today remember those numbers like a religion. In the 1970’s film resistors appeared, and the world of very accurate, stable, and cheap resistors came to be. Those 20% numbered values are nearly history. Now, 5% resistors are the norm, and 1% values are affordable. Choosing an exact value for your circuits is now easier and possibly more confusing for the added choices.
          As a person new to electronics, you have a lot to learn about the basics and the frustration of acquiring the art of knowing what works and why. Reading schematics and understanding datasheets takes time, and it is worth the effort. You will find some incredibly beautiful designs out there, among the horrible misguided attempts. Knowing the difference is important.
          Bill

          Reply
  24. Hi,
    when i upload the Project to my Aduino, i get the first sample right from the DS18B20, but all next sample are -127. I think it will be so when the Webserver is startet. If i comment the line of server.begin(); out, i can get samples from the DS18B20. Can you tell me, what versions of the librarys you use ?

    Reply
  25. Thank you for the great tutorials!
    Please tell me what happens when millis() approaches its maximum value and when it overflows in
    void loop() {
    if ((millis() – lastTime) > timerDelay) {
    // …..
    lastTime = millis();
    }
    }

    Reply
  26. I was getting no output on my web page until I realised my fundamental mistake.
    I made style.css and script.js in notepad++ and it added a hidden .txt to the end of each file.
    Once I fixed this it is running smoothly.
    Quick Question, what is the max distance I could have a DS18B20 sensor hard wired to the ESP32 and If I have 4 of them does that change that distance?

    Reply
  27. Hi, I am in trouble flashing the spiffs file despite i followed your guidance in the web site. I am operating Arduino IDE 2.3.2. After mounting the spiffs file i get the message upload successfully but when press the reset button on ESP32 the following error code apears:
    18:19:55.436 -> E (4132) SPIFFS: mount failed, -10025
    18:19:55.436 -> An error has occurred while mounting SPIFFS
    I am running to test the file w/o temperature sensors. Is that my problem? Do you have an Idea, why this application doesn`t work on my ESP32?

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.