Firebase: Управление GPIO ESP32 из любой точки мира

В этом руководстве вы узнаете, как управлять GPIO ESP32 из любой точки мира с помощью Firebase. Мы создадим узлы в Firebase Realtime Database для сохранения текущих состояний GPIO. Всякий раз, когда в узлах базы данных происходит изменение, ESP32 соответствующим образом обновляет свои GPIO. Вы можете изменять состояния GPIO, записывая данные в базу данных вручную, или можете создать веб-приложение для этого (смотрите это руководство).

Управление GPIO ESP32 из любой точки мира с помощью Firebase

Обновлено 26 мая 2025 г.

ЧАСТЬ 2: Управление GPIO ESP32/ESP8266 из любой точки мира (Firebase Web App)

У нас есть аналогичное руководство для платы ESP8266: Firebase: Управление GPIO ESP8266 NodeMCU из любой точки мира.

Другие руководства по Firebase с ESP32/ESP8266, которые могут вас заинтересовать:

Что такое Firebase?

Логотип Firebase

Firebase — это платформа разработки мобильных приложений Google, которая помогает вам создавать, улучшать и развивать ваше приложение. Firebase предоставляет бесплатные сервисы, такие как хостинг (hosting), аутентификация (authentication) и база данных реального времени (realtime database), которые позволяют создать полнофункциональное веб-приложение для управления и мониторинга плат ESP32 и ESP8266, что было бы гораздо сложнее и трудоёмнее создавать и настраивать самостоятельно.

Обзор проекта

На следующей диаграмме показан общий обзор проекта, который мы создадим.

ESP32 Firebase управление выходами из любой точки мира — обзор проекта
  1. ESP32 аутентифицируется как пользователь с email и паролем, чтобы получить доступ к базе данных (этот пользователь должен быть добавлен в методы аутентификации Firebase);

  2. База данных защищена с помощью правил базы данных. Мы добавим следующее правило: только аутентифицированные пользователи могут получить доступ к базе данных;

  3. В базе данных есть несколько узлов, которые сохраняют состояния GPIO ESP32. В качестве примера мы будем управлять тремя GPIO (12, 13 и 14). Вы можете добавлять или удалять узлы для управления большим или меньшим количеством GPIO.

  4. ESP32 будет прослушивать изменения в узлах GPIO базы данных. Всякий раз, когда происходит изменение, он обновляет состояния GPIO соответствующим образом.

  5. Вы можете изменять состояния GPIO вручную в базе данных с помощью консоли Firebase, или вы можете создать веб-страницу (доступную из любой точки мира) с кнопками для управления GPIO и отображения текущих состояний GPIO (смотрите ЧАСТЬ 2).

Вот основные шаги для завершения этого проекта:

  1. Создать проект Firebase

  2. Настроить методы аутентификации

  3. Получить API-ключ проекта

  4. Настроить Realtime Database

  5. Настроить правила безопасности базы данных

  6. Организовать узлы базы данных

  7. ESP32: Прослушивание изменений базы данных (управление GPIO)

Подготовка Arduino IDE

Для этого руководства мы будем программировать плату ESP32 с использованием ядра Arduino. Убедитесь, что у вас установлено дополнение ESP32 в Arduino IDE:

Если вы хотите программировать платы ESP32/ESP8266 с помощью VS Code с расширениями pioarduino или PlatformIO, следуйте этим руководствам:

1) Создание проекта Firebase

Следуйте приведённым ниже инструкциям, чтобы создать новый проект в Firebase.

  1. Перейдите в Firebase и войдите с помощью учётной записи Google.

  2. Перейдите в Firebase Console и создайте новый проект.

  3. Дайте имя вашему проекту, например: ESP-Project, и нажмите Continue.

Настройка проекта Firebase для ESP32 и ESP8266 — Шаг 1
  1. Далее включите или отключите AI-помощника для вашего проекта. Это необязательно.

