Wyobraź sobie, że pamięć w twoim Heltec V3 to małe mieszkanie. Są w nim pokoje na różne rzeczy: jeden na firmware, drugi na jego nową wersję przy aktualizacji, trzeci na ustawienia i tożsamość węzła. Na wejściu wisi plan, żeby portier wiedział, gdzie co jest. Kiedy ten plan albo jeden z pokoi się uszkodzi, urządzenie potrafi się restartować w nieskończoność. W części 1 zwiedzamy mieszkanie, a w części 2 zajmiemy się naprawianiem tego typu problemów.
Pamięć flash i jej podział
Heltec V3 (pełna nazwa: Heltec WiFi LoRa 32 V3) ma układ ESP32-S3 z 8 MB pamięci flash. To pamięć nieulotna, więc zapamiętuje dane także po odłączeniu zasilania. Nie jest to jednak jedna wielka szuflada: dzieli się na partycje, a ich układ opisuje tablica partycji. To ona określa, jak flash jest zorganizowany i jakie dane trafiają do której partycji.
MeshCore buduje firmware dla Heltec V3 ze standardowym schematem default_8MB z biblioteki Arduino-ESP32: konfiguracja tej płytki w repozytorium nie ustawia własnej tablicy, więc obowiązuje domyślna dla 8 MB. Zgadza się z tym raport kompilacji, który podaje limit programu 3 342 336 bajtów (czyli 0x330000, rozmiar jednego slotu aplikacji). Układ wygląda tak:
| Adres | Partycja | Rozmiar (ok.) | Do czego służy |
|---|---|---|---|
| 0x0 | bootloader | do 32 KB | Pierwszy kod po włączeniu. Wybiera, którą aplikację uruchomić. |
| 0x8000 | tablica partycji | 4 KB | Plan całej pamięci flash. |
| 0x9000 | nvs | 20 KB | Małe ustawienia systemowe: dane Wi-Fi, kalibracja układu radiowego i zapamiętane parowania Bluetooth. |
| 0xE000 | otadata | 8 KB | Informacja, który slot aplikacji jest aktywny (potrzebna przy aktualizacjach OTA). |
| 0x10000 | app0 | ok. 3,2 MB | Slot nr 1 na firmware. |
| 0x340000 | app1 | ok. 3,2 MB | Slot nr 2, do którego trafia nowy firmware przy aktualizacji OTA. |
| 0x670000 | spiffs | ok. 1,5 MB | System plików: tu MeshCore trzyma tożsamość, ustawienia i kontakty węzła. |
| 0x7F0000 | coredump | 64 KB | Zrzut awarii, o ile funkcja jest włączona. |
Partycja otadata jest potrzebna tylko wtedy, gdy aktualizacje OTA (bezprzewodowe) wybierają slot startowy. Do OTA potrzeba co najmniej dwóch slotów aplikacji, więc dwa duże „pokoje” app0 i app1 nie są marnotrawstwem. Pozwalają zapisać nowy firmware obok działającego. Ten sam zestaw partycji (nvs, otadata, app0, app1, spiffs o rozmiarze 1,5 MB i coredump) wiki Seeed Studio podaje dla oficjalnego obrazu MeshCore 1.15 na płytce XIAO ESP32S3, która też ma 8 MB pamięci flash.
Co MeshCore trzyma w partycji spiffs
SPIFFS to prosty system plików dla pamięci flash: da się w nim tworzyć pliki o nazwach, jak na dysku, ale bez folderów. Ukośnik w nazwie, np. /identity/_main.id, jest po prostu częścią nazwy pliku. W kodzie MeshCore dla ESP32 SPIFFS jest podstawowym miejscem na dane węzła:
- Tożsamość w pliku
/identity/_main.id. Ten sam plik czyta repeater (przekaźnik, który podaje pakiety dalej) i companion (węzeł, z którym łączy się aplikacja w telefonie). - Ustawienia, m.in. nazwa urządzenia, parametry radia i region. Do wersji 1.16 leżały w pliku binarnym (
/new_prefsw companionie,/com_prefsw repeaterze), a od 1.17.0 w pliku/prefs.json. - Dane companiona: kontakty (
/contacts3) i kanały (/channels2). Dla Heltec V3 firmware companiona ma limit 350 kontaktów i 40 kanałów grupowych (MAX_CONTACTS=350iMAX_GROUP_CHANNELS=40w konfiguracji tej płytki). - Dane repeatera: lista użytkowników z uprawnieniami (ACL) i regiony.
Tożsamość to klucz prywatny i publiczny: dzięki nim inni wiedzą, że to ten sam węzeł. Gdy firmware nie może odczytać pliku z tożsamością, po cichu tworzy nową parę kluczy. Węzeł wstaje, ale dla reszty sieci jest kimś nowym. Dlatego utrata partycji spiffs oznacza utratę nazwy, ustawień, a zwykle także dawnej tożsamości. Wiki Seeed ostrzega, że gdy partycji spiffs brakuje (nietypowa tablica pokazuje wtedy tylko nvs, phy_init i factory), ustawienia zapisane w aplikacji wyglądają na zapisane, ale po odcięciu zasilania znikają, bo są tylko w pamięci RAM.
Merged i zwykły .bin: dwa pliki, dwa adresy
Na flasherze MeshCore dla ESP32 są zwykle dwa pliki, a FAQ opisuje oba na przykładzie Heltec V3:
- Zwykły .bin zapisuje się od adresu
0x10000. To sama aplikacja, więc zachowuje m.in. istniejące parowania Bluetooth. - Plik
-merged.binzapisuje się od adresu0x0. Zawiera bootloader, tablicę partycji, początkowy stanotadatai aplikację, a luki między nimi wypełnia pustymi bajtami. Dlatego nadpisuje też partycjęnvs, a z nią bazę parowań Bluetooth. Konfiguracja MeshCore zostaje, bo leży dalej, w partycjispiffs.
Polecenia z FAQ dla programu esptool (port /dev/ttyUSB0 to przykład z Linuksa):
esptool.py -p /dev/ttyUSB0 --chip esp32-s3 write_flash 0x10000 plik.bin
esptool.py -p /dev/ttyUSB0 --chip esp32-s3 write_flash 0x0 plik-merged.bin
Różnicę widać w skrypcie merge-bin.py, który skleja plik merged:
flash_images = [
*env.Flatten(env.get("FLASH_EXTRA_IMAGES", [])),
"$ESP32_APP_OFFSET",
source[0].get_abspath(),
]
Dodatkowe obrazy (bootloader od 0x0, tablica partycji od 0x8000 i plik boot_app0.bin z początkowym stanem otadata od 0xE000) są doklejane przed aplikacją, a całość zapisuje jeden plik od zera. Dokumentacja forka OffbandMesh ostrzega, że zwykły .bin zapisany od 0x0 się nie uruchomi, a pełne wymazanie pamięci kasuje tożsamość i konfigurację, więc służy tylko do pierwszej instalacji i naprawy. Aktualizacja OTA na ESP32 też używa pliku bez „merged”: w repeaterze i serwerze pokoi polecenie start ota uruchamia otwartą sieć Wi-Fi MeshCore-OTA (w FAQ zapisaną jako „MeshCore OTA”), a plik wgrywa się przez przeglądarkę pod adresem http://192.168.4.1/update. Companion tej funkcji nie ma.
Gdzie szukać tego w kodzie
| Plik w repozytorium | Co tam jest |
|---|---|
platformio.ini |
Sekcja [esp32_base] wskazuje platformę [email protected] i skrypt merge-bin.py. Dla nRF52 są flagi -D EXTRAFS=1 i biblioteka CustomLFS. |
merge-bin.py |
Skleja plik merged (bootloader, tablica partycji, aplikacja). |
examples/companion_radio/main.cpp |
Wybór systemu plików dla platformy. |
examples/simple_repeater/main.cpp |
Montowanie SPIFFS i IdentityStore repeatera (plik /identity/_main.id). |
examples/companion_radio/DataStore.cpp |
Zapis danych companiona (m.in. /prefs.json i kontakty). Zawiera też odczyt użycia pamięci (SPIFFS.usedBytes(), totalBytes()). |
variants/*/platformio.ini |
Konfiguracja płyt, m.in. limit kontaktów. Dla T-Beam jest własna tablica partycji: board_build.partitions = min_spiffs.csv (komentarz: ominięcie limitu 4 MB flash). |
Najciekawszy jest fragment wyboru systemu plików w companion_radio/main.cpp. Dla nRF52 i STM32 używany jest InternalFS (oparty na LittleFS), dla RP2040 LittleFS, a dla ESP32 SPIFFS:
#elif defined(ESP32)
#include <SPIFFS.h>
DataStore store(SPIFFS, rtc_clock);
#endif
...
#elif defined(ESP32)
SPIFFS.begin(true);
store.begin();
Parametr true w SPIFFS.begin(true) oznacza „sformatuj, jeśli montowanie się nie uda”. Zwróć uwagę, że wynik wywołania nie jest sprawdzany. Wrócimy do tego niżej, bo właśnie ta drobnostka bywa źródłem problemów.
Co może uszkodzić partycje
1. Utrata zasilania w trakcie zapisu
Dokumentacja Espressif ostrzega, że przerwane zasilanie podczas operacji na systemie plików może uszkodzić SPIFFS. Czasem da się go potem naprawić funkcją sprawdzającą, ale nie zawsze. Najczęściej zapisuje companion: kilka sekund po każdym adwercie (ogłoszeniu, którym węzeł daje znać o sobie) od znanego kontaktu zapisuje na nowo listę kontaktów. Repeater zapisuje rzadko, głównie po zmianie ustawień i po zalogowaniu administratora.
2. Zapełnienie i „starzenie” systemu plików
SPIFFS nie wykrywa uszkodzonych bloków. Wiarygodnie można używać tylko ok. 75% partycji, a gdy brakuje miejsca, odśmiecanie przeszukuje system plików wielokrotnie i pojedynczy zapis może trwać kilka sekund. Im bardziej zapchana partycja, tym wolniej i ryzykowniej.
3. Pierwszy start po wymazaniu pamięci
Po pełnym wymazaniu pamięci SPIFFS musi się sformatować. Na Heltec V4, który ma 16 MB flasha i ponad 3 MB na SPIFFS, może to trwać do półtorej minuty – napisał Liam Cottle w dyskusji pod poprawką #3314. Sama poprawka (ang. pull request) opisuje coś gorszego: w repeaterze na ESP32 wywołanie SPIFFS.begin(true) nie ma sprawdzanego wyniku. Przy świeżo wymazanej pamięci montowanie, a nawet automatyczne formatowanie, może się nie udać. Kod idzie wtedy dalej z niedziałającym systemem plików, odczytuje śmieci i kończy awarią, po czym urządzenie restartuje się do tego samego stanu w nieskończoność. Autor poprawki odtworzył to na Heltec V4 w wersjach od 1.14.1 do 1.17.1. Proponuje sprawdzenie wyniku, jedną powtórkę z jawnym formatowaniem i zatrzymanie z komunikatem, jeśli i to się nie uda. Stan na 18 października 2026: poprawka czeka na przyjęcie i nie ma jej w wydaniu 1.18.0. Dotyczy tylko repeatera, a takie samo niesprawdzane wywołanie jest też w kodzie companiona i serwera pokoi.
4. Zły plik lub zły adres
Zwykły .bin zapisany od 0x0 się nie uruchomi. Wiki Seeed wiąże opisany wyżej brak partycji spiffs z wgraniem zwykłego .bin zamiast merged i radzi wtedy wymazać pamięć i wgrać obraz merged. Fork meshcomod opisuje też „czarny ekran” po wgraniu złego pliku i również zaleca obraz merged. Pomyłka w pliku potrafi więc wyglądać jak awaria sprzętu.
5. Zmiana układu partycji
Gdy nowa wersja firmware (albo fork) zmienia tablicę partycji, partycja spiffs może się przesunąć lub zmienić rozmiar, a stare dane przestają pasować. Ostrzega przed tym dokumentacja firmware MeshCore Observer: po zmianie układu trzeba za pierwszym razem wgrać obraz merged, a ustawienia zwykle trzeba potem wpisać od nowa. Dotyczy to płytek, którym Observer zmienia tablicę (m.in. LilyGo T3S3 i Station G2); Heltec V3 do nich nie należy. Przykładem świadomej zmiany układu jest też projekt dual-boot dla Heltec V4. Dzieli 16 MB pamięci między Meshtastic i MeshCore, a każdy z nich dostaje osobną partycję na dane. MeshCore montuje tam SPIFFS z partycji mc_spiffs, więc trzeba było zmienić kod montowania.
6. Stare dane po zmianie roli
Obraz merged kończy się na końcu aplikacji, więc partycji spiffs nie dotyka. Po zmianie roli urządzenia (np. z companiona na repeater) zostają w niej pliki poprzedniej roli, a nowa rola przejmuje dawną tożsamość, bo obie czytają ten sam plik /identity/_main.id. Kto chce zacząć od zera, powinien przed wgraniem obrazu merged wymazać całą pamięć.
7. Przerwane wgrywanie i migracje danych
Przerwane wgrywanie przez USB zostawia niepełną aplikację i urządzenie nie wstanie. Programu rozruchowego zapisanego w pamięci ROM układu nie da się jednak nadpisać, więc firmware można wgrać ponownie przez USB. Przerwana aktualizacja OTA jest mniej groźna: nowy firmware trafia do wolnego slotu, a otadata przełącza się na niego dopiero po pełnym, sprawdzonym zapisie. Po zerwaniu połączenia urządzenie uruchomi więc starą wersję.
Wydania zmieniają też format zapisu. Wersja 1.17.0 przeniosła ustawienia z plików binarnych do formatu JSON (/prefs.json). Stary plik zostaje nietknięty, więc powrót do starszej wersji jest możliwy, choć wracają wtedy ustawienia sprzed aktualizacji. To dobre rozwiązanie, ale każda migracja to dodatkowa okazja, żeby coś poszło nie tak.
Jak się chronić przed kłopotami
- Zrób kopię tożsamości. Na repeaterze klucz prywatny wyświetli polecenie
get prv.key– działa tylko w konsoli przez USB. W aplikacji dla companiona kopię zapisze opcja Export Config w sekcji Extra Tools ustawień (ikona zębatki). FAQ, które nazywa ją jeszcze Export Settings, zaleca to przed przywróceniem ustawień fabrycznych. - Do aktualizacji używaj zwykłego .bin, a merged tylko do pierwszej instalacji lub naprawy.
- Nie odłączaj zasilania w trakcie wgrywania i pierwszego startu po wymazaniu. Cierpliwości: na płytkach z dużą partycją
spiffsformatowanie trwa nawet półtorej minuty. - Zajrzyj do partycji. Wiki Seeed pokazuje, jak w przeglądarkowym narzędziu ESPConnect otworzyć stronę Partitions i upewnić się, że
spiffsistnieje. - Gdy nic nie pomaga, zostaje pełne wymazanie pamięci (na flasherze MeshCore pole Erase device przy płytkach ESP32, takich jak Heltec V3; przy nRF52 osobny przycisk Erase Flash) i ponowne wgranie firmware. To kasuje tożsamość i ustawienia, więc najpierw zrób kopię.
W części 2 pokażemy, jak uratować Heltec V3, który restartuje się co chwilę: najpierw zrobimy kopię klucza publicznego i prywatnego, potem użyjemy opcji Erase device przy ponownym wgrywaniu firmware, a na końcu przywrócimy klucze z kopii. Dzięki temu companion wróci do życia z tą samą tożsamością.
Aktualizacja 21.10.2026: poprawiono nazwy opcji na te, które widać w aplikacji MeshCore 1.50.0 i we flasherze dla ESP32 (Export Config zamiast Export Settings, Erase device zamiast Erase Flash).
