Инструкции

Хранение данных на ESP32: Preferences, LittleFS и схемы разделов

13 мин чтения·Август 2026·RoboArm R1

Переменная в скетче живёт до первой перезагрузки. Чтобы устройство помнило имя сети, калибровку или счётчик запусков, данные нужно класть во flash. На ESP32 для этого два разных инструмента, и путать их не стоит: Preferences — для настроек, LittleFS — для файлов. Разбираем оба, плюс схемы разделов, без которых ни то ни другое не заработает.

Всё проверено на пакете плат esp32 by Espressif Systems 3.x.

Что выбрать

PreferencesLittleFS
Что хранитпары «ключ — значение»файлы и папки
Типичный объёмбайты и короткие строкикилобайты и мегабайты
Для чегонастройки, калибровки, счётчикивеб-страницы, логи, конфиги
Нужен разделnvs, есть всегдаspiffs, есть в большинстве схем
Сложностьодна строка на значениеоткрыть, прочитать, закрыть

Правило простое: если данные помещаются в «ключ равно значение» — берите Preferences. Если это файл, который хочется положить целиком, — LittleFS.

Preferences: настройки

Библиотека встроена в пакет плат, ставить ничего не нужно. Данные лежат в разделе nvs, который есть в любой схеме разделов.

#include <Preferences.h>

Preferences preferences;

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

  preferences.begin("robot", false);          // имя раздела, false = чтение и запись

  uint32_t bootCount = preferences.getUInt("bootCount", 0);
  bootCount++;
  preferences.putUInt("bootCount", bootCount);

  Serial.printf("Запуск номер %u\n", bootCount);

  preferences.end();
}

void loop() {
}

Первый аргумент begin() — пространство имён: логическая группа настроек. Разные части программы могут использовать свои, не мешая друг другу. Второй аргумент — режим только для чтения.

У каждого типа своя пара методов: putUInt и getUInt, putFloat и getFloat, putString и getString, putBool и getBool и так далее. Второй аргумент геттера — значение по умолчанию, когда ключа ещё нет. Это удобно: не нужно отдельно проверять первый запуск.

Полезные вспомогательные методы:

preferences.isKey("wifiSsid");     // есть ли такой ключ
preferences.remove("wifiSsid");    // удалить один ключ
preferences.clear();               // очистить всё пространство имён
preferences.freeEntries();         // сколько записей ещё влезет
⚠️

Имя пространства имён и имя ключа — не длиннее 15 символов. Это ограничение самого NVS, а не библиотеки. Ключ temperatureCalibration (22 символа) молча не сохранится.

Ещё одна вещь, о которой не пишут в туториалах: методы putX() возвращают количество записанных байтов, и ноль означает ошибку. Если раздел nvs переполнен, запись просто не произойдёт — без исключения и без сообщения, если не включён отладочный вывод. В ответственных местах результат стоит проверять:

if (preferences.putString("wifiSsid", ssid) == 0) {
  Serial.println("Не удалось сохранить: NVS переполнен?");
}

LittleFS: файлы

Файловая система нужна, когда хранить надо не значения, а содержимое: HTML-страницу для веб-интерфейса, файл конфигурации, журнал измерений.

#include <LittleFS.h>

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

  // ВНИМАНИЕ: true — «отформатировать при любой неудаче монтирования».
  // Удобно при первом запуске, опасно потом: сбой чтения уничтожит все файлы.
  if (!LittleFS.begin(true)) {
    Serial.println("Не удалось смонтировать LittleFS");
    return;
  }

  Serial.printf("Всего %u байт, занято %u\n",
                LittleFS.totalBytes(), LittleFS.usedBytes());

  File file = LittleFS.open("/config.json", FILE_WRITE);
  if (file) {
    file.print("{\"speed\":120}");
    file.close();
  }

  file = LittleFS.open("/config.json", FILE_READ);
  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();
}

void loop() {
}

Режимы открытия: FILE_READ, FILE_WRITE (перезаписывает с нуля) и FILE_APPEND (дописывает в конец). Файл обязательно закрывать — иначе данные могут не попасть во flash.

В отличие от старой SPIFFS, LittleFS понимает настоящие папки:

LittleFS.mkdir("/logs");

File root = LittleFS.open("/");
for (File entry = root.openNextFile(); entry; entry = root.openNextFile()) {
  Serial.printf("%s — %u байт, папка: %d\n",
                entry.name(), entry.size(), entry.isDirectory());
}
ℹ️

Приятная неожиданность: LittleFS по умолчанию монтирует раздел с меткой spiffs — тот самый, что есть в стандартных схемах. Собственная схема разделов для неё не нужна, хотя многие руководства утверждают обратное. Достаточно выбрать любую схему, где раздел файловой системы вообще есть.

LittleFS или SPIFFS

Про SPIFFS в интернете часто пишут, что Espressif объявила её устаревшей. Это не так: ни в документации ESP-IDF, ни в исходниках нет ни отметки deprecated, ни предупреждения, а сам компонент по-прежнему поддерживается и остаётся в официальных примерах.

