Getting started with ESP32 and Firebase
By: Fabrice Beya
Tags: Esp32, Embedded Systems, IoT, Smart Home, Firebase

IoT

In this tutorial I will demonstrate the fastest method to connect your ESP32 to Google’s Firebase backend. Firebase has become a very convenient option for developers to rapidly prototype their ideas, in this tutorial we lay the ground work for the development of a realtime temperature monitoring solution for the Apple watch. This article will focus on setting up the link between the ESP32 and Firebase, and in the next article we will focus on the watchOS application.

Hardware Requirements

The following hardware is needed to complete this tutorial:

ESP32 development board (any ESP32 board is okay, for this tutorial we will be using the ESP32 DevKit v3).

DHT11 temperature and humidity sensor.

Backend Setup

We will be using Google’s Firebase for our backend, to get started head over to Firebase, and sign in using your Google account.

Select the Add project option to create a new project. You will be asked to Enter your project name, give the project a suitable name, and then select the blue Continue button. I’ve called my project Smart Hommie

Disable the Enable Google Analytics for this project option — this will not be needed this for this project. Finally, select Create project.

Firebase will begin setting up your project. Once completed select Continue, and you will be taken to your projects overview page on the Firebase console.

The first thing we want to do is setup authentication options for our project. Select the Authentication menu option on the top left, and you will be taken to the authentication page. Select the Get Started button.

We’re going to start by using the Anonymous sign in for our ESP32 devices, and later on in the tutorial we will devise a more intelligent sign in method. Enable Anonymous Sign In as shown below and select the Save option.

Next we need to create a database which will hold all our sensor data. To do this, select the Realtime Database menu option on the top left, and you’ll be taken to the Realtime Database page. Select the Create Database button to initialise the database creation menu.

In the database creation menu you can choose a location that is closest to you, and select Next. You will be presented with the option to initialise your database in locked mode or test mode. Select test mode for now. The main difference is that in test mode a database access rule is placed allowing unauthorised access to your database for thirty days. If you plan to take your project into production, you will need to disable this in the future. Select the Enable button.

Lastly you should see a new page with your new empty database, and we are all set!

Before we continue there are two items you need to copy and store for future use in our embedded application. The first item we need is our Realtime Database URL, which you can find by copying the URL of the Realtime Database page, in our case it will be https://smart-hommie-default-rtdb.firebaseio.com

The second item we need to store is the project’s API key. To get you API key navigation to the project settings page by selecting the settings icon on the top right, and then selecting the Project settings menu item as shown below:

In the project settings page you should see your Web API Key, copy this and store it.

We all set to get started on our embedded application.

Development Environment Setup

The first thing we need to do is setup our ESP32 development environment. For this tutorial we will be using Visual Studio Code along with the PlatformIO Plugin.

If you don’t already have VSCode, download and install it from here.

Next you need to install PlatformIO using the following instructions here. If everything went well you should see the PlatformIO on the right menu items as shown below.

The first thing we need to do is install the ESP32 Platform for PlatformIO. Select the PlatformIO menu icon to enter the PlatformIO home page.

Select the platforms menu option and select the Embedded page. Search for the Espressif framework by typing “Esp”, and select the Espressif 32 option. Install the platform by selecting the install button.

Now let’s create a new project for our embedded application. Head back to the home page and select the Create New Project menu. You will be presented with the Project Wizard, give your project a name and make sure you select your EPS32 board. we will be using the Arduino Library for this project, so keep the default settings as is, and select Finish.

Finally we need to add a library from mobizt which we will be using in our project. From the home page head to the library page and search for the FirebaseESP32 library as shown below.

And finally add the library to your project as shown below.

Embedded Application

In the project explorer open the main.cpp file

The first thing we need to do is import all the libraries we need in our application, the Arduino library should already be present by default:

#include <Arduino.h>

#include <WiFi.h>

#include <FirebaseESP32.h>

Next we need to add some helper library which are used by the FirebaseESP32 library. The TokenHelper library is used to manage the token generation process, and the RTDBHelper library provides helper functions for printing data coming from the Firebase Realtime Database:

//Provide the token generation process info.

#include "addons/TokenHelper.h" //Provide the RTDB payload printing info and other helper functions.

#include "addons/RTDBHelper.h"

We need to define a unique device ID which can used to differentiate data coming from multiple sensors.

// Device ID

#define DEVICE_UID "1X"

Next we need to capture our WiFi credentials. Replace WIFI_AP with your WiFi Identifier, and WIFI_PASSWORD with your WiFi Password.

Note: It’s never a good idea to hardcode password information in your embedded application, for production cases you need to apply a device provision strategy that includes a secure device registration process.

// Your WiFi credentials

#define WIFI_SSID "WIFI_AP"

#define WIFI_PASSWORD "WIFI_PASSWORD"

Next we need to add a constant to store our API key, as mentioned earlier you can get your Firebase project API key from the projects settings page. Replace API_KEY with your API key. Similarly to include your Firebase Realtime Database URL, replacing URL with your own.

// Your Firebase Project Web API Key

#define API_KEY "API_KEY"

// Your Firebase Realtime database URL

#define DATABASE_URL "https://smart-hommie-default-rtdb.firebaseio.com"

Next we initialise 3 objects courtesy of the FirebaseESP32 library which will be critical to linking our application to Firebase.

// Firebase Realtime Database Object

FirebaseData fbdo; // Firebase Authentication Object

FirebaseAuth auth; // Firebase configuration Object

FirebaseConfig config;

Next we define a few global variables which will come in handy.

// Device Location config

String device_location = "Living Room"; // Firebase Realtime Database Object

FirebaseData fbdo; // Firebase Authentication Object

FirebaseAuth auth; // Firebase configuration Object

FirebaseConfig config; // Firebase database path

String databasePath = ""; // Firebase Unique Identifier

String fuid = ""; // Stores the elapsed time from device start up

unsigned long elapsedMillis = 0; // The frequency of sensor updates to firebase, set to 10seconds

unsigned long update_interval = 10000; // Dummy counter to test initial firebase updates

int count = 0; // Store device authentication status

bool isAuthenticated = false;

The first function we need is to setup the WiFi, we define a function called WiFi_Init() . We make use of the built in WiFi API provided by the Arduino framework. We use the WiFi.begin() function to initialise a WiFi connection using our credentials, we then check every 300 ms to see if the connection has been successful using the WiFi.status() function, after which we print out the local IP address using the WiFi.localIP() function. For other WiFi related functionality which you might want to use check out the Arduino WiFi API documentation.