Настройка проекта Firebase для ESP32 и ESP8266 — Включение AI-помощника
  1. Отключите опцию Enable Google Analytics для этого проекта, так как она не нужна. Затем нажмите Create project.

Отключение Google Analytics для проекта Firebase
  1. Настройка проекта займёт несколько секунд. Нажмите Continue, когда всё будет готово.

Проект Firebase для ESP32 готов
  1. Вы будете перенаправлены на страницу консоли вашего проекта.

Консоль проекта Firebase

2) Настройка методов аутентификации

Чтобы разрешить аутентификацию с помощью email и пароля, сначала необходимо настроить методы аутентификации для вашего приложения.

«Большинству приложений необходимо знать идентификацию пользователя. Другими словами, это заботится о входе в систему и идентификации пользователей (в данном случае ESP32 или ESP8266). Знание идентификации пользователя позволяет приложению безопасно сохранять данные пользователя в облаке и обеспечивать одинаковый персонализированный опыт на всех устройствах пользователя.» Чтобы узнать больше о методах аутентификации, вы можете прочитать документацию.

  1. На левой боковой панели нажмите на Build > Authentication, а затем на Get started.

Настройка аутентификации проекта Firebase
  1. Существует несколько методов аутентификации, таких как email и пароль, учётная запись Google, учётная запись Facebook и другие.

Методы аутентификации Firebase
  1. Выберите Email/Password и включите этот метод аутентификации. Затем нажмите Save.

Включение входа по Email/Password в Firebase
  1. Затем вверху нажмите на вкладку Users. После этого нажмите Add user.

Firebase — Создание нового пользователя
  1. Создайте нового пользователя с email и паролем. Email может быть вашим личным. Создайте пароль для этого пользователя (вам нужно будет запомнить пароль позже). Наконец, нажмите Add user.

Firebase — Добавление пользователя с email и паролем
  1. Пользователь появится в списке пользователей. Вы можете увидеть информацию о пользователе, например, когда он был создан, когда последний раз входил в систему, и его User UID.

Пользователь Firebase создан

Firebase создаёт уникальный UID для каждого зарегистрированного пользователя. User UID позволяет нам идентифицировать пользователя и отслеживать его для предоставления или отказа в доступе к проекту или базе данных. Также есть столбец, который регистрирует дату последнего входа. На данный момент он пуст, потому что мы ещё не входили с этим пользователем.

Скопируйте User UID, потому что он понадобится вам позже.

3) Получение API-ключа проекта

Для взаимодействия с вашим проектом Firebase с помощью плат ESP32 или ESP8266 вам нужно получить API-ключ вашего проекта. Следуйте приведённым ниже шагам, чтобы получить API-ключ вашего проекта.

  1. Чтобы получить API-ключ вашего проекта, на левой боковой панели нажмите Project Settings.

Настройки проекта Firebase Realtime Database
  1. Скопируйте API Key в безопасное место, потому что он понадобится вам позже.

API-ключ проекта Firebase

4) Настройка Realtime Database

Теперь давайте создадим базу данных реального времени и настроим правила базы данных для нашего проекта.

1) На левой боковой панели нажмите Realtime Database, а затем нажмите Create Database.

Создание базы данных проекта Firebase

2) Выберите расположение вашей базы данных. Оно должно быть ближайшим к вашему местоположению.

Настройка расположения Firebase Realtime Database

3) Настройте правила безопасности для вашей базы данных. Вы можете выбрать Start in test mode. Мы изменим правила базы данных чуть позже.

Firebase Realtime Database — Запуск в тестовом режиме

4) Ваша база данных создана. Вам нужно скопировать и сохранить URL базы данных — выделенный на следующем изображении — потому что он понадобится вам позже в коде ESP32/ESP8266.

URL Firebase Realtime Database

5) Настройка правил безопасности базы данных

Теперь давайте настроим правила базы данных. На вкладке Realtime Database выберите вкладку Rules вверху. Затем нажмите Edit rules и добавьте следующие правила.

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

Вставьте UID пользователя, которого вы создали ранее. Затем нажмите Publish.

Эти правила базы данных определяют, что:

  • Только пользователь с этим конкретным UID может читать и записывать данные в базу данных (изменять состояния GPIO).

Добавление дополнительных пользователей

Чтобы добавить больше пользователей, просто перейдите на вкладку Authentication и нажмите Add user. Добавьте email и пароль для нового пользователя, и наконец нажмите Add user, чтобы создать пользователя.

Firebase — Добавление нового пользователя с Email и паролем через консоль

Скопируйте User UID этого нового пользователя и добавьте его в правила базы данных следующим образом:

{
  "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'"
  }
}

Например. В моём случае UID пользователей: RjO3taAzMMXB82Xmir2LQ7XXXXXX и 9QdDc9as5mRXGAjEsQiUJkXXXXXX. Таким образом, правила базы данных будут выглядеть следующим образом:

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

Наконец, опубликуйте ваши правила базы данных.

Чтобы узнать больше о правилах базы данных, вы можете ознакомиться с документацией Firebase.

6) Организация узлов базы данных

Все данные, хранящиеся в Firebase Realtime Database, хранятся как JSON-объекты. Таким образом, вы можете думать о базе данных как об облачном JSON-дереве. Когда вы добавляете данные в JSON-дерево, они становятся узлом с ассоциированным ключом в существующей структуре JSON.

Не знакомы с JSON? Прочитайте это краткое руководство.

Мы хотим управлять GPIO ESP32. Мы можем организовать данные таким образом, чтобы было легко добавлять больше GPIO и плат в будущем. Итак, мы можем структурировать базу данных следующим образом:

  • board1: - outputs:

    • digital: - 12: 0 - 13: 0 - 14: 0

В формате JSON это будет выглядеть так:

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

Создание узлов базы данных

Теперь давайте создадим узлы базы данных в нашей базе данных. Вы можете создать узлы вручную, записывая их в консоли Firebase, в веб-приложении или через ESP32. Мы создадим их вручную, чтобы было проще следовать руководству.

1) Нажмите на Realtime Database, чтобы начать создание узлов.

Меню Firebase Realtime Database

2) Вы можете создавать узлы базы данных вручную, используя значки (+) в базе данных. Однако, чтобы избежать опечаток, мы предоставляем JSON-файл, который вы можете загрузить для создания тех же узлов, что и наши. Нажмите на ссылку ниже, чтобы скачать JSON-файл.

После скачивания распакуйте папку, чтобы получить доступ к файлу .json.

3) Теперь вернитесь к вашей базе данных в консоли Firebase. Нажмите на значок с тремя точками и выберите Import JSON.

Firebase Realtime Database — Импорт JSON

4) Выберите JSON-файл, который вы только что скачали.

5) Ваша база данных должна выглядеть так, как показано ниже.

Пример структуры Firebase Realtime Database

Все узлы базы данных, необходимые для этого проекта, созданы. Вы можете перейти к следующему разделу.

7) ESP32: Прослушивание изменений базы данных (управление GPIO)

В этом разделе мы запрограммируем плату ESP32 для выполнения следующих задач:

  1. Аутентификация как пользователь с email и паролем (пользователь, которого вы настроили в разделе аутентификации);

  2. Прослушивание изменений базы данных в узлах GPIO и соответствующее изменение их состояний.

Необходимые компоненты

Для этого проекта вам понадобятся следующие компоненты:

Схема подключения

В этом примере мы будем управлять тремя светодиодами, подключёнными к GPIO 12, 13 и 14. Подключите три светодиода к ESP32. Вы можете следовать схеме подключения ниже.

Схема подключения ESP32 с тремя светодиодами

Вы можете использовать любые другие подходящие GPIO ESP32, но вам также нужно будет изменить узлы базы данных.

Установка библиотек

Для этого руководства вам нужно установить библиотеку FirebaseClient.

Установка — Arduino IDE

Следуйте этому разделу, если вы используете Arduino IDE.

Перейдите в Sketch > Include Library > Manage Libraries, найдите название библиотеки и установите её.

Установка библиотеки Firebase Client в Arduino IDE

Теперь вы готовы начать программирование плат ESP32 и ESP8266 для взаимодействия с базой данных.

Установка библиотек — VS Code

Следуйте этим инструкциям, если вы используете VS Code с расширением PlatformIO или pioarduino.

Установка библиотеки FirebaseClient

Нажмите на значок PIO Home и выберите вкладку Libraries. Найдите «FirebaseClient». Выберите FirebaseClient Library от Mobitz.

Установка библиотеки FirebaseClient в VS Code

Затем нажмите Add to Project и выберите проект, над которым вы работаете.

Добавление библиотеки FirebaseClient в проект в VS Code

Также измените скорость монитора на 115200, добавив следующую строку в файл platformio.ini вашего проекта:

monitor_speed = 115200

Код прослушивания изменений базы данных (состояния GPIO)

Скопируйте следующий код в Arduino IDE или в файл main.cpp, если вы используете VS Code.

Вам нужно вставить следующие данные в код перед загрузкой на вашу плату:

  • ваши сетевые учётные данные

  • API-ключ проекта

  • URL базы данных

  • email и пароль авторизованного пользователя

/*
  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/FirebaseClient/blob/main/examples/RealtimeDatabase/Get/Get.ino
*/
#define ENABLE_USER_AUTH
#define ENABLE_DATABASE

#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());
    }
  }
}

Посмотреть исходный код

Как работает код

Продолжайте чтение, чтобы узнать, как работает код, или перейдите к разделу демонстрации.

Подключение библиотек

Сначала подключите необходимые библиотеки. Этот код также совместим с 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 and Firebase credentials
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

API-ключ проекта Firebase, пользователь Firebase и URL базы данных

Вставьте API-ключ вашего проекта Firebase — тот, который вы получили в разделе получения API-ключа.

#define Web_API_KEY "REPLACE_WITH_YOUR_FIREBASE_PROJECT_API_KEY"

Вставьте URL вашей базы данных в следующую строку:

#define DATABASE_URL "REPLACE_WITH_YOUR_FIREBASE_DATABASE_URL"

Вставьте авторизованный email и соответствующий пароль — это данные пользователя, которого вы добавили в разделе настройки аутентификации.

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

Объявление аутентификации и компонентов Firebase

Следующая строка создаёт объект аутентификации, используя API-ключ проекта, email пользователя проекта и пароль.

UserAuth user_auth(Web_API_KEY, USER_EMAIL, USER_PASS);

Создайте два SSL-клиента. Один для обработки операций Firebase, а другой для потоковой передачи базы данных (необходим для прослушивания изменений базы данных).

SSL_CLIENT ssl_client, stream_ssl_client;

Это создаёт экземпляр FirebaseApp под названием app, который ссылается на приложение Firebase.

FirebaseApp app;

Следующие строки настраивают фреймворк асинхронной связи для взаимодействия с Firebase Realtime Database. Вам нужно создать экземпляр асинхронного клиента aClient, который обеспечивает безопасный HTTPS для обработки операций Firebase, и другой — streamClient — для обработки потоковой передачи базы данных (прослушивание изменений базы данных). Это позволит вам обрабатывать сетевые операции и потоковую передачу базы данных асинхронно.

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

Следующая строка создаёт объект RealtimeDatabase под названием Database, который представляет Firebase Realtime Database.

RealtimeDatabase Database;

Путь базы данных

Затем создайте переменную, которая сохраняет путь базы данных, по которому мы будем прослушивать изменения. Учитывая структуру базы данных, которую мы создали ранее, путь прослушивания должен быть следующим:

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