Тем не менее для нового кода стоит брать LittleFS, и по техническим причинам:

  • Папки. У SPIFFS их нет — только плоский список файлов, где слэш в имени ничего не значит.
  • Устойчивость к потере питания. LittleFS проектировалась с расчётом на внезапное отключение, SPIFFS при этом может повредиться.
  • Скорость при заполнении. Сборщик мусора SPIFFS по мере заполнения раздела начинает занимать всё больше времени — отдельная запись может уйти в секунды.
  • Полезный объём. SPIFFS надёжно использует около 75% раздела, дальше начинаются сбои.

Формулировка «SPIFFS — legacy, для нового берите LittleFS» точна. «Объявлена устаревшей» — нет.

⚠️

Образы SPIFFS и LittleFS двоично несовместимы. Вызов LittleFS.begin(true) на разделе, где лежала SPIFFS, отформатирует его и уничтожит данные. При переходе со старого проекта данные надо выгрузить заранее.

EEPROM: не используйте

А вот библиотека EEPROM действительно объявлена устаревшей — это единственная из трёх, где есть прямая формулировка Espressif. В её README сказано, что для новых приложений на ESP32 следует использовать Preferences, а EEPROM оставлена только для совместимости со старыми скетчами Arduino.

Причина техническая: настоящей EEPROM у ESP32 нет, и библиотека эмулирует её одним большим двоичным объектом внутри того же NVS. Получается контейнер в контейнере — медленно и без каких-либо преимуществ. Preferences работает с NVS напрямую.

Схемы разделов

Flash-память ESP32 разбита на разделы: загрузчик, приложение, NVS, файловая система. Разметка выбирается в Tools → Partition Scheme, и от неё зависит, сколько места достанется прошивке и файлам.

СхемаПриложениеФайловая системаOTA
Default 4MB with spiffs2 × 1,25 МБ1,375 МБДа
Minimal SPIFFS (1.9MB APP)2 × 1,875 МБ128 КБДа
No OTA (2MB APP/2MB SPIFFS)2 МБ1,875 МБНет
Huge APP (3MB No OTA/1MB SPIFFS)3 МБ896 КБНет
No FS 4MB (2MB APP x2)2 × 2 МБНетДа

Схема по умолчанию выглядит так:

# Name,   Type, SubType, Offset,   Size
nvs,      data, nvs,     0x9000,   0x5000
otadata,  data, ota,     0xe000,   0x2000
app0,     app,  ota_0,   0x10000,  0x140000
app1,     app,  ota_1,   0x150000, 0x140000
spiffs,   data, spiffs,  0x290000, 0x160000
coredump, data, coredump,0x3F0000, 0x10000

Два раздела app0 и app1 нужны для обновления по воздуху: новая прошивка пишется в неактивный. Отсюда важное следствие — доступный размер скетча равен размеру одного раздела, а не их сумме. Схемы с пометкой «No OTA» отдают всё место одному разделу и тем самым лишают устройство возможности обновляться по сети.

Практический выбор для платы с 4 МБ: если прошивка не влезает в 1,25 МБ, берите Minimal SPIFFS — 1,875 МБ на приложение с сохранением OTA, ценой урезанной до 128 КБ файловой системы. Это последний вариант, где обновление по воздуху ещё работает. Подробнее — в статье про OTA-обновление.

Как залить файлы в LittleFS

Файлы веб-интерфейса удобно готовить на компьютере и заливать целиком. Положите их в папку data рядом со скетчем.

Через плагин. Для Arduino IDE 2.x подходит arduino-littlefs-upload: он устанавливается как расширение и добавляет команду загрузки в палитру Ctrl+Shift+P. Старый плагин ESP32FS работает только с Arduino IDE 1.x.

Вручную. Оба нужных инструмента уже лежат в каталоге пакета плат, скачивать ничего не надо. Сначала собирается образ, потом заливается по смещению раздела:

mklittlefs -c data -b 4096 -p 256 -s 0x160000 littlefs.bin
esptool --chip esp32 --port /dev/cu.usbserial-0001 write_flash 0x290000 littlefs.bin

Смещение 0x290000 и размер 0x160000 берутся из таблицы разделов выбранной схемы — для схемы по умолчанию они как раз такие. Установка esptool разобрана в отдельной статье.

Износ flash

Flash-память выдерживает ограниченное число циклов перезаписи — обычно порядка ста тысяч на сектор. Для настроек, которые меняются раз в день, это вечность. Для счётчика, который пишется в каждом проходе loop(), — несколько часов.

Практические правила:

  • Не пишите в цикле. Сохраняйте только при реальном изменении значения.
  • Сравнивайте перед записью. Preferences не проверяет, изменилось ли значение, и честно пишет то же самое заново.
  • Копите в ОЗУ. Журнал измерений разумно накапливать в памяти и сбрасывать в файл раз в минуту, а не после каждого замера.

Что дальше

Рекомендуемые продукты

Связанные статьи