ArduinoVN
Đăng nhập Tham gia
Thư viện Code /u/linucat /16/05/2026

ESPAsyncWebServer: web server không chặn cho ESP32

Thảo luận

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ăngWebServer.hESPAsyncWebServer
APIĐơn giản, blockingCallback, async
Đa client đồng thờiTuần tựĐồng thời
WebSocketKhông
SSEKhông
RAMÍtNhiều hơn (~30KB)
Phù hợpProject đơn giảnDashboard, 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.

Thảo luận (0)

Đăng nhập để tham gia thảo luận.
Chưa có bình luận nào. Hãy là người đầu tiên!