Управление GPIO ESP32/ESP8266 из любой точки мира (Firebase Web App)
В этом руководстве вы создадите Firebase веб-приложение для управления GPIO ESP32 или ESP8266 из любой точки мира. Доступ к веб-приложению защищен аутентификацией по email и паролю. Состояния GPIO сохраняются в Firebase Realtime Database. Веб-приложение записывает данные в базу для изменения состояний GPIO, а платы ESP прослушивают изменения в базе данных для соответствующего обновления состояний GPIO.
Обновлено 12 июня 2025
Эта статья является Частью 2 предыдущего руководства. Перед продолжением вам необходимо выполнить одно из следующих руководств:
Обзор проекта
В этом руководстве (Часть 2) вы создадите веб-приложение для управления GPIO ESP32 или ESP8266 из любой точки мира. В предыдущем руководстве вы узнали, как настроить ESP32 или ESP8266 для прослушивания изменений в базе данных и соответствующего обновления GPIO. Вы изменяли состояния GPIO вручную в Realtime Database через консоль Firebase. Теперь вы создадите собственное веб-приложение, размещенное на Firebase, для управления вашими платами из любой точки мира.
Следующая диаграмма показывает общий обзор проекта, который мы создадим — программирование ESP32/ESP8266 и настройка проекта Firebase были выполнены в Части 1:
Firebase размещает ваше веб-приложение через глобальную CDN с использованием Firebase Hosting и предоставляет SSL-сертификат. Вы можете получить доступ к вашему веб-приложению из любой точки мира, используя доменное имя, сгенерированное Firebase.
При первом доступе к веб-приложению вам необходимо пройти аутентификацию с авторизованным email-адресом и паролем. Вы уже настроили этого пользователя и метод аутентификации в Части 1.
После аутентификации вы получите доступ к странице веб-приложения, которая показывает несколько кнопок для изменения состояний GPIO в базе данных.
ESP32 или ESP8266 прослушивает изменения в базе данных. Когда вы нажимаете на кнопки, состояния GPIO изменяются в базе данных, и ESP обновляет состояния соответственно.
Веб-приложение также показывает текущее состояние GPIO.
В качестве примера мы будем управлять тремя GPIO (12, 13 и 14). Как упоминалось в предыдущем руководстве, вы можете добавлять/удалять больше GPIO и плат или управлять другими GPIO.
После входа в систему вы можете выйти в любое время. В следующий раз при доступе к приложению вам нужно будет войти снова.
Следующий скриншот показывает, как выглядит веб-страница в браузере компьютера.
Предварительные требования
Прежде чем приступить к созданию Firebase Web App, необходимо проверить следующие предварительные требования.
Создание проекта Firebase
Сначала вы должны были выполнить одно из следующих руководств:
ESP32/ESP8266 должен работать с кодом, предоставленным в этом руководстве. Realtime Database и аутентификация также должны быть настроены, как показано в руководстве.
Установка необходимого программного обеспечения
Перед началом работы вам необходимо установить необходимое программное обеспечение для создания Firebase Web App. Вот список программного обеспечения, которое нужно установить (нажмите на ссылки для получения инструкций):
1) Добавление приложения в проект Firebase
1) Перейдите в консоль вашего проекта Firebase и добавьте приложение в ваш проект, нажав кнопку +Add app.
2) Выберите значок веб-приложения.
3) Дайте вашему приложению имя. Затем установите галочку рядом с Also set up Firebase Hosting for this App. Нажмите Register app.
4) Затем скопируйте объект firebaseConfig и сохраните его, потому что он понадобится вам позже.
После этого вы также можете получить доступ к объекту firebaseConfig, если перейдете в настройки проекта в консоли Firebase.
5) Нажмите Next на последующих шагах и наконец Continue to console.
2) Настройка проекта Firebase Web App (VS Code)
Следуйте следующим шагам для создания проекта Firebase Web App с использованием VS Code.
1) Создание папки проекта
1) Создайте папку на вашем компьютере, где вы хотите сохранить ваш проект Firebase — например, Firebase-Project на Рабочем столе.
2) Откройте VS Code. Перейдите в File > Open Folder… и выберите папку, которую вы только что создали.
3) Перейдите в Terminal > New Terminal. Должно открыться новое окно терминала в пути вашего проекта.
2) Вход в Firebase
4) В предыдущем окне терминала введите следующее:
firebase login
5) Вас спросят о сборе информации об использовании CLI и отчетах об ошибках. Введите «n» и нажмите Enter для отказа.
Примечание: Если вы уже вошли в систему, появится сообщение: «Already logged in as user@gmail.com».
6) После этого откроется новое окно в вашем браузере для входа в вашу учетную запись Firebase.
7) Разрешите Firebase CLI доступ к вашей учетной записи Google.
8) После этого вход в Firebase CLI должен быть успешным. Вы можете закрыть окно браузера.
3) Инициализация проекта Firebase Web App
9) После успешного входа выполните следующую команду для запуска директории проекта Firebase в текущей папке.
firebase init
10) Вас спросят, хотите ли вы инициализировать проект Firebase в текущей директории. Введите Y и нажмите Enter.
11) Затем используйте стрелки вверх и вниз и клавишу пробела для выбора опций. Выберите следующие опции:
Realtime Database: Configure security rules file for Realtime Database and (optionally) provision default instance.
Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
Выбранные опции будут отмечены зеленой звездочкой. Затем нажмите Enter.
12) Выберите опцию «Use an existing project» — она должна быть выделена синим — затем нажмите Enter.
13) После этого выберите проект Firebase для этой директории — это должен быть проект, созданный в Части 1. Затем нажмите Enter.
14) Затем выберите опции хостинга, как показано ниже:
What do you want to use as your public directory? Нажмите Enter для выбора public.
Configure as a single-page app (rewrite urls to /index.html)? No
Set up automatic builds and deploys with GitHub? No
15) Нажмите Enter на следующий вопрос для выбора файла правил безопасности базы данных по умолчанию: «What file should be used for Realtime Database Security Rules?»
16) Проект Firebase теперь должен быть успешно инициализирован. Обратите внимание, что VS Code создал некоторые необходимые файлы в папке вашего проекта.
Файл index.html содержит HTML-текст для создания веб-страницы. Пока оставьте HTML-текст по умолчанию. Идея состоит в том, чтобы заменить его собственным HTML-текстом для создания пользовательской веб-страницы для ваших нужд. Мы сделаем это позже в этом руководстве.
17) Чтобы проверить, все ли прошло как ожидалось, выполните следующую команду в окне терминала VS Code.
firebase deploy
Вы должны получить сообщение Deploy complete! и URL-адрес консоли проекта и URL-адрес хостинга.
18) Скопируйте URL-адрес хостинга и вставьте его в окно веб-браузера. Вы должны увидеть следующую веб-страницу. Вы можете получить доступ к этой веб-странице из любой точки мира.
Веб-страница, которую вы видели ранее, построена с помощью HTML-файла, расположенного в папке public вашего проекта Firebase. Изменяя содержимое этого файла, вы можете создать собственное веб-приложение. Именно это мы и будем делать в следующем разделе.
3) Создание Firebase Web App
Теперь, когда вы успешно создали проект Firebase приложения в VS Code, следуйте следующим шагам для настройки приложения для отображения показаний датчиков на защищенной входом веб-странице.
index.html
Скопируйте следующее в ваш файл index.html. Этот HTML-файл создает простую веб-страницу с кнопками ON и OFF для управления GPIO 12, 13 и 14.
Если вы не аутентифицированы, отображается форма входа. Когда вы аутентифицируетесь с авторизованным email пользователя и соответствующим паролем, отображается пользовательский интерфейс с кнопками.
<!DOCTYPE html>
<!-- Complete Project Details at: https://RandomNerdTutorials.com/ -->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP IoT Firebase App</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="topnav">
<h1>ESP GPIO Control <i class="fas fa-lightbulb"></i></h1>
</div>
<div id="authentication-bar" style="display: none;">
<p><span id="authentication-status">User logged in</span>
<span id="user-details">USEREMAIL</span>
<a href="/" id="logout-link">(logout)</a>
</p>
</div>
<form id="login-form" style="display: none;">
<div class="form-elements-container">
<label for="input-email"><b>Email</b></label>
<input type="text" placeholder="Enter Username" id="input-email" required>
<label for="input-password"><b>Password</b></label>
<input type="password" placeholder="Enter Password" id="input-password" required>
<button type="submit" id="login-button">Login</button>
<p id="error-message" style="color:red;"></p>
</div>
</form>
<div class="content-sign-in" id="content-sign-in" style="display: none;">
<div class="card-grid">
<div class="card">
<p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 12</p>
<p>
<button class="button-on" id="btn1On">ON</button>
<button class="button-off" id="btn1Off">OFF</button>
</p>
<p class="state">State:<span id="state1"></span></p>
</div>
<div class="card">
<p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 13</p>
<p>
<button class="button-on" id="btn2On">ON</button>
<button class="button-off" id="btn2Off">OFF</button>
</p>
<p class="state">State:<span id="state2"></span></p>
</div>
<div class="card">
<p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 14</p>
<p>
<button class="button-on" id="btn3On">ON</button>
<button class="button-off" id="btn3Off">OFF</button>
</p>
<p class="state">State:<span id="state3"></span></p>
</div>
</div>
</div>
<script type="module" src="scripts/index.js"></script>
<script type="module" src="scripts/auth.js"></script>
</body>
</html>
Как это работает
Давайте кратко рассмотрим HTML-файл, или перейдите к следующему разделу.
В секции <head> HTML-файла мы должны добавить все необходимые метаданные.
Заголовок веб-страницы — ESP Firebase App, но вы можете изменить его в следующей строке.
<title>ESP Firebase App</title>
Следующая строка позволяет нам использовать иконки fontawesome:
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
Следующая строка включает favicon на нашей веб-странице.
<link rel="icon" type="image/png" href="favicon.png">
Наконец, ссылка на внешний файл style.css для форматирования HTML-страницы.
<link rel="stylesheet" type="text/css" href="style.css">
Мы закончили с метаданными. Теперь давайте перейдем к частям HTML, которые видны пользователю — между тегами <body> и </body>.
Мы создаем верхнюю «навигационную» панель с названием нашего приложения и маленькой иконкой из fontawesome.
<!--TOP BAR-->
<div class="topnav">
<h1>ESP GPIO Control <i class="fas fa-lightbulb"></i></h1>
</div>
Следующие строки создают панель с данными аутентифицированного пользователя (email). Также отображается ссылка logout для выхода пользователя из системы.
<div id="authentication-bar" style="display: none;">
<p><span id="authentication-status">User logged in</span>
<span id="user-details">USEREMAIL</span>
<a href="/" id="logout-link">(logout)</a>
</p>
</div>
Сначала мы устанавливаем стиль отображения всех элементов на none. Мы будем скрывать и показывать контент в зависимости от того, аутентифицирован пользователь или нет — мы обработаем это с помощью JavaScript.
Далее следующие строки создают форму входа с полем ввода email и полем ввода пароля:
<form id="login-form" style="display: none;">
<div class="form-elements-container">
<label for="input-email"><b>Email</b></label>
<input type="text" placeholder="Enter Username" id="input-email" required>
<label for="input-password"><b>Password</b></label>
<input type="password" placeholder="Enter Password" id="input-password" required>
<button type="submit" id="login-button">Login</button>
<p id="error-message" style="color:red;"></p>
</div>
</form>
Внутри формы также есть параграф для отображения сообщения об ошибке, если вход не удался.
<p id="error-message" style="color:red;"></p>
Наконец, мы создаем сетку для отображения различных карточек для GPIO.
<div class="content-sign-in" id="content-sign-in" style="display: none;">
<div class="card-grid">
Например, следующие строки создают карточку для GPIO 12:
<div class="card">
<p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 12</p>
<p>
<button class="button-on" id="btn1On">ON</button>
<button class="button-off" id="btn1Off">OFF</button>
</p>
<p class="state">State:<span id="state1"></span></p>
</div>
Кнопки имеют определенные id, чтобы мы могли ссылаться на них позже в файлах JavaScript. Также есть тег span с определенным id для вставки состояния GPIO.
Создание карточек для других GPIO аналогично:
<div class="card">
<p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 13</p>
<p>
<button class="button-on" id="btn2On">ON</button>
<button class="button-off" id="btn2Off">OFF</button>
</p>
<p class="state">State:<span id="state2"></span></p>
</div>
<div class="card">
<p class="card-title"><i class="fas fa-lightbulb"></i> GPIO 14</p>
<p>
<button class="button-on" id="btn3On">ON</button>
<button class="button-off" id="btn3Off">OFF</button>
</p>
<p class="state">State:<span id="state3"></span></p>
</div>
Важно помнить id каждого из этих элементов, чтобы было проще идентифицировать их в JavaScript-файле. Вы можете использовать любые другие id, которые имеют смысл для вашего проекта.
GPIO 12 |
GPIO 13 |
GPIO 14 |
|
|---|---|---|---|
Кнопка ON |
btn1On |
btn2On |
btn3On |
Кнопка OFF |
btn1Off |
btn2Off |
btn3Off |
Состояние |
state1 |
state2 |
state3 |
Наконец, нам нужно добавить ссылки на внешние файлы JavaScript. Для нашего приложения мы создадим два файла JavaScript: auth.js (который обрабатывает все, связанное с аутентификацией) и index.js, который обрабатывает все, связанное с пользовательским интерфейсом. Мы создадим эти файлы внутри папки scripts внутри папки public нашего приложения.
<script type="module" src="scripts/index.js"></script>
<script type="module" src="scripts/auth.js"></script>
style.css
Внутри папки public создайте файл с именем style.css. Чтобы создать файл, выберите папку public, а затем нажмите на значок +file в верхней части Проводника файлов. Назовите его style.css.
Затем скопируйте следующее в файл style.css
html {
font-family: Verdana, Geneva, Tahoma, sans-serif;
display: inline-block;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
.topnav {
overflow: hidden;
background-color: #049faa;
color: white;
font-size: 1rem;
padding: 10px;
}
#authentication-bar{
background-color:mintcream;
padding-top: 10px;
padding-bottom: 10px;
}
#user-details{
color: cadetblue;
}
.content {
padding: 20px;
}
body {
margin: 0;
}
.card-grid {
max-width: 800px;
margin: 0 auto;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.card {
background-color: white;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
}
.card-title {
font-size: 1.2rem;
color: #034078
}
.state {
color:#1282A2;
}
button {
background-color: #049faa;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
border-radius: 4px;
}
.button-on {
background-color:#034078;
}
.button-on:hover {
background-color: #1282A2;
}
.button-off {
background-color:#858585;
}
.button-off:hover {
background-color: #252524;
}
.form-elements-container{
padding: 16px;
width: 250px;
margin: 0 auto;
}
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
CSS-файл включает некоторые простые стили для улучшения внешнего вида нашей веб-страницы. Мы не будем обсуждать, как работает CSS, в этом руководстве. Вы можете легко изменить CSS-файл для изменения цветов и размера шрифта, например.
Файлы JavaScript
Мы создадим два файла JavaScript (auth.js и index.js) внутри папки scripts внутри папки public.
Выберите папку public, затем нажмите на значок +folder для создания новой папки. Назовите эту новую папку scripts.
Затем выберите папку scripts и нажмите на значок +file. Создайте файл с именем auth.js. Затем повторите предыдущие шаги для создания index.js.
Следующее изображение показывает, как должна выглядеть структура папок вашего проекта веб-приложения.
auth.js
Теперь давайте реализуем вход пользователя с использованием аутентификации Firebase. Мы реализуем вход с помощью email и пароля.
Скопируйте следующее в файл auth.js, который вы создали ранее.
import { auth, setupUI } from "./index.js";
import { signInWithEmailAndPassword, signOut, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-auth.js";
document.addEventListener("DOMContentLoaded", () => {
// Listen for auth status changes
onAuthStateChanged(auth, (user) => {
if (user) {
console.log("User logged in:", user.email);
setupUI(user);
} else {
console.log("User logged out");
setupUI(null);
}
});
// Login
const loginForm = document.querySelector('#login-form');
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const email = loginForm['input-email'].value;
const password = loginForm['input-password'].value;
try {
await signInWithEmailAndPassword(auth, email, password);
loginForm.reset();
console.log("Logged in:", email);
} catch (error) {
document.getElementById("error-message").textContent = error.message;
console.error("Login error:", error.message);
}
});
// Logout
const logoutLink = document.querySelector('#logout-link');
logoutLink.addEventListener('click', async (e) => {
e.preventDefault();
try {
await signOut(auth);
console.log("User signed out");
} catch (error) {
console.error("Logout error:", error.message);
}
});
});
Затем сохраните файл. Этот файл заботится обо всем, что связано с входом и выходом пользователя. Продолжайте чтение, чтобы узнать, как работает код, или перейдите к следующему разделу.
Сначала мы импортируем необходимые функции и модули из файла index.js (который мы создадим позже) и из JavaScript-библиотеки Firebase.
import { auth, setupUI } from "./index.js";
import { signInWithEmailAndPassword, signOut, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-auth.js";
Вход (Login)
Следующие строки отвечают за вход пользователя в систему.
// Login
const loginForm = document.querySelector('#login-form');
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const email = loginForm['input-email'].value;
const password = loginForm['input-password'].value;
try {
await signInWithEmailAndPassword(auth, email, password);
loginForm.reset();
console.log("Logged in:", email);
} catch (error) {
document.getElementById("error-message").textContent = error.message;
console.error("Login error:", error.message);
}
});
Мы создаем переменную, которая ссылается на HTML-элемент формы входа, под названием loginForm.
const loginForm = document.querySelector('#login-form');
Если вы вернетесь к файлу index.html, вы увидите, что форма имеет id login-form.
Мы добавляем обработчик события типа submit к форме. Это означает, что последующие инструкции будут выполняться каждый раз при отправке формы.
loginForm.addEventListener('submit', (e) => {
Вы можете получить отправленные данные следующим образом.
const email = loginForm['input-email'].value;
const password = loginForm['input-password'].value;
Если вы вернетесь к HTML-файлу, вы увидите, что поля ввода содержат следующие id: input-email и input-password для email и пароля соответственно.
Теперь, когда у нас есть введенные email и пароль, мы можем попробовать войти в Firebase. Для этого передайте email и пароль пользователя в следующий метод: signInWithEmailAndPassword:
await signInWithEmailAndPassword(auth, email, password);
После входа мы сбрасываем форму и выводим email пользователя в консоль.
try {
await signInWithEmailAndPassword(auth, email, password);
loginForm.reset();
console.log("Logged in:", email);
}
В случае ошибки входа мы перехватываем сообщение об ошибке и отображаем его в HTML-элементе error-message (параграф под формой).
} catch (error) {
document.getElementById("error-message").textContent = error.message;
console.error("Login error:", error.message)
Выход (Logout)
Следующий фрагмент отвечает за выход пользователя из системы.
// Logout
const logoutLink = document.querySelector('#logout-link');
logoutLink.addEventListener('click', async (e) => {
e.preventDefault();
try {
await signOut(auth);
console.log("User signed out");
} catch (error) {
console.error("Logout error:", error.message);
}
});
Когда пользователь вошел в систему, ссылка logout видна на панели аутентификации. Эта ссылка имеет id logout-link (смотрите в HTML-файле). Итак, сначала мы создаем переменную logoutLink, которая ссылается на ссылку выхода.
const logoutLink = document.querySelector('#logout-link');
Затем мы добавляем обработчик события типа click. Это означает, что последующие инструкции будут выполняться каждый раз при нажатии на ссылку выхода.
logoutLink.addEventListener('click', async (e) => {
При нажатии кнопки мы выполняем выход пользователя с помощью метода signOut.
await signOut(auth);
Изменения состояния аутентификации
Для отслеживания состояния аутентификации пользователя — чтобы знать, вошел пользователь в систему или вышел — существует метод onAuthStateChanged, который позволяет получать событие при каждом изменении состояния аутентификации.
document.addEventListener("DOMContentLoaded", () => {
// Listen for auth status changes
onAuthStateChanged(auth, (user) => {
if (user) {
console.log("User logged in:", user.email);
setupUI(user);
} else {
console.log("User logged out");
setupUI(null);
}
})
Если возвращаемый user равен null, пользователь в данный момент вышел из системы. В противном случае он в данный момент вошел в систему.
В обоих сценариях мы выводим текущее состояние пользователя в консоль и вызываем функцию setupUI(). Мы еще не создали эту функцию (мы создадим ее в следующем разделе), но она будет отвечать за обработку пользовательского интерфейса в соответствии с состоянием аутентификации.
Когда пользователь вошел в систему, мы передаем user в качестве аргумента функции setupUI(). В этом случае мы отобразим полный пользовательский интерфейс с кнопками для управления GPIO, как вы увидите позже.
if (user) {
console.log("User logged in:", user.email);
setupUI(user);
Если пользователь вышел из системы, мы вызываем функцию setupUI() с аргументом null. В этом сценарии мы просто отобразим сообщение о том, что пользователь вышел из системы и не имеет доступа к интерфейсу (как мы увидим позже).
} else {
console.log("User logged out");
setupUI(null);
index.js
Файл index.js обрабатывает пользовательский интерфейс — он показывает правильное содержимое в зависимости от статуса аутентификации пользователя. Когда пользователь вошел в систему, он получает состояния GPIO из базы данных и обновляет состояния GPIO в интерфейсе. Затем он изменяет состояния при нажатии кнопок.
Скопируйте следующее в файл index.js.
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-app.js";
import { getAuth } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-auth.js";
import { getDatabase, ref, onValue, set } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-database.js";
// Firebase configuration
const firebaseConfig = {
apiKey: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
authDomain: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
databaseURL: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
projectId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
storageBucket: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
messagingSenderId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
appId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const database = getDatabase(app);
// Export auth for use in auth.js
export { auth };
// DOM elements
const loginElement = document.querySelector('#login-form');
const contentElement = document.querySelector("#content-sign-in");
const userDetailsElement = document.querySelector('#user-details');
const authBarElement = document.querySelector("#authentication-bar");
// Elements for GPIO states
const stateElement1 = document.getElementById("state1");
const stateElement2 = document.getElementById("state2");
const stateElement3 = document.getElementById("state3");
// Button Elements
const btn1On = document.getElementById('btn1On');
const btn1Off = document.getElementById('btn1Off');
const btn2On = document.getElementById('btn2On');
const btn2Off = document.getElementById('btn2Off');
const btn3On = document.getElementById('btn3On');
const btn3Off = document.getElementById('btn3Off');
// Database paths for GPIO states
const dbPathOutput1 = 'board1/outputs/digital/12';
const dbPathOutput2 = 'board1/outputs/digital/13';
const dbPathOutput3 = 'board1/outputs/digital/14';
// Database references using modular API
const dbRefOutput1 = ref(database, dbPathOutput1);
const dbRefOutput2 = ref(database, dbPathOutput2);
const dbRefOutput3 = ref(database, dbPathOutput3);
// Manage Login/Logout UI
const setupUI = (user) => {
console.log('setupUI called with user:', user ? user.email : null);
if (user) {
// Toggle UI elements for logged-in state
loginElement.style.display = 'none';
contentElement.style.display = 'block';
authBarElement.style.display = 'block';
userDetailsElement.style.display = 'block';
userDetailsElement.textContent = user.email;
// Update states depending on database value
onValue(dbRefOutput1, (snap) => {
stateElement1.textContent = snap.val() === 1 ? "ON" : "OFF";
});
onValue(dbRefOutput2, (snap) => {
stateElement2.textContent = snap.val() === 1 ? "ON" : "OFF";
});
onValue(dbRefOutput3, (snap) => {
stateElement3.textContent = snap.val() === 1 ? "ON" : "OFF";
});
// Update database upon button click
btn1On.addEventListener('click', () => set(dbRefOutput1, 1));
btn1Off.addEventListener('click', () => set(dbRefOutput1, 0));
btn2On.addEventListener('click', () => set(dbRefOutput2, 1));
btn2Off.addEventListener('click', () => set(dbRefOutput2, 0));
btn3On.addEventListener('click', () => set(dbRefOutput3, 1));
btn3Off.addEventListener('click', () => set(dbRefOutput3, 0));
} else {
// Toggle UI elements for logged-out state
loginElement.style.display = 'block';
authBarElement.style.display = 'none';
userDetailsElement.style.display = 'none';
contentElement.style.display = 'none';
}
};
// Export setupUI for use in other modules
export { setupUI };
Важно: вам нужно изменить код, указав ваш собственный объект firebaseConfig — тот, который вы получили на предыдущих шагах.
const firebaseConfig = {
apiKey: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
authDomain: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
databaseURL: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
projectId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
storageBucket: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
messagingSenderId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
appId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION"
};
Продолжайте чтение, чтобы узнать, как работает код, или перейдите к следующему разделу.
Импорт JavaScript модулей
Сначала мы импортируем все необходимые JavaScript модули.
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-app.js";
import { getAuth } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-auth.js";
import { getDatabase, ref, onValue, set } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-database.js";
Объект конфигурации Firebase
Затем вставьте конфигурации вашего проекта Firebase.
// Firebase configuration
const firebaseConfig = {
apiKey: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
authDomain: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
databaseURL: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
projectId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
storageBucket: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
messagingSenderId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION",
appId: "REPLACE_WITH_YOUR_Firebase_CONFIGURATION"
};
Инициализация приложения Firebase
Следующие строки инициализируют приложение Firebase и экспортируют объект auth для использования в файле auth.js.
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const database = getDatabase(app);
// Export auth for use in auth.js
export { auth };
Получение HTML элементов
Мы создаем переменные для ссылки на несколько элементов пользовательского интерфейса по их id. Для идентификации этих элементов мы рекомендуем вам взглянуть на предоставленный HTML-файл и найти элементы с указанными id.
const loginElement = document.querySelector('#login-form');
const contentElement = document.querySelector("#content-sign-in");
const userDetailsElement = document.querySelector('#user-details');
const authBarElement = document.querySelector("#authentication-bar");
loginElement соответствует форме входа. contentElement соответствует секции веб-страницы, которая видна, когда пользователь вошел в систему (которая показывает показания датчиков). userDetailsElement соответствует секции, которая будет отображать email вошедшего пользователя. authBarElement соответствует панели аутентификации, которая показывает текущий статус пользователя, email аутентифицированного пользователя и ссылку выхода.
Следующий код создает переменные для ссылки на кнопки и состояния GPIO в интерфейсе:
// Elements for GPIO states
const stateElement1 = document.getElementById("state1");
const stateElement2 = document.getElementById("state2");
const stateElement3 = document.getElementById("state3");
// Button Elements
const btn1On = document.getElementById('btn1On');
const btn1Off = document.getElementById('btn1Off');
const btn2On = document.getElementById('btn2On');
const btn2Off = document.getElementById('btn2Off');
const btn3On = document.getElementById('btn3On');
const btn3Off = document.getElementById('btn3Off');
Пути и ссылки базы данных
Затем нам нужно создать пути базы данных к тем местам, где сохранены состояния GPIO:
// Database path for GPIO states
var dbPathOutput1 = 'board1/outputs/digital/12';
var dbPathOutput2 = 'board1/outputs/digital/13';
var dbPathOutput3 = 'board1/outputs/digital/14';
Чтобы иметь возможность взаимодействовать с базой данных по этим путям, нам нужно создать ссылки на базу данных, используя эти пути:
const dbRefOutput1 = ref(database, dbPathOutput1);
const dbRefOutput2 = ref(database, dbPathOutput2);
const dbRefOutput3 = ref(database, dbPathOutput3);
Функция setupUI()
Затем мы создаем функцию setupUI(), которая будет обрабатывать пользовательский интерфейс в соответствии с состоянием аутентификации пользователя.
В файле auth.js мы вызвали функцию setupUI() с аргументом user — setupUI(user), если пользователь вошел в систему; или функцию с аргументом null — setupUI(null), когда пользователь вышел из системы.
Итак, давайте проверим, что происходит, когда пользователь вошел в систему.
if (user) {
Мы показываем панель аутентификации (которая показывает данные пользователя и ссылку выхода). Для этого мы можем установить стиль отображения на block. Мы также хотим, чтобы основное содержимое веб-страницы с показаниями датчиков было видимым.
contentElement.style.display = 'block';
authBarElement.style.display ='block';
Наконец, мы можем получить email вошедшего пользователя с помощью user.email и отобразить его в секции userDetailsElement следующим образом:
userDetailsElement.textContent = user.email;
Получение состояний GPIO
Следующие строки получают состояния GPIO при каждом изменении в базе данных и обновляют соответствующие HTML-элементы новыми значениями. Для этого мы используем функцию onValue().
// Update states depending on database value
onValue(dbRefOutput1, (snap) => {
stateElement1.textContent = snap.val() === 1 ? "ON" : "OFF";
});
onValue(dbRefOutput2, (snap) => {
stateElement2.textContent = snap.val() === 1 ? "ON" : "OFF";
});
onValue(dbRefOutput3, (snap) => {
stateElement3.textContent = snap.val() === 1 ? "ON" : "OFF";
});
События кнопок
Затем мы добавляем события к кнопкам для записи 1 или 0 в соответствующий путь базы данных в зависимости от нажатой кнопки.
btn1On.addEventListener('click', () => set(dbRefOutput1, 1));
btn1Off.addEventListener('click', () => set(dbRefOutput1, 0));
btn2On.addEventListener('click', () => set(dbRefOutput2, 1));
btn2Off.addEventListener('click', () => set(dbRefOutput2, 0));
btn3On.addEventListener('click', () => set(dbRefOutput3, 1));
btn3Off.addEventListener('click', () => set(dbRefOutput3, 0));
Интерфейс при выходе из системы
Следующий фрагмент обрабатывает интерфейс, когда пользователь выходит из системы. Мы хотим скрыть панель аутентификации и основное содержимое веб-страницы (состояния GPIO и соответствующие кнопки) и показать форму входа.
} else {
// Toggle UI elements for logged-out state
loginElement.style.display = 'block';
authBarElement.style.display = 'none';
userDetailsElement.style.display = 'none';
contentElement.style.display = 'none';
}
Файл Favicon
Чтобы отобразить favicon в вашем веб-приложении, вам нужно переместить изображение, которое вы хотите использовать в качестве favicon, в папку public. Изображение должно называться favicon.png. Вы можете просто перетащить файл favicon с вашего компьютера в папку public в VS Code.
Мы используем следующую иконку в качестве favicon для нашего веб-приложения:
Развертывание приложения
После сохранения HTML, CSS и JavaScript файлов разверните ваше приложение в VS Code, выполнив следующую команду в окне терминала.
firebase deploy
Терминал должен отобразить что-то подобное:
Firebase предлагает бесплатный сервис хостинга для размещения ваших ресурсов и веб-приложений. После этого вы можете получить доступ к вашему веб-приложению из любой точки мира.
Вы можете использовать предоставленный URL-адрес хостинга для доступа к вашему веб-приложению из любой точки мира.
Демонстрация
Поздравляем! Вы успешно развернули ваше приложение. Теперь оно размещено на глобальной CDN с использованием хостинга Firebase. Вы можете получить доступ к вашему веб-приложению из любой точки мира по предоставленному URL-адресу хостинга. В моем случае это https://esp-firebase-demo.web.app.
Веб-приложение адаптивно, и вы можете получить к нему доступ с помощью смартфона, компьютера или планшета.
Введите email и пароль авторизованного пользователя, которого вы добавили в методы аутентификации Firebase. После этого вы можете получить доступ к панели управления для управления GPIO ESP32 или ESP8266.
Перейдите на вкладку Hosting в консоли Firebase вашего проекта. Вы можете увидеть домены вашего приложения, историю развертываний, и вы даже можете откатиться к предыдущим версиям вашего приложения.
Заключение
В этом руководстве вы узнали, как создать Firebase Web App с аутентификацией входа/выхода для управления GPIO ESP32 или ESP8266 из любой точки мира. Состояния GPIO сохраняются в Realtime Database, и ESP прослушивает любые изменения, происходящие в базе данных, для немедленного обновления GPIO. Вы можете легко добавить больше GPIO или больше плат в этот проект.
База данных защищена с помощью правил базы данных. Только авторизованные аутентифицированные пользователи могут получить доступ к веб-приложению для управления GPIO.
Вы можете объединить этот проект с другими проектами ESP32/ESP8266 Firebase, которые мы опубликовали, и добавить больше функциональности в ваше приложение.
Если вы хотите узнать больше о Firebase, мы рекомендуем ознакомиться с нашей электронной книгой, посвященной исключительно этой теме:
У нас есть другие ресурсы, связанные с ESP32 и ESP8266, которые могут вам понравиться:
Спасибо за чтение.
Источник: Control ESP32/ESP8266 GPIOs from Anywhere (Firebase Web App)