void Wifi_Init() { WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting to Wi-Fi"); while (WiFi.status() != WL_CONNECTED){ Serial.print("."); delay(300); } Serial.println(); Serial.print("Connected with IP: "); Serial.println(WiFi.localIP()); Serial.println(); }

Next we need to implement our Firebase initialisation function which will connect to the Firebase backend, authenticate our device and initialise our Firebase library. We begin by passing our API key and database URL to the configuration object, along with enabling WiFi reconnection on upon setup. We then attempt to sign up our device anonymously, note that we send an an empty email and password to achieve this. If the signup is successful we define the database path where all our device data will be logged, we call this path Temp_Sensor , and we append the unique Firebase identifier from Firebase which we get from the auth object, which after successful signup will include the uid property. To explore further Firebase authentication functionalities check out the library documentation here. After signup we then initialise the Firebase library with the Firebase.begin() function.

Note: Each time we perform a Firebase.signUp() a new anonymous user is created in the backend, which mean each time we power the device on and off a new user is created.

void firebase_init() { // configure firebase API Key

config.api_key = API_KEY; // configure firebase realtime database url

config.database_url = DATABASE_URL; // Enable WiFi reconnection

Firebase.reconnectWiFi(true); Serial.println("------------------------------------");

Serial.println("Sign up new user..."); // Sign in to firebase Anonymously if (Firebase.signUp(&config, &auth, "", ""))

{ Serial.println("Success");

isAuthenticated = true; // Set the database path where updates will be loaded for this device

databasePath = "/" + device_location;

fuid = auth.token.uid.c_str();

}

else

{

Serial.printf("Failed, %s

", config.signer.signupError.message.c_str()); isAuthenticated = false; } // Assign the callback function for the long running token generation task, see addons/TokenHelper.h

config.token_status_callback = tokenStatusCallback; // Initialise the firebase library

Firebase.begin(&config, &auth); }

Putting it all together, our setup function will look as follows:

void setup() { // Initialise serial communication for local diagnostics Serial.begin(115200); // Initialise Connection with location WiFi Wifi_Init(); // Initialise firebase configuration and signup anonymously firebase_init(); }

Finally, let’s implement a test function which will upload sample data to our Firebase database, this will allow us to test that everything has been setup correctly. We create a new function called database_test() . Before performing any update, we check that 10 seconds has elapsed, and that the device is authenticated and lastly that the Firebase library is ready. Before doing an update we specify the node path of the Realtime Database whose value we want to set, and then we update that node with value of our dummy counter. The update is done using the Firebase.set() function which will override the previous value with the new updated value every 10 seconds. To explore other Firebase functions see the FirebaseESP32 API documentation here.

void database_test() { // Check that 10 seconds has elapsed before, device is authenticated and the firebase service is ready. if (millis() - elapsedMillis > update_interval && isAuthenticated && Firebase.ready())

{ elapsedMillis = millis(); Serial.println("------------------------------------"); Serial.println("Set int test..."); // Specify the key value for our data and append it to our path String node = path + "/value"; // Send the value our count to the firebase realtime database if (Firebase.set(fbdo, node.c_str(), count++)) {

// Print firebase server response Serial.println("PASSED"); Serial.println("PATH: " + fbdo.dataPath()); Serial.println("TYPE: " + fbdo.dataType()); Serial.println("ETag: " + fbdo.ETag()); Serial.print("VALUE: "); printResult(fbdo); //see addons/RTDBHelper.h Serial.println("------------------------------------"); Serial.println(); }

else

{ Serial.println("FAILED");

Serial.println("REASON: " + fbdo.errorReason());

Serial.println("------------------------------------");

Serial.println();

}

}

}

Finally we place our function in the main loop as follows:

void loop() { database_test(); }

Our code is all set for us to perform our dummy test. Connect your ESP32 board to your USB ports and compile your code by pressing the compile button in the bottom panel shown below:

After the build is successful, upload the code to the ESP32 device by using the upload button on the right of the compile button

If everything went well, you should be able to go back to the Realtime Database database browser on the Firebase console, and you should see your updates coming every 10 seconds as shown below:

You can also use the serial communication interface to check that the following outputs are being sent every 10 seconds.

Integration of DHT11 Temperature Sensor

Lastly let’s integrate our temperature sensor.

Firstly we need to ensure that our hardware connections are correct. The DHT11 sensor uses the I2C peripheral which requires two pins. Let’s start with a review of the DHT11 pin allocation:

The following table shows the DHT22/DHT11 pinout. When the sensor is facing you, pin numbering starts at 1 from left to right. We going to be using GPIO4 in this example, thus our hardware wiring will be as follows:

Now that we have our hardware configuration setup, let’s update our embedded application to utilise the DHT11 sensor. Let’s start by adding the DHT library from Adafruit to our project:

Let’s also add the DHT library to imports.

#include <Arduino.h>

#include <WiFi.h>

#include <FirebaseESP32.h>

#include "DHT.h"

We then need to setup our our DHT11 library, to do this we specify the GPIO pin which we plan to use to receive sensor updates from the the DHT11, we also specify which DHT sensor we are using, as the library is also compatible with the DHT22 sensor.

// Digital pin connected to the DHT sensor

#define DHTPIN 4

#define DHTTYPE DHT11 // Initialise DHT sensor

DHT dht(DHTPIN, DHTTYPE);

We then need to define some global variables to store our temperature.

Note: The DHT11 also tracks humidity, but for the sake of this tutorial we will only work with the temperature readings.

// Variables to hold sensor readings

float temperature = 0;

float humidity = 0; // JSON object to hold updated sensor values to be sent to be firebase

FirebaseJson temperature_json;

FirebaseJson humidity_json;

We need to update our setup() function to initialise our DHT library, the library will take care of the I2C setup:

void setup() { // Initialise serial communication for local diagnostics

Serial.begin(115200); // Initialise Connection with location WiFi

Wifi_Init(); // Initialise firebase configuration and signup anonymously

firebase_init(); // Initialise DHT library

dht.begin(); // Initialise temperature and humidity json data

temperature_json.add("deviceuid", DEVICE_UID);

temperature_json.add("name", "DHT11-Temp");

temperature_json.add("type", "Temperature");

temperature_json.add("location", device_location);

temperature_json.add("value", temperature); // Print out initial temperature values

String jsonStr;

temperature_json.toString(jsonStr, true);

Serial.println(jsonStr); humidity_json.add("deviceuid", DEVICE_UID);

humidity_json.add("name", "DHT11-Hum");

humidity_json.add("type", "Humidity");

humidity_json.add("location", device_location);

humidity_json.add("value", humidity); // Print out initial humidity values

String jsonStr2;

humidity_json.toString(jsonStr2, true);

Serial.println(jsonStr2); }

We then introduce a new function updateSensorReadings() which will be responsible to update our sensor readings and store them in our global JSON object variables which will later be sent to Firebase.

void updateSensorReadings(){ Serial.println("------------------------------------");

Serial.println("Reading Sensor data ..."); temperature = dht.readTemperature();

humidity = dht.readHumidity(); // Check if any reads failed and exit early (to try again).

if (isnan(temperature) || isnan(humidity)) {

Serial.println(F("Failed to read from DHT sensor!"));

return;

} Serial.printf("Temperature reading: %.2f

", temperature);

Serial.printf("Humidity reading: %.2f

", humidity); temperature_json.set("value", temperature);

humidity_json.set("value", humidity); }

Next we need to delete our old database_test() function and replace with with a new function called uploadSensorData() as shown below. We changed the node description to temperature and humidity . Lastly we then replaced the Firebase.set() with the Firebase.setJSON() function instead, and pass our two JSON objects holding by our temperature and humidity information. And lastly we replace the database_test() function with our new uploadSensorData() function in the main while loop. The final code version can be found on Github here.

void uploadSensorData() { if (millis() - elapsedMillis > update_interval && isAuthenticated && Firebase.ready())

{

elapsedMillis = millis(); updateSensorReadings(); String temperature_node = databasePath + "/temperature";

String humidity_node = databasePath + "/humidity"; if (Firebase.setJSON(fbdo, temperature_node.c_str(), temperature_json))

{ Serial.println("PASSED");

Serial.println("PATH: " + fbdo.dataPath());

Serial.println("TYPE: " + fbdo.dataType());

Serial.println("ETag: " + fbdo.ETag());

Serial.print("VALUE: ");

printResult(fbdo); //see addons/RTDBHelper.h

Serial.println("------------------------------------");

Serial.println(); }

else

{ Serial.println("FAILED");

Serial.println("REASON: " + fbdo.errorReason());

Serial.println("------------------------------------");

Serial.println();

} if (Firebase.setJSON(fbdo, humidity_node.c_str(), humidity_json))

{ Serial.println("PASSED");

Serial.println("PATH: " + fbdo.dataPath());

Serial.println("TYPE: " + fbdo.dataType());

Serial.println("ETag: " + fbdo.ETag());

Serial.print("VALUE: ");

printResult(fbdo); //see addons/RTDBHelper.h

Serial.println("------------------------------------");

Serial.println();

}

else

{

Serial.println("FAILED");

Serial.println("REASON: " + fbdo.errorReason());

Serial.println("------------------------------------");

Serial.println();

}

}

} void loop() {

uploadSensorData();

}

Compile and upload this new version and check that the Firebase database now shows a live temperature reading of your room temperature as shown below.

In order to make our application more realistic we going to need to populate some interesting sensor data on our database.

If you followed the tutorial, you can update the embedded application to change the location of the device and the device UID to simulate different sensor data coming from different rooms. Even better if you have 4 ESP32 and 4 DHT11 sensors, you can install each on in a separate room for the sake of this tutorial the following approach was taken.

The end result is that our realtime database will now include sensor data from each room as follows:

Awesome stuff! You’ve successful integrated your ESP32 device with Firebase.

Conclusion

To round it up, we’ve setup a new Firebase project which allows any ESP32 device to sign up anonymously and send temperature readings to the Realtime Database every 10 seconds. In our next tutorial we will build an watchOS application which will query our Realtime Database and provide us with realtime updates from all rooms onto an Apple Watch.

You can find the final code for this tutorial on Github here.

And finally lots of love and thanks to the guys from mobizt for giving us this amazing FirebaseESP32 library, there is lots of functionality there including the ability to send Firebase Cloud Messages, which could come in very handy for one of your next IoT projects.

Resources