Lập trình ESP32 SPIFFS Webserver — phục vụ trang web từ bộ nhớ flash

Lập trình ESP32 SPIFFS Webserver

Trong bài này chúng ta sẽ làm ESP32 SPIFFS Webserver: cho ESP32 phục vụ 1 trang web được lưu sẵn trong bộ nhớ flash (SPIFFS), thay vì nhét cả đống chuỗi HTML vào giữa code C++ như các bài Webserver trước. Đây là bước quan trọng nếu bạn muốn làm giao diện web phức tạp hơn mà không biến file main.cpp thành một mớ hỗn độn.

Bài 5 Networking trong serie Lập trình ESP32 từ A tới Z

SPIFFS là gì?

SPIFFS (SPI Flash File System) là một hệ thống file nhẹ, được thiết kế riêng cho bộ nhớ flash của các vi điều khiển như ESP32. Ngoài phần bộ nhớ flash dùng để chứa firmware (code đã biên dịch), ESP32 còn để dành ra 1 vùng riêng gọi là partition SPIFFS — nơi bạn có thể lưu file y như một ổ đĩa mini, đọc/ghi bằng các hàm quen thuộc kiểu fopen/fread của C.

Điểm hay nhất: dữ liệu trong SPIFFS không bị mất khi cúp điện hay reset, giống hệt cách Flash lưu lại cấu hình dù mất điện mà mình hay ví với chiếc điều hòa nhà bạn — set 27 độ, tắt điện, bật lại vẫn chạy đúng 27 độ.

Tại sao dùng SPIFFS để phục vụ web thay vì nhúng HTML vào code?

  • Trang HTML/CSS/JS được tách riêng khỏi logic C++, dễ sửa giao diện mà không cần đọc lại code xử lý.
  • File HTML dài, nhiều thẻ, để trong chuỗi String hay mảng char[] giữa code C++ rất khó đọc và dễ lỗi escape ký tự.
  • Có thể phục vụ nhiều loại file cùng lúc: HTML, CSS, JS, ảnh… miễn còn chỗ trống trong SPIFFS.

Lập trình ESP32 SPIFFS Webserver

Chuẩn bị file HTML trong thư mục data

Với PlatformIO, các file muốn nạp vào SPIFFS phải đặt trong thư mục data/ ngay tại gốc project (ngang hàng với src/). Mình tạo file data/index.html:

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ESP32 SPIFFS Webserver</title>
  <style>
    html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center; }
    h1 { color: Tomato; }
    p { font-size: 20px; }
  </style>
</head>
<body>
  <h1>ESP32 SPIFFS Webserver</h1>
  <p>Trang HTML nay khong nam trong code C++, ma duoc doc truc tiep tu file <code>/index.html</code> luu trong bo nho SPIFFS cua ESP32.</p>
  <p><a href="https://khuenguyencreator.com">khuenguyencreator.com</a></p>
</body>
</html>

Lưu ý: File trong thư mục data/ không tự động nạp vào ESP32 khi bạn nhấn nút Upload như code bình thường. Trong PlatformIO, phải chạy riêng lệnh “Upload Filesystem Image” (icon hình thư mục trong thanh PlatformIO, hoặc pio run --target uploadfs) thì nội dung data/ mới được ghi vào SPIFFS của board.

Cấu hình platformio.ini

board_build.filesystem = spiffs

Dòng này chỉ định rõ kiểu hệ thống file dùng cho partition — ESP32 core hỗ trợ cả SPIFFS lẫn LittleFS, ở đây mình chọn SPIFFS cho quen thuộc với đa số tutorial.

Code đọc file từ SPIFFS trả về cho client

#include <SPIFFS.h>

WebServer server(80);

void handleRoot() {
  File file = SPIFFS.open("/index.html", "r");
  if (!file) {
    server.send(404, "text/plain", "Khong tim thay file index.html trong SPIFFS");
    return;
  }
  server.streamFile(file, "text/html");
  file.close();
}
  • SPIFFS.open("/index.html", "r") mở file để đọc ("r"), trả về đối tượng File — nếu file không tồn tại, đối tượng này sẽ ở trạng thái falsy, kiểm tra được bằng if (!file).
  • server.streamFile(file, "text/html") đọc dữ liệu từ file theo từng khối và gửi thẳng cho client, không cần load toàn bộ nội dung vào 1 biến String trong RAM trước — quan trọng khi file lớn vì RAM của ESP32 có hạn.
  • Nhớ file.close() sau khi dùng xong, tránh rò rỉ tài nguyên.

