ESP32/ESP8266 Plot Sensor Readings in Real Time Charts – Web Server

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

ESP32 ESP8266 Plot Data Chart Web Server BME280 Temperautre Arduino IDE

Project Overview

In this tutorial we’ll build an asynchronous web server using the ESPAsyncWebServer library.

The HTML to build the web page will be stored on the ESP32 or ESP8266 Filesystem (SPIFFS). To learn more about building a web server using SPIFFS, you can refer to the next tutorials:

We’ll display temperature, humidity and pressure readings from a BME280 sensor on a chart, but you can modify this project to display sensor readings from any other sensor. To learn more about the BME280, read our guides:

To build the charts, we’ll use the Highcharts library. We’ll create three charts: temperature, humidity and pressure over time. The charts display a maximum of 40 data points, and a new reading is added every 30 seconds, but you change these values in your code.

Watch the Video Demonstration

To see how the project works, you can watch the following video demonstration:

Prerequisites

Make sure you check all the prerequisites in this section before continuing with the project in order to compile the code.

1. Install ESP Board in Arduino IDE

We’ll program the ESP32 and ESP8266 using Arduino IDE. So, you must have the ESP32 or ESP8266 add-on installed. Follow one of the next tutorials to install the ESP add-on:

2. Filesystem Uploader Plugin

To upload the HTML file to the ESP32 and ESP8266 flash memory, we’ll use a plugin for Arduino IDE: Filesystem uploader. Follow one of the next tutorials to install the filesystem uploader depending on the board you’re using:

3. Installing Libraries

To build the asynchronous web server, you need to install the following libraries.

These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation folder.

To get readings from the BME280 sensor module you need to have the next libraries installed:

You can install these libraries through the Arduino Library Manager.

Parts Required

ESP32 wit BME280 sensor module temperature humidity pressure

To follow this tutorial you need the following parts:

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

The BME280 sensor module we’re using communicates via I2C communication protocol, so you need to connect it to the ESP32 or ESP8266 I2C pins.

BME280 wiring to ESP32

BME280ESP32
SCK (SCL Pin) GPIO 22
SDI (SDA pin) GPIO 21

So, assemble your circuit as shown in the next schematic diagram.

BME280 Sensor Module wiring to ESP32 board

Recommended reading: ESP32 Pinout Reference Guide

BME280 wiring to ESP8266

BME280ESP8266
SCK (SCL Pin) GPIO 5
SDI (SDA pin) GPIO 4

Assemble your circuit as in the next schematic diagram if you’re using an ESP8266 board.

BME280 Sensor Module wiring to ESP8266 board

Recommended reading: ESP8266 Pinout Reference Guide

Organizing your Files

To build the web server you need two different files. The Arduino sketch and the HTML file. The HTML file should be saved inside a folder called data inside the Arduino sketch folder, as shown below:

SPIFFS Web Server to Display Charts File Organization

Creating the HTML File

Create an index.html file with the following content or download all project files here:

<!DOCTYPE HTML><html>
<!-- Rui Santos - Complete project details at https://RandomNerdTutorials.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. -->
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://code.highcharts.com/highcharts.js"></script>
  <style>
    body {
      min-width: 310px;
    	max-width: 800px;
    	height: 400px;
      margin: 0 auto;
    }
    h2 {
      font-family: Arial;
      font-size: 2.5rem;
      text-align: center;
    }
  </style>
</head>
<body>
  <h2>ESP Weather Station</h2>
  <div id="chart-temperature" class="container"></div>
  <div id="chart-humidity" class="container"></div>
  <div id="chart-pressure" class="container"></div>
</body>
<script>
var chartT = new Highcharts.Chart({
  chart:{ renderTo : 'chart-temperature' },
  title: { text: 'BME280 Temperature' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#059e8a' }
  },
  xAxis: { type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Temperature (Celsius)' }
    //title: { text: 'Temperature (Fahrenheit)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartT.series[0].data.length > 40) {
        chartT.series[0].addPoint([x, y], true, true, true);
      } else {
        chartT.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 30000 ) ;

var chartH = new Highcharts.Chart({
  chart:{ renderTo:'chart-humidity' },
  title: { text: 'BME280 Humidity' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    }
  },
  xAxis: {
    type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Humidity (%)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartH.series[0].data.length > 40) {
        chartH.series[0].addPoint([x, y], true, true, true);
      } else {
        chartH.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 30000 ) ;

var chartP = new Highcharts.Chart({
  chart:{ renderTo:'chart-pressure' },
  title: { text: 'BME280 Pressure' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#18009c' }
  },
  xAxis: {
    type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Pressure (hPa)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartP.series[0].data.length > 40) {
        chartP.series[0].addPoint([x, y], true, true, true);
      } else {
        chartP.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/pressure", true);
  xhttp.send();
}, 30000 ) ;
</script>
</html>

View raw code

Let’s take a quick look at the relevant parts to build a chart.

First, you need to include the highcharts library:

<script src="https://code.highcharts.com/highcharts.js"></script>

You need to create a <div> section for each graphic with a unique id. In this case: chart-temperature, chart-humidity and chart-pressure.

<div id="chart-temperature" class="container"></div>
<div id="chart-humidity" class="container"></div>
<div id="chart-pressure" class="container"></div>

To create the charts and add data to the charts, we use javascript code. It should go inside the <script> and </script> tags.

The following spinet creates the temperature chart. You define the chart id, you can set the title, the axis labels, etc…

var chartT = new Highcharts.Chart({
  chart:{ renderTo : 'chart-temperature' },
  title: { text: 'BME280 Temperature' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#059e8a' }
  },
  xAxis: { type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Temperature (Celsius)' }
    //title: { text: 'Temperature (Fahrenheit)' }
  },
  credits: { enabled: false }
});

Then, the setInvertal() function adds points to the charts. Every 30 seconds it makes a request to the /temperature URL to get the temperature readings from your ESP32 or ESP8266.

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartT.series[0].data.length > 40) {
        chartT.series[0].addPoint([x, y], true, true, true);
      } else {
        chartT.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 30000 ) ;

The other graphics are created in a similar way. We make a request on the /humidity and /pressure URLs to get the humidity and pressure readings, respectively.

In the Arduino sketch, we should handle what happens when we receive those requests: we should send the corresponding sensor readings.

Arduino Sketch

Copy the following code to the Arduino IDE or download all project files here. Then, you need to type your network credentials (SSID and password) to make it work.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

// Import required libraries
#ifdef ESP32
  #include <WiFi.h>
  #include <ESPAsyncWebServer.h>
  #include <SPIFFS.h>
#else
  #include <Arduino.h>
  #include <ESP8266WiFi.h>
  #include <Hash.h>
  #include <ESPAsyncTCP.h>
  #include <ESPAsyncWebServer.h>
  #include <FS.h>
#endif
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

String readBME280Temperature() {
  // Read temperature as Celsius (the default)
  float t = bme.readTemperature();
  // Convert temperature to Fahrenheit
  //t = 1.8 * t + 32;
  if (isnan(t)) {    
    Serial.println("Failed to read from BME280 sensor!");
    return "";
  }
  else {
    Serial.println(t);
    return String(t);
  }
}

String readBME280Humidity() {
  float h = bme.readHumidity();
  if (isnan(h)) {
    Serial.println("Failed to read from BME280 sensor!");
    return "";
  }
  else {
    Serial.println(h);
    return String(h);
  }
}

String readBME280Pressure() {
  float p = bme.readPressure() / 100.0F;
  if (isnan(p)) {
    Serial.println("Failed to read from BME280 sensor!");
    return "";
  }
  else {
    Serial.println(p);
    return String(p);
  }
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
  
  bool status; 
  // default settings
  // (you can also pass in a Wire library object like &Wire2)
  status = bme.begin(0x76);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  // Initialize SPIFFS
  if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");
  });
  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readBME280Temperature().c_str());
  });
  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readBME280Humidity().c_str());
  });
  server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readBME280Pressure().c_str());
  });

  // Start server
  server.begin();
}
 
void loop(){
  
}

View raw code

How the code works

Let’s take a quick look at the code and see how it works.

Including libraries

First, include the necessary libraries. You include different libraries depending on the board you’re using. If you’re using an ESP32, the code loads the following libraries:

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>

If you’re using an ESP8266, the code loads these libraries:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Create an instance to communicate with the BME280 sensor using I2C:

Adafruit_BME280 bme; // I2C

Insert your network credentials in the following variables:

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Create AsyncWebServer object on port 80:

AsyncWebServer server(80);

Read Temperature, Humidity and Pressure

Then, we create three functions readBME280Temperature(), readBME280Humidity() and readBME280Pressure(). These functions request the temperature, humidity and pressure from the BME280 sensor and return the readings as a String type.

String readBME280Temperature() {
  // Read temperature as Celsius (the default)
  float t = bme.readTemperature();
  // Convert temperature to Fahrenheit
  //t = 1.8 * t + 32;
  if (isnan(t)) {
    Serial.println("Failed to read from BME280 sensor!");
    return "";
  }
  else {
    Serial.println(t);
    return String(t);
  }
}

Init BME280

In the setup(), initialize the sensor:

status = bme.begin(0x76);
if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}

Init SPIFFS

Initialize the filesystem (SPIFFS):

if(!SPIFFS.begin()){
  Serial.println("An Error has occurred while mounting SPIFFS");
  return;
}

Connect to Wi-Fi

Connect to Wi-Fi and print the IP address in the Serial Monitor:

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
}

// Print ESP32 Local IP Address
Serial.println(WiFi.localIP());

Handle requests

Then, we need to handle what happens when the ESP receives a request.

When it receives a request on the root URL, we send the HTML text that is saved in SPIFFS under the index.html name:

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

When we receive a request on the /temperature, /humidity or /pressure URLs, call the functions that return the sensor readings.

For example, if we receive a request on the /temperature URL, we call the readBME280Temperature() function that returns the temperature.

server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readBME280Temperature().c_str());
});

The same happens for the other readings.

Finally, start the server:

server.begin();

Because this is an asynchronous web server we don’t need to write anything in the loop().

void loop(){

}

Uploading Code and Files

Save the code as ESP_Chart_Web_Server or download all project files here. Go to Sketch > Show Sketch Folder, and create a folder called data. Inside that folder you should save the HTML file created previously.

Now you need to upload the HTML file to the ESP32 or ESP8266 filesystem. Go to Tools ESP32/ESP8266 Data Sketch Upload and wait for the files to be uploaded.

ESP32/ESP8266 Data Sketch Upload filesystem Uploader

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

Arduino IDE upload sketch button

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

ESP32 ESP8266 IP Address Serial Monitor

Demonstration

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

Here is an example of the humidity chart:

ESP32 ESP8266 Chart Web Server BME280 Humidity Arduino IDE

You can select each point to see the exact timestamp.

ESP32 ESP8266 BME280 Temperature Pressure Humidity Plot Sensor readings chart web server

Wrapping Up

In this tutorial you’ve learned how to create charts to display data in your web server. You can modify this project to create as many charts as you want and using any other sensors.

Next, we recommend building a project that displays charts from data stored on your database. Here’s other tutorials that you might like:

Learn more about the ESP32 with our best-selling course:

Thank you for reading.


Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

