ESP32-CAM: Отображение фотографий в веб-приложении Firebase

В этом руководстве вы создадите веб-приложение Firebase для отображения последней фотографии, сделанной ESP32-CAM и сохранённой в Firebase Storage. Веб-приложение также показывает дату и время, когда была сделана последняя фотография. Веб-приложение бесплатно размещено на серверах Firebase, и вы можете получить к нему доступ из любого места.

ESP32-CAM отображение фотографий в веб-приложении Firebase

Эта статья является Частью 2 предыдущего руководства: ESP32-CAM: Сохранение фотографий в Firebase Storage. Сначала выполните это руководство, прежде чем продолжить.

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

В этом руководстве (Часть 2) вы создадите веб-приложение для отображения последней фотографии, сделанной ESP32-CAM и сохранённой в Firebase Storage (см. предыдущее руководство).

На следующей диаграмме показан общий обзор проекта, который мы будем создавать — программирование ESP32-CAM и настройка проекта Firebase со Storage были выполнены в Части 1.

Обзор проекта ESP32-CAM Firebase Storage
  • Firebase размещает ваше веб-приложение через глобальную CDN с помощью Firebase Hosting и предоставляет SSL-сертификат. Вы можете получить доступ к вашему веб-приложению из любого места, используя доменное имя, сгенерированное Firebase.

  • Веб-приложение отображает последнюю фотографию, сохранённую в Firebase Storage по пути data/photo.jpg.

  • Оно также отображает информацию о том, когда была сделана фотография (дата и время).

  • Если веб-страница открыта и тем временем появилась новая фотография, вам нужно нажать кнопку Refresh, чтобы получить новую фотографию из хранилища.

  • Позже вы можете обновить веб-приложение, чтобы это происходило автоматически. Например, вы можете сохранять время последней сделанной фотографии в Firebase Realtime Database (RTDB). Затем вы можете отслеживать изменения в базе данных и обновлять веб-страницу по мере необходимости для отображения последней фотографии (мы можем рассмотреть это в будущем, более продвинутом руководстве).

Предварительные требования

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

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

Вы должны были выполнить следующее руководство:

ESP32-CAM должен работать с кодом, предоставленным в этом руководстве, чтобы в Firebase Storage была сохранена фотография. Firebase Storage должен быть настроен, как показано в руководстве.

Установка необходимого программного обеспечения

Прежде чем начать, необходимо установить необходимое программное обеспечение для создания веб-приложения Firebase. Вот список программного обеспечения, которое необходимо установить (перейдите по ссылкам для получения инструкций):


1) Добавление приложения в ваш проект Firebase

1) Перейдите в консоль вашего проекта Firebase и добавьте приложение в проект, созданный в предыдущем руководстве, нажав кнопку +Add app.

Добавление приложения в проект Firebase

2) Выберите значок веб-приложения.

3) Дайте вашему приложению имя. Затем установите флажок рядом с Also set up Firebase Hosting for this App. Нажмите Register app.

Firebase добавление веб-приложения в проект с хостингом

4) Затем скопируйте объект firebaseConfig и сохраните его, потому что он понадобится позже.

Объект конфигурации firebaseConfig — скопируйте и сохраните

После этого вы также можете получить доступ к объекту firebaseConfig, если перейдёте в настройки проекта в консоли Firebase.

5) Нажмите Next на последующих шагах и, наконец, Continue to console.


2) Настройка проекта веб-приложения Firebase (VS Code)

Следуйте следующим шагам для создания проекта веб-приложения Firebase с помощью VS Code.

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

1) Создайте папку на вашем компьютере для сохранения проекта Firebase — например, Firebase-Project на рабочем столе.

2) Откройте VS Code. Перейдите в File > Open Folder… и выберите только что созданную папку.

3) Перейдите в Terminal > New Terminal. Новое окно терминала должно открыться по пути вашего проекта.

Окно терминала VS Code с папкой проекта Firebase

2) Вход в Firebase

4) В предыдущем окне терминала введите следующее:

firebase login

5) Вам будет предложено собирать информацию об использовании CLI и отчёты об ошибках. Введите «n» и нажмите Enter, чтобы отказать.

Вход в Firebase через окно терминала VS Code

Примечание: Если вы уже вошли, появится сообщение: «Already logged in as user@gmail.com».

6) После этого в вашем браузере откроется новое окно для входа в учётную запись Firebase.