Если вы хотите добавить больше плат, вам просто нужно изменить путь прослушивания соответствующим образом.

Создайте переменные для выходов, которыми вы будете управлять. В нашем случае мы управляем GPIO 12, 13 и 14. Вы можете управлять любыми другими GPIO ESP32 (вам также нужно будет изменить узлы базы данных):

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

initWifi()

Функция initWifi() подключает ESP32 к вашей локальной сети. Мы вызовем её позже в setup() для инициализации 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()

Давайте теперь перейдём к setup().

Инициализируйте Serial Monitor:

Serial.begin(115200);

Объявите ваши GPIO как выходы с помощью функции pinMode().

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

Вызовите функцию initWiFi(), которую мы создали ранее, чтобы подключить вашу плату к локальной сети.

initWiFi();

Настройте SSL-клиент.

// 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

Следующая строка инициализирует приложение Firebase с аутентификацией и устанавливает processData() в качестве функции обратного вызова для асинхронных результатов (это означает, что любые результаты функции initializeApp() будут обработаны в функции обратного вызова processData()).

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

Затем укажите, что вы хотите установить объект Database, определённый ранее, как базу данных для нашего приложения Firebase.

app.getApp<RealtimeDatabase>(Database);

Наконец, установите URL базы данных.

Database.url(DATABASE_URL);

Потоковая передача базы данных — Прослушивание изменений

Теперь нам нужно установить прослушиватель базы данных, чтобы мы получали данные на ESP32 при изменении базы данных.

Следующая строка настраивает типы событий Server-Sent Events (SSE), которые streamClient будет прослушивать при подключении к Firebase Realtime Database. В данном случае мы включаем все типы событий.

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

Наконец, следующая строка инициирует потоковое подключение к Firebase Realtime Database по указанному listenerPath, используя streamClient для прослушивания обновлений в реальном времени. Когда происходят события, они обрабатываются функцией обратного вызова processData() (определённой далее в коде).

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

loop()

Функция loop() пуста и только выводит, как долго работает программа (это необязательно). Вам просто нужно поддерживать команду app.loop().

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 — Обработка изменений базы данных

Функция processData() будет обрабатывать все события, связанные с операциями Firebase, включая изменения данных в базе данных.

Мы можем проверить, есть ли новые данные в базе данных, используя команду aResult.available().

if (aResult.available()){

Затем мы преобразуем результат в формат, к которому можно получить доступ в нашем коде — переменную типа RealtimeDatabaseResult.

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

Мы проверяем, получены ли данные от потокового клиента.

if (RTDB.isStream()) {

Затем мы можем вывести доступные данные о событии.

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() сообщает нам, какой тип данных мы получили из базы данных. Результаты могут быть:

  • realtime_database_data_type_undefined или -1

  • realtime_database_data_type_null или 0

  • realtime_database_data_type_integer или 1

  • realtime_database_data_type_float или 2

  • realtime_database_data_type_double или 3

  • realtime_database_data_type_boolean или 4

  • realtime_database_data_type_string или 5

  • realtime_database_data_type_json или 6

  • realtime_database_data_type_array или 7

Когда ESP впервые подключается к базе данных, он срабатывает по корневому (/) пути и возвращает JSON-объект со всеми дочерними узлами (RTDB.type() = 6). Таким образом, мы можем получить все значения из базы данных и обновить GPIO ESP32 при первом запуске. Это также полезно, потому что если ESP32 перезагрузится, он всегда сначала получит этот JSON-объект и сможет обновить все GPIO.

ESP32 Потоковая передача базы данных — Первый запуск

Как видно из предыдущего скриншота, JSON-объект, который он получает, выглядит следующим образом (он может отличаться в зависимости от состояний GPIO):

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

Когда это происходит, полученные данные имеют тип JSON. Мы можем получить их и преобразовать в JSON-переменную doc следующим образом:

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;
    }

Затем мы можем перебрать JSON-объект и получить ключи (GPIO) и соответствующие значения (состояния GPIO). В каждой итерации мы сохраняем GPIO в переменную gpioPin и его соответствующее состояние в переменную state. Затем мы вызываем функцию digitalWrite() для обновления его состояния.

// 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);
}

Это проходит через все ключи и значения, позволяя нам обновить все GPIO.

Если RTDB.type() равен 4 или 1, это означает, что мы вставили в базу данных логическое или целочисленное значение. В этом случае мы получаем путь базы данных, который соответствует номеру GPIO, и вставленные данные, которые соответствуют состоянию GPIO.

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

После получения этих данных мы можем обновить состояние GPIO в соответствии с изменениями в базе данных.

digitalWrite(GPIO_number, state);

Демонстрация

После вставки всех необходимых учётных данных загрузите код на вашу плату.

После загрузки откройте Serial Monitor на скорости 115200 бод и перезагрузите плату. Вы должны увидеть что-то вроде показанного ниже.

ESP32 Потоковая передача базы данных — Первый запуск

Как видите, при первом запуске ESP получает JSON-объект со всеми состояниями GPIO.

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

Затем перейдите в Firebase Realtime Database в консоли Firebase. Вручную измените состояния GPIO (0 или 1). После ввода нового значения нажмите Enter.

Firebase Realtime Database — Сохранение состояний GPIO ESP

Сразу после этого вы увидите в Serial Monitor, что ESP32 обнаружил изменения.

Firebase Потоковая передача базы данных — ESP обнаруживает изменения в Serial Monitor

И он обновит состояния GPIO и зажжёт светодиоды практически мгновенно.

Управление выходами ESP32 из любой точки мира

Затем, если вы перезагрузите плату (нажмите кнопку RST или отключите и подключите питание снова), при перезапуске она получит последние состояния GPIO из базы данных и сразу обновит их.

Развитие проекта — Добавление дополнительных плат

Вы можете развить этот проект далее и добавить больше плат. Для этого создайте новые узлы базы данных для второй платы. Вы можете добавить платы ESP32 или ESP8266.

Вы можете скачать следующий JSON-файл и импортировать его в вашу базу данных, и он создаст узлы для двух плат:

После загрузки JSON-файла база данных будет выглядеть следующим образом:

Firebase Realtime Database — Сохранение состояний GPIO нескольких плат

Теперь вы можете загрузить тот же код на новую плату (он совместим с ESP32 и ESP8266). Но не забудьте изменить путь прослушивания. Он должен быть:

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

Теперь вы можете управлять обеими платами, изменяя состояния GPIO в базе данных.

Управление выходами ESP32 и ESP8266 из любой точки мира — Несколько плат

В Части 2 мы создадим веб-приложение Firebase, чтобы у вас был удобный интерфейс для управления вашими GPIO из любой точки мира без необходимости использовать консоль Firebase и вручную изменять базу данных:

Заключение

В этом руководстве вы узнали, как использовать Firebase Realtime Database для сохранения состояний GPIO ESP. Вы также научились программировать ESP32 для прослушивания изменений базы данных. Всякий раз, когда обнаруживается изменение, мы обновляем соответствующие состояния GPIO. Вы можете изменить код так, чтобы ESP прослушивал любые другие данные, сохранённые в базе данных, а не только состояния GPIO. Поскольку к Firebase Realtime Database можно получить доступ из любой точки мира, вы можете управлять своими платами из любой точки мира. Это отлично подходит для IoT-проектов.

В Части 2 мы создадим веб-приложение для управления вашими GPIO из любой точки мира, без необходимости входить вручную в консоль Firebase:

Узнайте больше об ESP32 с нашими ресурсами:

Примечание

Данная статья является переводом материала с сайта Random Nerd Tutorials.