ESP32/ESP8266: Firebase Web App to Display Sensor Readings (with Authentication)

In this guide, you’ll create a Firebase Web App to display sensor readings saved on the Firebase Realtime Database. The sensor readings web page is protected with authentication with email and password. You’ll learn how to display data from the database and how to add authentication to your web app.

ESP32 ESP8266 NodeMCU Firebase Web App to Display Sensor Readings with Authentication

This article is Part 2 of this previous tutorial: ESP32/ESP8266 Firebase: Send BME280 Sensor Readings to the Realtime Database. Follow that tutorial first, before proceeding.

Project Overview

In this tutorial (Part 2), you’ll create a web app to display the sensor readings saved on the Firebase Realtime Database (read this previous tutorial).

The following diagram shows a high-level overview of the project we’ll build—programming the ESP32/ESP8266 and setting up the Firebase Project was done in Part 1.

ESP32 ESP8266 Firebase Web App Sensor Readings Authentication Project Overview
  • 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.
  • 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 authentication method in Part 1.
  • After authentication, you can access a web app page that shows the sensor readings saved on the realtime database. The realtime database was set up on Part 1.
  • Once you’re logged in, you can logout any time. The next time you’ll acces the app you’ll need to login again.

Prerequisites

Before start creating the Firebase Web App, you need to check the following prerequisites.

Creating a Firebase Project

You should have followed the following tutorial first:

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.

Install Required Software

Before getting started you need to install the required software to create the Firebase Web App. Here’s a list of the software you need to install (click on the links for instructions):


1) Add an App to Your Firebase Project

1) Go to your Firebase project Console and add an app to your project by clicking on the +Add app button.

Firebase Add App to Project

2) Select the web app icon.

Firebase Add Web App to Project

3) Give your app a name. Then, check the box next to √ Also set up Firebase Hosting for this App. Click Register app.

Firebase Add Web App to Project Hosting

4) Then, copy the firebaseConfig object and save it because you’ll need it later.

firebaseConfig object configuration copy save

After this, you can also access the firebaseConfig object if you go to your Project settings in your Firebase console.

5) Click Next on the proceeding steps, and finally on Continue to console.


2) Setting Up a Firebase Web App Project (VS Code)

Follow the next steps to create a Firebase Web App Project using VS Code.

1) Creating a Project Folder

1) Create a folder on your computer where you want to save your Firebase project—for example, Firebase-Project on the Desktop.

2) Open VS Code. Go to File > Open Folder… and select the folder you’ve just created.

3) Go to Terminal > New Terminal. A new Terminal window should open on your project path.

Install Firebase Tools 2

2) Firebase Login

4) On the previous Terminal window, type the following:

firebase login

5) You’ll be asked to collect CLI usage and error reporting information. Enter “n” and press Enter to deny.

Login Firebase VS Code Terminal Window

Note: If you are already logged in, it will show a message saying: “Already logged in as [email protected]”.

6) After this, it will pop up a new window on your browser to login into your firebase account.

Login Firebase Account

7) Allow Firebase CLI to access your google account.

Login Firebase Account allow Firebase CLI

8) After this, Firebase CLI login should be successful. You can close the browser window.

Login Firebase Account allow Firebase CLI Login Successful

3) Initializing Web App Firebase Project

9) After successfully login in, run the following command to start a Firebase project directory in the current folder.

firebase init

10) You’ll be asked if you want to initialize a Firebase project in the current directory. Enter Y and hit Enter.

Login Firebase Account allow Firebase CLI firebase init

11) Then, use up and down arrows and the Space key to select the options. Select the following options:

  • Realtime Database: Configure security rules file for Realtime Database and (optionally) provision default instance.
  • Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys

The selected options will show up with a green asterisk. Then, hit Enter.

Login Firebase Account allow Firebase CLI configure directory

12) Select the option “Use an existing project”—it should be highlighted in blue—then, hit Enter.

Firebase Project Setup VS Code

13) After that, select the Firebase project for this directory—it should be the project created in this previous tutorial. In my case, it is called esp-firebase-demo. Then hit Enter.

Firebase Project VS Code create project

14) Press Enter on the following question to select the default database security rules file: “What file should be used for Realtime Database Security Rules?

15) Then, select the hosting options as shown below:

  • What do you want to use as your public directory? Hit Enter to select public.
  • Configure as a single-page app (rewrite urls to /index.html)? No
  • Set up automatic builds and deploys with GitHub? No
Firebase initialization complete

16) The Firebase project should now be initialized successfully. Notice that VS code created some essential files under your project folder.

Firebase Project Files Created successfully

The index.html 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’ll do that later in this tutorial.

17) To check if everything went as expected, run the following command on the VS Code Terminal window.

firebase deploy
Firebase App First Deploy Testing

You should get a Deploy complete! message and an URL to the Project Console and the Hosting URL.

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

Firebase test page hosting setup complete

The web page you’ve seen previously is built with the HTML file placed in the public folder of your Firebase project. By changing the content of that file, you can create your own web app. That’s what we’re going to do in the next section.


3) Creating Firebase Web App

Now that you’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.

index.html

Copy the following to your index.html file. This HTML file creates a simple web page that displays the readings saved on the Realtime Database created on this previous project.

If you aren’t authenticated, it shows a login form. When you authenticate with an authorized user email and corresponding password, it shows the user interface with the sensor readings.

<!-- Complete Project Details at: https://RandomNerdTutorials.com/ -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>ESP IoT Firebase App</title>

    <!-- update the version number as needed -->
    <script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-app.js"></script>

    <!-- include only the Firebase features as you need -->
    <script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-database.js"></script>

    <script>
     // REPLACE WITH YOUR web app's Firebase configuration
      const firebaseConfig = {
        apiKey: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
        authDomain: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
        databaseURL: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
        projectId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
        storageBucket: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
        messagingSenderId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
        appId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION"
      };

      // Initialize firebase
      firebase.initializeApp(firebaseConfig);

      // Make auth and database references
      const auth = firebase.auth();
      const db = firebase.database();

    </script>
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
    <link rel="icon" type="image/png" href="favicon.png">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
  <!--TOP BAR-->
  <div class="topnav">
    <h1>Sensor Readings App <i class="fas fa-clipboard-list"></i></h1>
  </div>

  <!--AUTHENTICATION BAR (USER DETAILS/LOGOUT BUTTON)-->
  <div id="authentication-bar" style="display: none;">
    <p><span id="authentication-status">User logged in</span>
       <span id="user-details">USEREMAIL</span>
       <a href="/" id="logout-link">(logout)</a>
    </p>
  </div>

  <!--LOGIN FORM-->
  <form id="login-form" style="display: none;">
    <div class="form-elements-container">
      <label for="input-email"><b>Email</b></label>
      <input type="text" placeholder="Enter Username" id="input-email" required>

      <label for="input-password"><b>Password</b></label>
      <input type="password" placeholder="Enter Password" id="input-password" required>

      <button type="submit" id="login-button">Login</button>
      <p id="error-message" style="color:red;"></p>
    </div>
  </form>

  <!--CONTENT (SENSOR READINGS)-->
  <div class="content-sign-in" id="content-sign-in" style="display: none;">
    <div class="cards">
      <!--TEMPERATURE-->
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
        <p><span class="reading"><span id="temp"></span> &deg;C</span></p>
      </div>
      <!--HUMIDITY-->
      <div class="card">
        <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p>
        <p><span class="reading"><span id="hum"></span> &percnt;</span></p>
      </div>
      <!--PRESSURE-->
      <div class="card">
        <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p>
        <p><span class="reading"><span id="pres"></span> hPa</span></p>
      </div>
    </div>
  </div>
    <script src="scripts/auth.js"></script>
    <script src="scripts/index.js"></script>
  </body>
</html>

View raw code

You need to modify the code with your own firebaseConfig object—the one you’ve got in this step.

How it Works

Let’s take a quick look at the HTML file, or skip to the next section.

In the <head> of the HTML file, we must add all the required metadata.

The title of the web page is ESP Firebase App, but you can change it in the following line.

<title>ESP Firebase App</title>

You must add the following line to be able to use Firebase with your app.

<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>

You must also add any Firebase products you want to use. In this example, we’re using the Realtime Database and Authentication.

<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-database.js"></script>

Then, replace the firebaseConfig object with the one you’ve gotten from this step.

const firebaseConfig = {
  apiKey: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
  authDomain: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
  databaseURL: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
  projectId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
  storageBucket: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
  messagingSenderId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
  appId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION"
};

Finally, Firebase is initialized, and we create two global variables db and auth that refer to Firebase authentication and to Firebase realtime database.

// Initialize firebase
firebase.initializeApp(firebaseConfig);
      
// Make auth and database references
const auth = firebase.auth();
const db = firebase.database();

The following line allows us to use fontawesome icons:

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">

The next includes a favicon on our web page.

<link rel="icon" type="image/png" href="favicon.png">

Finally, reference an external style.css file to format the HTML page.

<link rel="stylesheet" type="text/css" href="style.css">

We’re done with the metadata. Now, let’s go to the HTML parts that are visible to the user—go between the <body> and </body> tags.

We create a top “navigation” bar with the name of our app and a small icon from fontawesome.

<div class="topnav">
  <h1>Sensor Readings App <i class="fas fa-clipboard-list"></i></h1>
</div>

The following lines create a bar with the details of the authenticated user (email). It also shows a logout link to log out the user.

<div id="authentication-bar" style="display: none;">
  <p><span id="authentication-status">User logged in</span>
     <span id="user-details">USEREMAIL</span>
     <a href="/" id="logout-link">(logout)</a>
  </p>
</div>

First, we set the display style of all elements to none. We’ll hide and show content depending if the user is authenticated or not—we’ll handle that using JavaScript.

Next, the following lines create the login form with an input field for the email and an input field for the password:

<form id="login-form" style="display: none;">  
  <div class="form-elements-container">
    <label for="input-email"><b>Email</b></label>
    <input type="text" placeholder="Enter Username" id="input-email" required>
  
    <label for="input-password"><b>Password</b></label>
    <input type="password" placeholder="Enter Password" id="input-password" required>
          
    <button type="submit" id="login-button">Login</button>
    <p id="error-message" style="color:red;"></p>
  </div>
</form>

Inside the form, there’s also a paragraph to display an error message if the login fails.

<p id="error-message" style="color:red;"></p>

Finally, we create a grid to display the sensor readings.

<!--CONTENT (SENSOR READINGS)-->
<div class="content-sign-in" id="content-sign-in" style="display: none;">
  <div class="cards">
    <!--TEMPERATURE-->
    <div class="card">
      <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> TEMPERATURE</p>
      <p><span class="reading"><span id="temp"></span> &deg;C</span></p>
    </div>
    <!--HUMIDITY-->
    <div class="card">
      <p><i class="fas fa-tint" style="color:#00add6;"></i> HUMIDITY</p>
      <p><span class="reading"><span id="hum"></span> &percnt;</span></p>
    </div>
    <!--PRESSURE-->
    <div class="card">
      <p><i class="fas fa-angle-double-down" style="color:#e1e437;"></i> PRESSURE</p>
      <p><span class="reading"><span id="pres"></span> hPa</span></p>
    </div>
  </div>
</div>

The places where we’ll insert the sensor readings have <span> tags with specific ids so that we can refer to those HTML elements using JavaScript and insert sensor readings saved on the database.

  • temperature: id = “temp”
  • humidity: id = “hum”
  • pressure: id = “pres”

Finally, we need to add references to the external JavaScript files. For our application, we’ll create two JavaScript files: auth.js (that handles everything related to the authentication) and index.js that handles everything related to the UI. We’ll create those files inside a folder called scripts inside the public folder of our application.

<script src="scripts/auth.js"></script>
<script src="scripts/index.js"></script>

After making the necessary changes (inserting your firebaseConfig object), you can save the HTML file.

style.css

Inside the public folder create a file called style.css. To create the file, select the public folder, and then click on the +file icon at the top of the File Explorer. Call it style.css.

Create CSS File VS Code

Then, copy the following to the style.css file

html {
    font-family: Verdana, Geneva, Tahoma, sans-serif;
    display: inline-block;
    text-align: center;
}

p {
    font-size: 1.2rem;
}

body {
    margin: 0;
}

.topnav {
    overflow: hidden;
    background-color: #049faa;
    color: white;
    font-size: 1rem;
    padding: 10px;
}

#authentication-bar{
    background-color:mintcream;
    padding-top: 10px;
    padding-bottom: 10px;
}

#user-details{
    color: cadetblue;
}

.content {
    padding: 20px;
}

.card {
    background-color: white;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
    padding: 5%;
}

.cards {
    max-width: 800px;
    margin: 0 auto;
    display: grid;
    grid-gap: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.reading {
    font-size: 1.4rem;
}

button {
    background-color: #049faa;
    color: white;
    padding: 14px 20px;
    margin: 8px 0;
    border: none;
    cursor: pointer;
    border-radius: 4px;
}
button:hover {
    opacity: 0.8;
}

.form-elements-container{
    padding: 16px;
    width: 250px;
    margin: 0 auto;
}

input[type=text], input[type=password] {
    width: 100%;
    padding: 12px 20px;
    margin: 8px 0;
    display: inline-block;
    border: 1px solid #ccc;
    box-sizing: border-box;
}

View raw code

The CSS file includes some simple styles to make our webpage look better. We won’t discuss how CSS works in this tutorial.

JavaScript Files

We’ll create two JavaScript files (auth.js and index.js) inside a scripts folder inside the public folder.

  • Select the public folder, then click on the +folder icon to create a new folder. Call scripts to that new folder.
  • Then, select the scripts folder and click on the +file icon. Create a file called auth.js. Then, repeat the previous steps to create an index.js.

The following image show how your web app project folder structure should look like.

Firebase Project VS Code Folder File Structure

auth.js

Now let’s implement user sign-in using Firebase authentication. We’ll implement sign-in using email and password.

Copy the following to the auth.js file you created previously.

// listen for auth status changes
auth.onAuthStateChanged(user => {
 if (user) {
   console.log("user logged in");
   console.log(user);
   setupUI(user);
   var uid = user.uid;
   console.log(uid);
 } else {
   console.log("user logged out");
   setupUI();
 }
});

// login
const loginForm = document.querySelector('#login-form');
loginForm.addEventListener('submit', (e) => {
 e.preventDefault();
 // get user info
 const email = loginForm['input-email'].value;
 const password = loginForm['input-password'].value;
 // log the user in
 auth.signInWithEmailAndPassword(email, password).then((cred) => {
   // close the login modal & reset form
   loginForm.reset();
   console.log(email);
 })
 .catch((error) =>{
   const errorCode = error.code;
   const errorMessage = error.message;
   document.getElementById("error-message").innerHTML = errorMessage;
   console.log(errorMessage);
 });
});

// logout
const logout = document.querySelector('#logout-link');
logout.addEventListener('click', (e) => {
 e.preventDefault();
 auth.signOut();
});

View raw code

Then, save the file. This file takes care of everything related to the login and logout of the user. Continue reading to learn how the code works or skip to the next section.

Login

The following lines are responsible for logging in the user.

const loginForm = document.querySelector('#login-form');
loginForm.addEventListener('submit', (e) => {
  e.preventDefault();
  // get user info
  const email = loginForm['input-email'].value;
  const password = loginForm['input-password'].value;
  // log the user in
  auth.signInWithEmailAndPassword(email, password).then((cred) => {
    // close the login modal & reset form
    loginForm.reset();
    console.log(email);
  })
  .catch((error) =>{
    const errorCode = error.code;
    const errorMessage = error.message;
    document.getElementById("error-message").innerHTML = errorMessage;
    console.log(errorMessage);
  });
});

We create a variable that refers to the login form HTML element called loginForm.

const loginForm = document.querySelector('#login-form');

If you go back to the index.html file, you can see that the form has the login-form id.

We add an event listener of type submit to the form. This means that the subsequent instructions will run whenever the form is submitted.

loginForm.addEventListener('submit', (e) => {

You can get the submitted data as follows.

const email = loginForm['input-email'].value;
const password = loginForm['input-password'].value;

If you go back to the HTML file, you’ll see that the input fields contain the following ids: input-email and input-password for the email and password, respectively.

Now that we have the inserted email and password, we can try to log in to Firebase. To do that, pass the user’s email address and password to the following method: signInWithEmailAndPassword:

auth.signInWithEmailAndPassword(email, password).then((cred) => {

After logging in, we reset the form and print the user email in the console.

auth.signInWithEmailAndPassword(email, password).then((cred) => {
  // close the login modal & reset form
  loginForm.reset();
  console.log(email);
})

In case there is an error signing in, we catch the error message, and display it on the error-message HTML element (a paragraph below the form).

.catch((error) =>{
  const errorCode = error.code;
  const errorMessage = error.message;
  document.getElementById("error-message").innerHTML = errorMessage;
  console.log(errorMessage);
});

Logout

The following snippet is responsible for logging out the user.

const logout = document.querySelector('#logout-link');
logout.addEventListener('click', (e) => {
  e.preventDefault();
  auth.signOut();
});

When the user is logged in, a logout link is visible in the authentication bar. That link has the logout-link id (see on the HTML file). So, first, we create a variable called logout that refers to the logout link.

const logout = document.querySelector('#logout-link');

Then, we add an event listener of type click. This means the subsequent instructions will run whenever you click on the logout link.

logout.addEventListener('click', (e) => {

When the button is clicked, we sign out the user using the signOut method.

auth.signOut();

Auth State Changes

To keep track of the user authentication state—to know if the user is logged in or logged out, there is a method called onAuthSateChanged that allows you to receive an event whenever the authentication state changes.

auth.onAuthStateChanged(user => {
  if (user) {
    console.log("user logged in");
    console.log(user);
    setupUI(user);
    var uid = user.uid;
    console.log(uid);
  } else {
    console.log("user logged out");
    setupUI();
  }
});

If the user returned is null, the user is currently signed out. Otherwise, it is currently signed in.

In both scenarios, we print the current user state to the console and call the setupUI() function. We haven’t created that function yet (we’ll create it in the next section), but it will be responsible for handling the user interface accordingly to the authentication state.

When the user is logged in, we pass the user as an argument to the setupUI() function. In this case, we’ll display the complete user interface to show the sensor readings, as you’ll see later.

if (user) {
  console.log("user logged in");
  console.log(user);
  setupUI(user);

We also get the user UID that we’ll need later to insert and read data from the database.

var uid = user.uid;
console.log(uid);

If the user is logged out, we call the setupUI() function without any argument. In that scenario, we’ll simply display a message informing that the user is logged out and doesn’t have access to the interface (as we’ll see later).

} else {
  console.log("user logged out");
  setupUI();
}

index.js

The index.js file handles the UI – it 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’s a change.

Copy the following to the index.js file.

const loginElement = document.querySelector('#login-form');
const contentElement = document.querySelector("#content-sign-in");
const userDetailsElement = document.querySelector('#user-details');
const authBarElement = document.querySelector("#authentication-bar");

// Elements for sensor readings
const tempElement = document.getElementById("temp");
const humElement = document.getElementById("hum");
const presElement = document.getElementById("pres");

// MANAGE LOGIN/LOGOUT UI
const setupUI = (user) => {
  if (user) {
    //toggle UI elements
    loginElement.style.display = 'none';
    contentElement.style.display = 'block';
    authBarElement.style.display ='block';
    userDetailsElement.style.display ='block';
    userDetailsElement.innerHTML = user.email;

    // get user UID to get data from database
    var uid = user.uid;
    console.log(uid);

    // Database paths (with user UID)
    var dbPathTemp = 'UsersData/' + uid.toString() + '/temperature';
    var dbPathHum = 'UsersData/' + uid.toString() + '/humidity';
    var dbPathPres = 'UsersData/' + uid.toString() + '/pressure';

    // Database references
    var dbRefTemp = firebase.database().ref().child(dbPathTemp);
    var dbRefHum = firebase.database().ref().child(dbPathHum);
    var dbRefPres = firebase.database().ref().child(dbPathPres);

    // Update page with new readings
    dbRefTemp.on('value', snap => {
      tempElement.innerText = snap.val().toFixed(2);
    });

    dbRefHum.on('value', snap => {
      humElement.innerText = snap.val().toFixed(2);
    });

    dbRefPres.on('value', snap => {
      presElement.innerText = snap.val().toFixed(2);
    });

  // if user is logged out
  } else{
    // toggle UI elements
    loginElement.style.display = 'block';
    authBarElement.style.display ='none';
    userDetailsElement.style.display ='none';
    contentElement.style.display = 'none';
  }
}

View raw code

Continue reading to learn how the code works or skip to the next section.

Getting HTML Elements

First, we create variables to refer to several elements on the UI interface by referring to their ids. To identify these elements, we recommend that you take a look at the HTML file provided and find the elements with the referred ids.

const loginElement = document.querySelector('#login-form');
const contentElement = document.querySelector("#content-sign-in");
const userDetailsElement = document.querySelector('#user-details');
const authBarElement = document.querySelector("#authentication-bar");

// Elements for sensor readings
const tempElement = document.getElementById("temp");
const humElement = document.getElementById("hum");
const presElement = document.getElementById("pres");

The loginElement corresponds to the login form. The contentElement corresponds to the section of the web page that is visible when the user is logged in (that shows the sensor readings). The userDetailsElement corresponds to a section that will display the email of the logged in user. The auhtBarElement corresponds to the authentication bar that shows the current user status, the email of the authenticated user and the logout link.

sertupUI() Function

Then, we create the setupUI() function that will handle the UI accordingly to the state of the user authentication.

In the auth.js file, we called the setupUI() function with the user argument setupUI(user) if the user is logged in; or the function without argument setupUI() when the user is logged out.

So, let’s check what happens when the user is logged in.

if (user) {

We define which parts of the UI should be visible or invisible. When the user is logged in, we want to hide the login form. To hide an element, we can set the display style to none.

loginElement.style.display = 'none';

We show the authentication bar (that shows the user details and the logout link). To do that, we can set its display style to block. We also want the web page’s main content with the sensor readings to be visible.

contentElement.style.display = 'block';
authBarElement.style.display ='block';

Finally, we can get the logged in user email with user.email and display it in the userDetailsElement section as follows:

userDetailsElement.innerHTML = user.email;

User UID and Database Paths

After we have a logged-in user, we can get its UID with user.uid.

var uid = user.uid;
console.log(uid);

After getting the user UID, we create variables to refer to the database paths where we save the data.

// Database paths (with user UID)
var dbPathTemp = 'UsersData/' + uid.toString() + '/temperature';
var dbPathHum = 'UsersData/' + uid.toString() + '/humidity';
var dbPathPres = 'UsersData/' + uid.toString() + '/pressure';

Then, we create database references to those paths.

var dbRefTemp = firebase.database().ref().child(dbPathTemp);
var dbRefHum = firebase.database().ref().child(dbPathHum);
var dbRefPres = firebase.database().ref().child(dbPathPres);

Display Sensor Readings

The following lines get the new sensor readings whenever there’s a change and update the corresponding HTML elements with the new values.

// Update page with new readings
dbRefTemp.on('value', snap => {
  tempElement.innerText = snap.val().toFixed(2);
});

dbRefHum.on('value', snap => {
  humElement.innerText = snap.val().toFixed(2);
});

dbRefPres.on('value', snap => {
  presElement.innerText = snap.val().toFixed(2);
});

Logged Out UI

The following snippet handles the UI when the user logs out. We want to hide the authentication bar and the main webpage content (sensor readings) and show the login form.

} else{
  // toggle UI elements
  loginElement.style.display = 'block';
  authBarElement.style.display ='none';
  contentElement.style.display = 'none';
}

Favicon File

To display a favicon in your web app, you need to move the picture you want to use as favicon to the public folder. The picture should be called favicon.png. You can simply drag the favicon file from your computer into the public folder in VS Code.

We’re using the following icon as a favicon for our web app:

Deploy your App

After saving the HTML, CSS, and JavaScript files, deploy your app on VS Code by running the following command on the Terminal window.

firebase deploy

The Terminal should display something as follows:

ESP32 ESP8266 Deploy Firebase Web App

Firebase offers a free hosting service to serve your assets and web apps. Then, you can access your web app from anywhere.

You can use the Hosting URL provided to access your web app from anywhere.

Demonstration

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 https://esp-firebase-demo.web.app.

ESP32 ESP8266 Sensor Readings Firebase Web App

The web app is responsive, and you can access it using your smartphone, computer, or tablet.

Insert the email and password of the authorized user you added in the Firebase Authentication methods. After that, you can access the latest sensor readings.

Go to your project’s Firebase console Hosting tab. You can see your app domains, deploy history, and you can even roll back to previous versions of your app.

Firebase Web App Deploy History and Domains

Wrapping Up

In this tutorial, you learned how to create a Firebase Web App with login/logout authentication that displays sensor readings. The sensor readings are saved on the realtime database. The database is protected using database rules (that you’ve already set up in the previous tutorial).

You can apply what you learned here to display any other type of data, and you can change the files in the public folder to add different functionalities and features to your project.

If you want to learn more about Firebase, we recommend taking a look at our new eBook, exclusively dedicated to this subject:

We have other resources related to ESP32 and ESP8266 that you may like:

Thanks for reading.



Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

25 thoughts on “ESP32/ESP8266: Firebase Web App to Display Sensor Readings (with Authentication)”

  1. Hi, great authentication and data logging example thanks a lot for the article, would recommend to use module bundlers, so that the firebase configuration won’t be visible to the end-user or publicly on the website
    https://firebase.google.com/docs/web/module-bundling
    https://webpack.js.org/
    here’s a practice been working on recently where i’ve used some of your codes to render the web page https://github.com/ramymagdy-rm/home_automation
    the bundling approach is way safer than the conventional index.js method, the user eventually gets to see the nods.js compiled pack which is an encoded static asset

    Reply
  2. Hi
    Works fine with the ESP32-WROOM board.

    With an 8266 NodeMCU board it works OK for an hour or so, then I get a token error code 400 that is repeated “forever”, varying intervals from a few seconds to a minute and varying numbers of errors (same 3 lines repeated). The only way to stop is to reset the board after which it returns to normal for a while.

    13:04:29.627 -> Writing value: 994.19 on the following path: /UsersData/xxxxxxx/pressure
    13:04:29.627 -> PASSED
    13:04:29.627 -> PATH: /UsersData/xxxxxxx/pressure
    13:04:29.627 -> TYPE: double
    13:16:55.479 -> Token info: type = id token, status = error
    13:16:55.479 -> Token error: code: 400, message: MISSING_REFRESH_TOKEN
    13:16:55.479 -> Token info: type = id token, status = on refreshing
    13:16:55.479 -> Token info: type = id token, status = error
    13:16:55.479 -> Token error: code: 400, message: MISSING_REFRESH_TOKEN
    13:16:55.479 -> Token info: type = id token, status = on refreshing

    Reply
    • Hi.
      I never faced that error.
      Did you change anything on the code?
      I have no idea what that error means?
      Did you enable the right authentication methods?
      Regards,
      Sara

      Reply
      • Hi Sara
        First of all thanks for this series of projects: I accidentally cut that from the first comment. It is a very interesting way to get information accessible from anywhere.
        The error code is due to a missing refresh token if I understand correctly. What is strange is that everything works fine for an hour or so, then breaks. I thought it might be due to a wdt, as there is a comment in the TokenHelper.h file regarding the 8266, but after reading up a bit I cannot see how that could be relevant as the hardware wdt is always active.
        I’ll see if I can find another 8266 to try it out on.

        BR
        Phi

        Reply
  3. Hello all.
    I cant compile this project.
    i receive the error

    “fatal error: Firebase_ESP_Client.h: No such file or directory
    compilation terminated.
    Compilation error: exit status 1}”
    I use Arduino IDE 2.0.0-rc3, but before i have try also with 1.8.19
    I have install also the boards. I use ESP32 Wrover Module
    The file Firebase_ESP_Client.h exist inside the correct folder of this library
    Some help please
    Thanks

    Reply
    • Hi.
      The error says it cannot find the library.
      Make sure you’ve installed the correct library and that it is installed properly as shown in the tutorial.
      Regards,
      Sara

      Reply
  4. Theodoros, I had a similar error, turned out I had installed the wrong library, must be “Firebase Arduino Client Library for Expressif…” not “Firebase ESP32 Client….” or similar. Also go into “Sketch”, “Include library” and click on the installed library. Check the library is in the sketch path as set in “preferences”, file must be in sketchpath\libraries\Fire_Base….\src\file.h , any other director path probably won’t work. Hope that helps.

    Sara thanks for very clear detailed project instructions, this makes it so easy! However I have found that when I “deploy” a project it doesn’t alway upload the edited saved files, even though it says “found 4 files in public” (correct number), “file upload complete”, changes made don’t appear on web page even though I have refreshed it or loaded from a different browser with no history. Or I have added a new file it is not found. Is there any way to forcefully upload all files, changed or unchanged? Any way to check what files are on the firebase server? The firebase “Hosting page” doesn’t seem to list the files.
    Thanks Bruce

    Reply
    • Thanks a lot Bruse. I will consider that you write to me because in addition I had a second problem with the appearance with “one drive”. It shows me that I have dual libraries. I disconnected the “one drive”
      I uninstalled Arduino ide2, cleaned the C: \ Users \ …. . ArduinoIDE folder and did the installation from the beginning and the compilation was done normally.
      Thanks

      Reply
  5. Hi All, previous problem now solved, the problem was VS Code was not always saving changes when I hit Ctl S, nothing to do with firebase deploy.

    I can read the data logged from the rtdb if I hard code in a timestamp example, but how can I iterate through timestamps? I have a little experience with simple sql but this is completely different!
    Thanks

    Reply
  6. Hello, I’m stuck with a command error, when I write: “firebase login” I have this error: “firebase: The term ‘firebase’ is not recognozed as the name of a cmdlet, function, script file, pr operable program …’

    Good day

    Reply
  7. Hello, I have another problem, when I press enter to the question “What file should be used for Realtime Database Security Rules?“ as said in the tutorial, an error is returned: “Error: An unexpected error has occured ”

    Good day

    Reply
      • I managed to solve the problem, just restart and everything works. I would like to know if it is possible to link several ESP 32 on the same account to have several statements and have them on the same account

        Reply
        • Hi.
          Yes, you can do that.
          You just have to make sure that each ESP publishes on a different path, otherwise, the readings will be overwritten.
          Regards,
          Sara

          Reply
          • Hello,
            Actually everything is fine.
            You do great tutorials.
            I allow myself to ask a few more questions: how can we make the user create his own account?

          • Hi.
            We don’t have any tutorials about that.
            But there are great youtube videos about that.
            Also, search on the firebase documentation
            I might cover that subject in the future, but at the moment, we don’t have any resources about that.
            Regards,
            Sara

  8. Hello Sara,

    I am trying to control my ESP from Fdtb but I cannot manage usage of setString/getString function on this library. Yes, you said, stay tuned until next moth, but it is much appreciate you to explain this.

    Here are my functions:

    // writing OFF on the path
    Firebase.RTDB.setString(&fbdo, led, “OFF”);

    //getting the string on the path
    void receiveString(String path, String value){
    if (Firebase.RTDB.getString(&fbdo, value)){
    Serial.print(“Writing value: “);
    Serial.print (value);
    Serial.print(” on the following path: “);
    Serial.println(path);
    Serial.println(“PASSED”);
    Serial.println(“PATH: ” + fbdo.dataPath());
    Serial.println(“TYPE: ” + fbdo.dataType());
    }
    else {
    Serial.println(“FAILED”);
    Serial.println(“REASON: ” + fbdo.errorReason());
    }
    }

    Reply

Leave a Reply to Ano Cancel reply

Download our Free eBooks and Resources

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