Вход в учётную запись Firebase

7) Разрешите Firebase CLI доступ к вашей учётной записи Google.

Разрешение доступа Firebase CLI к учётной записи Google

8) После этого вход в Firebase CLI должен быть успешным. Вы можете закрыть окно браузера.

Успешный вход в Firebase CLI

3) Инициализация проекта веб-приложения Firebase

9) После успешного входа выполните следующую команду, чтобы начать каталог проекта Firebase в текущей папке.

firebase init

10) Вам будет предложено инициализировать проект Firebase в текущем каталоге. Введите Y и нажмите Enter.

Запуск проекта Firebase в VS Code

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

  • Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys

  • Storage: Configure a security rules file for Cloud Storage

Выбранные параметры будут отмечены зелёной звёздочкой. Затем нажмите Enter.

Создание проекта Firebase с хранилищем

12) Выберите опцию «Use an existing project» — это должен быть проект, созданный в Части 1 этого руководства — затем нажмите Enter.

Выбор проекта Firebase в VS Code

13) Затем выберите параметры хостинга, как показано ниже:

  • 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

Настройка хостинга Firebase — все параметры

14) Затем нажмите Enter на следующем вопросе, чтобы выбрать файл по умолчанию для правил хранилища.

  • What file should be used for Storage Rules? (storage.rules). Нажмите Enter.

Настройка правил Firebase Storage

15) Проект Firebase теперь должен быть успешно инициализирован. Обратите внимание, что VS Code создал некоторые необходимые файлы в папке вашего проекта.

Файлы проекта Firebase Web App с хранилищем в VS Code

Файл index.html содержит некоторый HTML-текст для создания веб-страницы. Пока оставьте HTML-текст по умолчанию. Идея состоит в том, чтобы заменить его своим собственным HTML-текстом для создания пользовательской веб-страницы для ваших нужд. Мы сделаем это позже в этом руководстве.

16) Чтобы проверить, всё ли прошло, как ожидалось, выполните следующую команду в окне терминала VS Code.

firebase deploy

Вам будет предложено установить новую роль. Нажмите Enter для подтверждения.

Первое развёртывание приложения Firebase

Вы должны получить сообщение Deploy complete! и URL-адрес консоли проекта и URL-адрес хостинга.

17) Скопируйте URL хостинга и вставьте его в окно веб-браузера. Вы должны увидеть следующую веб-страницу. Вы можете получить доступ к этой веб-странице из любой точки мира.

Успешное развёртывание хостинга Firebase

Веб-страница, которую вы видели ранее, создана с помощью HTML-файла, размещённого в папке public вашего проекта Firebase. Изменяя содержимое этого файла, вы можете создать своё собственное веб-приложение. Именно это мы и будем делать в следующем разделе.


3) Создание веб-приложения Firebase

Теперь, когда вы успешно создали приложение проекта Firebase в VS Code, выполните следующие шаги для настройки приложения для отображения фотографии, сохранённой в Firebase Storage.

index.html

Скопируйте следующий код в ваш файл index.html. Этот HTML-файл создаёт простую веб-страницу, которая отображает последнюю фотографию, сохранённую в Firebase Storage, и время, когда она была сделана (создано в предыдущем проекте).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ESP 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="stylesheet" type="text/css" href="style.css">

    <script type="module" src="app.js"></script>
</head>
<body>
    <div class="topnav">
        <h1>ESP32-CAM Web App <i class="fas fa-camera"></i></h1>
    </div>
    <p><img id="img" width="500px"></p>
    <p>Last picture taken: <span id="date-time"></span></p>
    <button type="button" id="refreshBtn">Refresh</button>
</body>
</html>

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

Вам нужно изменить код, указав свой собственный объект firebaseConfig — тот, который вы получили на этом шаге.

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

Давайте кратко рассмотрим 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">

Ссылка на внешний файл style.css для форматирования HTML-страницы.

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

Загрузка файла JavaScript app.js.

<script type="module" src="app.js"></script>

Эта строка указывает браузеру загрузить и выполнить файл JavaScript app.js, расположенный в том же каталоге, что и файл index.html — мы создадим этот файл далее.

Мы закончили с метаданными. Теперь давайте перейдём к HTML-частям, видимым пользователю — между тегами <body> и </body>.

Мы создаём верхнюю панель «навигации» с именем нашего приложения и маленькой иконкой из fontawesome.

<div class="topnav">
  <h1>ESP32-CAM Web App <i class="fas fa-camera"></i></h1>
</div>

Следующая строка создаёт абзац с тегом изображения, где мы позже отобразим фотографию. Тег изображения имеет определённый id («img»), чтобы мы могли обращаться к этому элементу в файле JavaScript по его id.

<p><img id="img" width="500px"></p>

Далее мы создаём абзац для отображения даты и времени, когда была сделана фотография. Есть тег <span> с id date-time, где мы позже отобразим эту информацию.

<p>Last picture taken: <span id="date-time"></span></p>

Наконец, есть кнопка для обновления веб-страницы (для отображения новой фотографии, если она есть).

<button onclick="window.location.reload();">Refresh</button>

Сохраните HTML-файл.

style.css

Внутри папки public создайте файл с именем style.css. Чтобы создать файл, выберите папку public, а затем нажмите на значок +file в верхней части проводника файлов. Назовите его style.css.

Создание CSS-файла в VS Code

Затем скопируйте следующий код в файл style.css.

html {
    font-family: Arial, Helvetica, sans-serif;
    display: inline-block;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  p {
    font-size: 1.2rem;
  }
  .topnav {
    overflow: hidden;
    background-color: #0A1128;
  }
  body {
    margin: 0;
  }
  button{
    background-color: #034078;
    border: none;
    padding: 14px 20px;
    text-align: center;
    font-size: 20px;
    border-radius: 4px;
    transition-duration: 0.4s;
    width: 150px;
    color: white;
    cursor: pointer;
  }

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

CSS-файл содержит несколько простых стилей, чтобы наша веб-страница выглядела лучше. Мы не будем обсуждать работу CSS в этом руководстве.

Файл JavaScript

Создайте новый файл внутри папки public с именем app.js.

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

Структура файлов проекта веб-приложения Firebase

app.js

Скопируйте следующий код в файл app.js, который вы создали ранее.

import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-app.js';
import { getAuth, signInWithEmailAndPassword } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-auth.js';
import { getStorage, ref, getDownloadURL, getMetadata } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-storage.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"
};

const email = 'AUTHORIZED_USER_EMAIL'; // Replace with your user email
const password = 'PASSWORD'; // Replace with your user password

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const storage = getStorage(app);

// Format date and time
const formatDateTime = (date) => {
  const pad = (num) => String(num).padStart(2, '0');
  return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())} at ${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
};

// Authenticate user
const authenticate = async () => {
  try {
    const userCredential = await signInWithEmailAndPassword(auth, email, password);
    return userCredential.user;
  } catch (error) {
    document.getElementById('date-time').textContent = `Authentication error: ${error.message}`;
    throw error;
  }
};

// Load image and metadata
const loadImageData = async (user) => {
  try {
    const storageRef = ref(storage, 'data/photo.jp');
    const url = await getDownloadURL(storageRef);
    document.querySelector('#img').src = url;

    const metadata = await getMetadata(storageRef);
    document.getElementById('date-time').textContent = formatDateTime(new Date(metadata.timeCreated));
  } catch (error) {
    document.getElementById('date-time').textContent = `Error: ${error.message}`;
  }
};

// Initialize on page load
document.addEventListener('DOMContentLoaded', async () => {
  const user = await authenticate();
  await loadImageData(user);

  document.querySelector('#refreshBtn').addEventListener('click', async () => {
    const user = await authenticate();
    await loadImageData(user);
  });
});

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

Этот файл получает фотографию, сохранённую в Firebase Storage, и время, когда она была сделана, и отображает это на HTML-странице.

Модули Firebase

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

import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-app.js';
import { getAuth, signInWithEmailAndPassword } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-auth.js';
import { getStorage, ref, getDownloadURL, getMetadata } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-storage.js';
Объект конфигурации Firebase

Затем добавьте объект конфигурации Firebase для этого проекта. Тот, который вы получили на предыдущих шагах этого руководства.

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"
};
Email и пароль пользователя

Вставьте email и пароль авторизованного пользователя. Вы должны были настроить это в части 1 этого руководства.

const email = 'AUTHORIZED_USER_EMAIL'; // Replace with your user email
const password = 'PASSWORD'; // Replace with your user password
Инициализация приложения Firebase

Следующие строки инициализируют приложение Firebase.

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const storage = getStorage(app);
Форматирование даты и времени

