Самохостинг публичного сайта на 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;
}
}