ESPAsyncWebServer: web server không chặn cho ESP32
WebServer.h chính thức của ESP32 đơn giản nhưng blocking — khi xử lý 1 request, các client khác phải chờ. ESPAsyncWebServer (me-no-dev) giải quyết: async, non-blocking, hỗ trợ WebSocket, file upload, server-sent events. Đây là chuẩn cho web UI ESP32 nghiêm túc.
1. Cài đặt
Cài 2 thư viện (cùng author):
- ESPAsyncWebServer
- AsyncTCP (dependency cho ESP32; ESP8266 dùng ESPAsyncTCP)
Library Manager có cả hai. Nếu không tìm thấy, install qua GitHub:
https://github.com/me-no-dev/ESPAsyncWebServer
https://github.com/me-no-dev/AsyncTCP
2. Hello world
#include
#include
AsyncWebServer server(80);
void setup() {
Serial.begin(115200);
WiFi.begin("SSID", "PASS");
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, [](AsyncWebServerRequest* req){
req->send(200, "text/html", "Hello!
");
});
server.begin();
}
void loop() {} // server async, không cần handleClient()
Khác WebServer.h truyền thống: loop() trống — server tự xử lý qua AsyncTCP task.
3. Routing — methods
server.on("/api/temp", HTTP_GET, handler);
server.on("/api/relay", HTTP_POST, handler);
server.on("/wildcard", HTTP_ANY, handler);
server.onNotFound([](AsyncWebServerRequest* req){
req->send(404, "text/plain", "Not found");
});
4. Query parameters
server.on("/set", HTTP_GET, [](AsyncWebServerRequest* req){
if (req->hasParam("value")) {
String v = req->getParam("value")->value();
Serial.println(v);
}
req->send(200);
});
// GET /set?value=42
5. POST body — JSON
#include
#include
server.addHandler(new AsyncCallbackJsonWebHandler("/api/cfg",
[](AsyncWebServerRequest* req, JsonVariant& json){
JsonObject doc = json.as();
int port = doc["port"];
String ssid = doc["ssid"];
// save config...
req->send(200, "application/json", "{\"ok\":true}");
}));
6. Serve static file từ SPIFFS/LittleFS
#include
void setup() {
LittleFS.begin();
server.serveStatic("/", LittleFS, "/www/")
.setDefaultFile("index.html");
server.begin();
}
Upload trước file HTML/CSS/JS vào partition LittleFS qua plugin Arduino IDE (LittleFS Data Upload). Sau đó server tự serve.
7. Response chunked / stream
// Trả 5MB file mà không hết RAM
server.on("/big", HTTP_GET, [](AsyncWebServerRequest* req){
AsyncWebServerResponse* resp = req->beginResponseStream("text/plain");
for (int i = 0; i < 100000; i++) resp->printf("line %d\n", i);
req->send(resp);
});
8. WebSocket — real-time
AsyncWebSocket ws("/ws");
void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client,
AwsEventType type, void* arg, uint8_t* data, size_t len) {
if (type == WS_EVT_CONNECT) {
Serial.printf("Client #%u connect\n", client->id());
} else if (type == WS_EVT_DATA) {
String msg = String((char*)data).substring(0, len);
Serial.println(msg);
ws.textAll("echo: " + msg); // broadcast
}
}
void setup() {
// ... wifi
ws.onEvent(onWsEvent);
server.addHandler(&ws);
server.begin();
}
void loop() {
static uint32_t t = 0;
if (millis() - t > 1000) {
t = millis();
ws.textAll("tick " + String(millis()));
}
ws.cleanupClients();
}
WebSocket lý tưởng cho dashboard real-time: gửi cập nhật cảm biến, nhận lệnh không cần poll.
9. Server-Sent Events (SSE)
AsyncEventSource events("/events");
void setup() {
// ...
server.addHandler(&events);
}
// gửi event
events.send("hello", "message", millis());
SSE đơn giản hơn WebSocket nếu chỉ cần server → client (1 chiều).
10. File upload
server.on("/upload", HTTP_POST,
[](AsyncWebServerRequest* req){ req->send(200, "text/plain", "OK"); },
[](AsyncWebServerRequest* req, String filename,
size_t index, uint8_t* data, size_t len, bool final){
File f;
if (!index) f = LittleFS.open("/" + filename, "w");
else f = LittleFS.open("/" + filename, "a");
f.write(data, len);
f.close();
});
11. CORS
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "*");
12. Auth
server.on("/secret", HTTP_GET, [](AsyncWebServerRequest* req){
if (!req->authenticate("admin", "secret")) {
return req->requestAuthentication();
}
req->send(200, "text/plain", "OK");
});
Basic Auth. Không phải HTTPS — vẫn lộ password trong sniff.
13. So sánh với WebServer.h chính thức
| Tính năng | WebServer.h | ESPAsyncWebServer |
|---|---|---|
| API | Đơn giản, blocking | Callback, async |
| Đa client đồng thời | Tuần tự | Đồng thời |
| WebSocket | Không | Có |
| SSE | Không | Có |
| RAM | Ít | Nhiều hơn (~30KB) |
| Phù hợp | Project đơn giản | Dashboard, IoT chuyên |
14. Lưu ý production
- Đừng làm tác vụ nặng trong callback — block AsyncTCP task. Dùng FreeRTOS task riêng.
- WebSocket cleanup mỗi loop:
ws.cleanupClients()tránh memory leak. - HTTPS chưa hỗ trợ — nếu cần TLS, dùng reverse proxy (Nginx) phía trước.
Liên quan
Dùng trong ESP32 web server bật/tắt relay, Smart home MQTT + Home Assistant với ESP32. Đọc bài ESP32 OTA update qua WiFi để kết hợp WebOTA.