Firebase: Control ESP32 GPIOs from Anywhere

In this guide, you’ll learn how to control the ESP32 GPIOs from anywhere using Firebase. We’ll create nodes on the Firebase Realtime Database to save the current GPIO states. Whenever there’s a change in the database nodes, the ESP32 updates its GPIOs accordingly. You can change the GPIOs states by writing on the database yourself, or you can create a web app to do that (check this tutorial).

Control ESP32 GPIOs from Anywhere using Firebase

Updated 26 May 2025

PART 2: Control ESP32/ESP8266 GPIOs from Anywhere (Firebase Web App)

We have a similar tutorial for the ESP8266 board: Firebase: Control ESP8266 NodeMCU GPIOs from Anywhere.

Other Firebase Tutorials with the ESP32/ESP8266 that you might be interested in:

What is Firebase?

Firebase Logo

Firebase is Google’s mobile application development platform that helps you build, improve, and grow your app. Firebase provides free services like hosting, authentication, and realtime database that allow you to build a fully-featured web app to control and monitor the ESP32 and ESP8266 boards that would be much more difficult and laborious to build and set up on your own.

Project Overview

The following diagram shows a high-level overview of the project we’ll build.

ESP32 Firebase Control Outputs from Anywhere Project Overview
  1. The ESP32 authenticates as a user with email and password to be able to access the database (that user must be added to the Firebase authentication methods);
  2. The database is protected using database rules. We’ll add the following rule: only authenticated users can access the database;
  3. The database has several nodes that save the ESP32 GPIO states. As an example, we’ll control three GPIOs (12, 13, and 14). You can add or remove nodes to control more or fewer GPIOs.
  4. The ESP32 will listen for changes on the GPIOs database nodes. Whenever there’s a change, it will update the GPIO states accordingly.
  5. You can change the GPIO states manually on the database using the Firebase console, or you can create a web page (accessible from anywhere) with buttons to control the GPIOs and show the current GPIO states (check PART 2).

These are the main steps to complete this project:

  1. Create a Firebase Project
  2. Set Authentication Methods
  3. Get Project API Key
  4. Set up the Realtime Database
  5. Set up Database Security Rules
  6. Organizing your Database Nodes
  7. ESP32: Listening for Database Changes (control GPIOs)

Preparing Arduino IDE

For this tutorial, we’ll program the ESP32 board using the Arduino core. So, make sure you have the ESP32 add-on installed in your Arduino IDE:

If you want to program the ESP32/ESP8266 boards using VS Code with the pioarduino or PlatformIO extensions, follow these tutorials instead:

1) Create Firebase Project

Follow the next instructions to create a new project on Firebase.

  1. Go to Firebase and sign in using a Google Account.
  2. Go to the Firebase Console and create a new project.
  3. Give a name to your project, for example: ESP-Project, and click Continue.
    Set Up Firebase Project for ESP32 and ESP8266 Step 1
  4. Next, enable or disable AI assistance for your project. This is optional.
    Set Up Firebase Project for ESP32 and ESP8266 - Enable AI assistant
  5. Disable the option Enable Google Analytics for this project, as it is not needed. Then, click Create project.
    Disable Google Analytics for firebase project
  6. It will take a few seconds to set up your project. Click Continue when it’s ready.
    Firebase Project for ESP32 Ready

  7. You’ll be redirected to your Project console page.
    Firebase console project

2) Set Authentication Methods

To allow authentication with email and password, first, you need to set authentication methods for your app.

“Most apps need to know the identity of a user. In other words, it takes care of logging in and identifying the users (in this case, the ESP32 or ESP8266). Knowing a user’s identity allows an app to securely save user data in the cloud and provide the same personalized experience across all of the user’s devices.” To learn more about the authentication methods, you can read the documentation.

  1. On the left sidebar, click on Build > Authentication and then on Get started.
    Firebase project set authentication
  2. There are several authentication methods like email and password, Google Account, Facebook account, and others.
    SFirebase authentication methods
  3. Select Email/Password and enable that authentication method. Then, click Save.
    Enable Email Password Sign in Firebase
  4. Then, at the top, click on the Users tab. Then, click on Add user.
    Firebase Create a new user
  5. Create a new user with an email and password. The email can be your personal email. Create a password for that user (you need to remember the password later). Finally, click on Add user.
    Firebase add user email and password
  6. The User will show up on the list of users. You can see information about the user, like when it was created, the last time it signed in, and its user UID.
    Firebase User Created

Firebase creates a unique UID for each registered user. The user UID allows us to identify the user and keep track of the user to provide or deny access to the project or the database. There’s also a column that registers the date of the last sign-in. At the moment, it is empty because we haven’t signed in with that user yet.

Copy the User UID because you’ll need it later.

3) Get Project API Key

To interface with your Firebase project using the ESP32 or ESP8266 boards, you need to get your project API key. Follow the next steps to get your project API key.

  1. To get your project’s API key, on the left sidebar click on Project Settings.
    Firebase Realtime Database Project Settings
  2. Copy the API Key to a safe place because you’ll need it later.
    Firebase Project API Key

4) Set up the Realtime Database

Now, let’s create a realtime database and set up database rules for our project.

1) On the left sidebar, click on Realtime Database and then click on Create Database.

Firebase Project Create Database

2) Select your database location. It should be the closest to your location.

Firebase Realtime Database Set up location

3) Set up security rules for your database. You can select Start in test mode. We’ll change the database rules in just a moment.

Firebase Realtime Database Start in Test Mode

4) Your database is now created. You need to copy and save the database URL—highlighted in the following image—because you’ll need it later in your ESP32/ESP8266 code.

Firebase Real time database URL

5) Set up Database Security Rules

Now, let’s set up the database rules. On the Realtime Database tab, select the Rules tab at the top. Then, click on Edit rules, and add the following rules.

{
  "rules": {
    ".read": "auth.uid === 'REPLACE_WITH_YOUR_USER_UID'",
    ".write": "auth.uid === 'REPLACE_WITH_YOUR_USER_UID'"
  }
}

Insert the UID of the user you created previously. Then, click Publish.

These database rules determine that:

  • Only the user with that specific UID can read and write to the database (change the GPIO states).

Add More Users

To add more users, you can simply go to the Authentication tab and click Add user. Add an email and password for the new user, and finally click on Add user to create the user.

Firebase Add New User Email and Password Console

Copy the user UID of that new user and add it to the database rules, as follows:

{
  "rules": {
    ".read": "auth.uid === 'REPLACE_WITH_YOUR_USER_UID' || auth.uid === 'REPLACE_WITH_USER_UID2'",
    ".write": "auth.uid === 'REPLACE_WITH_YOUR_USER_UID' || auth.uid === 'REPLACE_WITH_USER_UID2'"
  }
}

For example. In my case, the UIDs of the users are: RjO3taAzMMXB82Xmir2LQ7XXXXXX and 9QdDc9as5mRXGAjEsQiUJkXXXXXX. So, the database rules will look as follows:

{
  "rules": {
    ".read": "auth.uid === 'RjO3taAzMMXB82Xmir2LQ7XXXXXX' || auth.uid === '9QdDc9as5mRXGAjEsQiUJkXXXXXX'",
    ".write": "auth.uid === 'RjO3taAzMMXB82Xmir2LQ7XXXXXX' || auth.uid === '9QdDc9as5mRXGAjEsQiUJkXXXXXX'"
  }
}

Finally, publish your database rules.

To learn more about database rules, you can check the Firebase documentation.

6) Organizing Your Database Nodes

All the data stored in the Firebase Realtime Database is stored as JSON objects. So, you can think of the database as a cloud-based JSON tree. When you add data to the JSON tree, it becomes a node with an associated key in the existing JSON structure.

Not familiar with JSON? Read this quick guide.

The best way to organize your data will depend on your project features and how users access the data.

We want to control the ESP32 GPIOs. We can organize the data in a way that makes it easy to add more GPIOs and boards later on. So, we can structure the database as follows:

  • board1:
    • outputs:
      • digital:
        • 12: 0
        • 13: 0
        • 14: 0

In JSON format, here’s what it would look like:

{
  "board1": {
    "outputs": {
      "digital": {
        "12": 0,
        "13": 0,
        "14": 0
      }
    }
  }
}

Creating Database Nodes

Now let’s create the database nodes in our database. You can create the nodes manually by writing the nodes on the Firebase console, on the web app, or via the ESP32. We’ll create them manually, so it is easier to follow the tutorial.

1) Click on the Realtime Database so that we start creating the nodes.

Firebase Realtime Database Menu

2) You can create the database nodes manually by using the (+) icons on the database. However, to prevent typos, we provide a JSON file that you can upload to create the same nodes as ours. Click the link below to download the JSON file.

After downloading, unzip the folder to access the .json file.

3) Now, go back to your database on the Firebase console. Click on the three-dot icon and select Import JSON.

Firebase Realtime Database Import JSON

4) Select the JSON file that you’ve just downloaded.

5) Your database should look as shown below.

Firebase realtime database structure example

All the database nodes required for this project are created. You can proceed to the next section.

7) ESP32: Listening for Database Changes (control GPIOs)

In this section, we’ll program the ESP32 board to do the following tasks:

  1. Authenticate as a user with email and password (the user you set up in this section);
  2. Listening for database changes on the GPIO nodes and changing their states accordingly.

Parts Required

For this project, you need the following parts:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Schematic Diagram

In this example, we’ll control three LEDs connected to GPIOs 12, 13, and 14. So, wire three LEDs to the ESP32. You can follow the next schematic diagram.

ESP32 connected to three LEDs Schematic diagram

You can use any other suitable ESP32 GPIOs, but you also need to change the database nodes.

Installing Libraries

For this tutorial, you need to install the FirebaseClient Library.

Installation – Arduino IDE

Follow this section if you’re using Arduino IDE.

Go to Sketch > Include Library > Manage Libraries, search for the library name, and install it.

Install Firebase Client Library Arduino IDE

Now, you’re all set to start programming the ESP32 and ESP8266 boards to interact with the database.

Installing Libraries – VS Code

Follow the next instructions if you’re using VS Code with the PlatformIO or pioarduino extension.

Install the FirebaseClient Library

Click on the PIO Home icon and select the Libraries tab. Search for “FirebaseClient“. Select the FirebaseClient Library by Mobitz.

Install FirebaseClient Library VS Code

Then, click Add to Project and select the project you’re working on.

Add FirebaseClient Library ro project in VS Code

Then, click Add to Project and select the project you’re working on.

Also, change the monitor speed to 115200 by adding the following line to the platformio.ini file of your project:

monitor_speed = 115200

Listening for Database Changes (GPIO states) — Code

Copy the following code to your Arduino IDE or to the main.cpp file if you’re using VS Code.

You need to insert the following in the code before uploading it to your board:

  • your network credentials
  • project API key
  • database URL
  • authorized user email and password
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at our blog.
    - ESP32: https://RandomNerdTutorials.com/firebase-control-esp32-gpios/
    - ESP8266: https://RandomNerdTutorials.com/firebase-control-esp8266-nodemcu-gpios/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  Based in the RTDB Basic Example by Firebase-ESP-Client library by mobizt
  https://github.com/mobizt/Firebase-ESP-Client/blob/main/examples/RTDB/Basic/Basic.ino
*/

#include <Arduino.h>
#if defined(ESP32)
    #include <WiFi.h>
#elif defined(ESP8266)
    #include <ESP8266WiFi.h>
#endif
#include <WiFiClientSecure.h>
#include <FirebaseClient.h>
#include "ExampleFunctions.h" // Provides the functions used in the examples.
#include <ArduinoJson.h>

// Network and Firebase credentials
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

#define Web_API_KEY "REPLACE_WITH_YOUR_FIREBASE_PROJECT_API_KEY"
#define DATABASE_URL "REPLACE_WITH_YOUR_FIREBASE_DATABASE_URL"
#define USER_EMAIL "REPLACE_WITH_FIREBASE_PROJECT_EMAIL_USER"
#define USER_PASS "REPLACE_WITH_FIREBASE_PROJECT_USER_PASS"

// User functions
void processData(AsyncResult &aResult);

// Authentication
UserAuth user_auth(Web_API_KEY, USER_EMAIL, USER_PASS);

SSL_CLIENT ssl_client, stream_ssl_client;

// Firebase components
FirebaseApp app;
using AsyncClient = AsyncClientClass;
AsyncClient aClient(ssl_client), streamClient(stream_ssl_client);
RealtimeDatabase Database;

// Timer variables for loop
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 10000; // 10 seconds in milliseconds

// Database  path (where the data is)
String listenerPath = "board1/outputs/digital/";

// Declare outputs
const int output1 = 12;
const int output2 = 13;
const int output3 = 14;

// Initialize WiFi
void initWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
  Serial.println();
}

void setup(){
  Serial.begin(115200);

  // Declare pins as outputs
  pinMode(output1, OUTPUT);
  pinMode(output2, OUTPUT);
  pinMode(output3, OUTPUT);

  initWiFi();

  // Configure SSL client
  ssl_client.setInsecure();
  stream_ssl_client.setInsecure();
  #if defined(ESP32)
    ssl_client.setConnectionTimeout(1000);
    ssl_client.setHandshakeTimeout(5);
    stream_ssl_client.setConnectionTimeout(1000);
    stream_ssl_client.setHandshakeTimeout(5);
  #elif defined(ESP8266)
    ssl_client.setTimeout(1000); // Set connection timeout
    ssl_client.setBufferSizes(4096, 1024); // Set buffer sizes
    stream_ssl_client.setTimeout(1000); // Set connection timeout
    stream_ssl_client.setBufferSizes(4096, 1024); // Set buffer sizes
  #endif

  // Initialize Firebase
  initializeApp(aClient, app, getAuth(user_auth), processData, "🔐 authTask");
  app.getApp<RealtimeDatabase>(Database);
  Database.url(DATABASE_URL);

  // Set a database listener
  streamClient.setSSEFilters("get,put,patch,keep-alive,cancel,auth_revoked");
  Database.get(streamClient, listenerPath, processData, true /* SSE mode (HTTP Streaming) */, "streamTask");
}

void loop(){
  // Maintain authentication and async tasks
  app.loop();

  // Check if authentication is ready
  if (app.ready()){
    //Do nothing - everything works with callback functions
    unsigned long currentTime = millis();
    if (currentTime - lastSendTime >= sendInterval){
      // Update the last send time
      lastSendTime = currentTime;
      Serial.printf("Program running for %lu\n", currentTime);        
    }
  }
}

void processData(AsyncResult &aResult){
  // Exits when no result available when calling from the loop.
  if (!aResult.isResult())
    return;

  if (aResult.isEvent()){
    Firebase.printf("Event task: %s, msg: %s, code: %d\n", aResult.uid().c_str(), aResult.eventLog().message().c_str(), aResult.eventLog().code());
  }

  if (aResult.isDebug()){
    Firebase.printf("Debug task: %s, msg: %s\n", aResult.uid().c_str(), aResult.debug().c_str());
  }

  if (aResult.isError()){
    Firebase.printf("Error task: %s, msg: %s, code: %d\n", aResult.uid().c_str(), aResult.error().message().c_str(), aResult.error().code());
  }

  // When it receives data from the database
  if (aResult.available()){
    RealtimeDatabaseResult &RTDB = aResult.to<RealtimeDatabaseResult>();
    // we received data from the streaming client
    if (RTDB.isStream()) {
      Serial.println("----------------------------");
      Firebase.printf("task: %s\n", aResult.uid().c_str());
      Firebase.printf("event: %s\n", RTDB.event().c_str());
      Firebase.printf("path: %s\n", RTDB.dataPath().c_str());
      Firebase.printf("etag: %s\n", RTDB.ETag().c_str());
      Firebase.printf("data: %s\n", RTDB.to<const char *>());
      Firebase.printf("type: %d\n", RTDB.type());

      // RTDB.type = 6 means the result is a JSON : https://github.com/mobizt/FirebaseClient/blob/main/resources/docs/realtime_database_result.md#--realtime_database_data_type-type
      // You receive a JSON when you initialize the stream
      if (RTDB.type() == 6) {
        Serial.println(RTDB.to<String>());
        // Parse JSON
        DynamicJsonDocument doc(512);
        DeserializationError error = deserializeJson(doc, RTDB.to<String>());
        if (error) {
          Serial.print("deserializeJson() failed: ");
          Serial.println(error.c_str());
          return;
        }
        // Iterate through JSON object
        for (JsonPair kv : doc.as<JsonObject>()) {
          int gpioPin = atoi(kv.key().c_str()); // Convert key (e.g., "12") to int
          bool state = kv.value().as<bool>();
          digitalWrite(gpioPin, state ? HIGH : LOW);
        }
      }

      // RTDB.type() = 4 means the result is a boolean
      // RTDB.type() = 1 means the result is an integer
      // learn more here: https://github.com/mobizt/FirebaseClient/blob/main/resources/docs/realtime_database_result.md#--realtime_database_data_type-type
      if (RTDB.type() == 4 || RTDB.type() == 1){
        // get the GPIO number
        int GPIO_number = RTDB.dataPath().substring(1).toInt();
        bool state = RTDB.to<bool>();
        digitalWrite(GPIO_number, state);
        Serial.println("Updating GPIO State");
      }

      // The stream event from RealtimeDatabaseResult can be converted to values as following.
      /*bool v1 = RTDB.to<bool>();
      int v2 = RTDB.to<int>();
      float v3 = RTDB.to<float>();
      double v4 = RTDB.to<double>();
      String v5 = RTDB.to<String>();
      Serial.println(v5); */
    }
    else{
        Serial.println("----------------------------");
        Firebase.printf("task: %s, payload: %s\n", aResult.uid().c_str(), aResult.c_str());
    }
  }
}

View raw code

How the Code Works

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

Include Libraries

First, include the required libraries. This code is also compatible with the ESP8266.

#include <Arduino.h>
#if defined(ESP32)
    #include <WiFi.h>
#elif defined(ESP8266)
    #include <ESP8266WiFi.h>
#endif
#include <WiFiClientSecure.h>
#include <FirebaseClient.h>
#include "ExampleFunctions.h" // Provides the functions used in the examples.
#include <ArduinoJson.h>

Network Credentials

Include your network credentials in the following lines so that your boards can connect to the internet using your local network.

// Network and Firebase credentials
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

Firebase Project API Key, Firebase User, and Database URL

Insert your Firebase project API key—the one you’ve gotten in this section.

#define Web_API_KEY "REPLACE_WITH_YOUR_FIREBASE_PROJECT_API_KEY"

Insert your database URL in the following line:

// Insert RTDB URLefine the RTDB URL
#define DATABASE_URL "REPLACE_WITH_YOUR_DATABASE_URL"

Insert the authorized email and the corresponding password—these are the details of the user you’ve added in this section.

#define USER_EMAIL "REPLACE_WITH_FIREBASE_PROJECT_EMAIL_USER"
#define USER_PASS "REPLACE_WITH_FIREBASE_PROJECT_USER_PASS"

Declaring Firebase Authentication and Components

The following line creates an authentication object using the project API key, the project user email, and password.

UserAuth user_auth(Web_API_KEY, USER_EMAIL, USER_PASS);

Create two SSL clients. One to handle Firebase operations and another for Database Streaming (needed to listen to database changes).

SSL_CLIENT ssl_client, stream_ssl_client;

This creates a FirebaseApp instance called app that refers to the Firebase application.

FirebaseApp app;

The following lines set up the asynchronous communication framework for interacting with Firebase’s Realtime Database. You need to instantiate an Asynchronous client called aClient that enables secure HTTPS for handling Firebase operations and another one called streamClient to handle database streaming (listening to database changes). This will allow you to handle network and database streaming operations asynchronously.

FirebaseApp app;
using AsyncClient = AsyncClientClass;
AsyncClient aClient(ssl_client), streamClient(stream_ssl_client);

