ESP32 Webserver AP na socketach
WiFi.softAP()
, a następnie uruchom serwer HTTP (np. ESPAsyncWebServer
) oraz end-point WebSocket. Dzięki temu każde urządzenie podłączone do sieci stworzonej przez ESP32 (domyślnie 192.168.4.1) będzie mogło wymieniać dane w czasie rzeczywistym przez gniazda TCP. Kluczowe punkty
• Tryb AP (WIFI_AP
) + statyczny IP 192.168.4.1
• Biblioteka ESPAsyncWebServer
+ AsyncTCP
lub natywne API ESP-IDF (esp_http_server
+ esp_websocket_server
)
• Endpoint WebSocket (np. „/ws”), obsługa zdarzeń CONNECT / DATA / DISCONNECT
• Pliki HTML/CSS/JS najlepiej w LittleFS/SPIFFS, opcjonalnie kompresja GZIP
• Zabezpieczenie: WPA2-PSK, ograniczenie liczby klientów, sanity-check komunikatów
#include <WiFi.h>
const char* ssid = "ESP32_WS_AP";
const char* pass = "Haslo123"; // ≥8 znaków
void setupAP()
{
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, pass, 1, 0, 4); // kanał 1, otwarty, max 4 klientów
WiFi.softAPConfig(IPAddress(192,168,4,1),
IPAddress(192,168,4,1),
IPAddress(255,255,255,0));
Serial.println(WiFi.softAPIP());
}
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void onWsEvent(AsyncWebSocket *w, AsyncWebSocketClient *c,
AwsEventType t, void *arg, uint8_t *data, size_t len)
{
if (t == WS_EVT_CONNECT) c->text("{\"hello\":\"client\"}");
if (t == WS_EVT_DISCONNECT) Serial.printf("C%d bye\n", c->id());
if (t == WS_EVT_DATA) {
data[len] = 0;
String msg = (char*)data;
// prosta komenda LED
if (msg == "LED:1") digitalWrite(LED_BUILTIN, HIGH);
if (msg == "LED:0") digitalWrite(LED_BUILTIN, LOW);
ws.textAll("{\"echo\":\""+msg+"\"}");
}
}
void mountFSandRoutes()
{
// 1) statyczne pliki z LittleFS (np. /index.html)
server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html").setCacheControl("max-age=86400");
// 2) API JSON
server.on("/json", HTTP_GET, [](AsyncWebServerRequest *r){
r->send(200, "application/json", "{\"uptime\":"+String(millis())+"}");
});
// 3) WebSocket
ws.onEvent(onWsEvent);
server.addHandler(&ws);
server.begin();
}
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
LittleFS.begin();
setupAP();
mountFSandRoutes();
}
void loop()
{
// Dzięki naturze async pętla może być pusta; ewentualnie co X ms:
static uint32_t t = millis();
if (millis() - t > 5000) {
t = millis();
ws.textAll("{\"uptime\":"+String(millis()/1000)+"}");
ws.cleanupClients();
}
}
<script>
let ws;
function init() {
ws = new WebSocket(`ws://${location.hostname}/ws`);
ws.onmessage = e => console.log(e.data);
}
function setLed(val){ ws.send(`LED:${val}`); }
window.onload = init;
</script>
<button onclick="setLed(1)">ON</button>
<button onclick="setLed(0)">OFF</button>
Teoretyczne podstawy
• AP tworzy sieć 192.168.4.0/24, brama 192.168.4.1
• WebSocket to nadbudowa nad TCP (RFC 6455). Handshake HTTP 101, później dwukierunkowe frames (opcode 0x1 - tekst, 0x2 - bin).
• ESPAsyncWebServer realizuje obsługę nieblokującą – kluczowe w MCU (brak wielowątkowości pre-emptive).
Praktyczne zastosowania
– konfiguratory IoT offline (np. pierwsze uruchomienie), panele HMI do LED / czujników, logger lokalny w terenie bez infrastruktury Wi-Fi.
• W rdzeniu ESP-IDF v5.x dostępny wbudowany esp_websocket_server
, co pozwala zrezygnować z bibliotek zewnętrznych w projektach produkcyjnych.
• Nowsze układy (ESP32-S3, ESP32-C6) obsługują Wi-Fi 6 / 802.11ax oraz wbudowany TLS 1.3 przy sprzętowym AES, co ułatwia szyfrowane połączenia (w AP tylko WPA2/WPA3).
• WebUI często przenosi się do React/Vue skompilowanego i zminifikowanego do statycznych zasobów .gz
– oszczędność ~70 % flash.
• Limit połączeń: biblioteka async – domyślnie 8; praktycznie 4-5 przy 160 kB RAM-heap.
• Ping/Pong: wysyłanie ws.pingAll()
co 30 s utrzymuje NAT-keepalive w telefonach.
• LittleFS vs SPIFFS – LittleFS ma wear-levelling i szybszy dostęp; do nowych projektów zalecany.
• Debug: narzędzie „websocat” lub Chrome DevTools (zakładka Network › WS
).
• Udostępnienie otwartej sieci bez hasła naraża użytkownika na podsłuch i niezamierzone podłączenia – zawsze WPA2-PSK lub tymczasowy token wyświetlany na OLED/serialu.
• Firmware powinien spełniać normy radiowe (CE/RED, FCC) – tryb AP z dużą mocą nadawczą w UE: ≤ 20 dBm.
• Jeśli przesyłane są dane osobowe (np. identyfikatory użytkowników), należy rozważyć szyfrowanie i zgodność z RODO/GDPR.
• Kompiluj z optymalizacją -Os
i wyłącz debug w produkcie.
• Po każdej aktualizacji biblioteki async sprawdź wersję zgodną z ESP32-Arduino core (aktualnie 3.1.2).
• Wstrzymaj GC klientów ws.cleanupClients()
w loop()
, aby zapobiec wyciekom RAM.
• Do testów wielodostępu użyj ab -n100 -c5 http://192.168.4.1/
(ApacheBench) lub autobahn-testsuite
dla WebSocket.
• HTML/CSS minifikuj (htmlmin
, uglify-js
), a przed wgrywaniem kompresuj gzip -k index.html
.
• Brak HTTPS w trybie AP – ESP32 nie wystawi ważnego certyfikatu; jeśli wymagane szyfrowanie, pozostaje własny protokół na TLS lub Bluetooth LE.
• W środowiskach „gęstych” (bloki, hale) AP na kanale 1/6/11 może być zakłócany; dodaj autoskan kanału lub manualny wybór w UI.
• Pamiętaj o watchdogu (esp_task_wdt) gdy w loop()
pojawią się operacje blokujące.
• Migracja ze stosu Arduino do czystego ESP-IDF + FreeRTOS (większa kontrola, niższe zużycie RAM).
• Implementacja Matter/CHIP
over Wi-Fi w trybie AP do konfiguracji „on-boarding”.
• Przetestowanie biblioteki Mongoose-OS
– wbudowany multithreaded web server z OTA i HTTPS.
• Analiza wpływu szyfrowania WPA3-SAE na czas nawiązywania połączenia i zużycie energii.
ESP32 potrafi jednocześnie stworzyć sieć Wi-Fi (AP) i pełnić rolę nieblokującego serwera HTTP/WebSocket, co umożliwia dwukierunkową komunikację w czasie rzeczywistym bez infrastruktury zewnętrznej. W praktyce wystarczy:
WiFi.softAP()
→ 2) ESPAsyncWebServer
+ end-point /ws
→ 3) statyczne pliki w LittleFS → 4) regularne ws.textAll()
i ws.cleanupClients()
.