Thư viện dùng ở bài này là WebServer.h (khác với WiFiServer thô ở các bài Webserver trước) — đây là thư viện webserver đồng bộ có sẵn trong ESP32 Arduino core, hỗ trợ đăng ký route bằng server.on() gọn hơn nhiều so với tự parse header thủ công.

Lưu ý: Luôn nhớ SPIFFS.begin(true) trong setup() trước khi dùng bất kỳ hàm SPIFFS nào. Tham số true cho phép ESP32 tự động format lại partition SPIFFS nếu phát hiện chưa từng được format — hữu ích cho lần chạy đầu tiên, nhưng cũng đồng nghĩa nếu bạn quên upload filesystem image, ESP32 sẽ mount một SPIFFS rỗng chứ không báo lỗi to tát gì, dễ khiến bạn tưởng nhầm code sai trong khi thực ra chỉ là quên upload file.

Code đầy đủ

Đây là full code, các bạn copy vào file main.cpp trong project PlatformIO là chạy được:

#include <Arduino.h>

#include <WiFi.h>
#include <WebServer.h>
#include <SPIFFS.h>

// Bai nay minh hoa cach ESP32 phuc vu 1 trang web duoc luu san trong SPIFFS
// (bo nho flash con lai sau khi tru phan chua firmware), thay vi nhung ca
// chuoi HTML vao trong code C++ nhu cac bai Webserver truoc.

const char* ssid = "Ten_Wifi_Cua_Ban";
const char* password = "Mat_Khau_Wifi";

WebServer server(80);

void handleRoot() {
  File file = SPIFFS.open("/index.html", "r");
  if (!file) {
    server.send(404, "text/plain", "Khong tim thay file index.html trong SPIFFS");
    return;
  }
  server.streamFile(file, "text/html");
  file.close();
}

void handleNotFound() {
  server.send(404, "text/plain", "404 - Khong tim thay trang");
}

void setup() {
  Serial.begin(115200);

  if (!SPIFFS.begin(true)) {
    Serial.println("Loi: khong mount duoc SPIFFS");
    return;
  }
  Serial.println("Mount SPIFFS thanh cong");

  Serial.print("Connecting to wifi ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server da khoi dong");
}

void loop() {
  server.handleClient();
}

Code đầy đủ (kèm file data/index.html) mình để trên GitHub, các bạn xem tại 03-WebServer/04_SPIFFS_Webserver nhé.

Kết quả

Nạp code và chạy “Upload Filesystem Image” (2 bước ghi vào 2 vùng flash khác nhau nên làm bước nào trước cũng được). Mở Serial Monitor để lấy IP của ESP32:

Log Serial Monitor ESP32 SPIFFS Webserver: mount SPIFFS, kết nối WiFi và in địa chỉ IP

Mở trình duyệt gõ đúng IP đó, các bạn sẽ thấy trang HTML hiện lên đúng như nội dung trong file data/index.html — không phải trong code C++.

Trình duyệt mở IP của ESP32 SPIFFS Webserver, hiển thị trang index.html đọc từ bộ nhớ SPIFFS

Nếu trình duyệt báo lỗi 404 hoặc trang trắng, khả năng cao nhất là bạn quên chạy “Upload Filesystem Image” — kiểm tra lại bước này trước khi nghi ngờ code sai, đúng không nào.

Kết

Phục vụ web từ SPIFFS là kỹ thuật gần như bắt buộc khi làm giao diện web nghiêm túc cho ESP32 — tách bạch giao diện và logic, dễ bảo trì hơn hẳn so với nhúng HTML trong code. Ở bài tiếp theo, chúng ta sẽ đi xa hơn một bước: không chỉ đọc file có sẵn, mà còn ghi dữ liệu mới vào SPIFFS để lưu lại xuyên suốt qua các lần mất điện.

Nếu thấy bài viết có ích, hãy chia sẻ cho bạn bè cùng học. Có thắc mắc gì cứ để lại bình luận, mình sẽ giải đáp nhé!

Rate this post

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *