Bảng phân vùng flash ESP32: app, OTA, NVS, LittleFS, chọn Partition Scheme và tự đặt partitions.csv

Bảng phân vùng flash ESP32 gồm những gì, so sánh các Partition Scheme có sẵn, vì sao OTA cần hai ô app, tự đặt partitions.csv đúng quy tắc, chương trình đọc bảng đang nạp.

Khi chương trình ESP32 lớn dần (Wi-Fi, web server, HTTPS, Bluetooth), sẽ tới lúc gặp lỗi "Sketch too big" dù chip có tới 4 MB flash. Lý do là flash được chia thành nhiều phân vùng, và phần dành cho chương trình mặc định chỉ có 1,25 MB. Bài này giải thích bảng phân vùng gồm những gì, các bảng có sẵn trong Arduino khác nhau ra sao, OTA cần hai ô chương trình như thế nào, cách tự đặt bảng bằng file partitions.csv, và một chương trình đọc lại bảng đang nạp trong chip.

1. Bảng phân vùng là gì

Bảng phân vùng là một danh sách nhỏ nạp ở địa chỉ 0x8000 của flash, cho biết mỗi vùng tên gì, dùng làm gì, bắt đầu ở đâu, dài bao nhiêu. Bảng mặc định của ESP32 DevKit V1 có:

Phân vùngKích thướcDùng cho
nvs20 KBPreferences, cấu hình Wi-Fi đã lưu, dữ liệu của thư viện
otadata8 KBGhi nhớ lần khởi động sau chạy ô chương trình nào
app01,25 MBChương trình (ô thứ nhất)
app11,25 MBChương trình (ô thứ hai, dùng khi cập nhật qua mạng)
spiffs1,375 MBHệ thống file LittleFS (hoặc SPIFFS)
coredump64 KBLưu thông tin lỗi khi chip bị panic, để đọc lại sau

36 KB đầu tiên dành cho bootloader và chính bảng phân vùng. Nạp chương trình chỉ ghi vào ô app, nên dữ liệu trong nvs và spiffs vẫn còn qua các lần nạp.

2. Các bảng có sẵn

Năm thanh chia 4 MB flash: Default có app0 và app1 mỗi ô 1,25 MB, spiffs 1,38 MB; Minimal SPIFFS hai ô 1,88 MB, spiffs nhỏ; No OTA một ô 2 MB, spiffs 1,88 MB; Huge APP một ô 3 MB, spiffs 0,88 MB; bảng tự đặt hai ô 1,75 MB và spiffs 0,38 MB
Bốn bảng có sẵn trong gói esp32 3.3 và một bảng tự đặt (đọc từ file .csv của gói).
  • Default: cân bằng, có OTA, có LittleFS 1,375 MB. Chương trình tối đa 1,25 MB.
  • Minimal SPIFFS: giữ OTA, mỗi ô chương trình 1,875 MB, LittleFS chỉ còn 128 KB. Hợp với chương trình lớn có cập nhật qua mạng.
  • No OTA: một ô chương trình 2 MB, LittleFS 1,875 MB, không cập nhật qua mạng được.
  • Huge APP: một ô 3 MB cho chương trình rất lớn (ví dụ Wi-Fi và Bluetooth cùng lúc), LittleFS 896 KB, không OTA.

Lưu ý với bo DOIT ESP32 DevKit V1: trong Arduino IDE, bo này không có menu Partition Scheme. Có hai cách đổi bảng: chọn bo "ESP32 Dev Module" (cùng loại chip, có đủ menu Partition Scheme), hoặc đặt file partitions.csv trong thư mục sketch như mục 4.

3. Vì sao OTA cần hai ô chương trình

Thanh phân vùng mặc định với chú thích: chương trình đang chạy ở app0, bản mới tải qua Wi-Fi ghi vào app1, kiểm tra xong ghi vào otadata để lần sau khởi động từ app1; khởi động lại chạy app1, lỗi thì quay về app0
Bản mới ghi vào ô đang không chạy; otadata ghi nhớ lần sau khởi động từ ô nào (hình minh họa).

Cập nhật qua mạng (OTA) không thể ghi đè lên chính chương trình đang chạy. Chương trình mới được tải về và ghi vào ô còn lại, kiểm tra toàn vẹn, rồi phân vùng otadata được sửa để lần khởi động sau chạy ô mới. Nếu tải dở, mất điện, hay bản mới lỗi, ô cũ vẫn còn nguyên để chạy tiếp. Cái giá là mỗi ô chỉ được một nửa: muốn OTA, chương trình phải vừa một ô.

4. Tự đặt bảng bằng partitions.csv

Đặt một file tên partitions.csv vào cùng thư mục với file .ino; gói esp32 sẽ dùng file đó thay cho bảng mặc định khi biên dịch và nạp. Ví dụ bảng giữ OTA, mỗi ô 1,75 MB, LittleFS 384 KB:

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x5000,
otadata,  data, ota,     0xe000,  0x2000,
app0,     app,  ota_0,   0x10000, 0x1C0000,
app1,     app,  ota_1,   0x1D0000,0x1C0000,
spiffs,   data, spiffs,  0x390000,0x60000,
coredump, data, coredump,0x3F0000,0x10000,

Quy tắc khi tự đặt:

  • Phân vùng sau bắt đầu đúng chỗ phân vùng trước kết thúc (địa chỉ + kích thước), không chồng lên nhau, không vượt dung lượng flash.
  • Phân vùng loại app phải bắt đầu ở địa chỉ chia hết cho 0x10000 (64 KB).
  • Giữ tên spiffs cho phân vùng file: LittleFS.begin() mặc định tìm phân vùng tên này.
  • Đổi bảng có thể làm mất dữ liệu cũ trong nvs và spiffs nếu địa chỉ của chúng thay đổi.

5. Chương trình đọc bảng đang nạp

Sau khi đổi Partition Scheme hay thêm partitions.csv, chương trình này cho biết bảng thật sự đang nằm trong chip: từng phân vùng, ô chương trình đang chạy, chương trình chiếm bao nhiêu phần trăm ô, OTA sẽ ghi vào đâu, và bộ nhớ NVS đã dùng bao nhiêu.

// PYWORLD.VN - Đọc bảng phân vùng flash đang nạp trong ESP32: tên, loại, địa chỉ, kích thước, phân vùng chương trình đang chạy, chỗ trống cho OTA
// Dùng để kiểm tra Partition Scheme hoặc file partitions.csv trong thư mục sketch đã có tác dụng chưa
// Chỉ cần ESP32, không cần thư viện ngoài (hàm esp_partition, esp_ota, nvs của ESP-IDF có sẵn trong gói esp32)

#include <esp_partition.h>
#include <esp_ota_ops.h>
#include <nvs.h>

const char *tenKieu(const esp_partition_t *p) {
  if (p->type == ESP_PARTITION_TYPE_APP) {
    if (p->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY) return "factory";
    if (p->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_MIN && p->subtype <= ESP_PARTITION_SUBTYPE_APP_OTA_MAX) return "ota_n";
    return "app?";
  }
  switch (p->subtype) {
    case ESP_PARTITION_SUBTYPE_DATA_OTA: return "ota";
    case ESP_PARTITION_SUBTYPE_DATA_NVS: return "nvs";
    case ESP_PARTITION_SUBTYPE_DATA_COREDUMP: return "coredump";
    case ESP_PARTITION_SUBTYPE_DATA_FAT: return "fat";
    case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: return "spiffs";
    default: return "khac";
  }
}

void inLoai(esp_partition_type_t loai, const esp_partition_t *dangChay) {
  // esp_partition_next() trả về NULL và tự giải phóng iterator khi hết danh sách
  for (esp_partition_iterator_t it = esp_partition_find(loai, ESP_PARTITION_SUBTYPE_ANY, NULL); it; it = esp_partition_next(it)) {
    const esp_partition_t *p = esp_partition_get(it);
    Serial.printf("%-10s %-5s %-9s 0x%06lx %8lu %s\n", p->label, loai == ESP_PARTITION_TYPE_APP ? "app" : "data", tenKieu(p),
                  (unsigned long)p->address, (unsigned long)p->size, p == dangChay ? "<- dang chay" : "");
  }
}

void setup() {
  Serial.begin(115200);
  delay(500);
  const esp_partition_t *dangChay = esp_ota_get_running_partition();
  const esp_partition_t *otaKeTiep = esp_ota_get_next_update_partition(NULL);
  Serial.printf("Flash: %lu byte (%lu MB)\n", (unsigned long)ESP.getFlashChipSize(), (unsigned long)(ESP.getFlashChipSize() >> 20));
  Serial.println("Ten        Loai  Kieu      Dia chi  Kich thuoc");
  inLoai(ESP_PARTITION_TYPE_APP, dangChay);
  inLoai(ESP_PARTITION_TYPE_DATA, dangChay);

  Serial.printf("Chuong trinh dang dung %lu byte (%lu%% phan vung %s)\n", (unsigned long)ESP.getSketchSize(),
                (unsigned long)(100ULL * ESP.getSketchSize() / dangChay->size), dangChay->label);
  if (otaKeTiep) Serial.printf("Cap nhat OTA se ghi vao: %s (%lu byte)\n", otaKeTiep->label, (unsigned long)otaKeTiep->size);
  else Serial.println("Bang phan vung khong co cho cho OTA");

  nvs_stats_t thongKe;
  if (nvs_get_stats(NULL, &thongKe) == ESP_OK) {
    Serial.printf("NVS (Preferences): da dung %u / %u muc\n", (unsigned)thongKe.used_entries, (unsigned)thongKe.total_entries);
  }
}

void loop() {}

6. Kết quả mong đợi

Serial Monitor: flash 4194304 byte, bảng gồm app0 đang chạy ở 0x010000 dài 1310720, app1, nvs, otadata, spiffs ở 0x290000 dài 1441792, coredump; chương trình dùng 283152 byte bằng 21 phần trăm app0; OTA sẽ ghi vào app1; NVS đã dùng 31 trên 630 mục
Bảng mặc định của ESP32 DevKit V1 đọc từ chính chip (số mục NVS là ví dụ).

Con số phần trăm trùng với dòng "Sketch uses … (21%) of program storage space" mà Arduino IDE in ra khi biên dịch: đó chính là tỉ lệ so với một ô app. Khi con số này vượt khoảng 90%, nên tính trước việc đổi bảng phân vùng.

7. Xử lý lỗi thường gặp

Hiện tượngNguyên nhân thường gặpCách xử lý
"Sketch too big" / "text section exceeds available space"Chương trình lớn hơn ô app 1,25 MBChọn Minimal SPIFFS, No OTA hoặc Huge APP; hoặc tự đặt bảng
Không thấy menu Partition SchemeĐang chọn bo DOIT ESP32 DevKit V1Chọn "ESP32 Dev Module" hoặc dùng partitions.csv
LittleFS không mở được sau khi đổi bảngBảng mới không có phân vùng spiffs, hoặc đổi tênGiữ phân vùng tên spiffs; begin(true) để định dạng
Mất cấu hình đã lưu sau khi đổi bảngĐịa chỉ nvs, spiffs thay đổiGiữ nguyên nvs ở 0x9000; sao lưu dữ liệu trước khi đổi
OTA báo không đủ chỗBảng không có app1, hoặc chương trình mới lớn hơn một ôDùng bảng có hai ô app đủ lớn
Bảng tự đặt nạp xong chip không khởi độngPhân vùng app không căn 64 KB, chồng nhau, vượt dung lượng flashKiểm tra lại địa chỉ, kích thước theo quy tắc ở mục 4

8. Xem thêm


Tin tức liên quan

IC là gì: kiểu vỏ, đếm chân, đọc datasheet và tụ decoupling
IC là gì: kiểu vỏ, đếm chân, đọc datasheet và tụ decoupling

IC là gì, bên trong có gì, các kiểu vỏ DIP, SOIC, QFP, QFN và kiểu nào hàn tay được, cách tìm chân 1 và đếm chân, đọc mã in trên IC, các mục cần đọc trong datasheet và tụ decoupling 100 nF.

Vali demo thiết bị, cảm biến cho kỹ sư bán hàng: bố trí panel, nguồn và dây
Vali demo thiết bị, cảm biến cho kỹ sư bán hàng: bố trí panel, nguồn và dây

Vali demo giúp kỹ sư bán hàng mang mẫu cảm biến, bộ điều khiển tới nhà máy và trình bày ngay khi mở nắp. Bài so sánh kiểu xốp khoét với kiểu panel, cách đi nguồn, dây và tính chiều sâu vali.

Điện áp, dòng điện, điện trở và định luật Ohm: hiểu bằng ví dụ và đo bằng ESP32
Điện áp, dòng điện, điện trở và định luật Ohm: hiểu bằng ví dụ và đo bằng ESP32

Điện áp, dòng điện, điện trở là gì, đơn vị và tiền tố, định luật Ohm U = I × R với ví dụ, đường U–I của điện trở và LED, thí nghiệm kiểm chứng định luật Ohm bằng DAC và ADC của ESP32.

Driver động cơ bước A4988, DRV8825, TMC2209: chọn loại, chỉnh dòng VREF và chạy êm
Driver động cơ bước A4988, DRV8825, TMC2209: chọn loại, chỉnh dòng VREF và chạy êm

So sánh driver động cơ bước A4988, DRV8825, TMC2209, cách nối dây, công thức và cách đo VREF để đặt dòng, bảng chọn vi bước, tản nhiệt, chạy êm và khi nào dùng driver rời.

Hàn linh kiện dán SMD bằng tay: cỡ 0805, SOT-23, SOIC, dụng cụ, các bước và lỗi hay gặp
Hàn linh kiện dán SMD bằng tay: cỡ 0805, SOT-23, SOIC, dụng cụ, các bước và lỗi hay gặp

Các cỡ linh kiện dán 1206, 0805, 0603, 0402, SOT-23, SOIC; dụng cụ cần có; bốn bước hàn điện trở, tụ dán; hàn IC bằng kéo thiếc; nhận ra mối hàn tốt và cách sửa lỗi.

Lập trình rèn cho trẻ cách giải quyết vấn đề có hệ thống: hiểu đề, chia nhỏ, lập kế hoạch, gỡ lỗi
Lập trình rèn cho trẻ cách giải quyết vấn đề có hệ thống: hiểu đề, chia nhỏ, lập kế hoạch, gỡ lỗi

Vì sao học lập trình rèn cho trẻ cách giải quyết vấn đề có hệ thống: bốn bước của Pólya, chia nhỏ, lập kế hoạch, gỡ lỗi từng bước, nghiên cứu liên quan và hoạt động tại nhà.

Pin mặt trời mini cho ESP32: chọn tấm pin, mạch sạc, pin 18650, ngủ sâu và tính năng lượng mỗi ngày
Pin mặt trời mini cho ESP32: chọn tấm pin, mạch sạc, pin 18650, ngủ sâu và tính năng lượng mỗi ngày

ESP32 chạy bằng pin mặt trời: các khối tấm pin, mạch sạc, pin 18650, ổn áp; tính năng lượng tiêu thụ và thu được mỗi ngày; chương trình ngủ sâu đo điện áp pin, tấm pin; lắp đặt ngoài trời.

OLED 0.96 hay 1.3 inch, SSD1306 hay SH1106: chọn màn hình nào và dùng thư viện nào cho đúng
OLED 0.96 hay 1.3 inch, SSD1306 hay SH1106: chọn màn hình nào và dùng thư viện nào cho đúng

So sánh OLED 0,96 inch và 1,3 inch, SSD1306 khác SH1106 ở đâu, chương trình kiểm tra màn dùng chip nào, hiện tượng khi chọn nhầm thư viện và cách đổi chương trình từ SSD1306 sang SH1106.


Bình luận
Đã thêm vào giỏ hàng