Следующие строки форматируют дату и время для отображения на веб-странице в определённом формате. Например: 11:05:11 at 2025-07-02.

// Format date and time
const formatDateTime = (date) => {
  const pad = (num) => String(num).padStart(2, '0');
  return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())} at ${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
};
Аутентификация

Следующая функция выполнит аутентификацию с использованием email и пароля, которые мы определили ранее.

// Authenticate user
const authenticate = async () => {
  try {
    const userCredential = await signInWithEmailAndPassword(auth, email, password);
    return userCredential.user;
  } catch (error) {
    document.getElementById('date-time').textContent = `Authentication error: ${error.message}`;
    throw error;
  }
};
Загрузка изображения и метаданных

Функция loadImageData получит изображение из Firebase Storage и разместит его в нужном месте на веб-странице.

const loadImageData = async (user) => {
  try {
    const storageRef = ref(storage, 'data/photo.jp');
    const url = await getDownloadURL(storageRef);
    document.querySelector('#img').src = url;

Это конкретная строка, которая получает ссылку на изображение:

const storageRef = ref(storage, 'data/photo.jp');

Примечание: в моём случае на данный момент есть своего рода баг, который обрезает имя фотографии до data/photo.jp вместо data/photo.jpg. Измените имя фотографии, если у вас не так.

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

const metadata = await getMetadata(storageRef);
document.getElementById('date-time').textContent = formatDateTime(new Date(metadata.timeCreated));
} catch (error) {
  document.getElementById('date-time').textContent = `Error: ${error.message}`;
}
Инициализация приложения при загрузке страницы

Следующие строки будут выполняться каждый раз, когда вы открываете или обновляете веб-страницу.

// Initialize on page load
document.addEventListener('DOMContentLoaded', async () => {
  const user = await authenticate();
  await loadImageData(user);

  document.querySelector('#refreshBtn').addEventListener('click', async () => {
    const user = await authenticate();
    await loadImageData(user);
  });
});

Когда это происходит, мы аутентифицируемся с помощью email и пароля.

const user = await authenticate();

Затем мы загружаем изображение.

await loadImageData(user);

Мы также добавляем обработчик событий к кнопке Refresh. При нажатии на эту кнопку будет выполнена аутентификация с email и паролем, а также загрузка изображения.

document.querySelector('#refreshBtn').addEventListener('click', async () => {
  const user = await authenticate();
  await loadImageData(user);
});

Правила Storage

В левой боковой панели вашего проекта Firebase должен быть файл с именем storage.rules.

Файл правил Firebase Storage в VS Code

Откройте этот файл и скопируйте следующие правила. Эти правила позволяют любому аутентифицированному пользователю читать и записывать в Firebase Storage. Сохраните файл.

rules_version = '2';

// Craft rules based on data in your Firestore database
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Развёртывание приложения

После сохранения HTML- и JavaScript-файлов разверните ваше приложение в VS Code, выполнив следующую команду.

firebase deploy

Firebase предлагает бесплатный хостинг для размещения ваших ресурсов и веб-приложений. После этого вы можете получить доступ к вашему веб-приложению из любого места.

Терминал должен отобразить что-то подобное:

Развёртывание веб-приложения Firebase ESP32 ESP8266

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

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

Поздравляем! Вы успешно развернули ваше приложение. Теперь оно размещено на глобальной CDN с помощью Firebase Hosting. Вы можете получить доступ к вашему веб-приложению из любого места по предоставленному URL хостинга. В моём случае это https://esp-firebase-demo.web.app.

Веб-приложение адаптивное, и вы можете получить к нему доступ с помощью смартфона, компьютера или планшета.

Веб-приложение ESP32-CAM Firebase на смартфоне

Перезагрузите плату ESP32-CAM, и она должна сделать новую фотографию при первом запуске. Затем нажмите кнопку Refresh в веб-приложении, чтобы получить последнюю сделанную фотографию.

Заключение

В этом руководстве вы узнали, как создать веб-приложение Firebase, которое взаимодействует с Firebase Storage для отображения последнего изображения, сделанного ESP32-CAM.

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

Если вы хотите узнать больше о Firebase, рекомендуем ознакомиться с нашей новой электронной книгой:

У нас также есть другие ресурсы, связанные с ESP32 и ESP8266, которые могут вам понравиться: