Самохостинг публичного сайта на ESP32: Полное руководство

Узнайте, как создать и опубликовать публичный самохостинговый сайт всего за $10 с помощью ESP32. Пошаговое руководство для энтузиастов IoT и домашней автоматизации.

Не указано

Введение и подготовка

Узнаем, что такое самохостинг на ESP32 и зачем он нужен, а также подготовим необходимое оборудование и программное обеспечение.

Нет кода для этого шага

Установка и настройка Arduino IDE

Устанавливаем Arduino IDE, добавляем поддержку плат ESP32 и устанавливаем необходимые библиотеки.

# В поле 'Дополнительные ссылки для менеджера плат' добавьте:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

Создание базового веб-сервера

Программируем ESP32 для создания простого веб-сервера, который отвечает на HTTP-запросы.

#include <WiFi.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "ваше_имя_сети";
const char* password = "ваш_пароль";

AsyncWebServer server(80);

void setup() {
  Serial.begin(115200);
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Подключение к Wi-Fi...");
  }
  
  Serial.println("IP-адрес: ");
  Serial.println(WiFi.localIP());
  
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "Привет от ESP32!");
  });
  
  server.begin();
}

void loop() {
  // Пустой цикл, так как сервер работает асинхронно
}

Создание веб-сайта

Разрабатываем структуру веб-сайта с HTML, CSS и JavaScript для взаимодействия с ESP32.

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Сайт на ESP32</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <h1>Привет от ESP32!</h1>
    <p>Это простой сайт, размещенный на микроконтроллере ESP32.</p>
    
    <div class="sensor-data">
      <h2>Датчики</h2>
      <p>Температура: <span id="temperature">--</span>°C</p>
      <p>Влажность: <span id="humidity">--</span>%</p>
    </div>
    
    <div class="controls">
      <button id="led-btn">Включить LED</button>
    </div>
  </div>
  
  <script src="script.js"></script>
</body>
</html>

Оптимизация сайта для ESP32

Оптимизируем сайт для работы на ресурсо-ограниченном устройстве ESP32.

// Оптимизация HTML-кода:
1. Минимизируйте количество элементов DOM
2. Избегайте вложенных таблиц
3. Используйте семантическую разметку
4. Уменьшайте количество изображений

// Оптимизация CSS:
1. Минимизируйте размер CSS-файлов
2. Используйте сокращенные свойства
3. Избегайте сложных селекторов

// Оптимизация JavaScript:
1. Минимизируйте размер JavaScript-файлов
2. Используйте сжатие (minification)
3. Оптимизируйте запросы к DOM

Публикация сайта в интернете

Настраиваем проброс портов и DDNS для доступа к сайту из интернета.

// Конфигурация статического IP
IPAddress localIP(192, 168, 1, 100);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress dns(8, 8, 8, 8);

WiFi.config(localIP, gateway, subnet, dns);

// Обновление DDNS
void updateDDNS() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    
    String url = String(ddnsService) + "?domain=" + ddnsDomain + "&token=" + ddnsToken + "&ip=" + WiFi.localIP().toString();
    
    http.begin(url);
    int httpCode = http.GET();
    
    if (httpCode > 0) {
      Serial.printf("DDNS обновлен, код ответа: %d\n", httpCode);
    }
    
    http.end();
  }
}

Обеспечение безопасности

Реализуем базовые меры безопасности для защиты сайта на ESP32.

// Middleware для проверки аутентификации
bool isAuthenticated(AsyncWebServerRequest *request) {
  if (!request->hasHeader("Authorization")) {
    return false;
  }
  String auth = request->getHeader("Authorization")->value();
  return auth == "Basic " + base64encode("username:password");
}

server.on("/admin", HTTP_GET, [](AsyncWebServerRequest *request){
  if (!isAuthenticated(request)) {
    request->send(401, "text/plain", "Unauthorized");
    return;
  }
  request->send(200, "text/html", "Административная панель");
});

Расширение функциональности

Добавляем API и динамический контент для более интерактивного взаимодействия с сайтом.

// Создание REST API
server.on("/api/sensors", HTTP_GET, [](AsyncWebServerRequest *request){
  StaticJsonDocument<200> doc;
  doc["temperature"] = readTemperature();
  doc["humidity"] = readHumidity();
  doc["timestamp"] = millis();
  
  String json;
  serializeJson(doc, json);
  request->send(200, "application/json", json);
});

// Использование веб-сокетов
#include <WebSocketsServer.h>

WebSocketsServer webSocket = WebSocketsServer(81);

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
  switch(type) {
    case WStype_DISCONNECTED:
      Serial.printf("[%u] Disconnected!\n", num);
      break;
    case WStype_CONNECTED: {
      IPAddress ip = webSocket.remoteIP(num);
      Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
      sendSensorData(num);
      break;
    }
    case WStype_TEXT:
      Serial.printf("[%u] got text: %s\n", num, payload);
      handleWebSocketCommand(num, payload);
      break;
  }
}