{"id":107865,"date":"2021-11-17T18:55:37","date_gmt":"2021-11-17T18:55:37","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=107865"},"modified":"2025-03-14T16:07:34","modified_gmt":"2025-03-14T16:07:34","slug":"esp32-web-server-gauges","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-web-server-gauges\/","title":{"rendered":"ESP32 Web Server: Display Sensor Readings in Gauges"},"content":{"rendered":"\n<p>Learn how to build a web server with the ESP32 to display sensor readings in gauges. As an example, we&#8217;ll display temperature and humidity from a BME280 sensor in two different gauges: linear and radial. You can easily modify the project to plot any other data. To build the gauges, we\u2019ll use the canvas-gauges 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\/11\/ESP32-Web-Server-Gauges.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server Display Sensor Readings in Gauges Arduino IDE\" class=\"wp-image-107873\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Web-Server-Gauges.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Web-Server-Gauges.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Web-Server-Gauges.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Web-Server-Gauges.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 board: <a href=\"https:\/\/randomnerdtutorials.com\/esp8266-web-server-gauges\/\">Web Server &#8211; Display Sensor Readings in Gauges<\/a> <\/p>\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 and humidity readings from a <a href=\"https:\/\/randomnerdtutorials.com\/esp32-bme280-arduino-ide-pressure-temperature-humidity\/\">BME280 sensor<\/a>. We&#8217;ll create a linear gauge that looks like a thermometer to display the temperature, and a radial gauge to display the humidity.<\/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=\"476\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Gauges-Web-Server-Overview.png?resize=750%2C476&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Gauges Web Server Overview\" class=\"wp-image-107883\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Gauges-Web-Server-Overview.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Gauges-Web-Server-Overview.png?resize=300%2C190&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\">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\" decoding=\"async\" width=\"750\" height=\"664\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Gauges-Web-Server-Overview-Server-Sent-Events.png?resize=750%2C664&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Gauges Web Server Overview Server-Sent Events\" class=\"wp-image-107884\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Gauges-Web-Server-Overview-Server-Sent-Events.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP32-Gauges-Web-Server-Overview-Server-Sent-Events.png?resize=300%2C266&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/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\/adafruit\/Adafruit_BME280_Library\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_BME280<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_Sensor\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_Sensor library<\/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<\/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 first three libraries using the Arduino Library Manager. Go to&nbsp;<strong>Sketch&nbsp;<\/strong>&gt;&nbsp;<strong>Include Library<\/strong>&nbsp;&gt;&nbsp;<strong>Manage Libraries<\/strong>&nbsp;and 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 href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32<\/a>&nbsp;(read&nbsp;<a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP32 development boards<\/a>)<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/bme280-sensor-module\/\" target=\"_blank\" rel=\"noreferrer noopener\">BME280 Sensor<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jumper wires<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\" rel=\"noreferrer noopener\">Breadboard<\/a><\/li>\n<\/ul>\n\n\n\n<p>You can use any other sensor that is useful for your project. If you don&#8217;t have the sensor, you can also experiment with random values to learn how the project works.<\/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>We&#8217;ll send temperature and humidity readings from a BME280 sensor. We\u2019re going to use I2C communication with the BME280 sensor module. For that, wire the sensor to the default ESP32&nbsp;SCL (<span class=\"rnthl rntcblue\">GPIO 22<\/span>)&nbsp;and&nbsp;SDA (<span class=\"rnthl rntcgreen\">GPIO 21<\/span>)&nbsp;pins, as shown in the following schematic diagram.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"675\" height=\"670\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?resize=675%2C670&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Wiring Circuit to BME280 Schematic Diagram\" class=\"wp-image-99755\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?w=675&amp;quality=100&amp;strip=all&amp;ssl=1 675w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?resize=300%2C298&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/10\/ESP32-BME280-Sensor-Temperature-Humidity-Pressure-Wiring-Diagram-Circuit_f.png?resize=150%2C150&amp;quality=100&amp;strip=all&amp;ssl=1 150w\" sizes=\"(max-width: 675px) 100vw, 675px\" \/><\/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\">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 gauges, etc.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"601\" height=\"358\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/folder-structure.png?resize=601%2C358&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Organizing Your Files Arduino sketch index html css javascript\" class=\"wp-image-158891\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/folder-structure.png?w=601&amp;quality=100&amp;strip=all&amp;ssl=1 601w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/folder-structure.png?resize=300%2C179&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 601px) 100vw, 601px\" \/><\/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_Gauges\/ESP32_Gauges.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;!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;http:\/\/cdn.rawgit.com\/Mikhus\/canvas-gauges\/gh-pages\/download\/2.1.7\/all\/gauge.min.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 GAUGES&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&lt;\/p&gt;\r\n          &lt;canvas id=&quot;gauge-temperature&quot;&gt;&lt;\/canvas&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class=&quot;card&quot;&gt;\r\n          &lt;p class=&quot;card-title&quot;&gt;Humidity&lt;\/p&gt;\r\n          &lt;canvas id=&quot;gauge-humidity&quot;&gt;&lt;\/canvas&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_Gauges\/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:\/\/canvas-gauges.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">JavaScript canvas-gauges 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=\"http:\/\/cdn.rawgit.com\/Mikhus\/canvas-gauges\/gh-pages\/download\/2.1.7\/all\/gauge.min.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>There is a <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;canvas&gt;<\/span><\/span> tag with the id <span class=\"rnthl rntliteral\">gauge-temperature<\/span> where we&#8217;ll render the temperature gauge later on. <\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;canvas id=\"gauge-temperature\"&gt;&lt;\/canvas&gt;<\/code><\/pre>\n\n\n\n<p>There is also another  <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;canvas&gt;<\/span><\/span> tag with the id <span class=\"rnthl rntliteral\">gauge-humidity<\/span>, where we&#8217;ll render the humidity gauge later on.  <\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;canvas id=\"gauge-humidity\"&gt;&lt;\/canvas&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. It styles the web page with simple colors and styles.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-css\">html {\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}<\/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_Gauges\/data\/style.css\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">JavaScript File (creating the gauges)<\/h2>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">script.js<\/span> file.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">\/\/ Get current sensor readings when the page loads  \r\nwindow.addEventListener('load', getReadings);\r\n\r\n\/\/ Create Temperature Gauge\r\nvar gaugeTemp = new LinearGauge({\r\n  renderTo: 'gauge-temperature',\r\n  width: 120,\r\n  height: 400,\r\n  units: &quot;Temperature C&quot;,\r\n  minValue: 0,\r\n  startAngle: 90,\r\n  ticksAngle: 180,\r\n  maxValue: 40,\r\n  colorValueBoxRect: &quot;#049faa&quot;,\r\n  colorValueBoxRectEnd: &quot;#049faa&quot;,\r\n  colorValueBoxBackground: &quot;#f1fbfc&quot;,\r\n  valueDec: 2,\r\n  valueInt: 2,\r\n  majorTicks: [\r\n      &quot;0&quot;,\r\n      &quot;5&quot;,\r\n      &quot;10&quot;,\r\n      &quot;15&quot;,\r\n      &quot;20&quot;,\r\n      &quot;25&quot;,\r\n      &quot;30&quot;,\r\n      &quot;35&quot;,\r\n      &quot;40&quot;\r\n  ],\r\n  minorTicks: 4,\r\n  strokeTicks: true,\r\n  highlights: [\r\n      {\r\n          &quot;from&quot;: 30,\r\n          &quot;to&quot;: 40,\r\n          &quot;color&quot;: &quot;rgba(200, 50, 50, .75)&quot;\r\n      }\r\n  ],\r\n  colorPlate: &quot;#fff&quot;,\r\n  colorBarProgress: &quot;#CC2936&quot;,\r\n  colorBarProgressEnd: &quot;#049faa&quot;,\r\n  borderShadowWidth: 0,\r\n  borders: false,\r\n  needleType: &quot;arrow&quot;,\r\n  needleWidth: 2,\r\n  needleCircleSize: 7,\r\n  needleCircleOuter: true,\r\n  needleCircleInner: false,\r\n  animationDuration: 1500,\r\n  animationRule: &quot;linear&quot;,\r\n  barWidth: 10,\r\n}).draw();\r\n  \r\n\/\/ Create Humidity Gauge\r\nvar gaugeHum = new RadialGauge({\r\n  renderTo: 'gauge-humidity',\r\n  width: 300,\r\n  height: 300,\r\n  units: &quot;Humidity (%)&quot;,\r\n  minValue: 0,\r\n  maxValue: 100,\r\n  colorValueBoxRect: &quot;#049faa&quot;,\r\n  colorValueBoxRectEnd: &quot;#049faa&quot;,\r\n  colorValueBoxBackground: &quot;#f1fbfc&quot;,\r\n  valueInt: 2,\r\n  majorTicks: [\r\n      &quot;0&quot;,\r\n      &quot;20&quot;,\r\n      &quot;40&quot;,\r\n      &quot;60&quot;,\r\n      &quot;80&quot;,\r\n      &quot;100&quot;\r\n\r\n  ],\r\n  minorTicks: 4,\r\n  strokeTicks: true,\r\n  highlights: [\r\n      {\r\n          &quot;from&quot;: 80,\r\n          &quot;to&quot;: 100,\r\n          &quot;color&quot;: &quot;#03C0C1&quot;\r\n      }\r\n  ],\r\n  colorPlate: &quot;#fff&quot;,\r\n  borderShadowWidth: 0,\r\n  borders: false,\r\n  needleType: &quot;line&quot;,\r\n  colorNeedle: &quot;#007F80&quot;,\r\n  colorNeedleEnd: &quot;#007F80&quot;,\r\n  needleWidth: 2,\r\n  needleCircleSize: 3,\r\n  colorNeedleCircleOuter: &quot;#007F80&quot;,\r\n  needleCircleOuter: true,\r\n  needleCircleInner: false,\r\n  animationDuration: 1500,\r\n  animationRule: &quot;linear&quot;\r\n}).draw();\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      var temp = myObj.temperature;\r\n      var hum = myObj.humidity;\r\n      gaugeTemp.value = temp;\r\n      gaugeHum.value = hum;\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    gaugeTemp.value = myObj.temperature;\r\n    gaugeHum.value = myObj.humidity;\r\n  }, false);\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_Gauges\/data\/script.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Here&#8217;s a summary 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 gauges;<\/li>\n\n\n\n<li>getting the latest sensor readings from the <span class=\"rnthl rntliteral\">new_readings<\/span> event and display them in the corresponding gauges;<\/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\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 (those are just arbitrary values).<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>{\n  \"temperature\" : \"25.02\",\n  \"humidity\" : \"64.01\",\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> variable is a JSON object that contains the temperature and humidity readings. We want to update the gauges values with the corresponding readings. <\/p>\n\n\n\n<p>Updating the value of a gauge is straightforward. For example, our temperature gauge is called <span class=\"rnthl rntliteral\">gaugeTemp<\/span> (as we&#8217;ll see later on), to update a value, we can simply call: <span class=\"rnthl rntliteral\">gaugeTemp.value = NEW_VALUE<\/span>. In our case, the new value is the temperature reading saved on the <span class=\"rnthl rntliteral\">myObj<\/span> JSON object.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>gaugeTemp.value = myObj.temperature;<\/code><\/pre>\n\n\n\n<p>It is similar for the humidity (our humidity gauge is called <span class=\"rnthl rntliteral\">gaugeHum<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>gaugeHum.value = myObj.humidity;<\/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      var temp = myObj.temperature;\n      var hum = myObj.humidity;\n      gaugeTemp.value = temp;\n      gaugeHum.value = hum;\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 Gauges<\/h3>\n\n\n\n<p>The canvas-charts library allows you to build linear and radial gauges to display your readings. It provides several examples, and it is very simple to use. We recommend taking a look at the documentation and exploring all the gauges functionalities:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/canvas-gauges.com\/documentation\/user-guide\/configuration\" target=\"_blank\" rel=\"noreferrer noopener\">Canvas-Gauges User Guide Configuration<\/a><\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Temperature Gauge<\/h4>\n\n\n\n<p>The following lines create the gauge to display the temperature.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Create Temperature Gauge\nvar gaugeTemp = new LinearGauge({\n  renderTo: 'gauge-temperature',\n  width: 120,\n  height: 400,\n  units: \"Temperature C\",\n  minValue: 0,\n  startAngle: 90,\n  ticksAngle: 180,\n  maxValue: 40,\n  colorValueBoxRect: \"#049faa\",\n  colorValueBoxRectEnd: \"#049faa\",\n  colorValueBoxBackground: \"#f1fbfc\",\n  valueDec: 2,\n  valueInt: 2,\n  majorTicks: &#091;\n      \"0\",\n      \"5\",\n      \"10\",\n      \"15\",\n      \"20\",\n      \"25\",\n      \"30\",\n      \"35\",\n      \"40\"\n  ],\n  minorTicks: 4,\n  strokeTicks: true,\n  highlights: &#091;\n      {\n          \"from\": 30,\n          \"to\": 40,\n          \"color\": \"rgba(200, 50, 50, .75)\"\n      }\n  ],\n  colorPlate: \"#fff\",\n  colorBarProgress: \"#CC2936\",\n  colorBarProgressEnd: \"#049faa\",\n  borderShadowWidth: 0,\n  borders: false,\n  needleType: \"arrow\",\n  needleWidth: 2,\n  needleCircleSize: 7,\n  needleCircleOuter: true,\n  needleCircleInner: false,\n  animationDuration: 1500,\n  animationRule: \"linear\",\n  barWidth: 10,\n}).draw();<\/code><\/pre>\n\n\n\n<p>To create a new linear gauge, use the <span class=\"rnthl rntliteral\">new LinearGauge()<\/span>  method and pass as an argument the properties of the gauge.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var gaugeTemp = new LinearGauge({<\/code><\/pre>\n\n\n\n<p>In the next line, define where you want to put the chart (it must be a <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;canvas&gt;<\/span><\/span> element). In our example, we want to place it in the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;canvas&gt;<\/span><\/span> HTML element with the <span class=\"rnthl rntliteral\">gauge-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>renderTo: 'gauge-temperature',<\/code><\/pre>\n\n\n\n<p>Then, we define other properties to customize our gauge. The names are self-explanatory, but we recommend taking a look at <a href=\"https:\/\/canvas-gauges.com\/documentation\/user-guide\/configuration\" target=\"_blank\" rel=\"noreferrer noopener\">all possible configurations<\/a> and changing the gauge to meet your needs. <\/p>\n\n\n\n<p>In the end, you need to apply the <span class=\"rnthl rntliteral\">draw()<\/span> method to actually display the gauge on the canvas.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>}).draw();<\/code><\/pre>\n\n\n\n<p>Special attention that if you need to change the gauge range, you need to change the <span class=\"rnthl rntliteral\">minValue<\/span> and <span class=\"rnthl rntliteral\">maxValue<\/span> properties:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>minValue: 0,<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>maxValue: 40,<\/code><\/pre>\n\n\n\n<p>You also need to adjust the <span class=\"rnthl rntliteral\">majorTicks<\/span> values for the values displayed on the axis.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>majorTicks: &#091;\n    \"0\",\n    \"5\",\n    \"10\",\n    \"15\",\n    \"20\",\n    \"25\",\n    \"30\",\n    \"35\",\n    \"40\"\n],<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Humidity Gauge<\/h4>\n\n\n\n<p>Creating the humidity gauge is similar, but we use the <span class=\"rnthl rntliteral\">new RadialGauge()<\/span> function instead and it is rendered to the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;canvas&gt;<\/span><\/span> with the <span class=\"rnthl rntliteral\">gauge-humidity<\/span> id. Notice that we apply the <span class=\"rnthl rntliteral\">draw()<\/span> method on the gauge so that it is drawn on the canvas.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Create Humidity Gauge\nvar gaugeHum = new RadialGauge({\n  renderTo: 'gauge-humidity',\n  width: 300,\n  height: 300,\n  units: \"Humidity (%)\",\n  minValue: 0,\n  maxValue: 100,\n  colorValueBoxRect: \"#049faa\",\n  colorValueBoxRectEnd: \"#049faa\",\n  colorValueBoxBackground: \"#f1fbfc\",\n  valueInt: 2,\n  majorTicks: &#091;\n      \"0\",\n      \"20\",\n      \"40\",\n      \"60\",\n      \"80\",\n      \"100\"\n\n  ],\n  minorTicks: 4,\n  strokeTicks: true,\n  highlights: &#091;\n      {\n          \"from\": 80,\n          \"to\": 100,\n          \"color\": \"#03C0C1\"\n      }\n  ],\n  colorPlate: \"#fff\",\n  borderShadowWidth: 0,\n  borders: false,\n  needleType: \"line\",\n  colorNeedle: \"#007F80\",\n  colorNeedleEnd: \"#007F80\",\n  needleWidth: 2,\n  needleCircleSize: 3,\n  colorNeedleCircleOuter: \"#007F80\",\n  needleCircleOuter: true,\n  needleCircleInner: false,\n  animationDuration: 1500,\n  animationRule: \"linear\"\n}).draw();<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Handle events<\/h3>\n\n\n\n<p>Update the readings on the gauge 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  gaugeTemp.value = myObj.temperature;\n  gaugeHum.value = myObj.humidity;\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 display the readings on the corresponding gauges.<\/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\n<p>You can also <a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_Gauges\/ESP32_Gauges.zip\" target=\"_blank\" rel=\"noreferrer noopener\">download all the files here<\/a>.<\/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-web-server-gauges\/\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;Adafruit_BME280.h&gt;\r\n#include &lt;Adafruit_Sensor.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 = 10000;\r\n\r\n\/\/ Create a sensor object\r\nAdafruit_BME280 bme; \/\/ BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)\r\n\r\n\/\/ Init BME280\r\nvoid initBME(){\r\n  if (!bme.begin(0x76)) {\r\n    Serial.println(&quot;Could not find a valid BME280 sensor, check wiring!&quot;);\r\n    while (1);\r\n  }\r\n}\r\n\r\n\/\/ Get Sensor Readings and return JSON object\r\nString getSensorReadings(){\r\n  readings[&quot;temperature&quot;] = String(bme.readTemperature());\r\n  readings[&quot;humidity&quot;] =  String(bme.readHumidity());\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  Serial.println(&quot;LittleFS mounted successfully&quot;);\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  initBME();\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_Gauges\/ESP32_Gauges.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\">Adafruit_Sensor<\/span> and <span class=\"rnthl rntliteral\">Adafruit_BME280<\/span> libraries are needed to interface with the BME280 sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Adafruit_BME280.h&gt;\n#include &lt;Adafruit_Sensor.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;WiFi.h&gt;\n#include &lt;AsyncTCP.h&gt;\n#include &lt;ESPAsyncWebServer.h&gt;<\/code><\/pre>\n\n\n\n<p>We&#8217;ll use LittleFS to save the files to build the web server.<\/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>unsigned long lastTime = 0;\nunsigned long timerDelay = 30000;<\/code><\/pre>\n\n\n\n<p>Create an <span class=\"rnthl rntliteral\">Adafruit_BME280<\/span> object called <span class=\"rnthl rntliteral\">bme<\/span> on the default ESP I2C pins.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Adafruit_BME280 bme;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize BME280 Sensor<\/h4>\n\n\n\n<p>The following function can be called to initialize the BME280 sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Init BME280\nvoid initBME(){\n  if (!bme.begin(0x76)) {\n    Serial.println(\"Could not find a valid BME280 sensor, check wiring!\");\n    while (1);\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Get BME280 Readings<\/h4>\n\n\n\n<p>To get temperature and humidity from the BME280 temperature, use the following methods on the <span class=\"rnthl rntliteral\">bme<\/span> object:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span class=\"rnthl rntliteral\">bme.readTemperature()<\/span><\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">bme.readHumidity()<\/span> <\/li>\n<\/ul>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">getSensorReadings()<\/span> function gets the sensor readings and saves them on the <span class=\"rnthl rntliteral\">readings<\/span> JSON array.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Get Sensor Readings and return JSON object\nString getSensorReadings(){\n  readings&#091;\"temperature\"] = String(bme.readTemperature());\n  readings&#091;\"humidity\"] =  String(bme.readHumidity());\n  String jsonString = JSON.stringify(readings);\n  return jsonString;\n}<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">readings<\/span> array is then converted into a JSON string variable using the <span class=\"rnthl rntliteral\">stringify()<\/span> method and saved on the <span class=\"rnthl rntliteral\">jsonString<\/span> variable.<\/p>\n\n\n\n<p>The function returns the <span class=\"rnthl rntliteral\">jsonString<\/span> variable with the current sensor readings. The JSON string has the following format (the values are just arbitrary numbers for explanation purposes).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>{\n  \"temperature\" : \"25\",\n  \"humidity\" : \"50\"\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, filesystem, and the BME280 sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void setup() {\n  \/\/ Serial port for debugging purposes\n  Serial.begin(115200);\n  initBME();\n  initWiFi();\n  initLittleFS ();<\/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 (<span class=\"rnthl rntliteral\">style.css<\/span> and <span class=\"rnthl rntliteral\">script.js<\/span>).<\/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 (<span class=\"rnthl rntliteral\">200<\/span>), 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>events.send(\"ping\",NULL,millis());\nevents.send(getSensorReadings().c_str(),\"new_readings\" ,millis());<\/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 <span class=\"rnthl rntliteral\">send()<\/span> method accepts a variable of type <span class=\"rnthl rntliteral\">char<\/span>, so we need to use the <span class=\"rnthl rntliteral\">c_str()<\/span> method to convert the variable. The name of the events is <span class=\"rnthl rntliteral\">new_readings<\/span>.<\/p>\n\n\n\n<p>Usually, we also send a ping message every X number of seconds. That line is not mandatory. It is used to check on the client side that the server is alive.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>events.send(\"ping\",NULL,millis());<\/code><\/pre>\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 show sketch 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 network credentials.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\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&#8217;t have this option is because you didn&#8217;t install the filesystem uploader plugin.<a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\" title=\"\"> 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=\"ESP32 Sketch Data Upload LittleFS 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 gauges with the latest sensor readings. <\/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=\"948\" height=\"696\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/Gauges-Web-Server-ESP32-ESP8266-Example.png?resize=948%2C696&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Gauges Web Server ESP32 ESP8266 Demonstration\" class=\"wp-image-107871\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/Gauges-Web-Server-ESP32-ESP8266-Example.png?w=948&amp;quality=100&amp;strip=all&amp;ssl=1 948w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/Gauges-Web-Server-ESP32-ESP8266-Example.png?resize=300%2C220&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/Gauges-Web-Server-ESP32-ESP8266-Example.png?resize=768%2C564&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 948px) 100vw, 948px\" \/><\/figure><\/div>\n\n\n<p>You can also check your gauges using your smartphone (the web page is mobile responsive).<\/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=\"526\" height=\"500\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP-Gauges-Web-Server-Smartphone.jpg?resize=526%2C500&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Gauges Web Server ESP32 ESP8266 Demonstration Smartphone\" class=\"wp-image-107877\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP-Gauges-Web-Server-Smartphone.jpg?w=526&amp;quality=100&amp;strip=all&amp;ssl=1 526w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/11\/ESP-Gauges-Web-Server-Smartphone.jpg?resize=300%2C285&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 526px) 100vw, 526px\" \/><\/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 a web server to display sensor readings in linear and radial gauges. As an example, we displayed temperature and humidity from a BME280 sensor. You can use those gauges to display any other values that may make sense for your project.<\/p>\n\n\n\n<p><strong>You might also like reading:<\/strong> <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp8266-plot-chart-web-server\/\">ESP32\/ESP8266 Plot Sensor Readings in Real Time Charts \u2013 Web Server<\/a><\/li>\n\n\n\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<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>Learn how to build a web server with the ESP32 to display sensor readings in gauges. As an example, we&#8217;ll display temperature and humidity from a BME280 sensor in two &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Web Server: Display Sensor Readings in Gauges\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-gauges\/#more-107865\" aria-label=\"Read more about ESP32 Web Server: Display Sensor Readings in Gauges\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":107873,"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-107865","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\/11\/ESP32-Web-Server-Gauges.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\/107865","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=107865"}],"version-history":[{"count":32,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/107865\/revisions"}],"predecessor-version":[{"id":167929,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/107865\/revisions\/167929"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/107873"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=107865"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=107865"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=107865"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}