The following line creates a RealtimeDatabase object called Database, which represents the Firebase Realtime Database.

RealtimeDatabase Database;

Database Path

Then, create a variable that saves the database path where we’ll be listening for changes. Taking into account the database structure we created previously, the listener database path should be as follows:

// Database  path (where the data is)
String listenerPath = "board1/outputs/digital/";

If you want to add more boards, then you just need to change the listener path accordingly.

Create variables for the outputs you’ll control. In our case, we’re controlling GPIOs 12, 13, and 14. You can control any other ESP32 GPIOs (you’ll also need to change the database nodes):

const int output1 = 12;
const int output2 = 13;
const int output3 = 14;

initWifi()

The iniWifi() function connects the ESP32 to your local network. We’ll call it later in the setup() to initialize Wi-Fi.

// Initialize WiFi
void initWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
  Serial.println();
}

setup()

Let’s now jump to the setup().

Initialize the Serial Monitor:

Serial.begin(115200);

Declare your GPIOs as outputs using the pinMode() function.

pinMode(output1, OUTPUT);
pinMode(output2, OUTPUT);
pinMode(output3, OUTPUT);

Call the initiWiFi() function we created previously, to connect your board to your local network.

initWiFi();

Configure the SSL Client.

// Configure SSL client
ssl_client.setInsecure();
stream_ssl_client.setInsecure();
#if defined(ESP32)
  ssl_client.setConnectionTimeout(1000);
  ssl_client.setHandshakeTimeout(5);
  stream_ssl_client.setConnectionTimeout(1000);
  stream_ssl_client.setHandshakeTimeout(5);
#elif defined(ESP8266)
  ssl_client.setTimeout(1000); // Set connection timeout
  ssl_client.setBufferSizes(4096, 1024); // Set buffer sizes
  stream_ssl_client.setTimeout(1000); // Set connection timeout
  stream_ssl_client.setBufferSizes(4096, 1024); // Set buffer sizes
#endif

The following line initializes the Firebase app with authentication and sets the processData() as the callback function for async results (this means that any results from the initializeApp() function will be handled on the processData() callback function).

initializeApp(aClient, app, getAuth(user_auth), processData, "🔐 authTask");

Then, tell that you want to set the Database object defined earlier as a database for our Firebase app.

app.getApp<RealtimeDatabase>(Database);

Finally, set the database URL.

Database.url(DATABASE_URL);

Stream Database – Listening for Changes

Now, we need to set a database listener so that we receive data on the ESP32 when the database changes.

The following line configures the types of Server-Sent Events (SSE) that the streamClient (will listen for when connected to the Firebase Realtime Database. In this case, we’re including all event types.

streamClient.setSSEFilters("get,put,patch,keep-alive,cancel,auth_revoked");

Finally, the following line initiates a streaming connection to the Firebase Realtime Database at the specified listenerPath, using the streamClient to listen for real-time updates. When events occur, they are processed by the processData() callback function (defined later in the code).

Database.get(streamClient, listenerPath, processData, true /* SSE mode (HTTP Streaming) */, "streamTask");

loop()

The loop() is empty and only prints for how long the program is running (this is optional). You just need to maintain the app.loop() command.

void loop(){
  // Maintain authentication and async tasks
  app.loop();

  // Check if authentication is ready
  if (app.ready()){
    //Do nothing - everything works with callback functions
    unsigned long currentTime = millis();
    if (currentTime - lastSendTime >= sendInterval){
      // Update the last send time
      lastSendTime = currentTime;
      Serial.printf("Program running for %lu\n", currentTime);        
    }
  }
}

processData Function – Handling Database Changes

The processData() function will handle all events related to Firebase operations, including changes in the database data.

We can check if we have new data in the database using the aResult.available() command.

if (aResult.available()){

Then, we convert the result to a format we can access in our code—a variable of type RealtimeDatabaseResult.

RealtimeDatabaseResult &RTDB = aResult.to<RealtimeDatabaseResult>();

We check if the received data is from the streaming client.

if (RTDB.isStream()) {

Then, we can print available data about the event.

Firebase.printf("task: %s\n", aResult.uid().c_str());
Firebase.printf("event: %s\n", RTDB.event().c_str());
Firebase.printf("path: %s\n", RTDB.dataPath().c_str());
Firebase.printf("etag: %s\n", RTDB.ETag().c_str());
Firebase.printf("data: %s\n", RTDB.to<const char *>());
Firebase.printf("type: %d\n", RTDB.type());

The RTDB.type() command tells us what type of data we received from the database. The results can be:

  • realtime_database_data_type_undefined or -1
  • realtime_database_data_type_null or 0.
  • realtime_database_data_type_integer or 1.
  • realtime_database_data_type_float or 2.
  • realtime_database_data_type_double or 3.
  • realtime_database_data_type_boolean or 4.
  • realtime_database_data_type_string or 5.
  • realtime_database_data_type_json or 6.
  • realtime_database_data_type_array or 7.

When the ESP first connects to the database, it is triggered on the root(/) path and returns a JSON object with all child nodes (RTDB.type() = 6). So, we can get all values from the database and update the ESP32 GPIOs when it first runs. This is also useful because if the ESP32 resets, it will always receive this JSON object first, and will be able to update all GPIOs.

ESP32 Streaming Database First Run

As you can see from the previous screenshot, the JSON object it receives looks as follows (it might be different depending on the GPIO states):

{
  "12": 0,
  "13": 0,
  "14": 0
}

When this happens, the returned data is of type JSON. So, we can get it and convert it to a JSON variable doc as follows:

if (RTDB.type() == 6) {
    Serial.println(RTDB.to<String>());
    // Parse JSON
    DynamicJsonDocument doc(512);
    DeserializationError error = deserializeJson(doc, RTDB.to<String>());
    if (error) {
      Serial.print("deserializeJson() failed: ");
      Serial.println(error.c_str());
      return;
    }

Then, we can iterate through the JSON object and get the keys (GPIOs) and corresponding values (GPIO states). In each iteration, we save the GPIO on the gpioPin variable and its corresponding state on the state variable. Then, we call the digitalWrite() function to update its state.

// Iterate through JSON object
for (JsonPair kv : doc.as<JsonObject>()) {
  int gpioPin = atoi(kv.key().c_str()); // Convert key (e.g., "12") to int
  bool state = kv.value().as<bool>();
  digitalWrite(gpioPin, state ? HIGH : LOW);
}

This runs through all keys and values allowing us to update all GPIOs.

If RTDB.type() is 4 or 1, it means we inserted a boolean or an integer value in the database. In that case, we get the database path that corresponds to the GPIO number, and the inserted data that corresponds to the GPIO state.

if (RTDB.type() == 4 || RTDB.type() == 1){
  // get the GPIO number
  int GPIO_number = RTDB.dataPath().substring(1).toInt();
  bool state = RTDB.to<bool>();

After getting that, we can update the GPIO state according to the changes in the database.

digitalWrite(GPIO_number, state);

Demonstration

After inserting all required credentials, upload the code to your board.

After uploading, open the Serial Monitor at a baud rate of 115200 and reset the board. You should get something as shown below.

ESP32 Streaming Database First Run

As you can see, when the ESP first runs, it gets a JSON object with all GPIO states.

{
  "12": 0,
  "13": 0,
  "14": 0
}

Then, go to the Firebase Realtime Database on the Firebase console. Manually change the GPIO states (either 0 or 1). After inserting a new value, press Enter.

Firebase Realtime Database Saving ESP GPIO States

Right after, you’ll see on the Serial Monitor that the ESP32 detected the changes.

Firebase Streaming Database ESP Detecting Changes Serial Monitor

And it will update the GPIO states and light up the LEDs almost instantaneously.

Control ESP32 Outputs from Anywhere

Then, if you reset your board (press the RST button or remove and apply power again), when it restarts, it will get the latest GPIO states from the database and update them right away.

Taking it Further – Add More Boards

You can take this project further and add more boards. To do that, create new database nodes for a second board. You can add ESP32 or ESP8266 boards.

You can download the following JSON file and import it to your database, and it will create nodes for two boards:

After uploading the JSON file, the database will look as follows:

Firebase Realtime Database Save GPIO Sates Multiple Boards

Now, you can upload the same code to the new board (it is compatible with the ESP32 and ESP8266). But don’t forget to change the listening path. It should be:

String listenerPath = "board2/outputs/digital/";

Now, you can control both boards by changing the GPIO states in the database.

Control ESP32 and ESP8266 Outputs from Anywhere Multiple Boards

In Part 2, we’ll create a Firebase web app so that you have a nice interface to control your GPIOs from anywhere without having to use the Firebase console and change the database manually:

Wrapping Up

In this tutorial, you learned how to use the Firebase Realtime Database to save the ESP GPIO states. You also learned how to program the ESP32 to listen for database changes. Whenever a change is detected, we update the corresponding GPIO states. You can change the code so that the ESP listens for any other data saved in the database, not only GPIO states. Because you can access the Firebase Realtime Database from anywhere, you can control your boards from anywhere too. This is great for IoT projects.

In Part 2, we’ll create a web app to control your GPIOs from anywhere, without the need to login manually on the Firebase console:

If you like Firebase projects, please take a look at our eBook. We’re sure you’ll like it:

Learn more about the ESP32 with our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

44 thoughts on “Firebase: Control ESP32 GPIOs from Anywhere”

  1. Another nifty RNT-project. Thank you very much!

    Maybe you could also create a version that allows operation on the RPI and RPI pico W.

    Also boards using the SIM800-chip would be of greate interest. I know that there are projects out there using SMS, but using Firebase is also promising.

    Again thank you!

    Reply
    • Hi Toni.
      Thank you for your feedback.
      Using the SIM800L with Firebase is a very useful topic. I’ll try to take a look at it.
      Regards,
      Sara

      Reply
  2. This is something I have been trying to make for a long time.
    Finely I got something that will not only read data, but also be able to change the GPIO from outside the router.

    Looking forwards for the web app.

    Thank you so much.

    Reply
  3. Hi Sara,

    I tested your tutorial and works great. I tried include it in my project but I have problems. In my project I also have to send data to firebase every 15 seconds, but sending to FB fails. I get these errors:

    http connection was used by other processes and
    send request failed

    Is it possible to stream and send data to firebase simultaneously ??

    Best regards

    Reply
    • Hi.
      Yes.
      It is possible to stream data and send data simultaneously.
      Maybe there is something wrong with how your code is structured?
      Regards,
      Sara

      Reply
  4. Hi,
    I want to control a esp32 and using firebase change variable from Android app.
    If I have esp32 (called for example esp1) and is connected to firebase database using one username (user 1), if I connected to same firebase database with another esp32(esp2) but with different username (user2).
    If I change the value using user 1, user 2 see new value or his older value?

    Reply
    • Hi.
      Both users can see the new value if they are listening for the paths that contain those values and if both have access to that path.
      Regards,
      Sara

      Reply
  5. My ESP32 stop working after some hours. Then I put in a reboot ever hour, and now it is working.

    My code look like this, if others won’t to use it.
    unsigned long bootDelay = 3600000; // One hour
    unsigned long bootPrevMillis;
    unsigned long previousMillis = 0;

    in Setup
    bootPrevMillis = millis();

    in loop
    if (millis() – bootPrevMillis > bootDelay){
    ESP.restart();
    }

    Reply
  6. Hi Sara,
    That is a very interesting project. I did face a few challenges install firebase tools but got it working now. I had to switch to a bash console as it didn’t work with power shell.

    Now I am stuck with following error message:
    Connecting to WiFi …..192.168….

    Token info: type = id token, status = on request
    Token info: type = id token, status = ready
    [ 6888][E][ssl_client.cpp:36] _handle_error(): [data_to_read():331]: (-76) UNKNOWN ERROR CODE (004C)

    Any idea what could happen?
    I did copy the raw ESP code and did fill my credentials. I also did follow your instructions to setup the database and added additional users
    Best regards,
    Roger

    Reply
  7. Hi Sara,
    I think I followed all your instructions.
    I registered API key, email etc. but finally in the Serial Monitor I get only this;
    Token info: type = id token, status = on request
    Token info: type = id token, status = error
    Token error: code: 400, message: EMAIL_NOT_FOUND
    Token info: type = id token, status = error
    Token error: code: 400, message: bad request
    In an another project :ESP32: Getting Started with Firebase (Realtime Database) all runs well.
    Any suggestion.

    Thanks
    Renzo

    Reply
    • Hi.
      Check if you have multiple Firebase projects.
      Check that you’re using the right project with the right user.
      As you can see in the error, it seems the email is not registered in the authorized users.
      Make sure you have the users registered in the project you’re using.
      Regards,
      Sara

      Reply
    • Hi Sara,
      I think I followed all your instructions.
      I registered API key, email etc. but finally in the Serial Monitor I get only this;
      Token info: type = id token, status = on request
      Token info: type = id token, status = error
      Token error: code: 400, message: EMAIL_NOT_FOUND
      Token info: type = id token, status = error
      Token error: code: 400, message: bad request
      In an another project :ESP32: Getting Started with Firebase (Realtime Database) all runs well.
      Any suggestion.

      Thanks
      Renzo
      Probably I found the error in Autentication>Users the column that registers the date of the last sign-in is empty.
      What have I to do?
      Thanks
      Renzo

      Reply
      • Hi.
        You probably have multiple Firebase projects created and are using the wrong configurations on your code.
        Double-check you’re using the right firebase project, with the right credentials, and with the user created and database rules.
        Regards,
        Sara

        Reply
  8. Hi Sara,
    Thank you for this very cool tutorial.
    I already used firebase with Android App to be notified whenever there is a change in the database.
    So I have a question: is the firebase stream similar to a notification or is it more like a “polling” done by the ESP32 ?
    I relaly would like the ESP32 to be notified of a change rather than having to permanently poll the database.

    Thanks
    JP

    Reply
  9. Hi,

    I was wondering about the costs firebase is charging. So I know that it varies if you have more traffic or using different plans.

    Can you tell me how much it would be for this specific project?

    Sadly I don’t find anything about it online. I would prefer using a raspberry if it is like 50$ / month. But if it is quite cheap, I will try it out.

    Thanks for all the nice projects. Hopping to get an answer for my question.

    Best regards

    Reply
      • Thank you very much !

        As the pricing is so complicated that you probably could write a Thesis about firebase costs🤣
        I really appreciate your fast answer !

        Reply
  10. Hello,

    I’m using MIT APP Inventor to write to Firebase database. MIT APP Inventor stores the numbers in Firebase as a string with quotes, just like this: “1” or “0”. What change do I need to make in the ESP32 code for it to convert this string to an integer?

    Reply
  11. Hello,
    How to get email notification once we on or off the light… We using firebase as database and vscode to design my website.

    Reply
  12. Hi Sara, thanks for all your great projects. Just one question: I’m stuck at this point on the serial monitor:

    Connecting to WiFi …….192.168.43.97

    Token info: type = id token (GITKit token), status = on request
    Token info: type = id token (GITKit token), status = ready

    and that’s it…..any help greatly appreciated.

    Reply
  13. Hey,
    Thank you for this cool guide!
    I´m struggling because I´m getting this error:

    stream timeout, resuming..
    error code: – 3 reason: not connected

    then it try to connect again, but the error still.
    Any Ideas?

    Thanks Marcel

    Reply
  14. Hi Sara. I have a problem. I try to compile the code in the Arduino IDE but I get the following error: C:\Users\Juan\AppData\Local\Arduino15\libraries\SD\src/utility/Sd2PinMap.h:524:2: error: #error Architecture or board not supported.
    #error Architecture or board not supported.
    ^
    Multiple libraries were found for “SD.h”
    Used: C:\Users\Juan\AppData\Local\Arduino15\libraries\SD
    Not used: C:\Users\Juan\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6\libraries\SD
    exit status 1

    Compilation error: exit status 1

    What should I do?

    Reply
  15. Hi,
    after downloading I get this:
    Sketch uses 1152566 bytes (87%) of program storage space. Maximum is 1310720 bytes.
    Global variables use 42384 bytes (12%) of dynamic memory, leaving 285296 bytes for local variables. Maximum is 327680 bytes.
    Just to control 3 leds ??? What could be wrong?
    Arduino ide 2.0.4, ESP32 DevKit C.
    Regards Jone

    Reply
      • Hi Sara,
        sorry to get back this late but my question actually was that if this compilation takes 87% of program space and the existing code already has 58 % including all controls and stuff so …?
        How is it possible to add your example into my code and get them fit into ESP32 program space? This #include <Firebase_ESP_Client.h> seems to take huge amount space in my compilation, could be something wrong here? How much do you get after compilation?

        Reply
  16. Hi! Nice tutorial.

    It would be great to continue this presentation and connect ESP32 – Firebase to Google Assistant and Google Home. This would open up great opportunities for the use of smart devices.
    Are you planning to create something like this?

    Reply
    • Hi.
      At the moment, I already have many projects lined up for the next few months.
      But thanks for the suggestion. I’ll add it to my list.
      Regards,
      Sara

      Reply
  17. Hello, thank you for your tutorial like always, I tried updating the gpio values whenever I press a physical button, the set() function in the esp32 code would erase every other values under the parent path unlike how the set() function in the javascript code would. I used some lines from here that was used to send sensor readings in order to send the button states
    https://randomnerdtutorials.com/esp32-esp8266-firebase-gauges-charts/

    FirebaseJson json;
    String buttonPath = dbButtons(buttonPressed);
    if(Firebase.ready() && localSwitch == true) {
    json.set(buttonPath, String(1));
    Serial.printf(“Set json… %s\n”, Firebase.RTDB.setJSON(&fbdo, listenerPath.c_str(), &json) ? “ok” : fbdo.errorReason().c_str());
    }
    this are the screenshots of what happens if used the webapp buttons vs the physical buttons.
    https://imgur.com/a/I0lbIFN

    Reply
  18. Hi Sara, I have a problem with the Firebase_ESP_Client library, today I updated the FirebaseESP32 library.

    The problem is what the streamCallback(FirebaseStream) tells me “variable or field ‘streamCallback’ declared null
    56 | void streamCallback (FirebaseStream data)”

    It’s right?

    I use the code on this page and update the call with the new library.

    //#include <Firebase_ESP_Client.h>
    #include <FirebaseESP32.h>

    Thanks for your future response for me.

    Reply
  19. Hi Sara, I have a problem with the Firebase_ESP_Client library, today I updated the FirebaseESP32 library.

    The problem is what the streamCallback(FirebaseStream) tells me “variable or field ‘streamCallback’ declared null
    56 | void streamCallback (FirebaseStream data)”

    It’s right?

    I use the code on this page and update the call with the new library.

    //#include <Firebase_ESP_Client.h>
    #include <FirebaseESP32.h>

    Thanks for your future response for me.

    Reply
  20. Is it possible to use push buttons in conjunction with Firebase? If yes, how can it be done and are there any example projects available?

    Reply
  21. Hi, thanks for this project. This was the project that got me interested in Firebase and the huge potential it has, and I’ve used Firebase a lot over the last year or two. The ‘Firebase-ESP-Client’ library has been deprecated though. Although it is still available, the author, mobizt, reports the library as having ‘unfixable issues’ and has created a new, very different library, ‘FirebaseClient’. Perhaps a similar tutorial using the new library would be something that would be something you would like to do. I and many others would benefit greatly from such a project.

    Reply
  22. I appreciate all the work that you do, and have a few of your books, and thought to share the experience i had with this project.

    to make a long story short i could not get it to compile with the latest ESP32 Cores or the latest Firebase ESP Client.

    Only esp32 core version 2.07 worked and i left it at that: And that could be a plus\minus version.
    Firebase ESP Client version 4.2.7 worked . The latest version to date 4.4.15 did not compile it_

    I have great respect for your work, thank you and glad to share.

    Ciao

    Reply

Leave a Comment

Download Our Free eBooks and Resources

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