98 thoughts on “ESP32/ESP8266 Plot Sensor Readings in Real Time Charts – Web Server”

  1. Thank you for making all of these free tutorials. They are well done easy to follow and the explanations of how it works are very good. Again thanks.

  2. Hi Rui, hi Sara, thanks for this other amazing tutorial!!!

    It would be really cool to have the opportunity to generate a log file in .csv file from the web page, the web server should be password protected.

    I see wen we make the web server the ESP8266 is set in AP and Station mode simultaneously and I see on my network another network with different IP classe. Could it be possible to use this network to set the SSID and WiFi password in order to avoid everytime to get connected to the laptop the device and fix the credentials data?

    Think about it, it will be the maximum!!!!

  3. Hi Ruis.
    Thanks for this good tutorial. You can use any JS library chart guys.

    I compiled all this using recommended libraries with my ESP32 DEVKIT V1, sont if you get errors when compiling do not remove ESPAsyncTCP library because it is used for ESP8266 MCUs as Ruis said. if you get errors during code compilation using ESP32 MCU Below are some changes to make in AsyncTCP library :

    Rename ESPAsyncTCP.cpp and ESPAsyncTCP.h files to AsyncTCP.cpp
    AsyncTCP.h files

    Rename ESPAsyncTCPbuffer.cpp and ESPAsyncTCPbuffer.h files to AsyncTCPbuffer.cpp
    AsyncTCPbuffer.h files

    In AsyncPrinter.cpp and SyncClient.cpp files, change the library ESPAsyncTCP.h to AsyncTCP.h.

    Once done, Inside every files correct ESPAsync to Async in order to get everything linked properly

    Also commenting panic() method helped me.

    The only update you have to make it’s regarding Arduino OTA. If you upgrade to the Arduino ESP32 latest library, make sure to update the Arduino OTA also to the latest.

    Thanks guys

  4. Hi;
    get a failure after installation on ESP32-WROOM:

    E (446) SPIFFS: mount failed, -10025
    An Error has occurred while mounting SPIFFS
    Van you help me?

    • Hi
      I also had the same problem, then i have changed in the menu: Tools / Flash size / assigned 2M for SPIFFS (otherwise set to zero).
      Now it Works…

  5. Hi Rui and Sara, thanks for this great tutorial.
    I need to change the time zone for + 2 hours.
    Can you show us what to add in code.
    Thanks in advance.

    • Hi.

      One of our readers suggested the following to fix that:

      I fixed my time zone problems by adding this in the start of the script.
      // Highcharts.setOptions({
      // global: {
      // useUTC: false
      // }
      // });
      By adding the code above it will use the time zone of the browser.”

      I hope this helps.
      Regards,
      Sara

  6. Hello

    The temperature is always 26.09 C. The humidity 47.89%. The air pressure always 1006.34. The serial monitor shows no values. The BME280 runs correctly with another program.
    Where could the problem be? I am using an ESP32.

    Mfg Andreas

  7. Unfortunately the time is not correct. Difference 2 hours. I live in Germany. How can I change this?

    Kind regards
    Andreas

    • Hi Andreas

      One of our readers suggested the following to fix that:

      I fixed my time zone problems by adding this in the start of the script.
      // Highcharts.setOptions({
      // global: {
      // useUTC: false
      // }
      // });
      By adding the code above it will use the time zone of the browser.”

      I hope this helps.
      Regards,
      Sara

  8. Really enjoying your tutorials and learning a lot in the process. Wondering how to use the ESP32-CAM for this project. I can’t seem to figure out how to connect the BME280 to I2C to get this to work. Any help would be appreciated.

    Thank you!

    • Hi Greg.
      Do you want to use an ESP32-CAM to do this project?
      The I2C pins of the ESP32-CAM are internally connected to the camera, so I don’t think you can use the camera with the BME280 at the same time. Unless, you configure your code to use other pins as I2C. For example, GPIO 12 and GPIO 13 if you’re not using SD card. However, I don’t know if this I2C will conflict with the camera I2C.

      Regards,
      Sara

  9. Hi Rui and Sara.
    I fixed my time zone problems by adding this in the start of the script.
    // Highcharts.setOptions({
    // global: {
    // useUTC: false
    // }
    // });
    By adding the code above it will use the time zone of the browser.

  10. Hi, thanks for the great tutorial.
    I installed and it was working very well. But after trying to change the intervall time in the html file and some header text changes i don’t get this again on the server. I changes into the data folder within the Sketch Folder and reloaded with the ESP8266 Sketch Data Upload , but no change on the Server output.
    Do you have an idea what can be wrong on my changes.
    Thanks in advance

    • Now I found out the issue. The ESP8286 Sketch Data Upload do not use the html file in the folder I created under Arduino/data instead it used the data folder I downloaded unter Download like your description.
      So i changed the data in this folder and it works fine.
      Regards Hans.

  11. Hello,
    Thank you for sharing your work, this looks very interesting, particularly the use of the charting libraries. However, I cannot access the code, GitHub returns an error 500 each time, even if I try to access your repo directly from my account.
    Is there something wrong at Github, or did you remove your code?
    Thank you.

  12. Another cracking tutorial…..well done guys.
    Had it up and running in a jiffy. I’ve registered with a DDNS supplier and have configured remote access so that I can access from anywhere on the internet.
    Waiting for the DB access that you mentioned in the write up as I have an SQL database set up for the same readings as per your last tutorial.
    See below my URL address so you can check my live readings.

    Keep up the great work……..
    Regards
    Alan

  13. Hi Rui and Sara.
    it is also possible to change the time zone by adding this in the start of the script.
    the minus sign is necessary to add the 2 hours.

    // Highcharts.setOptions({
    // time: {
    // timezoneOffset: -2 * 60
    // }
    // });

    Regards J.B.

  14. Hi again.
    I think this should take care of daylight saving time crossover if the other options don’t
    place this where the other scripts are declared without the //
    // https://code.highcharts.com/highcharts.js
    // https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js
    // https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.13/moment-timezone-with-data-2012-2022.min.js

    and replace the Highcharts.setOptions with this, without the //

    // Highcharts.setOptions({
    // time: {
    // /**
    // * Use moment-timezone.js to return the timezone offset for individual
    // * timestamps, used in the X axis labels and the tooltip header.
    // */
    // getTimezoneOffset: function (timestamp) {
    // var zone = ‘Europe/Oslo’,
    // timezoneOffset = -moment.tz(timestamp, zone).utcOffset();
    //
    // return timezoneOffset;
    // }
    // }
    // });

    • Where do I add this command? I put in the sketch, but returned an error saying that the Highcharts function was not declared.

  15. hello,
    my code does’nt work with an esp32. message : Could not find a valid BME280 sensor, check wiring!

    I checked the wiring and it’s ok

    any idea ?
    Thanks,

    marc,

  16. Guys, you’re just too good! I always follow this site and I have never being disappointed, in fact, I think I just need to buy one of your ebooks now.
    Please keep up the good job

  17. This is a great example. I got it running in no time. I made a couple of changes. I added FTP ( handle in loop) and added a few additional lines in loop to write the data to a file in SPIFFS, tmp.csv. I would like index.html to use the data in the csv file rather than the async data so the data is non-volatile. I have looked all over the web for an example of how to use Highcharts in this manner with no success.

    Can you give me enough hints to get me started?

  18. seems I am the first to have a problem compiling. lots of errors:

    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘void _handle_async_event(lwip_event_packet_t*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:166:68: error: no matching function for call to ‘AsyncClient::_s_dns_found(const char*&, ip_addr_t*, void*&)’
    AsyncClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg);
    ^
    In file included from /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:24:0:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.h:137:17: note: candidate: static void AsyncClient::_s_dns_found(const char*, ip_addr*, void*)
    static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.h:137:17: note: no known conversion for argument 2 from ‘ip_addr_t* {aka _ip_addr*}’ to ‘ip_addr*’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘bool _start_async_task()’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:204:141: error: ‘xTaskCreateUniversal’ was not declared in this scope
    xTaskCreateUniversal(_async_service_task, “async_tcp”, 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘void _tcp_dns_found(const char*, ip_addr*, void*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:303:55: error: invalid application of ‘sizeof’ to incomplete type ‘ip_addr’
    memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr));
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: At global scope:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:32: error: field ‘call’ has incomplete type ‘tcpip_api_call_data’
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: forward declaration of ‘struct tcpip_api_call_data’
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘esp_err_t _tcp_output(tcp_pcb*, AsyncClient*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:368:70: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:368:70: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘esp_err_t _tcp_write(tcp_pcb*, const char*, size_t, uint8_t, AsyncClient*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:391:69: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:391:69: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘esp_err_t _tcp_recved(tcp_pcb*, size_t, AsyncClient*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:413:70: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:413:70: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘esp_err_t _tcp_close(tcp_pcb*, AsyncClient*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:433:69: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:433:69: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘esp_err_t _tcp_abort(tcp_pcb*, AsyncClient*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:453:69: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:453:69: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘esp_err_t _tcp_connect(tcp_pcb*, ip_addr_t*, uint16_t, tcp_connected_fn)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:472:71: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:472:71: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘esp_err_t _tcp_bind(tcp_pcb*, ip_addr_t*, uint16_t)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:490:68: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:490:68: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In function ‘tcp_pcb* _tcp_listen_with_backlog(tcp_pcb*, uint8_t)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:508:70: error: invalid conversion from ‘err_t (*)(tcpip_api_call_data*) {aka signed char (*)(tcpip_api_call_data*)}’ to ‘tcpip_api_call_fn {aka signed char (*)(tcpip_api_call*)}’ [-fpermissive]
    tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg);
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:508:70: error: cannot convert ‘tcpip_api_call_data*’ to ‘tcpip_api_call*’ for argument ‘2’ to ‘err_t tcpip_api_call(tcpip_api_call_fn, tcpip_api_call*)’
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:328:12: note: class type ‘tcpip_api_call_data’ is incomplete
    struct tcpip_api_call_data call;
    ^
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp: In member function ‘void AsyncClient::_dns_found(ip_addr*)’:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:926:33: error: invalid use of incomplete type ‘struct ip_addr’
    connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port);
    ^
    In file included from /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.cpp:24:0:
    /home/dave/Arduino/libraries/AsyncTCP-master/src/AsyncTCP.h:53:8: note: forward declaration of ‘struct ip_addr’
    struct ip_addr;
    ^
    Multiple libraries were found for “WiFi.h”
    Used: /home/dave/Arduino/hardware/espressif/esp32/libraries/WiFi
    Not used: /home/dave/Documents/arduino-1.8.8/libraries/WiFi
    Multiple libraries were found for “Adafruit_BME280.h”
    Used: /home/dave/Arduino/libraries/Adafruit_BME280_Library
    Not used: /home/dave/Arduino/libraries/BME280-I2C-ESP32-master
    exit status 1
    Error compiling for board ESP32 Dev Module.

      • ESP32 Dev Module is selected

        MH-ET Live -ESP32 DEVKIT
        has a VROOM-32

        using the chip on a different sketch, it does run my OLED and display the pair of DS18B20 sensors and the pair of BME280 sensors.

      • Arduino IDE 1.8.8
        preferences boards ; separated by a comma
        arduino.esp8266.com/stable/package_esp8266com_index.json, dl.espressif.com/dl/package_esp32_index.json

        ESP32 sketch data upload is shown in tools
        MHET LIVE32devKIT board is selected

        AsyncTCP-master.zip in my library folder and added as a zip
        ESPAsyncWebServer-master.zip also present and added as zip

        since all the errors are from AsyncTCP-master
        I am not sure if that is getting bad data, or not handling things.

      • I added the ESPAsyncTCP.h for an ESP8266
        changed the board to an ESP8266 and it complied.

        then, changed the board back to the ESP32
        and it compiled without errors.

        ===============
        E (440) SPIFFS: mount failed, -10025
        An Error has occurred while mounting SPIFFS
        ===============

        now I just have to play with the unit to figure out what is going on.

  19. Hi, Sara and Rui,

    Thanks for uploading this nice tutorial, I wanted to plot the waveform of current but problem is that this code is updates the values after every 30 seconds which is not suitable for my case. So, please can you inform what I have to change to plot my current wave with zero delay?

  20. Thanks for the tutorial!
    I am using ESP8266-01 and the BME280 sensor did not return any data. To correct, I called the function Wire.begin (0,2);
    If anyone else has this problem, the tip is

  21. Hi Rui,

    Is it possible to store the data first in SPIFFS or on a SD card and plot the data within a selected period into the webbrowser, whenever you want to check it?

    Thx

    Wim

    • I hoped to do the same thing you describe. I made some progress, got frustrated and set it aside for a while. Here’s what I have got so far. This is my sketch:

      https://1drv.ms/u/s!AmXKqAwyCrbxiMArt67eeBAKWoy9Bg?e=ikLvVL

      I have made little progress on the HighChart side. I was not able to make HighCharts grab data from a local file. Using the sketch I shared the data can be downloaded by Highcharts using something like: xmlhttp.open(“GET”, “http://your externalshare:port/temp.csv”, true); Just using file://temp.csv does not appear possible. I guess that there is a security issue of a Java script exposing a local file. Even to make the working example function, I had to put an extension in Chrome ‘Allow-Control-Allow-Origin:’ because I was serving /temp.csv via http and not https. That’s where I gave up. I hope some of this help. Perhaps Rui can shine a light on a better way.

      Bill

      • Wouldnt it be possible that if you make a request as in e.g. in Rui’s example, that you let the server.on”/xxxxxx ” routine read data from an SDcard?

        • Sure Ed, that works. As Dave suggested the Sketch side can be modified to provide more data. The trick is how to modify index.html.

          • ah OK, then i misunderstood.
            About changing the index file, I think I replied that in tw posts below.
            Adding lines check arduinodiy.wordpress.com/2019/08/05/esp8266-plot-sensor-readings-in-real-time-charts-on-a-web-server/
            Using the date: var x = (new Date()).getTime(),
            in which ‘getTime()’ is your function of reading in the time, but it could e.g. be:
            var x=(newDate()).UTC(hh,min,sec)

    • in order to fill in a chart with stored data, you may be able to send a post/get request to the ESP. something like sending a command to turn on a switch.
      but, when you set the flag the ESP would open the spiff and read the last 20 saved data sets, then post one every second.
      that would use the ESP in the same way this is being done for you now.
      my need is for a garden temp , soil, rain. so I only want one reading a day.

  22. Dave, that is a good idea. Should work. Even nicer, I can use most of the code that I have already written. The only drawback to this approach that I can see is the limited life of EEPROM. That’s a lot of reading and writing. My weather station using Wview and a Raspberry PI uses a somewhat similar data structure. I have worn out a couple of uSDs.

    That being said, I don’t have any better ideas.

    • Just occurred to me, If multiple points are sent then the data will also have to include the time and date. I can revise the sketch to create the three responses, temp, humidity and pressure..

      Example, /temp could yield:

      08/06/2019, 08:09, 78.8
      08/06/2019, 08:10, 78.7
      08/06/2019, 08:11, 80.1

      Now, does any one know how to revise index.html to work with that data and is what I wrote above the easiest format to work with?

      • in Every setInterval function, you will find this section
        if (this.readyState == 4 && this.status == 200) {
        var x = (new Date()).getTime(),
        y = parseFloat(this.responseText);

        The value ‘x’, being the date. Instead of ‘getTime()), you need to supply the date that you save

  23. Makes sense to me. Now to figure out how to make HighCharts plot a series.

    For testing, I wrote:

    String hTest = “38 50.3 30.3 28.5 50.5”;

    server.on(“/humidity”, HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, “text/plain”, hTest.c_str());
    });

    As I expected Highchart only display the first value, 38.

  24. Thanks Ed. Now I am closer.

    That example does not address retaining the data but it does describe how to encode the data in the sketch in the familiar JSON string and how to read it to generate multiple lines. In that it address bringing multiple point into HighCharts, that’s pretty close. I think, however, the JSON string would need to be brought into HighCharts as a Series so that an unknown number of points could be plotted.

    This get me a bit closer but not all the way. I may have to read up on HighCharts. Sadly, I know a fair amount of C but not much Java Script.

  25. Hello, I find many of your tutorials very useful.

    I’m trying to modify this tutorial to work with an AD8232 Heart Rate Monitor and my ESP32. Before I tried to implement the web server, I was able to get the AD8232 working and displaying the output in the serial monitor and serial plotter. So I adjusted all of the Arduino code and the html file in this tutorial and was able to connect to the web server. The problem is that the graph only shows one value (4095).

    Do you have any idea how to get the proper values to display?

      • I am actually using ADC2 pins. I will reconfigure it and try again. Thank you very much for responding. I’ll post back and let you know if the issue is resolved.

      • Hello, so I changed the pins to ADC1 and that get the proper values showing. Thank you. Now, since it’s a heart rate monitor that I’m using, I need to increase the number of data points showing as well as increase the rate at which the data is displayed. I’ve tried changing the polling interval in the index.html file, but the rate of display hasn’t changed. Also, I thought I knew which line to change the number of data points shown, but when I changed it

        (if(chartH.series[0].data.length > 40),

        nothing much happened. Can you point me in the right direction to do those two things, please,

  26. Does anyone know how to change the script to plot the first reading immediately on load and then after every 30 seconds.
    It would be nice to be able to plot the first reading immediately, if you increase the time interval to more than 30 sec.
    Hope someone can show this in code.

  27. If anyone is interested, I have solved my problem plotting the first reading immediately on load and then after every 30 seconds, by adding this on top of the new chart section. without //

    // var myVar = setInterval(myTimer, 1000);
    // function myTimer() {
    // var xhttp = new XMLHttpRequest();
    // xhttp.onreadystatechange = function() {
    // if (this.readyState == 4 && this.status == 200) {
    // var x = (new Date()).getTime(),
    // y = parseFloat(this.responseText);
    // //console.log(this.responseText);
    // chartT.series[0].addPoint([x, y], true, false, true);
    // }
    // };
    // xhttp.open(“GET”, “/temperature”, true);
    // xhttp.send();
    // clearInterval(myVar);
    //};

    Jørgen B

  28. here is the code for the three readouts if anyone has trouble implementing it in the code.
    And of course you have to keep the original set interval functions.

    // var myVar = setInterval(myTimer, 1000);
    // function myTimer() {
    // var xhttp = new XMLHttpRequest();
    // xhttp.onreadystatechange = function() {
    // if (this.readyState == 4 && this.status == 200) {
    // var x = (new Date()).getTime(),
    // y = parseFloat(this.responseText);
    // //console.log(this.responseText);
    // chartT.series[0].addPoint([x, y], true, false, true);
    //
    // }
    // };
    // xhttp.open(“GET”, “/temperature”, true);
    // xhttp.send();
    //
    // var xhttp = new XMLHttpRequest();
    // xhttp.onreadystatechange = function() {
    // if (this.readyState == 4 && this.status == 200) {
    // var x = (new Date()).getTime(),
    // y = parseFloat(this.responseText);
    // //console.log(this.responseText);
    // chartH.series[0].addPoint([x, y], true, false, true);
    //
    // }
    // };
    // xhttp.open(“GET”, “/humidity”, true);
    // xhttp.send();
    //
    // var xhttp = new XMLHttpRequest();
    // xhttp.onreadystatechange = function() {
    // if (this.readyState == 4 && this.status == 200) {
    // var x = (new Date()).getTime(),
    // y = parseFloat(this.responseText);
    // //console.log(this.responseText);
    // chartP.series[0].addPoint([x, y], true, false, true);
    //
    // }
    // };
    // xhttp.open(“GET”, “/pressure”, true);
    // xhttp.send(); clearInterval(myVar);
    // clearInterval(myVar);
    // };

  29. The reading on /temperature URL is working, but just the graph is not showing up. It was actually working fine a few days ago and I dont remember changing anything in the code

  30. Hello,

    Thank you for sharing this very helpful post.

    I tried to use the same code but for the DHT22 and it doesn’t work.
    Could you help me ? I used this code:

    (…)

    Thank you very much !

  31. Dear Sara and Rui

    Is there a way to implement the project without an internet access, I mean, the graphs entirely genereated on chip?

    Best regards.
    Carlos.

    • Hi Carlos.
      This approach uses the highcharts library from the internet. So, I don’t think you can do this without internet access.
      There should be another way to do that, but we don’t have any tutorial about it.
      Regards,
      Sara

    • Please read this comment from one of our readers. That should do the trick.


      If anyone needs to run in access point mode, offline.
      Then add this
      /highcharts.js
      to the index.html
      and remove this
      https://code.highcharts.com/highcharts.js
      from the index.html

      Download the highcharts.js file, and put it in the data folder, together with index html.
      Remember to upload data to spiffs.

      Add this to your sketch.
      server.on(“/highcharts.js”, HTTP_GET, [](AsyncWebServerRequest *request){
      request->send(SPIFFS, “/highcharts.js”, “text/javascript”);
      });

      that’s how I did it, to make it work offline in access point mode.”
      I hope this helps.
      Regards,
      Sara

  32. A question: If I would like to host a JSON file that takes care of plotting the points on a graph and the arduino uses that instead of high charts, how would I do that? I am creating a project that is supposed to last a long time in a remote location and am worried that if for whatever reason high charts removes that JSON file from their server, then my project will stopping plotting points.

    • Hi. Vlad.
      Please read this comment from one of our readers. That should do the trick.


      If anyone needs to run in access point mode, offline.
      Then add this
      /highcharts.js
      to the index.html
      and remove this
      https://code.highcharts.com/highcharts.js
      from the index.html

      Download the highcharts.js file, and put it in the data folder, together with index html.
      Remember to upload data to spiffs.

      Add this to your sketch.
      server.on(“/highcharts.js”, HTTP_GET, [](AsyncWebServerRequest *request){
      request->send(SPIFFS, “/highcharts.js”, “text/javascript”);
      });

      that’s how I did it, to make it work offline in access point mode.”
      I hope this helps.
      Regards,
      Sara

  33. If anyone needs to run in access point mode, offline.
    Then add this
    /highcharts.js
    to the index.html
    and remove this
    https://code.highcharts.com/highcharts.js
    from the index.html

    Download the highcharts.js file, and put it in the data folder, together with index html.
    Remember to upload data to spiffs.

    Add this to your sketch.
    server.on(“/highcharts.js”, HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, “/highcharts.js”, “text/javascript”);
    });

    that’s how I did it, to make it work offline in access point mode.

  34. Hi I am posting it again.
    I can see there is missing some of my text in my post.
    Remove the slashes when inserting the code.

    If anyone needs to run in access point mode, offline.
    Then add this
    // /highcharts.js
    to the index.html
    and remove this
    //https://code.highcharts.com/highcharts.js
    from the index.html

    Download the highcharts.js file, and put it in the data folder, together with index html.
    Remember to upload data to spiffs.

    Add this to your sketch.
    // server.on(“/highcharts.js”, HTTP_GET, [](AsyncWebServerRequest *request){
    // request->send(SPIFFS, “/highcharts.js”, “text/javascript”);
    // });

    that’s how I did it, to make it work offline in access point mode.

  35. I can see that some of my text is still missing.
    I don’t know if it is for security reasons that some of my text has been removed from my post.
    I don’t know how to post my code to avoid some of the text being removed.
    Can you help me solve this.
    Regards, J. Bredahl.

    • Hi.
      Some text is omitted for security reasons.
      You can use pastebin to show your code.
      However, I think it was posted correctly now.
      Regards,
      Sara

  36. hi Sara.Thank you very much for your response.
    I can see that some of my comment is still missing.
    I hope your readers can figure out where to put the code and what to remove from the s c r i p t part in the index.html.
    //src=”/highcharts.js
    best regards to you
    J. Bredahl.

  37. As my BME280 sometimes sends corrupted data, e.g.
    temperature -280° or humidity 0% or 100% or pressure 0, all from my living-room, wich results in a nearly straight line with one or two spikes,
    I added the following to avoid these spikes –
    for the temperature:
    if (t>50) {t = 20;
    }
    if (t98) {h = 50;
    }
    if (h<10) {h = 50;
    }

    for pressure:
    if (p1200) {p = 1000;
    }

    To get a wide chart over about 30 hours, in the index.html file I changed

    max-width: 1900px;

    if(chartP.series[0].data.length > 180)

    and interval to 600000 (= 10 min.)

    For better overview I added to title: { text: “… Interval 10 min.”

    For the time zone problem I added
    Highcharts.setOptions({
    global: {
    useUTC: false
    }
    });
    as Sara suggested in July. It works fine.

    Regards,
    Siegfried

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.