{"id":103213,"date":"2021-04-28T13:31:18","date_gmt":"2021-04-28T13:31:18","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=103213"},"modified":"2025-03-14T16:17:48","modified_gmt":"2025-03-14T16:17:48","slug":"esp32-plot-readings-charts-multiple","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-plot-readings-charts-multiple\/","title":{"rendered":"ESP32 Plot Sensor Readings in Charts (Multiple Series)"},"content":{"rendered":"\n<p>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&#8217;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\u2019ll use the Highcharts JavaScript library.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Charts-Multiple-Series-Web-Server.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Plot Sensor Readings in Charts Multiple Series Arduino\" class=\"wp-image-103290\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Charts-Multiple-Series-Web-Server.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Charts-Multiple-Series-Web-Server.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Charts-Multiple-Series-Web-Server.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Charts-Multiple-Series-Web-Server.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>We have a similar tutorial for the ESP8266 NodeMCU board:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp8266-nodemcu-plot-readings-charts-multiple\/\">ESP8266 NodeMCU Plot Sensor Readings in Charts (Multiple Series)<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Project Overview<\/h2>\n\n\n\n<p>This project will build a web server with the ESP32 that displays temperature readings from four DS18B20 temperature sensors on the same chart\u2014chart 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.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"452\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/Sensor-readings-Multiple-Series-ESP32-Project-Overview-02.png?resize=750%2C452&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server Chart with Multiple Series ESP32\" class=\"wp-image-103287\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/Sensor-readings-Multiple-Series-ESP32-Project-Overview-02.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/Sensor-readings-Multiple-Series-ESP32-Project-Overview-02.png?resize=300%2C181&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">DS18B20 Temperature Sensor<\/h3>\n\n\n\n<p>The&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/ds18b20-temperature-sensor-2\/\" target=\"_blank\">DS18B20 temperature sensor<\/a>&nbsp;is a one-wire digital temperature sensor. This means that it just requires one data line to communicate with your microcontroller.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"490\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/07\/DS18B20-tempeature-sensor-pinout.jpg?resize=750%2C490&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"DS18B20 Temperature Sensor one-wire digital sensor module\" class=\"wp-image-86735\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/07\/DS18B20-tempeature-sensor-pinout.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2019\/07\/DS18B20-tempeature-sensor-pinout.jpg?resize=300%2C196&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>Each sensor has a unique 64-bit serial number, which means you can connect multiple sensors to the same GPIO\u2014as we&#8217;ll do in this tutorial. Learn more about the DS18B20 temperature sensor:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-ds18b20-temperature-arduino-ide\/\">ESP32 DS18B20 Temperature Sensor with Arduino IDE (Single, Multiple, Web Server)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-multiple-ds18b20-temperature-sensors\/\">ESP32 with Multiple DS18B20 Temperature Sensors<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Server-Sent Events<\/h3>\n\n\n\n<p>The readings are updated automatically on the web page using Server-Sent Events (SSE). <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"751\" height=\"721\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/Sensor-readings-Multiple-Series-Server-Sent-Events-DS18B20.png?resize=751%2C721&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Sensor Readings Multiple Series Server-Sent Events DS18B20\" class=\"wp-image-103299\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/Sensor-readings-Multiple-Series-Server-Sent-Events-DS18B20.png?w=751&amp;quality=100&amp;strip=all&amp;ssl=1 751w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/Sensor-readings-Multiple-Series-Server-Sent-Events-DS18B20.png?resize=300%2C288&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 751px) 100vw, 751px\" \/><\/figure><\/div>\n\n\n<p>To learn more about SSE, you can read: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\">ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Files Saved on the Filesystem<\/h3>\n\n\n\n<p>To keep our project better organized and easier to understand, we&#8217;ll save the HTML, CSS, and JavaScript files to build the web page on the board&#8217;s filesystem (LittleFS).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>Make sure you check all the prerequisites in this section before continuing with the project.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Install ESP32 Board in Arduino IDE<\/h3>\n\n\n\n<p>We&#8217;ll program the ESP32 using Arduino IDE. So, you must have the ESP32 add-on installed. Follow the next tutorial if you haven&#8217;t already:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-esp32-arduino-ide-2-0\/\" title=\"\">Installing ESP32 Board in Arduino IDE (Windows, Mac OS X, Linux)<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you want to use VS Code with the PlatformIO extension, follow the next tutorial instead to learn how to program the ESP32:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/vs-code-platformio-ide-esp32-esp8266-arduino\/\">Getting Started with VS Code and PlatformIO IDE for ESP32 and ESP8266 (Windows, Mac OS X, Linux Ubuntu)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Filesystem Uploader Plugin<\/h3>\n\n\n\n<p>To upload the HTML, CSS, and JavaScript files to the ESP32 flash memory (LittleFS), we\u2019ll use a plugin for Arduino IDE:&nbsp;<strong>LittleFS<\/strong> <strong>Filesystem uploader<\/strong>. Follow the next tutorial to install the filesystem uploader plugin:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\">Arduino IDE 2: Install ESP32 LittleFS Uploader (Upload Files to the Filesystem)<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you&#8217;re using VS Code with the PlatformIO extension, read the following tutorial to learn how to upload files to the filesystem:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-vs-code-platformio-littlefs\/\">ESP32 with VS Code and PlatformIO: Upload Files to LittleFS Filesystem<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Installing Libraries<\/h3>\n\n\n\n<p>To build this project, you need to install the following libraries:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/PaulStoffregen\/OneWire\" target=\"_blank\" rel=\"noreferrer noopener\">OneWire (by Paul Stoffregen)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.arduinolibraries.info\/libraries\/dallas-temperature\" target=\"_blank\" rel=\"noreferrer noopener\">DallasTemperature<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/arduino-libraries\/Arduino_JSON\" target=\"_blank\" rel=\"noreferrer noopener\">Arduino_JSON library by Arduino version 0.1.0<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncWebServer&nbsp;<\/a>by ESP32Async<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/AsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\">AsyncTCP<\/a> by ESP32Async<\/li>\n<\/ul>\n\n\n\n<p>You can install the libraries using the Arduino Library Manager. Go to\u00a0<strong>Sketch\u00a0<\/strong>>\u00a0<strong>Include Library<\/strong>\u00a0>\u00a0<strong>Manage Libraries<\/strong>\u00a0and search for the libraries&#8217; names.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parts Required<\/h2>\n\n\n\n<p>To follow this tutorial you need the following parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\">ESP32<\/a>&nbsp;(read&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\">Best ESP32 development boards<\/a>)<\/li>\n\n\n\n<li>4x <a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/ds18b20-temperature-sensor-2\/\" target=\"_blank\">DS18B20 temperature sensor<\/a>&nbsp;(one or multiple sensors) \u2013&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/ds18b20-digital-temperature-sensor\/\" target=\"_blank\">waterproof version<\/a><\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/resistors-kits\/\" target=\"_blank\">4.7k Ohm resistor<\/a><\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\">Jumper wires<\/a><\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\">Breadboard<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you don&#8217;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\u2014to experiment with this project&#8230;)<\/p>\n\n\n<p>You can use the preceding links or go directly to <a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\">MakerAdvisor.com\/tools<\/a> to find all the parts for your projects at the best price!<\/p><p style=\"text-align:center;\"><a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/10\/header-200.png?w=1200&#038;quality=100&#038;strip=all&#038;ssl=1\"><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Schematic Diagram<\/h2>\n\n\n\n<p>Wire four DS18B20 sensors to your board.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"898\" height=\"962\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-DS18B20-Sensors-Diagram.png?resize=898%2C962&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Multiple DS18B20 Sensors Diagram Circuit wiring\" class=\"wp-image-103308\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-DS18B20-Sensors-Diagram.png?w=898&amp;quality=100&amp;strip=all&amp;ssl=1 898w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-DS18B20-Sensors-Diagram.png?resize=280%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-DS18B20-Sensors-Diagram.png?resize=768%2C823&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 898px) 100vw, 898px\" \/><\/figure><\/div>\n\n\n<p class=\"rntbox rntclblue\">Recommended reading: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-pinout-reference-gpios\/\">ESP32 Pinout Reference: Which GPIO pins should you use?<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"address\">Getting the DS18B20 Sensors&#8217; Addresses<\/h2>\n\n\n\n<p>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\u2019re reading the temperature.<\/p>\n\n\n\n<p>Upload the following code to the ESP32. Make sure you have the right board and COM port selected.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\n * Rui Santos \n * Complete Project Details https:\/\/randomnerdtutorials.com\n *\/\n\n#include &lt;OneWire.h&gt;\n\n\/\/ Based on the OneWire library example\n\nOneWire ds(4);  \/\/data wire connected to GPIO 4\n\nvoid setup(void) {\n  Serial.begin(115200);\n}\n\nvoid loop(void) {\n  byte i;\n  byte addr[8];\n  \n  if (!ds.search(addr)) {\n    Serial.println(&quot; No more addresses.&quot;);\n    Serial.println();\n    ds.reset_search();\n    delay(250);\n    return;\n  }\n  Serial.print(&quot; ROM =&quot;);\n  for (i = 0; i &lt; 8; i++) {\n    Serial.write(' ');\n    Serial.print(addr[i], HEX);\n  }\n}\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/Find_DS18B20_Addresses.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Wire just one sensor at a time to find its address (or successively add a new sensor) so that you\u2019re able to identify each one by its address. Then, you can add a physical label to each sensor.<\/p>\n\n\n\n<p>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):<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"412\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/DS18B20-addresses-Serial-Monitor.png?resize=1200%2C412&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Getting onewire DS18B20 address Serial Monitor\" class=\"wp-image-103227\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/DS18B20-addresses-Serial-Monitor.png?w=1529&amp;quality=100&amp;strip=all&amp;ssl=1 1529w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/DS18B20-addresses-Serial-Monitor.png?resize=300%2C103&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/DS18B20-addresses-Serial-Monitor.png?resize=1024%2C352&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/DS18B20-addresses-Serial-Monitor.png?resize=768%2C264&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>Untick the \u201c<strong>Autoscroll<\/strong>\u201d option so that you\u2019re able to copy the addresses. In our case, we\u2019ve got the following addresses:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sensor 1:&nbsp;<strong>28 FF A0 11 33 17 3 96<\/strong><\/li>\n\n\n\n<li>Sensor 2:&nbsp;<strong>28 FF B4 6 33 17 3 4B<\/strong><\/li>\n\n\n\n<li>Sensor 3:&nbsp;<strong>28 FF 11 28 33 18 1 6B<\/strong><\/li>\n\n\n\n<li>Sensor 4: <strong>28 FF 43 F5 32 18 2 A8<\/strong><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Organizing Your Files<\/h2>\n\n\n\n<p>To keep the project organized and make it easier to understand, we\u2019ll create four files to build the web server:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Arduino sketch&nbsp;<\/strong>that handles the web server;<\/li>\n\n\n\n<li><strong>index.html<\/strong>: to define the content of the web page;<\/li>\n\n\n\n<li><strong>sytle.css<\/strong>: to style the web page;<\/li>\n\n\n\n<li><strong>script.js<\/strong>: to program the behavior of the web page\u2014handle web server responses, events, create the chart, etc.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/folder-structure-1.png?quality=100&#038;strip=all&#038;ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"511\" height=\"358\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/folder-structure-1.png?resize=511%2C358&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Organizing Your Files Arduino sketch index html css javascript\" class=\"wp-image-158912\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/folder-structure-1.png?w=511&amp;quality=100&amp;strip=all&amp;ssl=1 511w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/folder-structure-1.png?resize=300%2C210&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 511px) 100vw, 511px\" \/><\/a><\/figure><\/div>\n\n\n<p>You should save the HTML, CSS, and JavaScript files inside a folder called&nbsp;<em><strong>data<\/strong>&nbsp;<\/em>inside the Arduino sketch folder, as shown in the previous diagram. We\u2019ll upload these files to the ESP32 filesystem (LittleFS).<\/p>\n\n\n\n<p><strong>You can download all project files:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Charts_Multiple_Series.zip\" target=\"_blank\" rel=\"noreferrer noopener\">Download All the Arduino Project Files<\/a><\/strong><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"html-file\">HTML File<\/h2>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">index.html<\/span> file.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!-- Complete project details: https:\/\/randomnerdtutorials.com\/esp32-plot-readings-charts-multiple\/ --&gt;\r\n\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n  &lt;head&gt;\r\n    &lt;title&gt;ESP IOT DASHBOARD&lt;\/title&gt;\r\n    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n    &lt;link rel=&quot;icon&quot; type=&quot;image\/png&quot; href=&quot;favicon.png&quot;&gt;\r\n    &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;style.css&quot;&gt;\r\n    &lt;script src=&quot;https:\/\/code.highcharts.com\/highcharts.js&quot;&gt;&lt;\/script&gt;\r\n  &lt;\/head&gt;\r\n  &lt;body&gt;\r\n    &lt;div class=&quot;topnav&quot;&gt;\r\n      &lt;h1&gt;ESP WEB SERVER CHARTS&lt;\/h1&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class=&quot;content&quot;&gt;\r\n      &lt;div class=&quot;card-grid&quot;&gt;\r\n        &lt;div class=&quot;card&quot;&gt;\r\n          &lt;p class=&quot;card-title&quot;&gt;Temperature Chart&lt;\/p&gt;\r\n          &lt;div id=&quot;chart-temperature&quot; class=&quot;chart-container&quot;&gt;&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;script src=&quot;script.js&quot;&gt;&lt;\/script&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Charts_Multiple_Series\/data\/index.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>The HTML file for this project is very simple. It includes the <a href=\"https:\/\/www.highcharts.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">JavaScript Highcharts library<\/a> in the head of the HTML file:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;script src=\"https:\/\/code.highcharts.com\/highcharts.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>There is a <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;div&gt;<\/span><\/span> section with the id <span class=\"rnthl rntliteral\">chart-temperature<\/span> where we&#8217;ll render our chart later on. <\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div id=\"chart-temperature\" class=\"chart-container\"&gt;&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">CSS File<\/h2>\n\n\n\n<p>Copy the following styles to your <span class=\"rnthl rntliteral\">style.css<\/span> file.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-css\">\/*  Complete project details: https:\/\/randomnerdtutorials.com\/esp32-plot-readings-charts-multiple\/  *\/\r\n\r\nhtml {\r\n  font-family: Arial, Helvetica, sans-serif;\r\n  display: inline-block;\r\n  text-align: center;\r\n}\r\nh1 {\r\n  font-size: 1.8rem;\r\n  color: white;\r\n}\r\np {\r\n  font-size: 1.4rem;\r\n}\r\n.topnav {\r\n  overflow: hidden;\r\n  background-color: #0A1128;\r\n}\r\nbody {\r\n  margin: 0;\r\n}\r\n.content {\r\n  padding: 5%;\r\n}\r\n.card-grid {\r\n  max-width: 1200px;\r\n  margin: 0 auto;\r\n  display: grid;\r\n  grid-gap: 2rem;\r\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\r\n}\r\n.card {\r\n  background-color: white;\r\n  box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);\r\n}\r\n.card-title {\r\n  font-size: 1.2rem;\r\n  font-weight: bold;\r\n  color: #034078\r\n}\r\n.chart-container {\r\n  padding-right: 5%;\r\n  padding-left: 5%;\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Charts_Multiple_Series\/data\/style.css\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">JavaScript File (creating the charts)<\/h2>\n\n\n\n<p>Copy the following to the&nbsp;<span class=\"rnthl rntliteral\">script.js<\/span>&nbsp;file. Here&#8217;s a list of what this code does:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>initializing the event source protocol;<\/li>\n\n\n\n<li>adding an event listener for the <span class=\"rnthl rntliteral\">new_readings<\/span> event;<\/li>\n\n\n\n<li>creating the chart;<\/li>\n\n\n\n<li>getting the latest sensor readings from the <span class=\"rnthl rntliteral\">new_readings<\/span> event and plot them in the chart;<\/li>\n\n\n\n<li>making an HTTP GET request for the current sensor readings when you access the web page for the first time.<\/li>\n<\/ul>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">\/\/ Complete project details: https:\/\/randomnerdtutorials.com\/esp32-plot-readings-charts-multiple\/\r\n\r\n\/\/ Get current sensor readings when the page loads\r\nwindow.addEventListener('load', getReadings);\r\n\r\n\/\/ Create Temperature Chart\r\nvar chartT = new Highcharts.Chart({\r\n  chart:{\r\n    renderTo:'chart-temperature'\r\n  },\r\n  series: [\r\n    {\r\n      name: 'Temperature #1',\r\n      type: 'line',\r\n      color: '#101D42',\r\n      marker: {\r\n        symbol: 'circle',\r\n        radius: 3,\r\n        fillColor: '#101D42',\r\n      }\r\n    },\r\n    {\r\n      name: 'Temperature #2',\r\n      type: 'line',\r\n      color: '#00A6A6',\r\n      marker: {\r\n        symbol: 'square',\r\n        radius: 3,\r\n        fillColor: '#00A6A6',\r\n      }\r\n    },\r\n    {\r\n      name: 'Temperature #3',\r\n      type: 'line',\r\n      color: '#8B2635',\r\n      marker: {\r\n        symbol: 'triangle',\r\n        radius: 3,\r\n        fillColor: '#8B2635',\r\n      }\r\n    },\r\n    {\r\n      name: 'Temperature #4',\r\n      type: 'line',\r\n      color: '#71B48D',\r\n      marker: {\r\n        symbol: 'triangle-down',\r\n        radius: 3,\r\n        fillColor: '#71B48D',\r\n      }\r\n    },\r\n  ],\r\n  title: {\r\n    text: undefined\r\n  },\r\n  xAxis: {\r\n    type: 'datetime',\r\n    dateTimeLabelFormats: { second: '%H:%M:%S' }\r\n  },\r\n  yAxis: {\r\n    title: {\r\n      text: 'Temperature Celsius Degrees'\r\n    }\r\n  },\r\n  credits: {\r\n    enabled: false\r\n  }\r\n});\r\n\r\n\r\n\/\/Plot temperature in the temperature chart\r\nfunction plotTemperature(jsonValue) {\r\n\r\n  var keys = Object.keys(jsonValue);\r\n  console.log(keys);\r\n  console.log(keys.length);\r\n\r\n  for (var i = 0; i &lt; keys.length; i++){\r\n    var x = (new Date()).getTime();\r\n    console.log(x);\r\n    const key = keys[i];\r\n    var y = Number(jsonValue[key]);\r\n    console.log(y);\r\n\r\n    if(chartT.series[i].data.length &gt; 40) {\r\n      chartT.series[i].addPoint([x, y], true, true, true);\r\n    } else {\r\n      chartT.series[i].addPoint([x, y], true, false, true);\r\n    }\r\n\r\n  }\r\n}\r\n\r\n\/\/ Function to get current readings on the webpage when it loads for the first time\r\nfunction getReadings(){\r\n  var xhr = new XMLHttpRequest();\r\n  xhr.onreadystatechange = function() {\r\n    if (this.readyState == 4 &amp;&amp; this.status == 200) {\r\n      var myObj = JSON.parse(this.responseText);\r\n      console.log(myObj);\r\n      plotTemperature(myObj);\r\n    }\r\n  };\r\n  xhr.open(&quot;GET&quot;, &quot;\/readings&quot;, true);\r\n  xhr.send();\r\n}\r\n\r\nif (!!window.EventSource) {\r\n  var source = new EventSource('\/events');\r\n\r\n  source.addEventListener('open', function(e) {\r\n    console.log(&quot;Events Connected&quot;);\r\n  }, false);\r\n\r\n  source.addEventListener('error', function(e) {\r\n    if (e.target.readyState != EventSource.OPEN) {\r\n      console.log(&quot;Events Disconnected&quot;);\r\n    }\r\n  }, false);\r\n\r\n  source.addEventListener('message', function(e) {\r\n    console.log(&quot;message&quot;, e.data);\r\n  }, false);\r\n\r\n  source.addEventListener('new_readings', function(e) {\r\n    console.log(&quot;new_readings&quot;, e.data);\r\n    var myObj = JSON.parse(e.data);\r\n    console.log(myObj);\r\n    plotTemperature(myObj);\r\n  }, false);\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Charts_Multiple_Series\/data\/script.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Get Readings<\/h3>\n\n\n\n<p>When you access the web page for the first time, we\u2019ll 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.<\/p>\n\n\n\n<p>Add an event listener that calls the <span class=\"rnthl rntliteral\">getReadings<\/span> function when the web page loads.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Get current sensor readings when the page loads\nwindow.addEventListener('load', getReadings);<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">window<\/span> object represents an open window in a browser. The <span class=\"rnthl rntliteral\">addEventListener()<\/span> method sets up a function to be called when a certain event happens. In this case, we\u2019ll call the <span class=\"rnthl rntliteral\">getReadings<\/span> function when the page loads (<span class=\"rnthl rntliteral\">&#8216;load&#8217;<\/span>) to get the current sensor readings.<\/p>\n\n\n\n<p>Now, let\u2019s take a look at the <span class=\"rnthl rntliteral\">getReadings<\/span> function. Create a new <span class=\"rnthl rntliteral\">XMLHttpRequest<\/span> object. Then, send a <span class=\"rnthl rntliteral\">GET<\/span> request to the server on the <span class=\"rnthl rntliteral\">\/readings<\/span> URL using the <span class=\"rnthl rntliteral\">open()<\/span> and <span class=\"rnthl rntliteral\">send()<\/span> methods.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function getReadings() {\n  var xhr = new XMLHttpRequest();\n  xhr.open(\"GET\", \"\/readings\", true);\n  xhr.send();\n}<\/code><\/pre>\n\n\n\n<p>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\u2019ll use the <span class=\"rnthl rntliteral\">onreadystatechange<\/span> property that defines a function to be executed when the <span class=\"rnthl rntliteral\">readyState<\/span> property changes. The <span class=\"rnthl rntliteral\">readyState<\/span> property holds the status of the <span class=\"rnthl rntliteral\">XMLHttpRequest<\/span>. The response of the request is ready when the <span class=\"rnthl rntliteral\">readyState<\/span> is <span class=\"rnthl rntliteral\">4<\/span>, and the status is <span class=\"rnthl rntliteral\">200<\/span>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span class=\"rnthl rntliteral\">readyState = 4<\/span> means that the request finished and the response is ready;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">status = 200<\/span> means \u201cOK\u201d<\/li>\n<\/ul>\n\n\n\n<p>So, the request should look something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function getStates(){\n  var xhr = new XMLHttpRequest();\n  xhr.onreadystatechange = function() {\n    if (this.readyState == 4 &amp;&amp; this.status == 200) {\n      \u2026 DO WHATEVER YOU WANT WITH THE RESPONSE \u2026\n    }\n  };\n  xhr.open(\"GET\", \"\/states\", true);\n  xhr.send();\n}<\/code><\/pre>\n\n\n\n<p>The response sent by the ESP is the following text in JSON format.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>{\n  \"sensor1\" : \"25\",\n  \"sensor2\" : \"21\",\n  \"sensor3\" : \"22\",\n  \"sensor4\" : \"23\"\n}<\/code><\/pre>\n\n\n\n<p>We need to convert the JSON string into a JSON object using the <span class=\"rnthl rntliteral\">parse()<\/span> method. The result is saved on the <span class=\"rnthl rntliteral\">myObj<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var myObj = JSON.parse(this.responseText);<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">myObj<\/span> varible is a JSON object that contains all the temperature readings. We want to plot those readings on the same chart. For that, we&#8217;ve created a function called <span class=\"rnthl rntliteral\">plotTemperature()<\/span> that plots the temperatures stored in a JSON object on a chart.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>plotTemperature(myObj);<\/code><\/pre>\n\n\n\n<p>Here\u2019s the complete <span class=\"rnthl rntliteral\">getReadings()<\/span> function.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function getReadings(){\n  var xhr = new XMLHttpRequest();\n  xhr.onreadystatechange = function() {\n    if (this.readyState == 4 &amp;&amp; this.status == 200) {\n      var myObj = JSON.parse(this.responseText);\n      console.log(myObj);\n      plotTemperature(myObj);\n    }\n  }; \n  xhr.open(\"GET\", \"\/readings\", true);\n  xhr.send();\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the Chart<\/h3>\n\n\n\n<p>The following lines create the charts with multiple series.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Create Temperature Chart\nvar chartT = new Highcharts.Chart({\n  chart:{ \n    renderTo:'chart-temperature' \n  },\n  series: &#091;\n    {\n      name: 'Temperature #1',\n      type: 'line',\n      color: '#101D42',\n      marker: {\n        symbol: 'circle',\n        radius: 3,\n        fillColor: '#101D42',\n      }\n    },\n    {\n      name: 'Temperature #2',\n      type: 'line',\n      color: '#00A6A6',\n      marker: {\n        symbol: 'square',\n        radius: 3,\n        fillColor: '#00A6A6',\n      }\n    },\n    {\n      name: 'Temperature #3',\n      type: 'line',\n      color: '#8B2635',\n      marker: {\n        symbol: 'triangle',\n        radius: 3,\n        fillColor: '#8B2635',\n      }\n    },\n    {\n      name: 'Temperature #4',\n      type: 'line',\n      color: '#71B48D',\n      marker: {\n        symbol: 'triangle-down',\n        radius: 3,\n        fillColor: '#71B48D',\n      }\n    },\n  ],\n  title: { \n    text: undefined\n  },\n  xAxis: {\n    type: 'datetime',\n    dateTimeLabelFormats: { second: '%H:%M:%S' }\n  },\n  yAxis: {\n    title: { \n      text: 'Temperature Celsius Degrees' \n    }\n  },\n  credits: { \n    enabled: false \n  }\n});<\/code><\/pre>\n\n\n\n<p>To create a new chart, use the new <span class=\"rnthl rntliteral\">Highcharts.Chart()<\/span> method and pass as argument the chart properties.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var chartT = new Highcharts.Chart({<\/code><\/pre>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">chart-temperature<\/span> id\u2014see the <a href=\"#html-file\">HTML file section<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>chart:{ \n  renderTo:'chart-temperature' \n},<\/code><\/pre>\n\n\n\n<p>Then, define the options for the series. The following lines create the first series:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>series: &#091;\n  {\n    name: 'Temperature #1',\n    type: 'line',\n    color: '#101D42',\n    marker: {\n      symbol: 'circle',\n      radius: 3,\n      fillColor: '#101D42',\n  }<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">name<\/span> property defines the series name. The <span class=\"rnthl rntliteral\">type<\/span> property defines the type of chart\u2014in this case, we want to build a line chart. The <span class=\"rnthl rntliteral\">color<\/span> refers to the color of the line\u2014you can change it to whatever color you desire. <\/p>\n\n\n\n<p>Next, define the <span class=\"rnthl rntliteral\">marker<\/span> properties. You can choose from several default symbols\u2014<span class=\"rnthl rntliteral\">square<\/span>, <span class=\"rnthl rntliteral\">circle<\/span>, <span class=\"rnthl rntliteral\">diamond<\/span>, <span class=\"rnthl rntliteral\">triangle<\/span>, <span class=\"rnthl rntliteral\">triangle-down<\/span>. You can also create your own symbols. The <span class=\"rnthl rntliteral\">radius<\/span> refers to the size of the marker, and the <span class=\"rnthl rntliteral\">fillColor<\/span> refers to the color of the marker. There are other properties you can use to customize the marker\u2014<a href=\"https:\/\/api.highcharts.com\/highcharts\/plotOptions.series.marker\" target=\"_blank\" rel=\"noreferrer noopener\">learn more<\/a>. <\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>marker: {\n  symbol: 'circle',\n  radius: 3,\n  fillColor: '#101D42',\n}<\/code><\/pre>\n\n\n\n<p>Creating the other series is similar, but we&#8217;ve chosen different names, markers and colors.<\/p>\n\n\n\n<p>There are many other options you can use to customize your series\u2014<a href=\"https:\/\/api.highcharts.com\/highcharts\/plotOptions.series\" target=\"_blank\" rel=\"noreferrer noopener\">check the documentation about plotOptions<\/a>.<\/p>\n\n\n\n<p>You can also define the chart title\u2014in this case, as we&#8217;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 <span class=\"rnthl rntliteral\">undefined<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>title: { \n  text: undefined\n},<\/code><\/pre>\n\n\n\n<p>Define the properties for the X axis\u2014this is the axis where we&#8217;ll display data and time. Check more options to <a href=\"https:\/\/api.highcharts.com\/highcharts\/xAxis\" target=\"_blank\" rel=\"noreferrer noopener\">customize the X axis<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>xAxis: {\n  type: 'datetime',\n  dateTimeLabelFormats: { second: '%H:%M:%S' }\n},<\/code><\/pre>\n\n\n\n<p>We set the title for the y axis. See all available <a href=\"https:\/\/api.highcharts.com\/highcharts\/yAxis\" target=\"_blank\" rel=\"noreferrer noopener\">properties for the y axis<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>yAxis: {\n  title: { \n    text: 'Temperature Celsius Degrees' \n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"timezone\">Time Zone<\/h4>\n\n\n\n<p>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:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>Highcharts.setOptions({\n  time: {\n    timezoneOffset: -60 \/\/Add your time zone offset here in minutes\n  }\n});<\/code><\/pre>\n\n\n\n<p>The charts will show the time in UTC. If you want it to display in your timezone, you must set the <span class=\"rnthl rntliteral\">useUTC<\/span> parameter (which is a <span class=\"rnthl rntliteral\">time<\/span> parameter) as <span class=\"rnthl rntliteral\">false<\/span>:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>time:{\n  useUTC: false\n},<\/code><\/pre>\n\n\n\n<p>So, add that when creating the chart as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var chart = new Highcharts.Chart({\n  time:{\n    useUTC: false\n  },\n(\u2026)<\/code><\/pre>\n\n\n\n<p>To learn more about this property, check this link on the documentation: <a href=\"https:\/\/api.highcharts.com\/highcharts\/time.useUTC\" target=\"_blank\" rel=\"noopener\" title=\"\">https:\/\/api.highcharts.com\/highcharts\/time.useUTC<\/a><\/p>\n\n\n\n<p>Finally, set the <span class=\"rnthl rntliteral\">credits<\/span> option to <span class=\"rnthl rntliteral\">false<\/span> to hide the credits of the Highcharts library.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>credits: { \n  enabled: false \n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Plot Temperatures<\/h3>\n\n\n\n<p>We&#8217;ve created the <span class=\"rnthl rntliteral\">plotTemperature()<\/span> function that accepts as an argument a JSON object with the temperature readings we want to plot.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/Plot temperature in the temperature chart\nfunction plotTemperature(jsonValue) {\n\n  var keys = Object.keys(jsonValue);\n  console.log(keys);\n  console.log(keys.length);\n\n  for (var i = 0; i &lt; keys.length; i++){\n    var x = (new Date()).getTime();\n    console.log(x);\n    const key = keys&#091;i];\n    var y = Number(jsonValue&#091;key]);\n    console.log(y);\n    \n    if(chartT.series&#091;i].data.length &gt; 40) {\n      chartT.series&#091;i].addPoint(&#091;x, y], true, true, true);\n    } else {\n      chartT.series&#091;i].addPoint(&#091;x, y], true, false, true);\n    }\n \n  }\n}<\/code><\/pre>\n\n\n\n<p>First, we get the keys of our JSON object and save them on the <span class=\"rnthl rntliteral\">keys<\/span> variable. This allows us to go through all the keys in the object.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var keys = Object.keys(jsonValue);<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">keys<\/span> variable will be an array with all the keys in the JSON object. In our case:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>&#091;\"sensor1\", \"sensor2\", \"sensor3\", \"sensor4\"]<\/code><\/pre>\n\n\n\n<p>This works if you have a JSON object with a different number of keys or with different keys. Then, we&#8217;ll go through all the keys (<span class=\"rnthl rntliteral\">keys.length()<\/span>) to plot each of its value in the chart.<\/p>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">x<\/span> value for the chart is the timestamp.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var x = (new Date()).getTime()<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">key<\/span> variable holds the current key in the loop. The first time we go through the loop, the <span class=\"rnthl rntliteral\">key<\/span> variable is <span class=\"rnthl rntliteral\">&#8220;sensor1&#8221;<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>const key = keys&#091;i];<\/code><\/pre>\n\n\n\n<p>Then, we get the value of the key (<span class=\"rnthl rntliteral\">jsonValue[key]<\/span>) and save it as a number in the <span class=\"rnthl rntliteral\">y<\/span> variable.<\/p>\n\n\n\n<p>Our chart has multiple series (index starts at 0). We can access the first series in the<br>temperature chart using: <span class=\"rnthl rntliteral\">chartT.series[0]<\/span>, which corresponds to <span class=\"rnthl rntliteral\">chartT.series[i]<\/span> the first time we go through the loop.<\/p>\n\n\n\n<p>First, we check the series data length:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If the series has more than 40 points: append and shift a new point;<\/li>\n\n\n\n<li>Or if the series has less than 40 points: append a new point.<\/li>\n<\/ul>\n\n\n\n<p>To add a new point use the <span class=\"rnthl rntliteral\">addPoint()<\/span> method that accepts the following arguments:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The value to be plotted. If it is a single number, a point with that <span class=\"rnthl rntliteral\">y<\/span> value is<br>appended to the series. If it is an array, it will be interpreted as <span class=\"rnthl rntliteral\">x<\/span> and <span class=\"rnthl rntliteral\">y<\/span> values. In our case, we pass an array with the <span class=\"rnthl rntliteral\">x<\/span> and <span class=\"rnthl rntliteral\">y<\/span> values;<\/li>\n\n\n\n<li>Redraw option (boolean): set to <span class=\"rnthl rntliteral\">true<\/span> to redraw the chart after the point is added.<\/li>\n\n\n\n<li>Shift option (boolean): If <span class=\"rnthl rntliteral\">true<\/span>, 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 <span class=\"rnthl rntliteral\">true<\/span>.<\/li>\n\n\n\n<li>withEvent option (boolean): Used internally to fire the series addPoint event\u2014learn more <a href=\"https:\/\/api.highcharts.com\/class-reference\/Highcharts.Series\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/li>\n<\/ul>\n\n\n\n<p>So, to add a point to the chart, we use the next lines:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>if(chartT.series&#091;i].data.length &gt; 40) {\n  chartT.series&#091;i].addPoint(&#091;x, y], true, true, true);\n} else {\n  chartT.series&#091;i].addPoint(&#091;x, y], true, false, true);\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Handle events<\/h3>\n\n\n\n<p>Plot the readings on the charts when the client receives the readings on the <span class=\"rnthl rntliteral\">new_readings<\/span> event.<\/p>\n\n\n\n<p>Create a new <span class=\"rnthl rntliteral\">EventSource<\/span> object and specify the URL of the page sending the updates. In our case, it\u2019s <span class=\"rnthl rntliteral\">\/events<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>if (!!window.EventSource) {\n  var source = new EventSource('\/events');<\/code><\/pre>\n\n\n\n<p>Once you\u2019ve instantiated an event source, you can start listening for messages from the server with <span class=\"rnthl rntliteral\">addEventListener()<\/span>.<\/p>\n\n\n\n<p>These are the default event listeners, as shown here in the AsyncWebServer&nbsp;<a href=\"https:\/\/github.com\/me-no-dev\/ESPAsyncWebServer#setup-event-source-in-the-browser\" target=\"_blank\" rel=\"noreferrer noopener\">documentation<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('open', function(e) {\n  console.log(\"Events Connected\");\n}, false);\n\nsource.addEventListener('error', function(e) {\n  if (e.target.readyState != EventSource.OPEN) {\n    console.log(\"Events Disconnected\");\n  }\n}, false);\n\nsource.addEventListener('message', function(e) {\n  console.log(\"message\", e.data);\n}, false);<\/code><\/pre>\n\n\n\n<p>Then, add the event listener for <span class=\"rnthl rntliteral\">new_readings<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('new_readings', function(e) {<\/code><\/pre>\n\n\n\n<p>When new readings are available, the ESP32 sends an event (<span class=\"rnthl rntliteral\">new_readings<\/span>) to the client. The following lines handle what happens when the browser receives that event.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('new_readings', function(e) {\n  console.log(\"new_readings\", e.data);\n  var myObj = JSON.parse(e.data);\n  console.log(myObj);\n  plotTemperature(myObj);\n}, false);<\/code><\/pre>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">plotTemperature()<\/span> function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Arduino Sketch<\/h2>\n\n\n\n<p>Copy the following code to your Arduino IDE or to the <span class=\"rnthl rntliteral\">main.cpp<\/span> file if you&#8217;re using PlatformIO.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*********\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete instructions at https:\/\/RandomNerdTutorials.com\/esp32-plot-readings-charts-multiple\/\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*********\/\r\n#include &lt;Arduino.h&gt;\r\n#include &lt;WiFi.h&gt;\r\n#include &lt;AsyncTCP.h&gt;\r\n#include &lt;ESPAsyncWebServer.h&gt;\r\n#include &quot;LittleFS.h&quot;\r\n#include &lt;Arduino_JSON.h&gt;\r\n#include &lt;OneWire.h&gt;\r\n#include &lt;DallasTemperature.h&gt;\r\n\r\n\/\/ Replace with your network credentials\r\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\r\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\r\n\r\n\/\/ Create AsyncWebServer object on port 80\r\nAsyncWebServer server(80);\r\n\r\n\/\/ Create an Event Source on \/events\r\nAsyncEventSource events(&quot;\/events&quot;);\r\n\r\n\/\/ Json Variable to Hold Sensor Readings\r\nJSONVar readings;\r\n\r\n\/\/ Timer variables\r\nunsigned long lastTime = 0;\r\nunsigned long timerDelay = 30000;\r\n\r\n\/\/ GPIO where the DS18B20 sensors are connected to\r\nconst int oneWireBus = 4;\r\n\r\n\/\/ Setup a oneWire instance to communicate with OneWire devices (DS18B20)\r\nOneWire oneWire(oneWireBus);\r\n\r\n\/\/ Pass our oneWire reference to Dallas Temperature sensor\r\nDallasTemperature sensors(&amp;oneWire);\r\n\r\n\/\/ Address of each sensor\r\nDeviceAddress sensor3 = { 0x28, 0xFF, 0xA0, 0x11, 0x33, 0x17, 0x3, 0x96 };\r\nDeviceAddress sensor1 = { 0x28, 0xFF, 0xB4, 0x6, 0x33, 0x17, 0x3, 0x4B };\r\nDeviceAddress sensor2 = { 0x28, 0xFF, 0x43, 0xF5, 0x32, 0x18, 0x2, 0xA8 };\r\nDeviceAddress sensor4 = { 0x28, 0xFF, 0x11, 0x28, 0x33, 0x18, 0x1, 0x6B };\r\n\r\n\/\/ Get Sensor Readings and return JSON object\r\nString getSensorReadings(){\r\n  sensors.requestTemperatures();\r\n  readings[&quot;sensor1&quot;] = String(sensors.getTempC(sensor1));\r\n  readings[&quot;sensor2&quot;] = String(sensors.getTempC(sensor2));\r\n  readings[&quot;sensor3&quot;] = String(sensors.getTempC(sensor3));\r\n  readings[&quot;sensor4&quot;] = String(sensors.getTempC(sensor4));\r\n\r\n  String jsonString = JSON.stringify(readings);\r\n  return jsonString;\r\n}\r\n\r\n\/\/ Initialize LittleFS\r\nvoid initLittleFS() {\r\n  if (!LittleFS.begin()) {\r\n    Serial.println(&quot;An error has occurred while mounting LittleFS&quot;);\r\n  }\r\n  else{\r\n    Serial.println(&quot;LittleFS mounted successfully&quot;);\r\n  }\r\n}\r\n\r\n\/\/ Initialize WiFi\r\nvoid initWiFi() {\r\n  WiFi.mode(WIFI_STA);\r\n  WiFi.begin(ssid, password);\r\n  Serial.print(&quot;Connecting to WiFi ..&quot;);\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    Serial.print('.');\r\n    delay(1000);\r\n  }\r\n  Serial.println(WiFi.localIP());\r\n}\r\n\r\nvoid setup() {\r\n  \/\/ Serial port for debugging purposes\r\n  Serial.begin(115200);\r\n  initWiFi();\r\n  initLittleFS();\r\n\r\n  \/\/ Web Server Root URL\r\n  server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(LittleFS, &quot;\/index.html&quot;, &quot;text\/html&quot;);\r\n  });\r\n\r\n  server.serveStatic(&quot;\/&quot;, LittleFS, &quot;\/&quot;);\r\n\r\n  \/\/ Request for the latest sensor readings\r\n  server.on(&quot;\/readings&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    String json = getSensorReadings();\r\n    request-&gt;send(200, &quot;application\/json&quot;, json);\r\n    json = String();\r\n  });\r\n\r\n  events.onConnect([](AsyncEventSourceClient *client){\r\n    if(client-&gt;lastId()){\r\n      Serial.printf(&quot;Client reconnected! Last message ID that it got is: %u\\n&quot;, client-&gt;lastId());\r\n    }\r\n    \/\/ send event with message &quot;hello!&quot;, id current millis\r\n    \/\/ and set reconnect delay to 1 second\r\n    client-&gt;send(&quot;hello!&quot;, NULL, millis(), 10000);\r\n  });\r\n  server.addHandler(&amp;events);\r\n\r\n  \/\/ Start server\r\n  server.begin();\r\n}\r\n\r\nvoid loop() {\r\n  if ((millis() - lastTime) &gt; timerDelay) {\r\n    \/\/ Send Events to the client with the Sensor Readings Every 10 seconds\r\n    events.send(&quot;ping&quot;,NULL,millis());\r\n    events.send(getSensorReadings().c_str(),&quot;new_readings&quot; ,millis());\r\n    lastTime = millis();\r\n  }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Charts_Multiple_Series\/ESP32_Charts_Multiple_Series.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the code works<\/h3>\n\n\n\n<p>Let\u2019s take a look at the code and see how it works to send readings to the client using server-sent events.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Including Libraries<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">OneWire<\/span> and <span class=\"rnthl rntliteral\">DallasTemperature<\/span> libraries are needed to interface with the DS18B20 temperature sensors.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;OneWire.h&gt;\n#include &lt;DallasTemperature.h&gt;<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">WiFi<\/span>, <span class=\"rnthl rntliteral\">ESPAsyncWebServer<\/span> and <span class=\"rnthl rntliteral\">AsyncTCP<\/span> libraries are used to create the web server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Arduino.h&gt;\n#include &lt;WiFi.h&gt;\n#include &lt;AsyncTCP.h&gt;\n#include &lt;ESPAsyncWebServer.h&gt;<\/code><\/pre>\n\n\n\n<p>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.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include \"LittleFS.h\"<\/code><\/pre>\n\n\n\n<p>You also need to include the <span class=\"rnthl rntliteral\">Arduino_JSON<\/span> library to make it easier to handle JSON strings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Arduino_JSON.h&gt;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Network Credentials<\/h4>\n\n\n\n<p>Insert your network credentials in the following variables, so that the ESP32 can connect to your local network using Wi-Fi.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>const char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">AsyncWebServer and AsyncEventSource<\/h4>\n\n\n\n<p>Create an <span class=\"rnthl rntliteral\">AsyncWebServer<\/span> object on port 80.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>AsyncWebServer server(80);<\/code><\/pre>\n\n\n\n<p>The following line creates a new event source on <span class=\"rnthl rntliteral\">\/events<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>AsyncEventSource events(\"\/events\");<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Declaring Variables<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">readings<\/span> variable is a JSON variable to hold the sensor readings in JSON format.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>JSONVar readings;<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">lastTime<\/span> and the <span class=\"rnthl rntliteral\">timerDelay<\/span> variables will be used to update sensor readings every X number of seconds. As an example, we\u2019ll get new sensor readings every 30 seconds (30000 milliseconds). You can change that delay time in the <span class=\"rnthl rntliteral\">timerDelay<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Timer variables\nunsigned long lastTime = 0;\nunsigned long timerDelay = 30000;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">DS18B20 Sensors<\/h4>\n\n\n\n<p>The DS18B20 temperature sensors are connected to GPIO 4.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ GPIO where the DS18B20 sensors are connected to\nconst int oneWireBus = 4;<\/code><\/pre>\n\n\n\n<p>Setup a <span class=\"rnthl rntliteral\">oneWire<\/span> instance to communicate with OneWire devices (DS18B20):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>OneWire oneWire(oneWireBus);<\/code><\/pre>\n\n\n\n<p>Pass our <span class=\"rnthl rntliteral\">oneWire<\/span> reference to Dallas Temperature sensor<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>DallasTemperature sensors(&amp;oneWire);<\/code><\/pre>\n\n\n\n<p>Insert the addresses of your DS18B20 Sensors in the following lines (<a href=\"#address\" target=\"_blank\" rel=\"noreferrer noopener\">check this section<\/a> if you don&#8217;t have the addresses of your sensors):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Address of each sensor\nDeviceAddress sensor3 = { 0x28, 0xFF, 0xA0, 0x11, 0x33, 0x17, 0x3, 0x96 };\nDeviceAddress sensor1 = { 0x28, 0xFF, 0xB4, 0x6, 0x33, 0x17, 0x3, 0x4B };\nDeviceAddress sensor2 = { 0x28, 0xFF, 0x43, 0xF5, 0x32, 0x18, 0x2, 0xA8 };\nDeviceAddress sensor4 = { 0x28, 0xFF, 0x11, 0x28, 0x33, 0x18, 0x1, 0x6B };<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Get DS18B20 Readings<\/h4>\n\n\n\n<p>To get readings from the DS18B20 temperature sensors, first, you need to call the <span class=\"rnthl rntliteral\">requesTemperatures()<\/span> method on the <span class=\"rnthl rntliteral\">sensors<\/span> object. Then, use the <span class=\"rnthl rntliteral\">getTempC()<\/span> function and pass as argument the address of the sensor you want to get the temperature\u2014this gets the temperature in celsius degrees.<\/p>\n\n\n\n<p class=\"rntbox rntclblue\"><strong>Note: <\/strong> if you want to get the temperature in Fahrenheit degrees, use the <span class=\"rnthl rntliteral\">getTemF()<\/span> function instead.<\/p>\n\n\n\n<p>Finally, save the readings in a JSON string (<span class=\"rnthl rntliteral\">jsonString<\/span> variable) and return that variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Get Sensor Readings and return JSON object\nString getSensorReadings(){\n  sensors.requestTemperatures();\n  readings&#091;\"sensor1\"] = String(sensors.getTempC(sensor1));\n  readings&#091;\"sensor2\"] = String(sensors.getTempC(sensor2));\n  readings&#091;\"sensor3\"] = String(sensors.getTempC(sensor3));\n  readings&#091;\"sensor4\"] = String(sensors.getTempC(sensor4));\n\n  String jsonString = JSON.stringify(readings);\n  return jsonString;\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize LittleFS<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initLittleFS()<\/span> function initializes the LittleFS filesystem:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Initialize LittleFS\nvoid initLittleFS() {\n  if (!LittleFS.begin()) {\n    Serial.println(\"An error has occurred while mounting LittleFS\");\n  }\n  else{\n    Serial.println(\"LittleFSmounted successfully\");\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Intialize WiFi<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initWiFi()<\/span> function initializes Wi-Fi and prints the IP address on the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Initialize WiFi\nvoid initWiFi() {\n  WiFi.mode(WIFI_STA);\n  WiFi.begin(ssid, password);\n  Serial.print(\"Connecting to WiFi ..\");\n  while (WiFi.status() != WL_CONNECTED) {\n    Serial.print('.');\n    delay(1000);\n  }\n  Serial.println(WiFi.localIP());\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">setup()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, initialize the Serial Monitor, Wi-Fi and filesystem.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);\ninitWiFi();\ninitLittleFS();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Handle Requests<\/h4>\n\n\n\n<p>When you access the ESP32 IP address on the root <span class=\"rnthl rntliteral\">\/<\/span> URL, send the text that is stored on the <span class=\"rnthl rntliteral\">index.html<\/span> file to build the web page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(LittleFS, \"\/index.html\", \"text\/html\");\n});<\/code><\/pre>\n\n\n\n<p>Serve the other static files requested by the client (<em>style.css<\/em> and <em>script.js<\/em>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.serveStatic(\"\/\", LittleFS, \"\/\");<\/code><\/pre>\n\n\n\n<p>Send the JSON string with the current sensor readings when you receive a request on the <span class=\"rnthl rntliteral\">\/readings<\/span> URL.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Request for the latest sensor readings\nserver.on(\"\/readings\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  String json = getSensorReadings();\n  request-&gt;send(200, \"application\/json\", json);\n  json = String();\n});<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">json<\/span> variable holds the return from the <span class=\"rnthl rntliteral\">getSensorReadings()<\/span> function. To send a JSON string as response, the <span class=\"rnthl rntliteral\">send()<\/span> method accepts as first argument the response code (200), the second is the content type (<span class=\"rnthl rntliteral\">&#8220;application\/json&#8221;<\/span>) and finally the content (<span class=\"rnthl rntliteral\">json<\/span> variable).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Server Event Source<\/h4>\n\n\n\n<p>Set up the event source on the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>events.onConnect(&#091;](AsyncEventSourceClient *client){\n  if(client-&gt;lastId()){\n    Serial.printf(\"Client reconnected! Last message ID that it got is: %u\\n\", client-&gt;lastId());\n  }\n  \/\/ send event with message \"hello!\", id current millis\n  \/\/ and set reconnect delay to 1 second\n  client-&gt;send(\"hello!\", NULL, millis(), 10000);\n});\nserver.addHandler(&amp;events);<\/code><\/pre>\n\n\n\n<p>Finally, start the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.begin();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">loop()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, send events to the browser with the newest sensor readings to update the web page every 30 seconds.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if ((millis() - lastTime) &gt; timerDelay) {\n  \/\/ Send Events to the client with the Sensor Readings Every 10 seconds\n  events.send(\"ping\",NULL,millis());\n  events.send(getSensorReadings().c_str(),\"new_readings\" ,millis());\n  lastTime = millis();\n}<\/code><\/pre>\n\n\n\n<p>Use the <span class=\"rnthl rntliteral\">send()<\/span> method on the <span class=\"rnthl rntliteral\">events<\/span> 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 <span class=\"rnthl rntliteral\">getSensorReadings()<\/span> function. The name of the events is <span class=\"rnthl rntliteral\">new_readings<\/span>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Uploading Code and Files<\/h2>\n\n\n\n<p>After inserting your network credentials, save the code. Go to&nbsp;<strong>Sketch<\/strong>&nbsp;&gt;&nbsp;<strong>Show Sketch Folder<\/strong>, and create a folder called&nbsp;<strong><em>data<\/em><\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"351\" height=\"271\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/arduino-ide-show-sketch-folder.png?resize=351%2C271&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Arduino IDE Open Sketch Folder to create data folder\" class=\"wp-image-158892\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/arduino-ide-show-sketch-folder.png?w=351&amp;quality=100&amp;strip=all&amp;ssl=1 351w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/arduino-ide-show-sketch-folder.png?resize=300%2C232&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 351px) 100vw, 351px\" \/><\/figure><\/div>\n\n\n<p><strong>Inside that folder you should save the HTML, CSS and JavaScript files.<\/strong><\/p>\n\n\n\n<p>Then, upload the code to your ESP32 board. Make sure you have the right board and COM port selected. Also, make sure you\u2019ve added your networks credentials and the sensors&#8217; addresses to the code.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"36\" height=\"39\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/05\/arduino-ide-2-upload-button.png?resize=36%2C39&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Arduino IDE 2 Upload Button\" class=\"wp-image-146269\"\/><\/figure><\/div>\n\n\n<p><strong>After uploading the code, you need to upload the files to the filesystem.<\/strong><\/p>\n\n\n\n<p>Press [<strong>Ctrl<\/strong>] + [<strong>Shift<\/strong>] + [<strong>P<\/strong>] on Windows or [<strong>\u2318<\/strong>] + [<strong>Shift<\/strong>] + [<strong>P<\/strong>] on MacOS to open the command palette. Search for the&nbsp;<strong>Upload LittleFS to Pico\/ESP8266\/ESP32<\/strong>&nbsp;command and click on it.<\/p>\n\n\n\n<p>If you don\u2019t have this option is because you didn\u2019t install the filesystem uploader plugin.<a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\">&nbsp;Check this tutorial<\/a>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"744\" height=\"401\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/upload-files-little-fs-esp32-arduino-ide.png?resize=744%2C401&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Upload LittleFS to Pico ESP8266 ESP32 Arduino IDE\" class=\"wp-image-158893\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/upload-files-little-fs-esp32-arduino-ide.png?w=744&amp;quality=100&amp;strip=all&amp;ssl=1 744w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/upload-files-little-fs-esp32-arduino-ide.png?resize=300%2C162&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 744px) 100vw, 744px\" \/><\/figure><\/div>\n\n\n<p class=\"rntbox rntcred\"><strong>Important:&nbsp;<\/strong>make sure the Serial Monitor is closed before uploading to the filesystem. Otherwise, the upload will fail.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>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.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"970\" height=\"719\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-DS18B20-Sensor-Readings-Multiple-Charts-Demonstration-Browser.png?resize=970%2C719&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP Web Server Charts demonstration temperature\" class=\"wp-image-103327\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-DS18B20-Sensor-Readings-Multiple-Charts-Demonstration-Browser.png?w=970&amp;quality=100&amp;strip=all&amp;ssl=1 970w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-DS18B20-Sensor-Readings-Multiple-Charts-Demonstration-Browser.png?resize=300%2C222&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-DS18B20-Sensor-Readings-Multiple-Charts-Demonstration-Browser.png?resize=768%2C569&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 970px) 100vw, 970px\" \/><\/figure><\/div>\n\n\n<p>You can select a point to see its value and timestamp.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"970\" height=\"719\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-Series-Chart-DS18B20-Highlight.png?resize=970%2C719&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP Web Server Charts demonstration temperature multiple series\" class=\"wp-image-103328\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-Series-Chart-DS18B20-Highlight.png?w=970&amp;quality=100&amp;strip=all&amp;ssl=1 970w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-Series-Chart-DS18B20-Highlight.png?resize=300%2C222&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Multiple-Series-Chart-DS18B20-Highlight.png?resize=768%2C569&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 970px) 100vw, 970px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial, you\u2019ve 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.<\/p>\n\n\n\n<p><strong>You might also like reading:<\/strong> <a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp8266-plot-chart-web-server\/\">ESP32\/ESP8266 Plot Sensor Readings in Real Time Charts \u2013 Web Server<\/a><\/p>\n\n\n\n<p>Learn more about the ESP32 with our resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\"><strong>Build Web Servers with ESP32 and ESP8266 eBook<\/strong><\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE (eBook + video course)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\">More ESP32 tutorials and projects\u2026<\/a><\/li>\n<\/ul>\n\n\n\n<p>Thank you for reading.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8217;ll plot sensor readings from four &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Plot Sensor Readings in Charts (Multiple Series)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-plot-readings-charts-multiple\/#more-103213\" aria-label=\"Read more about ESP32 Plot Sensor Readings in Charts (Multiple Series)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":103290,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[281,276,277,299,264],"tags":[],"class_list":["post-103213","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32-project","category-esp32","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/04\/ESP32-Charts-Multiple-Series-Web-Server.jpg?fit=1280%2C720&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/103213","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=103213"}],"version-history":[{"count":6,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/103213\/revisions"}],"predecessor-version":[{"id":167939,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/103213\/revisions\/167939"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/103290"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=103213"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=103213"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=103213"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}