{"id":108701,"date":"2025-05-15T10:00:00","date_gmt":"2025-05-15T10:00:00","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=108701"},"modified":"2025-05-15T14:32:17","modified_gmt":"2025-05-15T14:32:17","slug":"esp32-esp8266-firebase-gauges-charts","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-esp8266-firebase-gauges-charts\/","title":{"rendered":"ESP32\/ESP8266: Firebase Data Logging Web App (Gauges, Charts, and Table)"},"content":{"rendered":"\n<p>In this project, you&#8217;ll create a Firebase Web App that displays all the sensor readings saved on the Firebase Realtime Database. We&#8217;ll create a web interface with gauges, charts, and a table to display all your data records. We&#8217;ll also add a button that allows you to delete all data from the database and checkboxes to customize the user interface. This web application will be protected with authentication (using email and password) and all the data is restricted to the user using database rules.<\/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\/2022\/02\/ESP32-ESP8266-Firebase-BME280-Datalogging-web-app-updated.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 ESP8266 NodeMCU Firebase Data Logging Web App with Gauges Charts and Table\" class=\"wp-image-170184\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/ESP32-ESP8266-Firebase-BME280-Datalogging-web-app-updated.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/ESP32-ESP8266-Firebase-BME280-Datalogging-web-app-updated.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/ESP32-ESP8266-Firebase-BME280-Datalogging-web-app-updated.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/ESP32-ESP8266-Firebase-BME280-Datalogging-web-app-updated.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 class=\"rntbox rntclgray\"><em>Updated 15 May 2025<\/em><\/p>\n\n\n\n<p>This project is Part 2 of the following tutorial (there is a version for ESP32 and a version for ESP8266):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-data-logging-firebase-realtime-database\/\">ESP32 Data Logging to Firebase Realtime Database<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp8266-data-logging-firebase-realtime-database\/\" title=\"https:\/\/randomnerdtutorials.com\/esp8266-data-logging-firebase-realtime-database\/\">ESP8266 Data Logging to Firebase Realtime Database<\/a><\/li>\n<\/ul>\n\n\n\n<p>You must follow one of those tutorials first, before proceeding<\/p>\n\n\n\n<p>Here&#8217;s a summary of the web app features:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>login with email and password<\/li>\n\n\n\n<li>displays time of the last update<\/li>\n\n\n\n<li>cards to display the last sensor readings<\/li>\n\n\n\n<li><span style=\"font-size: inherit;\">gauges to display the last sensor readings<\/span><\/li>\n\n\n\n<li>charts that display data history with timestamps<\/li>\n\n\n\n<li>select how many readings to display on charts<\/li>\n\n\n\n<li>checkboxes to enable\/disable the different display options<\/li>\n\n\n\n<li>table that displays all readings saved on the database<\/li>\n\n\n\n<li>button to delete database data<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Project Overview<\/h2>\n\n\n\n<p>In this tutorial (Part 2), you&#8217;ll create a web app to display the sensor readings logged with timestamps on the Firebase Realtime Database (read this previous tutorial &#8211; <a href=\"https:\/\/randomnerdtutorials.com\/esp32-data-logging-firebase-realtime-database\/\">ESP32 version<\/a> \/ <a href=\"https:\/\/randomnerdtutorials.com\/esp8266-data-logging-firebase-realtime-database\/\">ESP8266 version<\/a>).<\/p>\n\n\n\n<p>The following video shows the web app project we\u2019ll build\u2014programming the ESP32\/ESP8266 and setting up the Firebase Project was done in&nbsp;Part 1 (<a href=\"https:\/\/randomnerdtutorials.com\/esp32-data-logging-firebase-realtime-database\/\">ESP32 Part 1<\/a>; <a href=\"https:\/\/randomnerdtutorials.com\/esp8266-data-logging-firebase-realtime-database\/\">ESP8266 Part 1<\/a>).<\/p>\n\n\n<div style=\"text-align:center\"><iframe src=\"https:\/\/player.vimeo.com\/video\/678763382?color=ff9933&title=1&byline=0&portrait=0\" width=\"720\" height=\"405\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen><\/iframe><\/div><\/br>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Firebase hosts your web app over a global CDN using Firebase Hosting and provides an SSL certificate. You can access your web app from anywhere using the Firebase-generated domain name.<\/li>\n\n\n\n<li>When you first access the web app, you need to authenticate with an authorized email address and password. You already set up that user and the&nbsp;<a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp8266-firebase-bme280-rtdb\/#Set-Authentication-Methods\">authentication method in Part 1<\/a>.<\/li>\n\n\n\n<li>After authentication, you can access a web app page that shows the sensor readings. The sensor readings are displayed in cards, gauges, charts and table. You can select how many readings you want to show on the charts and you can also choose how you can view your data. <\/li>\n\n\n\n<li>There is a button to show\/hide all readings saved on the database on a table with timestamps.<\/li>\n\n\n\n<li>There&#8217;s also a Delete button that allows you to delete all data from the database.<\/li>\n\n\n\n<li>All the data is restricted using database rules.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>Before start creating the Firebase Web App, you need to check the following prerequisites:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating a Firebase Project<\/h3>\n\n\n\n<p>You should have followed one of the next tutorials first:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-data-logging-firebase-realtime-database\/\">ESP32 Data Logging to Firebase Realtime Database<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp8266-data-logging-firebase-realtime-database\/\">ESP8266 Data Logging to Firebase Realtime Database<\/a><\/li>\n<\/ul>\n\n\n\n<p>The ESP32\/ESP8266 must be running the code provided in that tutorial. The realtime database and authentication must be set up also as shown in the tutorial.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Install Required Software<\/h3>\n\n\n\n<p>Before getting started you need to install the required software to create the Firebase Web App. Here&#8217;s a list of the software you need to install (click on the links for instructions):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-firebase-web-app\/#install-vs-code\">Visual Studio Code<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-firebase-web-app\/#install-nodejs\">Node.JS LTS version<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-firebase-web-app\/#install-firebase-tools\">Install Firebase Tools<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1) Add an App to Your Firebase Project<\/h2>\n\n\n\n<p><strong>1)<\/strong> Go to your Firebase project Console and add an app to your project by clicking on the <strong>+Add app <\/strong>button. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"740\" height=\"198\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/12\/Add-App-to-Firebase-Project.png?resize=740%2C198&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Add App to Firebase Project\" class=\"wp-image-169592\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/12\/Add-App-to-Firebase-Project.png?w=740&amp;quality=100&amp;strip=all&amp;ssl=1 740w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/12\/Add-App-to-Firebase-Project.png?resize=300%2C80&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 740px) 100vw, 740px\" \/><\/figure><\/div>\n\n\n<p><strong>2)<\/strong> Select the web app icon.<\/p>\n\n\n\n<p><strong>3)<\/strong> Give your app a name. Then, check the box next to&nbsp;<strong>\u221a Also set up Firebase Hosting for this App<\/strong>. Click <strong>Register app<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"552\" height=\"601\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/add-firebase-to-web-app-example.png?resize=552%2C601&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Add Web App to Project Hosting\" class=\"wp-image-107458\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/add-firebase-to-web-app-example.png?w=552&amp;quality=100&amp;strip=all&amp;ssl=1 552w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/add-firebase-to-web-app-example.png?resize=276%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 276w\" sizes=\"(max-width: 552px) 100vw, 552px\" \/><\/figure><\/div>\n\n\n<p id=\"firebaseconfig-object\"><strong>4)<\/strong>&nbsp;Then, copy the&nbsp;<span class=\"rnthl rntliteral\">firebaseConfig<\/span>&nbsp;object and save it because you&#8217;ll need it later.<\/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=\"666\" height=\"210\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/app-firebase-config-object.png?resize=666%2C210&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"firebaseConfig\u00a0object configuration copy save\" class=\"wp-image-107462\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/app-firebase-config-object.png?w=666&amp;quality=100&amp;strip=all&amp;ssl=1 666w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/app-firebase-config-object.png?resize=300%2C95&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 666px) 100vw, 666px\" \/><\/figure><\/div>\n\n\n<p>After this, you can also access the&nbsp;<span class=\"rnthl rntliteral\">firebaseConfig<\/span>&nbsp;object if you go to your Project settings in your Firebase console.<\/p>\n\n\n\n<p><strong>5)<\/strong> Click&nbsp;<strong>Next<\/strong>&nbsp;on the proceeding steps, and finally on <strong>Continue to console<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2) Setting Up a Firebase Web App Project (VS Code)<\/h2>\n\n\n\n<p>Follow the next steps to create a Firebase Web App Project using VS Code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-project-folder\">1) Creating a Project Folder<\/h3>\n\n\n\n<p id=\"create-project-folder\"><strong>1)<\/strong> Create a folder on your computer where you want to save your Firebase project\u2014for example, <span class=\"rnthl rntliteral\"><em>Firebase-Project<\/em><\/span> on the Desktop.<\/p>\n\n\n\n<p><strong>2)<\/strong> Open VS Code. Go to <strong>File<\/strong> &gt; <strong>Open Folder&#8230;<\/strong> and select the folder you&#8217;ve just created.<\/p>\n\n\n\n<p><strong>3)<\/strong> Go to <strong>Terminal <\/strong>&gt; <strong>New Terminal<\/strong>. A new Terminal window should open on your project path.<\/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=\"746\" height=\"163\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Terminal-Window-Firebase-Folder-Project.png?resize=746%2C163&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Install Firebase Tools 2\" class=\"wp-image-106193\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Terminal-Window-Firebase-Folder-Project.png?w=746&amp;quality=100&amp;strip=all&amp;ssl=1 746w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Terminal-Window-Firebase-Folder-Project.png?resize=300%2C66&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 746px) 100vw, 746px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"firebase-login\">2) Firebase Login<\/h3>\n\n\n\n<p><strong>4)<\/strong> On the previous Terminal window, type the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><strong>firebase <\/strong>login<\/code><\/pre>\n\n\n\n<p><strong>5)<\/strong> You&#8217;ll be asked to collect CLI usage and error reporting information. Enter &#8220;<strong>n<\/strong>&#8221; and press <strong>Enter<\/strong> to deny.<\/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=\"750\" height=\"397\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Terminal-Window-Login-Firebase-VS-Code.png?resize=750%2C397&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Login Firebase VS Code Terminal Window\" class=\"wp-image-106194\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Terminal-Window-Login-Firebase-VS-Code.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Terminal-Window-Login-Firebase-VS-Code.png?resize=300%2C159&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p><strong>Note:<\/strong> If you are already logged in, it will show a message saying: &#8220;Already logged in as user@gmail.com&#8221;.<\/p>\n\n\n\n<p><strong>6)<\/strong> After this, it will pop up a new window on your browser to login into your Firebase account.<\/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=\"750\" height=\"648\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Login-Firebase-Account.png?resize=750%2C648&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Login Firebase Account\" class=\"wp-image-106195\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Login-Firebase-Account.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Login-Firebase-Account.png?resize=300%2C259&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p><strong>7)<\/strong> Allow Firebase CLI to access your Google account.<\/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=\"750\" height=\"875\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Login-Firebase-Account-2.png?resize=750%2C875&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Login Firebase Account allow Firebase CLI\" class=\"wp-image-106196\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Login-Firebase-Account-2.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Login-Firebase-Account-2.png?resize=257%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 257w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p><strong>8)<\/strong> After this, Firebase CLI login should be successful. You can close the browser window.<\/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=\"689\" height=\"454\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-CLI-Login-Successful.png?resize=689%2C454&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Login Firebase Account allow Firebase CLI Login Successful\" class=\"wp-image-106197\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-CLI-Login-Successful.png?w=689&amp;quality=100&amp;strip=all&amp;ssl=1 689w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-CLI-Login-Successful.png?resize=300%2C198&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 689px) 100vw, 689px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"init-project\">3) Initializing Web App Firebase Project<\/h3>\n\n\n\n<p><strong>9)<\/strong> After successfully login in, run the following command to start a Firebase project directory in the current folder.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><strong>firebase <\/strong>init<\/code><\/pre>\n\n\n\n<p><strong>10)<\/strong> You&#8217;ll be asked if you want to initialize a Firebase project in the current directory. Enter <strong>Y<\/strong> and hit <strong>Enter<\/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=\"684\" height=\"187\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-Start-Project-VS-Code.png?resize=684%2C187&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Login Firebase Account allow Firebase CLI firebase init\" class=\"wp-image-106199\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-Start-Project-VS-Code.png?w=684&amp;quality=100&amp;strip=all&amp;ssl=1 684w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-Start-Project-VS-Code.png?resize=300%2C82&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 684px) 100vw, 684px\" \/><\/figure><\/div>\n\n\n<p><strong>11)<\/strong> Then, use up and down arrows and the Space key to select the options. Select the following options:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Realtime Database<\/strong>: Configure security rules file for Realtime Database and (optionally) provision default instance.  <\/li>\n\n\n\n<li><strong>Hosting<\/strong>: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys<\/li>\n<\/ul>\n\n\n\n<p>The selected options will show up with a green asterisk. Then, hit <strong>Enter<\/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=\"750\" height=\"186\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/realtime-database-hosting-options.png?resize=750%2C186&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Login Firebase Account allow Firebase CLI configure directory\" class=\"wp-image-106239\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/realtime-database-hosting-options.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/realtime-database-hosting-options.png?resize=300%2C74&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p><strong>12)<\/strong> Select the option &#8220;Use an existing project&#8221;\u2014it should be highlighted in blue\u2014then, hit Enter.<\/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=\"658\" height=\"228\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-Project-Setup-VS-Code.png?resize=658%2C228&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Project Setup VS Code\" class=\"wp-image-106201\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-Project-Setup-VS-Code.png?w=658&amp;quality=100&amp;strip=all&amp;ssl=1 658w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/Firebase-Project-Setup-VS-Code.png?resize=300%2C104&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 658px) 100vw, 658px\" \/><\/figure><\/div>\n\n\n<p><strong>13)<\/strong> After that, select the Firebase project for this directory\u2014it should be <strong><a href=\"https:\/\/randomnerdtutorials.com\/esp32-data-logging-firebase-realtime-database\/\" title=\"\">the project created in this previous tutorial<\/a><\/strong>. In my case, it is called <span class=\"rnthl rntliteral\"><em>ESP-project<\/em><\/span>. Then hit <strong>Enter<\/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=\"662\" height=\"174\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/select-firebase-project-VS-Code.png?resize=662%2C174&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Select Firebase Project on VS Code\" class=\"wp-image-170134\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/select-firebase-project-VS-Code.png?w=662&amp;quality=100&amp;strip=all&amp;ssl=1 662w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2025\/04\/select-firebase-project-VS-Code.png?resize=300%2C79&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 662px) 100vw, 662px\" \/><\/figure><\/div>\n\n\n<p><strong>14)<\/strong> Then, select the hosting options as shown below:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>What do you want to use as your public directory? Hit <strong>Enter <\/strong>to select <strong>public<\/strong>.<\/li>\n\n\n\n<li>Configure as a single-page app (rewrite urls to \/index.html)? <strong>No<\/strong><\/li>\n\n\n\n<li>Set up automatic builds and deploys with GitHub? <strong>No<\/strong><\/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=\"657\" height=\"277\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/firebase-hosting-setup-all-options.png?resize=657%2C277&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase initialization complete\" class=\"wp-image-106240\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/firebase-hosting-setup-all-options.png?w=657&amp;quality=100&amp;strip=all&amp;ssl=1 657w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/firebase-hosting-setup-all-options.png?resize=300%2C126&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 657px) 100vw, 657px\" \/><\/figure><\/div>\n\n\n<p><strong>15)<\/strong> Press <strong>Enter <\/strong>on the following question to select the default database security rules file: &#8220;<strong>What file should be used for Realtime Database Security Rules?<\/strong>&#8220;<\/p>\n\n\n\n<p><strong>16)<\/strong> The Firebase project should now be initialized successfully. Notice that VS Code created some essential files under your project folder.<\/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=\"750\" height=\"382\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/firebase-project-app-created-successfully.png?resize=750%2C382&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Project Files Created successfully\" class=\"wp-image-106297\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/firebase-project-app-created-successfully.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/08\/firebase-project-app-created-successfully.png?resize=300%2C153&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>The <span class=\"rnthl rntliteral\">index.html<\/span> file contains some HTML text to build a web page. For now, leave the default HTML text. The idea is to replace that with your own HTML text to build a custom web page for your needs. We&#8217;ll do that later in this tutorial.<\/p>\n\n\n\n<p><strong>17)<\/strong> To check if everything went as expected, run the following command on the VS Code Terminal window.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><strong>firebase <\/strong>deploy<\/code><\/pre>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"713\" height=\"384\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/deploy-firebase-project.png?resize=713%2C384&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase App First Deploy Testing\" class=\"wp-image-170137\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/deploy-firebase-project.png?w=713&amp;quality=100&amp;strip=all&amp;ssl=1 713w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/deploy-firebase-project.png?resize=300%2C162&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 713px) 100vw, 713px\" \/><\/figure><\/div>\n\n\n<p>You should get a <strong>Deploy complete!<\/strong> message and a URL to the Project Console and the Hosting URL.<\/p>\n\n\n\n<p><strong>18)<\/strong> Copy the hosting URL and paste it into a web browser window. You should see the following web page. You can access that web page from anywhere in the world.<\/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=\"740\" height=\"534\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Hosting-Setup-Complete.png?resize=740%2C534&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Hosting Setup Complete\" class=\"wp-image-170138\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Hosting-Setup-Complete.png?w=740&amp;quality=100&amp;strip=all&amp;ssl=1 740w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Hosting-Setup-Complete.png?resize=300%2C216&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 740px) 100vw, 740px\" \/><\/figure><\/div>\n\n\n<p>The web page you&#8217;ve seen previously is built with the HTML file placed in the <span class=\"rnthl rntliteral\">public<\/span> folder of your Firebase project. By changing the content of that file, you can create your own web app. That&#8217;s what we&#8217;re going to do in the next section.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3) Creating Firebase Web App<\/h2>\n\n\n\n<p>Now that you&#8217;ve created a Firebase project app successfully on VS Code, follow the next steps to customize the app to display the sensor readings on a login-protected web page.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">index.html<\/h3>\n\n\n\n<p>Copy the following to your <span class=\"rnthl rntliteral\">index.html<\/span> file (it is inside the <span class=\"rnthl rntliteral\">public<\/span> folder).<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n  &lt;head&gt;\n    &lt;meta charset=&quot;utf-8&quot;&gt;\n    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\n    &lt;title&gt;ESP Datalogging Firebase App&lt;\/title&gt;\n\n    &lt;!-- include highchartsjs to build the charts--&gt;\n    &lt;script src=&quot;https:\/\/code.highcharts.com\/highcharts.js&quot;&gt;&lt;\/script&gt;\n    &lt;!-- include to use jquery--&gt;\n    &lt;script src=&quot;https:\/\/ajax.googleapis.com\/ajax\/libs\/jquery\/3.5.1\/jquery.min.js&quot;&gt;&lt;\/script&gt;\n    &lt;!--include icons from fontawesome--&gt;\n    &lt;link rel=&quot;stylesheet&quot; href=&quot;https:\/\/use.fontawesome.com\/releases\/v5.7.2\/css\/all.css&quot; integrity=&quot;sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr&quot; crossorigin=&quot;anonymous&quot;&gt;\n    &lt;!-- include Gauges Javascript library--&gt;\n    &lt;script src=&quot;https:\/\/cdn.rawgit.com\/Mikhus\/canvas-gauges\/gh-pages\/download\/2.1.7\/all\/gauge.min.js&quot;&gt;&lt;\/script&gt;\n    &lt;!--reference for favicon--&gt;\n    &lt;link rel=&quot;icon&quot; type=&quot;image\/png&quot; href=&quot;favicon.png&quot;&gt;\n    &lt;!--reference a stylesheet--&gt;\n    &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;style.css&quot;&gt;\n\n  &lt;\/head&gt;\n\n  &lt;body&gt;\n\n    &lt;!--TOP BAR--&gt;\n    &lt;div class=&quot;topnav&quot;&gt;\n      &lt;h1&gt;Sensor Readings App &lt;i class=&quot;fas fa-clipboard-list&quot;&gt;&lt;\/i&gt;&lt;\/h1&gt;\n    &lt;\/div&gt;\n\n    &lt;!--AUTHENTICATION BAR (USER DETAILS\/LOGOUT BUTTON)--&gt;\n    &lt;div id=&quot;authentication-bar&quot; style=&quot;display: none;&quot;&gt;\n      &lt;p&gt;&lt;span id=&quot;authentication-status&quot;&gt;User logged in&lt;\/span&gt;\n        &lt;span id=&quot;user-details&quot;&gt;USEREMAIL&lt;\/span&gt;\n        &lt;a href=&quot;\/&quot; id=&quot;logout-link&quot;&gt;(logout)&lt;\/a&gt;\n      &lt;\/p&gt;\n    &lt;\/div&gt;\n\n    &lt;!--LOGIN FORM--&gt;\n    &lt;form id=&quot;login-form&quot; style=&quot;display: none;&quot;&gt;\n      &lt;div class=&quot;form-elements-container&quot;&gt;\n        &lt;label for=&quot;input-email&quot;&gt;&lt;b&gt;Email&lt;\/b&gt;&lt;\/label&gt;\n        &lt;input type=&quot;text&quot; placeholder=&quot;Enter Username&quot; id=&quot;input-email&quot; required&gt;\n\n        &lt;label for=&quot;input-password&quot;&gt;&lt;b&gt;Password&lt;\/b&gt;&lt;\/label&gt;\n        &lt;input type=&quot;password&quot; placeholder=&quot;Enter Password&quot; id=&quot;input-password&quot; required&gt;\n\n        &lt;button type=&quot;submit&quot; id=&quot;login-button&quot;&gt;Login&lt;\/button&gt;\n        &lt;p id=&quot;error-message&quot; style=&quot;color:red;&quot;&gt;&lt;\/p&gt;\n      &lt;\/div&gt;\n    &lt;\/form&gt;\n\n    &lt;!--CONTENT (SENSOR READINGS)--&gt;\n    &lt;div class=&quot;content-sign-in&quot; id=&quot;content-sign-in&quot; style=&quot;display: none;&quot;&gt;\n\n      &lt;!--LAST UPDATE--&gt;\n      &lt;p&gt;&lt;span class =&quot;date-time&quot;&gt;Last update: &lt;span id=&quot;lastUpdate&quot;&gt;&lt;\/span&gt;&lt;\/span&gt;&lt;\/p&gt;\n      &lt;p&gt;\n        Cards: &lt;input type=&quot;checkbox&quot; id=&quot;cards-checkbox&quot; name=&quot;cards-checkbox&quot; checked&gt;\n        Gauges: &lt;input type=&quot;checkbox&quot; id=&quot;gauges-checkbox&quot; name=&quot;gauges-checkbox&quot; checked&gt;\n        Charts: &lt;input type=&quot;checkbox&quot; id=&quot;charts-checkbox&quot; name=&quot;charts-checkbox&quot; unchecked&gt;\n      &lt;\/p&gt;\n      &lt;div id=&quot;cards-div&quot;&gt;\n        &lt;div class=&quot;cards&quot;&gt;\n          &lt;!--TEMPERATURE--&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;p&gt;&lt;i class=&quot;fas fa-thermometer-half&quot; style=&quot;color:#059e8a;&quot;&gt;&lt;\/i&gt; TEMPERATURE&lt;\/p&gt;\n            &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;temp&quot;&gt;&lt;\/span&gt; &amp;deg;C&lt;\/span&gt;&lt;\/p&gt;\n          &lt;\/div&gt;\n          &lt;!--HUMIDITY--&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;p&gt;&lt;i class=&quot;fas fa-tint&quot; style=&quot;color:#00add6;&quot;&gt;&lt;\/i&gt; HUMIDITY&lt;\/p&gt;\n            &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;hum&quot;&gt;&lt;\/span&gt; &amp;percnt;&lt;\/span&gt;&lt;\/p&gt;\n          &lt;\/div&gt;\n          &lt;!--PRESSURE--&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;p&gt;&lt;i class=&quot;fas fa-angle-double-down&quot; style=&quot;color:#e1e437;&quot;&gt;&lt;\/i&gt; PRESSURE&lt;\/p&gt;\n            &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;pres&quot;&gt;&lt;\/span&gt; hPa&lt;\/span&gt;&lt;\/p&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n      &lt;!--GAUGES--&gt;\n      &lt;div id =&quot;gauges-div&quot;&gt;\n        &lt;div class=&quot;cards&quot;&gt;\n          &lt;!--TEMPERATURE--&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;canvas id=&quot;gauge-temperature&quot;&gt;&lt;\/canvas&gt;\n          &lt;\/div&gt;\n          &lt;!--HUMIDITY--&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;canvas id=&quot;gauge-humidity&quot;&gt;&lt;\/canvas&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n\n      &lt;!--CHARTS--&gt;\n      &lt;div id=&quot;charts-div&quot; style=&quot;display:none&quot;&gt;\n        &lt;!--SET NUMBER OF READINGS INPUT FIELD--&gt;\n        &lt;div&gt;\n          &lt;p&gt; Number of readings: &lt;input type=&quot;number&quot; id=&quot;charts-range&quot;&gt;&lt;\/p&gt;\n        &lt;\/div&gt;\n        &lt;!--TEMPERATURE-CHART--&gt;\n        &lt;div class=&quot;cards&quot;&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;p&gt;&lt;i class=&quot;fas fa-thermometer-half&quot; style=&quot;color:#059e8a;&quot;&gt;&lt;\/i&gt; TEMPERATURE CHART&lt;\/p&gt;\n            &lt;div id=&quot;chart-temperature&quot; class=&quot;chart-container&quot;&gt;&lt;\/div&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;!--HUMIDITY-CHART--&gt;\n        &lt;div class=&quot;cards&quot;&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;p&gt;&lt;i class=&quot;fas fa-tint&quot; style=&quot;color:#00add6;&quot;&gt;&lt;\/i&gt; HUMIDITY CHART&lt;\/p&gt;\n            &lt;div id=&quot;chart-humidity&quot; class=&quot;chart-container&quot;&gt;&lt;\/div&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;!--PRESSURE-CHART--&gt;\n        &lt;div class=&quot;cards&quot;&gt;\n          &lt;div class=&quot;card&quot;&gt;\n            &lt;p&gt;&lt;i class=&quot;fas fa-angle-double-down&quot; style=&quot;color:#e1e437;&quot;&gt;&lt;\/i&gt; PRESSURE CHART&lt;\/p&gt;\n            &lt;div id=&quot;chart-pressure&quot; class=&quot;chart-container&quot;&gt;&lt;\/div&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n\n    &lt;!--BUTTONS TO HANDLE DATA--&gt;\n    &lt;p&gt;\n      &lt;!--View data button--&gt;\n      &lt;button id=&quot;view-data-button&quot;&gt;View all data&lt;\/button&gt;\n      &lt;!--Hide data button--&gt;\n      &lt;button id=&quot;hide-data-button&quot; style= &quot;display:none;&quot;&gt;Hide data&lt;\/button&gt;\n      &lt;!--Delete data button--&gt;\n      &lt;button id=&quot;delete-button&quot; class=&quot;deletebtn&quot;&gt;Delete data&lt;\/button&gt;\n    &lt;\/p&gt;\n    &lt;!--Modal to delete data--&gt;\n    &lt;div id=&quot;delete-modal&quot; class=&quot;modal&quot; sytle=&quot;display:none&quot;&gt;\n      &lt;span onclick = &quot;document.getElementById('delete-modal').style.display='none'&quot; class=&quot;close&quot; title=&quot;Close Modal&quot;&gt;\u00d7&lt;\/span&gt;\n      &lt;form id= &quot;delete-data-form&quot; class=&quot;modal-content&quot; action=&quot;\/&quot;&gt;\n        &lt;div class=&quot;container&quot;&gt;\n          &lt;h1&gt;Delete Data&lt;\/h1&gt;\n          &lt;p&gt;Are you sure you want to delete all data from database?&lt;\/p&gt;\n          &lt;div class=&quot;clearfix&quot;&gt;\n            &lt;button type=&quot;button&quot; onclick=&quot;document.getElementById('delete-modal').style.display='none'&quot; class=&quot;cancelbtn&quot;&gt;Cancel&lt;\/button&gt;\n            &lt;button type=&quot;submit&quot; onclick=&quot;document.getElementById('delete-modal').style.display='none'&quot; class=&quot;deletebtn&quot;&gt;Delete&lt;\/button&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n      &lt;\/form&gt;\n    &lt;\/div&gt;\n\n    &lt;!--TABLE WITH ALL DATA--&gt;\n    &lt;div class =&quot;cards&quot;&gt;\n      &lt;div class=&quot;card&quot; id=&quot;table-container&quot; style= &quot;display:none;&quot;&gt;\n        &lt;table id=&quot;readings-table&quot;&gt;\n            &lt;tr id=&quot;theader&quot;&gt;\n              &lt;th&gt;Timestamp&lt;\/th&gt;\n              &lt;th&gt;Temp (\u00baC)&lt;\/th&gt;\n              &lt;th&gt;Hum (%)&lt;\/th&gt;\n              &lt;th&gt;Pres (hPa)&lt;\/th&gt;\n            &lt;\/tr&gt;\n            &lt;tbody id=&quot;tbody&quot;&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n        &lt;p&gt;&lt;button id=&quot;load-data&quot; style= &quot;display:none;&quot;&gt;More results...&lt;\/button&gt;&lt;\/p&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n\n  &lt;\/div&gt;\n\n    &lt;!--INCLUDE JS FILES--&gt;\n    &lt;script type=&quot;module&quot; src=&quot;scripts\/auth.js&quot;&gt;&lt;\/script&gt;\n    &lt;script type=&quot;module&quot; src=&quot;scripts\/charts-definition.js&quot;&gt;&lt;\/script&gt;\n    &lt;script type=&quot;module&quot; src=&quot;scripts\/gauges-definition.js&quot;&gt;&lt;\/script&gt;\n    &lt;script type=&quot;module&quot; src=&quot;scripts\/index.js&quot;&gt;&lt;\/script&gt;\n\n  &lt;\/body&gt;\n\n&lt;\/html&gt;\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Firebase-ESP\/raw\/main\/ESP-Firebase-Datalogging-Web-App\/index.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">style.css<\/h3>\n\n\n\n<p>Inside the <span class=\"rnthl rntliteral\">public<\/span> folder create a file called <span class=\"rnthl rntliteral\">style.css<\/span>. To create the file, select the <span class=\"rnthl rntliteral\">public<\/span> folder, and then click on the <strong>+file<\/strong> icon at the top of the File Explorer. Call it <span class=\"rnthl rntliteral\">style.css<\/span>.<\/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=\"578\" height=\"277\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/Firebase-Project-VS-Code-CSS-File.png?resize=578%2C277&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Create CSS File VS Code\" class=\"wp-image-107545\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/Firebase-Project-VS-Code-CSS-File.png?w=578&amp;quality=100&amp;strip=all&amp;ssl=1 578w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/10\/Firebase-Project-VS-Code-CSS-File.png?resize=300%2C144&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 578px) 100vw, 578px\" \/><\/figure><\/div>\n\n\n<p>Then, copy the following to the <span class=\"rnthl rntliteral\">style.css<\/span> file<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-css\">html {\r\n    font-family: Verdana, Geneva, Tahoma, sans-serif;\r\n    display: inline-block;\r\n    text-align: center;\r\n}\r\n\r\nbody {\r\n    margin: 0;\r\n    width: 100%;\r\n}\r\n\r\n.topnav {\r\n    overflow: hidden;\r\n    background-color: #049faa;\r\n    color: white;\r\n    font-size: 1rem;\r\n    padding: 5px;\r\n}\r\n\r\n#authentication-bar{\r\n    background-color:mintcream;\r\n    padding-top: 10px;\r\n    padding-bottom: 10px;\r\n}\r\n\r\n#user-details{\r\n    color: cadetblue;\r\n}\r\n\r\n.content {\r\n    padding: 20px;\r\n}\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    padding: 5%;\r\n}\r\n\r\n.cards {\r\n    max-width: 800px;\r\n    margin: 0 auto;\r\n    margin-bottom: 10px;\r\n    display: grid;\r\n    grid-gap: 2rem;\r\n    grid-template-columns: repeat(auto-fit, minmax(200px, 2fr));\r\n}\r\n\r\n.reading {\r\n    color: #193036;\r\n}\r\n\r\n.date-time{\r\n    font-size: 0.8rem;\r\n    color: #1282A2;\r\n}\r\n\r\nbutton {\r\n    background-color: #049faa;\r\n    color: white;\r\n    padding: 14px 20px;\r\n    margin: 8px 0;\r\n    border: none;\r\n    cursor: pointer;\r\n    border-radius: 4px;\r\n}\r\nbutton:hover {\r\n   opacity: 0.8;\r\n}\r\n.deletebtn{\r\n    background-color: #c52c2c;\r\n}\r\n\r\n.form-elements-container{\r\n    padding: 16px;\r\n    width: 250px;\r\n    margin: 0 auto;\r\n}\r\n\r\ninput[type=text], input[type=password] {\r\n    width: 100%;\r\n    padding: 12px 20px;\r\n    margin: 8px 0;\r\n    display: inline-block;\r\n    border: 1px solid #ccc;\r\n    box-sizing: border-box;\r\n}\r\n\r\ntable {\r\n    width: 100%;\r\n    text-align: center;\r\n    font-size: 0.8rem;\r\n}   \r\ntr, td {\r\n    padding: 0.25rem;\r\n}\r\ntr:nth-child(even) {\r\n    background-color: #f2f2f2\r\n}\r\ntr:hover {\r\n    background-color: #ddd;\r\n}\r\nth {\r\n    position: sticky;\r\n    top: 0;\r\n    background-color: #50b8b4;\r\n    color: white;\r\n}\r\n\r\n\/* The Modal (background) *\/\r\n.modal {\r\n    display: none; \/* Hidden by default *\/\r\n    position: fixed; \/* Stay in place *\/\r\n    z-index: 1; \/* Sit on top *\/\r\n    left: 0;\r\n    top: 0;\r\n    width: 100%; \/* Full width *\/\r\n    height: 100%; \/* Full height *\/\r\n    overflow: auto; \/* Enable scroll if needed *\/\r\n    background-color: #474e5d;\r\n    padding-top: 50px;\r\n}\r\n  \r\n\/* Modal Content\/Box *\/\r\n.modal-content {\r\n    background-color: #fefefe;\r\n    margin: 5% auto 15% auto; \/* 5% from the top, 15% from the bottom and centered *\/\r\n    border: 1px solid #888;\r\n    width: 80%; \/* Could be more or less, depending on screen size *\/\r\n}\r\n  \r\n\/* Style the horizontal ruler *\/\r\nhr {\r\n    border: 1px solid #f1f1f1;\r\n    margin-bottom: 25px;\r\n}\r\n\r\n\/* The Modal Close Button (x) *\/\r\n.close {\r\n    position: absolute;\r\n    right: 35px;\r\n    top: 15px;\r\n    font-size: 40px;\r\n    font-weight: bold;\r\n    color: #f1f1f1;\r\n}\r\n\r\n.close:hover,\r\n.close:focus {\r\n    color: #f44336;\r\n    cursor: pointer;\r\n}\r\n\r\n\/* Clear floats *\/\r\n.clearfix::after {\r\n    content: &quot;&quot;;\r\n    clear: both;\r\n    display: table;\r\n}\r\n\r\n\/* Change styles for cancel button and delete button on extra small screens *\/\r\n@media screen and (max-width: 300px) {\r\n    .cancelbtn, .deletebtn {\r\n        width: 100%;\r\n    }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Firebase-ESP\/raw\/main\/ESP-Firebase-Datalogging-Web-App\/style.css\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>The CSS file includes some simple styles to make our webpage look better. We won&#8217;t discuss how CSS works in this tutorial.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">JavaScript Files<\/h3>\n\n\n\n<p>We&#8217;ll create four JavaScript files (<span class=\"rnthl rntliteral\">auth.js<\/span>, <span class=\"rnthl rntliteral\">index.js<\/span>, <span class=\"rnthl rntliteral\">charts-definition.js<\/span>, and <span class=\"rnthl rntliteral\">gauges-definition.js<\/span>) inside a <span class=\"rnthl rntliteral\">scripts<\/span> folder inside the <span class=\"rnthl rntliteral\">public<\/span> folder.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Select the <span class=\"rnthl rntliteral\">public<\/span> folder, then click on the <strong>+folder<\/strong> icon to create a new folder. Call <span class=\"rnthl rntliteral\">scripts<\/span> to that new folder.<\/li>\n\n\n\n<li>Then, select the <span class=\"rnthl rntliteral\">scripts<\/span> folder and click on the <strong>+file<\/strong> icon. Create a file called <span class=\"rnthl rntliteral\">auth.js<\/span>. Then, repeat the previous steps to create the <span class=\"rnthl rntliteral\">index.js<\/span>, <span class=\"rnthl rntliteral\">charts-definition.js<\/span>, and <span class=\"rnthl rntliteral\">gauges-definition.js<\/span> files.<\/li>\n<\/ul>\n\n\n\n<p>The following image shows what your web app project folder structure should look like.<\/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=\"750\" height=\"386\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Datalogging-Project-Structure-Web-App.png?resize=750%2C386&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Project VS Code Folder File Structure\" class=\"wp-image-109342\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Datalogging-Project-Structure-Web-App.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Datalogging-Project-Structure-Web-App.png?resize=300%2C154&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<h4 class=\"wp-block-heading\">auth.js<\/h4>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">auth.js<\/span> file you created previously.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">import { auth } from &quot;.\/index.js&quot;;\r\nimport { signInWithEmailAndPassword, signOut, onAuthStateChanged } from &quot;https:\/\/www.gstatic.com\/firebasejs\/11.6.0\/firebase-auth.js&quot;;\r\n\r\ndocument.addEventListener(&quot;DOMContentLoaded&quot;, () =&gt; {\r\n  \/\/ Listen for auth status changes\r\n  onAuthStateChanged(auth, (user) =&gt; {\r\n    if (user) {\r\n      console.log(&quot;User logged in:&quot;, user.email);\r\n      setupUI(user);\r\n    } else {\r\n      console.log(&quot;User logged out&quot;);\r\n      setupUI(null);\r\n    }\r\n  });\r\n\r\n  \/\/ Login\r\n  const loginForm = document.querySelector('#login-form');\r\n  loginForm.addEventListener('submit', async (e) =&gt; {\r\n    e.preventDefault();\r\n    const email = loginForm['input-email'].value;\r\n    const password = loginForm['input-password'].value;\r\n    try {\r\n      await signInWithEmailAndPassword(auth, email, password);\r\n      loginForm.reset();\r\n      console.log(&quot;Logged in:&quot;, email);\r\n    } catch (error) {\r\n      document.getElementById(&quot;error-message&quot;).innerHTML = error.message;\r\n      console.error(&quot;Login error:&quot;, error.message);\r\n    }\r\n  });\r\n\r\n  \/\/ Logout\r\n  const logoutLink = document.querySelector('#logout-link');\r\n  logoutLink.addEventListener('click', async (e) =&gt; {\r\n    e.preventDefault();\r\n    try {\r\n      await signOut(auth);\r\n      console.log(&quot;User signed out&quot;);\r\n    } catch (error) {\r\n      console.error(&quot;Logout error:&quot;, error.message);\r\n    }\r\n  });\r\n});\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Firebase-ESP\/raw\/main\/ESP-Firebase-Datalogging-Web-App\/scripts\/auth.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Then, save the file. This file takes care of everything related to the login and logout of the user. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">index.js<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">index.js<\/span> file handles the UI\u2014it shows the right content depending on the user authentication status. When the user is logged in, this file gets new readings from the database whenever there&#8217;s a change and displays them in the right places.<\/p>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">index.js<\/span> file. <\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">import { initializeApp } from &quot;https:\/\/www.gstatic.com\/firebasejs\/11.6.0\/firebase-app.js&quot;;\r\nimport { getAuth } from &quot;https:\/\/www.gstatic.com\/firebasejs\/11.6.0\/firebase-auth.js&quot;;\r\nimport { getDatabase, ref, onValue, set, remove, query, orderByKey, limitToLast, onChildAdded, endAt, get } from &quot;https:\/\/www.gstatic.com\/firebasejs\/11.6.0\/firebase-database.js&quot;;\r\nimport { createTemperatureChart, createHumidityChart, createPressureChart } from &quot;.\/charts-definition.js&quot;;\r\nimport { createTemperatureGauge, createHumidityGauge } from &quot;.\/gauges-definition.js&quot;;\r\n\r\n\/\/ Firebase configuration\r\nconst firebaseConfig = {\r\n  apiKey: &quot;REPLACE_WITH_YOUR_Firebase_CONFIGURATION&quot;,\r\n  authDomain: &quot;REPLACE_WITH_YOUR_Firebase_CONFIGURATION&quot;,\r\n  databaseURL: &quot;REPLACE_WITH_YOUR_Firebase_CONFIGURATION&quot;,\r\n  projectId: &quot;REPLACE_WITH_YOUR_Firebase_CONFIGURATION&quot;,\r\n  storageBucket: &quot;REPLACE_WITH_YOUR_Firebase_CONFIGURATION&quot;,\r\n  messagingSenderId: &quot;REPLACE_WITH_YOUR_Firebase_CONFIGURATION&quot;,\r\n  appId: &quot;REPLACE_WITH_YOUR_Firebase_CONFIGURATION&quot;\r\n};\r\n\r\n\/\/ Initialize Firebase\r\nconst app = initializeApp(firebaseConfig);\r\nconst auth = getAuth(app);\r\nconst database = getDatabase(app);\r\n\r\n\/\/ Export auth for use in auth.js\r\nexport { auth };\r\n\r\n\r\nconst epochToJsDate = (epochTime) =&gt; new Date(epochTime * 1000);\r\n\r\nconst epochToDateTime = (epochTime) =&gt; {\r\n  if (!epochTime) return 'N\/A';\r\n  const date = epochToJsDate(epochTime);\r\n  return `${date.getFullYear()}\/${String(date.getMonth() + 1).padStart(2, '0')}\/${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;\r\n};\r\n\r\n\/\/ Validate and format sensor reading\r\nconst formatReading = (value) =&gt; (typeof value === 'number' ? value.toFixed(2) : 'N\/A');\r\n\r\nconst plotValues = (chart, timestamp, value) =&gt; {\r\n  if (!timestamp || typeof value !== 'number') return;\r\n  const x = epochToJsDate(timestamp).getTime();\r\n  const y = Number(value);\r\n  const shift = chart.series[0].data.length &gt; 40;\r\n  chart.series[0].addPoint([x, y], true, shift, true);\r\n};\r\n\r\n\/\/ DOM elements\r\nconst loginElement = document.querySelector('#login-form');\r\nconst contentElement = document.querySelector('#content-sign-in');\r\nconst userDetailsElement = document.querySelector('#user-details');\r\nconst authBarElement = document.querySelector('#authentication-bar');\r\nconst deleteButtonElement = document.getElementById('delete-button');\r\nconst deleteModalElement = document.getElementById('delete-modal');\r\nconst deleteDataFormElement = document.querySelector('#delete-data-form');\r\nconst viewDataButtonElement = document.getElementById('view-data-button');\r\nconst hideDataButtonElement = document.getElementById('hide-data-button');\r\nconst tableContainerElement = document.querySelector('#table-container');\r\nconst chartsRangeInputElement = document.getElementById('charts-range');\r\nconst loadDataButtonElement = document.getElementById('load-data');\r\nconst cardsCheckboxElement = document.querySelector('input[name=cards-checkbox]');\r\nconst gaugesCheckboxElement = document.querySelector('input[name=gauges-checkbox]');\r\nconst chartsCheckboxElement = document.querySelector('input[name=charts-checkbox]');\r\nconst cardsReadingsElement = document.querySelector('#cards-div');\r\nconst gaugesReadingsElement = document.querySelector('#gauges-div');\r\nconst chartsDivElement = document.querySelector('#charts-div');\r\nconst tempElement = document.getElementById('temp');\r\nconst humElement = document.getElementById('hum');\r\nconst presElement = document.getElementById('pres');\r\nconst updateElement = document.getElementById('lastUpdate');\r\n\r\n\/\/ Chart and Gauge variables\r\nlet chartT, chartH, chartP;\r\nlet gaugeT, gaugeH;\r\n\r\n\/\/ Manage Login\/Logout UI\r\nconst setupUI = (user) =&gt; {\r\n  console.log('setupUI called with user:', user ? user.email : null);\r\n  if (user) {\r\n    \/\/ Toggle UI elements for logged-in state\r\n    loginElement.style.display = 'none';\r\n    contentElement.style.display = 'block';\r\n    authBarElement.style.display = 'block';\r\n    userDetailsElement.style.display = 'block';\r\n    userDetailsElement.innerHTML = user.email;\r\n\r\n    const uid = user.uid;\r\n    const dbPath = `UsersData\/${uid}\/readings`;\r\n    const chartPath = `UsersData\/${uid}\/charts\/range`;\r\n\r\n    \/\/ Database references\r\n    const dbRef = ref(database, dbPath);\r\n    const chartRef = ref(database, chartPath);\r\n\r\n    \/\/ Initialize gauges\r\n    gaugeT = createTemperatureGauge();\r\n    gaugeH = createHumidityGauge();\r\n    gaugeT.draw();\r\n    gaugeH.draw();\r\n\r\n    \/\/ Charts\r\n    onValue(chartRef, (snapshot) =&gt; {\r\n      const rawValue = snapshot.val();\r\n      \/\/ Ensure chartRange is a positive integer; default to 100 if invalid\r\n      const chartRange = Number.isInteger(Number(rawValue)) &amp;&amp; Number(rawValue) &gt; 0 ? Number(rawValue) : 100;\r\n\r\n      \/\/ Destroy existing charts\r\n      if (chartT) chartT.destroy();\r\n      if (chartH) chartH.destroy();\r\n      if (chartP) chartP.destroy();\r\n\r\n      \/\/ Create new charts\r\n      chartT = createTemperatureChart();\r\n      chartH = createHumidityChart();\r\n      chartP = createPressureChart();\r\n\r\n      \/\/ Query and plot latest readings\r\n      const readingsQuery = query(dbRef, orderByKey(), limitToLast(chartRange));\r\n      onChildAdded(readingsQuery, (snapshot) =&gt; {\r\n        const data = snapshot.val();\r\n        console.log('Chart reading:', data);\r\n        if (data &amp;&amp; typeof data === 'object') {\r\n          const { temperature, humidity, pressure, timestamp } = data;\r\n          plotValues(chartT, timestamp, temperature);\r\n          plotValues(chartH, timestamp, humidity);\r\n          plotValues(chartP, timestamp, pressure);\r\n        }\r\n      });\r\n    });\r\n\r\n    \/\/ Update chart range\r\n    chartsRangeInputElement.addEventListener('change', () =&gt; {\r\n      const newValue = Number(chartsRangeInputElement.value);\r\n      \/\/ Only update if the new value is a positive integer\r\n      if (Number.isInteger(newValue) &amp;&amp; newValue &gt; 0) {\r\n        set(chartRef, newValue);\r\n      } else {\r\n        console.warn('Invalid chart range input; must be a positive integer');\r\n        chartsRangeInputElement.value = ''; \/\/ Clear invalid input\r\n      }\r\n    });\r\n\r\n    \/\/ Checkboxes\r\n    cardsCheckboxElement.addEventListener('change', () =&gt; {\r\n      cardsReadingsElement.style.display = cardsCheckboxElement.checked ? 'block' : 'none';\r\n    });\r\n\r\n    gaugesCheckboxElement.addEventListener('change', () =&gt; {\r\n      gaugesReadingsElement.style.display = gaugesCheckboxElement.checked ? 'block' : 'none';\r\n    });\r\n\r\n    chartsCheckboxElement.addEventListener('change', () =&gt; {\r\n      chartsDivElement.style.display = chartsCheckboxElement.checked ? 'block' : 'none';\r\n    });\r\n\r\n    \/\/ Cards\r\n    const lastReadingQuery = query(dbRef, orderByKey(), limitToLast(1));\r\n    onChildAdded(lastReadingQuery, (snapshot) =&gt; {\r\n      const data = snapshot.val();\r\n      console.log('Card reading:', data);\r\n      if (data &amp;&amp; typeof data === 'object') {\r\n        const { temperature, humidity, pressure, timestamp } = data;\r\n        tempElement.innerHTML = formatReading(temperature);\r\n        humElement.innerHTML = formatReading(humidity);\r\n        presElement.innerHTML = formatReading(pressure);\r\n        updateElement.innerHTML = epochToDateTime(timestamp);\r\n      } else {\r\n        tempElement.innerHTML = 'N\/A';\r\n        humElement.innerHTML = 'N\/A';\r\n        presElement.innerHTML = 'N\/A';\r\n        updateElement.innerHTML = 'N\/A';\r\n      }\r\n    });\r\n\r\n    \/\/ Gauges\r\n    onChildAdded(lastReadingQuery, (snapshot) =&gt; {\r\n      const data = snapshot.val();\r\n      console.log('Gauge reading:', data);\r\n      if (data &amp;&amp; typeof data === 'object') {\r\n        const { temperature, humidity, timestamp } = data;\r\n        gaugeT.value = typeof temperature === 'number' ? temperature : 0;\r\n        gaugeH.value = typeof humidity === 'number' ? humidity : 0;\r\n        updateElement.innerHTML = epochToDateTime(timestamp);\r\n      }\r\n    });\r\n\r\n    \/\/ Delete Data\r\n    deleteButtonElement.addEventListener('click', (e) =&gt; {\r\n      e.preventDefault();\r\n      deleteModalElement.style.display = 'block';\r\n    });\r\n\r\n    deleteDataFormElement.addEventListener('submit', (e) =&gt; {\r\n      e.preventDefault();\r\n      remove(dbRef);\r\n      deleteModalElement.style.display = 'none';\r\n      \/\/ Reset UI after deletion\r\n      tempElement.innerHTML = 'N\/A';\r\n      humElement.innerHTML = 'N\/A';\r\n      presElement.innerHTML = 'N\/A';\r\n      updateElement.innerHTML = 'N\/A';\r\n      gaugeT.value = 0;\r\n      gaugeH.value = 0;\r\n    });\r\n\r\n    \/\/ Table\r\n    let lastReadingTimestamp;\r\n    const createTable = () =&gt; {\r\n      const tableQuery = query(dbRef, orderByKey(), limitToLast(100));\r\n      let firstRun = true;\r\n      onChildAdded(tableQuery, (snapshot) =&gt; {\r\n        const data = snapshot.val();\r\n        console.log('Table reading:', data);\r\n        if (data &amp;&amp; typeof data === 'object') {\r\n          const { temperature, humidity, pressure, timestamp } = data;\r\n          const content = `\r\n            &lt;tr&gt;\r\n              &lt;td&gt;${epochToDateTime(timestamp)}&lt;\/td&gt;\r\n              &lt;td&gt;${formatReading(temperature)}&lt;\/td&gt;\r\n              &lt;td&gt;${formatReading(humidity)}&lt;\/td&gt;\r\n              &lt;td&gt;${formatReading(pressure)}&lt;\/td&gt;\r\n            &lt;\/tr&gt;`;\r\n          $('#tbody').prepend(content);\r\n          if (firstRun &amp;&amp; timestamp) {\r\n            lastReadingTimestamp = timestamp;\r\n            firstRun = false;\r\n          }\r\n        }\r\n      });\r\n    };\r\n\r\n    const appendToTable = async () =&gt; {\r\n      const tableQuery = query(dbRef, orderByKey(), limitToLast(100), endAt(String(lastReadingTimestamp)));\r\n      const snapshot = await get(tableQuery);\r\n      const dataList = [];\r\n      snapshot.forEach((child) =&gt; {\r\n        const data = child.val();\r\n        console.log('Append table reading:', data);\r\n        if (data &amp;&amp; typeof data === 'object') {\r\n          dataList.push(data);\r\n        }\r\n      });\r\n      if (dataList.length &gt; 0) {\r\n        lastReadingTimestamp = dataList[0].timestamp;\r\n        const reversedList = dataList.reverse();\r\n        reversedList.forEach((element, index) =&gt; {\r\n          if (index === 0) return; \/\/ Skip first reading\r\n          const { temperature, humidity, pressure, timestamp } = element;\r\n          const content = `\r\n            &lt;tr&gt;\r\n              &lt;td&gt;${epochToDateTime(timestamp)}&lt;\/td&gt;\r\n              &lt;td&gt;${formatReading(temperature)}&lt;\/td&gt;\r\n              &lt;td&gt;${formatReading(humidity)}&lt;\/td&gt;\r\n              &lt;td&gt;${formatReading(pressure)}&lt;\/td&gt;\r\n            &lt;\/tr&gt;`;\r\n          $('#tbody').append(content);\r\n        });\r\n      }\r\n    };\r\n\r\n    viewDataButtonElement.addEventListener('click', () =&gt; {\r\n      tableContainerElement.style.display = 'block';\r\n      viewDataButtonElement.style.display = 'none';\r\n      hideDataButtonElement.style.display = 'inline-block';\r\n      loadDataButtonElement.style.display = 'inline-block';\r\n      createTable();\r\n    });\r\n\r\n    loadDataButtonElement.addEventListener('click', appendToTable);\r\n\r\n    hideDataButtonElement.addEventListener('click', () =&gt; {\r\n      tableContainerElement.style.display = 'none';\r\n      viewDataButtonElement.style.display = 'inline-block';\r\n      hideDataButtonElement.style.display = 'none';\r\n      loadDataButtonElement.style.display = 'none';\r\n    });\r\n\r\n    \/\/ Initialize charts\/range if it doesn't exist\r\n    get(chartRef).then((snapshot) =&gt; {\r\n      if (!snapshot.exists()) {\r\n        set(chartRef, 100); \/\/ Set default chart range\r\n        console.log('Initialized charts\/range to 100');\r\n      }\r\n    });\r\n  } else {\r\n    \/\/ Toggle UI elements for logged-out state\r\n    console.log('Showing login form');\r\n    loginElement.style.display = 'block';\r\n    authBarElement.style.display = 'none';\r\n    userDetailsElement.style.display = 'none';\r\n    contentElement.style.display = 'none';\r\n    \/\/ Destroy gauges on logout\r\n    if (gaugeT) gaugeT.destroy();\r\n    if (gaugeH) gaugeH.destroy();\r\n    gaugeT = null;\r\n    gaugeH = null;\r\n  }\r\n};\r\n\r\n\/\/ Expose setupUI to global scope for auth.js\r\nwindow.setupUI = setupUI;\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Firebase-ESP\/raw\/main\/ESP-Firebase-Datalogging-Web-App\/scripts\/index.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p><strong>Important<\/strong>: you need to modify the code with your own <span class=\"rnthl rntliteral\">firebaseConfig<\/span> object\u2014the one you&#8217;ve got <a href=\"#firebaseconfig-object\">in this step<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>const firebaseConfig = {\n  apiKey: \"REPLACE_WITH_YOUR_Firebase_CONFIGURATION\",\n  authDomain: \"REPLACE_WITH_YOUR_Firebase_CONFIGURATION\",\n  databaseURL: \"REPLACE_WITH_YOUR_Firebase_CONFIGURATION\",\n  projectId: \"REPLACE_WITH_YOUR_Firebase_CONFIGURATION\",\n  storageBucket: \"REPLACE_WITH_YOUR_Firebase_CONFIGURATION\",\n  messagingSenderId: \"REPLACE_WITH_YOUR_Firebase_CONFIGURATION\",\n  appId: \"REPLACE_WITH_YOUR_Firebase_CONFIGURATION\"\n};<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">charts-definition.js<\/h4>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">charts-definition.js<\/span> file. This file creates the different charts using the <a href=\"https:\/\/www.highcharts.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">highcharts javascript library<\/a>. <\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">export function createTemperatureChart() {\r\n  return Highcharts.chart('chart-temperature', {\r\n    chart: {\r\n      type: 'line',\r\n      zoomType: 'x'\r\n    },\r\n    title: {\r\n      text: 'Temperature'\r\n    },\r\n    xAxis: {\r\n      type: 'datetime',\r\n      title: {\r\n        text: 'Time'\r\n      }\r\n    },\r\n    yAxis: {\r\n      title: {\r\n        text: 'Temperature (\u00b0C)'\r\n      }\r\n    },\r\n    series: [{\r\n      name: 'Temperature',\r\n      data: []\r\n    }]\r\n  });\r\n}\r\n\r\nexport function createHumidityChart() {\r\n  return Highcharts.chart('chart-humidity', {\r\n    chart: {\r\n      type: 'line',\r\n      zoomType: 'x'\r\n    },\r\n    title: {\r\n      text: 'Humidity'\r\n    },\r\n    xAxis: {\r\n      type: 'datetime',\r\n      title: {\r\n        text: 'Time'\r\n      }\r\n    },\r\n    yAxis: {\r\n      title: {\r\n        text: 'Humidity (%)'\r\n      }\r\n    },\r\n    series: [{\r\n      name: 'Humidity',\r\n      data: []\r\n    }]\r\n  });\r\n}\r\n\r\nexport function createPressureChart() {\r\n  return Highcharts.chart('chart-pressure', {\r\n    chart: {\r\n      type: 'line',\r\n      zoomType: 'x'\r\n    },\r\n    title: {\r\n      text: 'Pressure'\r\n    },\r\n    xAxis: {\r\n      type: 'datetime',\r\n      title: {\r\n        text: 'Time'\r\n      }\r\n    },\r\n    yAxis: {\r\n      title: {\r\n        text: 'Pressure (hPa)'\r\n      }\r\n    },\r\n    series: [{\r\n      name: 'Pressure',\r\n      data: []\r\n    }]\r\n  });\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Firebase-ESP\/raw\/main\/ESP-Firebase-Datalogging-Web-App\/scripts\/charts-definition.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">gauges-definition.js<\/h4>\n\n\n\n<p>In our web app, we&#8217;ll display a gauge for the temperature and another for the humidity. The <span class=\"rnthl rntliteral\">gauges-definition.js<\/span> file contains functions to create the gauges.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">export function createTemperatureGauge() {\r\n    return 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    });\r\n}\r\n\r\nexport function createHumidityGauge() {\r\n    return 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        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    });\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Firebase-ESP\/raw\/main\/ESP-Firebase-Datalogging-Web-App\/scripts\/gauges-definition.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Favicon File<\/h3>\n\n\n\n<p>To display a favicon in your web app, you need to move the picture you want to use as favicon to the <span class=\"rnthl rntliteral\">public<\/span> folder. The picture should be called <span class=\"rnthl rntliteral\">favicon.png<\/span>. You can simply drag the favicon file from your computer into the <span class=\"rnthl rntliteral\">public<\/span> folder in VS Code.<\/p>\n\n\n\n<p>We&#8217;re using the following icon as a favicon for our web app:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/build-web-servers-dl\/raw\/main\/favicon.zip\" target=\"_blank\" rel=\"noreferrer noopener\">favicon.png<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Deploy your App<\/h3>\n\n\n\n<p>After saving the HTML, CSS, and JavaScript files, deploy your app on VS Code by running the following command on the Terminal window.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><strong>firebase<\/strong> deploy<\/code><\/pre>\n\n\n\n<p>The Terminal should display something as follows:<\/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=\"750\" height=\"263\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-deploy-web-app.png?resize=750%2C263&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Deploy web app\" class=\"wp-image-109343\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-deploy-web-app.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-deploy-web-app.png?resize=300%2C105&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>Firebase offers a free hosting service to serve your assets and web apps. Then, you can access your web app from anywhere.<\/p>\n\n\n\n<p>You can use the Hosting URL provided to access your web app from anywhere.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>Congratulations! You successfully deployed your app. It is now hosted on a global CDN using Firebase hosting. You can access your web app from anywhere on the Hosting URL provided. In my case, it is <span class=\"rnthl rntliteral\">https:\/\/esp-firebase-demo.web.app<\/span>.<\/p>\n\n\n\n<p>The web app is responsive, and you can access it using your smartphone, computer, or tablet.<\/p>\n\n\n\n<p>When you first access the web app, you&#8217;ll see a form to insert the email username and password.<\/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=\"222\" height=\"450\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Login-Page.jpg?resize=222%2C450&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Web App Login Page\" class=\"wp-image-109363\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Login-Page.jpg?w=222&amp;quality=100&amp;strip=all&amp;ssl=1 222w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Login-Page.jpg?resize=148%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 148w\" sizes=\"(max-width: 222px) 100vw, 222px\" \/><\/figure><\/div>\n\n\n<p>Insert the email and password of the authorized user you added in the Firebase Authentication methods. If the form doesn&#8217;t show up at first, refresh the web page. After that, you can access the web page with the 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=\"505\" height=\"450\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Cards-Gauges.jpg?resize=505%2C450&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Web app Sensor readings cards and gauges\" class=\"wp-image-109364\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Cards-Gauges.jpg?w=505&amp;quality=100&amp;strip=all&amp;ssl=1 505w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Cards-Gauges.jpg?resize=300%2C267&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 505px) 100vw, 505px\" \/><\/figure><\/div>\n\n\n<p>The readings are displayed in cards, gauges, charts, and a table. You can also select which interfaces you want to see by checking\/unchecking the checkboxes.<\/p>\n\n\n\n<p>You can also check the readings displayed on charts. You can select the charts range, but keep in mind that selecting more than 30 readings will take some time.<\/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=\"222\" height=\"450\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Charts.jpg?resize=222%2C450&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Web app Sensor readings table with all data\" class=\"wp-image-109365\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Charts.jpg?w=222&amp;quality=100&amp;strip=all&amp;ssl=1 222w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Charts.jpg?resize=148%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 148w\" sizes=\"(max-width: 222px) 100vw, 222px\" \/><\/figure><\/div>\n\n\n<p>Finally, if you want to see all the readings. You can open the readings table. At the end of the table, there&#8217;s a button to load more readings until all readings are displayed.<\/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=\"513\" height=\"450\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-table-all-data.jpg?resize=513%2C450&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Web app Sensor readings table with all data\" class=\"wp-image-109367\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-table-all-data.jpg?w=513&amp;quality=100&amp;strip=all&amp;ssl=1 513w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-table-all-data.jpg?resize=300%2C263&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 513px) 100vw, 513px\" \/><\/figure><\/div>\n\n\n<p>There is also a button to delete all data if you want to remove all readings from the database.<\/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=\"222\" height=\"450\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Delete-Data.jpg?resize=222%2C450&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Firebase Web app Sensor readings table with all data delete data\" class=\"wp-image-109366\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Delete-Data.jpg?w=222&amp;quality=100&amp;strip=all&amp;ssl=1 222w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/02\/Firebase-Sensor-Readings-App-Delete-Data.jpg?resize=148%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 148w\" sizes=\"(max-width: 222px) 100vw, 222px\" \/><\/figure><\/div>\n\n\n<p>Here&#8217;s a video showing how the web app works.<\/p>\n\n\n<div style=\"text-align:center\"><iframe src=\"https:\/\/player.vimeo.com\/video\/678763382?color=ff9933&title=1&byline=0&portrait=0\" width=\"720\" height=\"405\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen><\/iframe><\/div><\/br>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial, you created a Firebase Web App with login\/logout authentication that displays sensor readings in many different ways. The sensor readings are saved on the realtime database. The database is protected using database rules (that you&#8217;ve already set up in a previous tutorial).<\/p>\n\n\n\n<p>You can apply what you learned here to display any other type of data, and you can change the files in the <span class=\"rnthl rntliteral\">public<\/span> folder to add different functionalities and features to your project.<\/p>\n\n\n\n<p>We didn&#8217;t explain how the javascript files work because the project is quite long. However, if there is enough interest in this subject, we can split this application into smaller projects so that you understand how to handle data using queries and how to display it in different ways. Let us know what you think in the comments below.<\/p>\n\n\n\n<p>If you want to learn more about Firebase, we recommend taking a look at our Firebase eBook, exclusively dedicated to this subject:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><a href=\"https:\/\/randomnerdtutorials.com\/firebase-esp32-esp8266-ebook\/\">Firebase Web App with ESP32 and ESP8266<\/a><\/strong><\/li>\n<\/ul>\n\n\n\n<p>We have other resources related to ESP32 and ESP8266 that you may like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/home-automation-using-esp8266\/\">Home Automation using ESP8266<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\">Build Web Servers with ESP32 and ESP8266<\/a><\/li>\n<\/ul>\n\n\n\n<p>Thanks for reading.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this project, you&#8217;ll create a Firebase Web App that displays all the sensor readings saved on the Firebase Realtime Database. We&#8217;ll create a web interface with gauges, charts, and &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32\/ESP8266: Firebase Data Logging Web App (Gauges, Charts, and Table)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-esp8266-firebase-gauges-charts\/#more-108701\" aria-label=\"Read more about ESP32\/ESP8266: Firebase Data Logging Web App (Gauges, Charts, and Table)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":170184,"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-108701","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\/2022\/02\/ESP32-ESP8266-Firebase-BME280-Datalogging-web-app-updated.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\/108701","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=108701"}],"version-history":[{"count":22,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/108701\/revisions"}],"predecessor-version":[{"id":170186,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/108701\/revisions\/170186"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/170184"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=108701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=108701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=108701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}