T5S3 Open E-Reader: od problematického bootu k robustnímu recovery systému
Z několika původně plánovaných úprav T5S3 Readeru se postupně stal vlastní hardening fork. Ve druhém díle ukazuji, jak jsem řešil boot bez SD karty, recovery, poškozenou konfiguraci, NVS i překvapivý problém se stackem.
V prvním díle jsem popsal svou cestu k LILYGO T5 E-Paper S3 Pro Lite a myšlenku vytvořit z něj otevřenou čtečku, kterou budu moci postupně přizpůsobovat svým potřebám.
Původně to přitom nevypadalo, že by bylo potřeba zasahovat do firmwaru nějak zvlášť hluboko. Existující T5S3 Reader poskytoval funkční základ, na kterém se dalo stavět. Čím déle jsem ale zařízení používal a testoval i mimo běžné scénáře, tím více se začaly objevovat situace, ve kterých firmware nebyl tak robustní, jak bych od zařízení pro každodenní používání chtěl.
A právě v této chvíli se z několika plánovaných úprav začal postupně rodit vlastní fork.
Nešlo mi přitom o přepisování fungujícího projektu od nuly. Naopak. T5S3 Reader i projekt CrossPoint, ze kterého část jeho reader enginu vychází, poskytly obrovské množství práce, bez které by tento projekt vůbec nevznikl.
Cílem mého forku je něco trochu jiného:
vzít funkční základ a systematicky zpevnit místa, která selhávají v méně obvyklých nebo chybových stavech.
První takovou oblastí se stal samotný start zařízení.
Když čtečka očekává, že všechno bude v pořádku
Typický scénář je jednoduchý.
Zapnu čtečku, microSD karta je vložená, konfigurace je validní a firmware pokračuje do hlavního menu.
Jenže skutečné zařízení musí zvládnout i jiné situace.
Co když SD karta chybí?
Co když ji řadič při prvním pokusu nedokáže připojit?
Co když je soubor s nastavením poškozený?
Co když je v NVS uložená neplatná hodnota?
A co když se uživatel potřebuje dostat do recovery režimu právě proto, že běžný boot nefunguje?
To jsou přesně scénáře, které při běžném používání nemusí nastat celé měsíce. Jakmile ale jednou nastanou, zařízení by se nemělo změnit v černou skříňku, u které jediným řešením bude připojení USB kabelu a nové nahrání firmwaru.
T5S3 jsem proto začal testovat i způsobem, který bych mohl nazvat „co všechno se dá během bootu pokazit“.
A ukázalo se, že možností je překvapivě hodně.
Boot bez microSD karty
Jedním z prvních problémů byl start zařízení bez vložené microSD karty.
Pro čtečku, která používá SD kartu nejen pro knihy, ale také pro část svých pracovních dat, jde samozřejmě o důležitou komponentu. To ale podle mě neznamená, že její absence musí automaticky znamenat nekontrolovaný nebo slepý konec bootovacího procesu.
Zařízení by mělo být schopné poznat:
SD karta není dostupná, ale samotný firmware stále funguje.
A podle toho se zachovat.
Proto vznikla samostatná bootstrap vrstva, jejímž úkolem je rozhodovat o tom, jak má systém při startu pokračovat.
Jednou z hlavních částí se stal StorageBootstrapCoordinator.
Jeho smyslem není přidávat další funkce do čtečky. Naopak se snaží udělat bootovací proces co nejvíce deterministický.
Zjednodušeně dnes boot vypadá přibližně takto:
Power on / wake
│
▼
Vyhodnocení důvodu probuzení
│
▼
Kontrola recovery požadavku
│
▼
Inicializace úložiště
│
┌────┴────┐
│ │
SD OK SD FAIL
│ │
▼ ▼
Settings No-SD stav
│
▼
Validace konfigurace
│
▼
Běžný start čtečkyDůležité je hlavně to, že jednotlivé stavy mají jasně definované pokračování.
Absence SD karty už není neočekávaná událost. Je to jeden z podporovaných stavů systému.

Recovery musí fungovat právě tehdy, když něco nefunguje
S bootem úzce souvisí recovery režim.
Recovery mechanismus nemá velkou hodnotu, pokud závisí na tom, že se před jeho spuštěním úspěšně inicializují všechny ostatní části systému.
Proto bylo potřeba upravit také pořadí některých kroků během startu.
Boot intent se nyní vyhodnocuje dostatečně brzo, aby bylo možné požadavek na recovery zachytit ještě před tím, než se firmware vydá běžnou cestou.
Součástí práce bylo i správné zachování informace o důvodu probuzení zařízení.
To zní jako detail, ale přesně podobné detaily dokážou u embedded zařízení vytvořit velmi zvláštní chyby.
Pokud se informace o wake reason během bootu přepíše nebo začne být interpretována v jiném kontextu, může firmware například zaměnit:
- běžné stisknutí tlačítka,
- probuzení z deep sleep,
- recovery gesto,
- nebo pozdější uživatelský vstup.
Výsledkem pak mohou být falešné akce nebo naopak recovery režim, který se aktivuje jen někdy.
Po úpravách jsme proto testovali nejen samotný vstup do recovery, ale i následné chování tlačítka po jeho uvolnění a opětovném stisknutí.
Na skutečném T5S3 prošly mimo jiné scénáře, kdy:
- recovery gesto správně vstoupilo do recovery režimu,
- delší držení tlačítka nezpůsobilo neočekávané vypnutí,
- jeho uvolnění nevytvořilo falešný navigační vstup,
- a další samostatné podržení už zařízení korektně vypnulo.
To je přesně typ chování, který se těžko ověřuje pouze pohledem na zdrojový kód.
Poškozené nastavení nesmí znamenat konec
Další kategorií problémů byla konfigurace.
Firmware musí počítat s tím, že obsah persistentního úložiště nemusí být vždy validní.
Zařízení může být resetováno ve špatný okamžik, soubor může být poškozený a při vývoji může dojít i k situaci, kdy novější firmware čte data vytvořená starší verzí.
Proto jsme přidali robustnější práci s neplatným settings.json.
Pokud systém zjistí, že konfiguraci nelze bezpečně použít, není vhodné pokračovat s částečně načtenými nebo neověřenými hodnotami.
Problematický soubor se proto oddělí od běžné konfigurace a firmware může pokračovat s bezpečnými výchozími hodnotami.
Podobné testy jsme následně rozšířili také na NVS.
NVS fault injector: když si chybu vytvoříme schválně
Testovat pouze stavy, které se náhodou objeví, není příliš spolehlivá strategie.
Pro další fázi proto vznikla samostatná maintenance utilita umožňující cíleně vytvářet chybové stavy v NVS.
Místo čekání na to, jestli se někdy persistentní hodnota poškodí, tak můžeme například úmyslně vytvořit stav s neplatným checksumem a sledovat, jak na něj produkční firmware zareaguje.
Tím se testování výrazně mění.
Už neověřujeme:
„Mně se to zatím nerozbilo.“
Ale:
„Tady je konkrétní poškozený stav a přesně víme, jak se v něm firmware zachová.“
Právě pomocí fault injection jsme ověřovali například fallback při nevalidních hodnotách, chování bez SD karty nebo následný retry.
Maintenance utilita přitom zůstává oddělená od běžného produkčního firmware.
Problém, který nebyl na displeji vidět
Během hardeningu se objevil ještě jeden zajímavý problém.
Tentokrát nešlo přímo o SD kartu ani recovery.
Šlo o stack.
Jedna z cest souvisejících se seznamem nastavení vytvářela na stacku výrazně větší frame, než bylo potřeba. Na běžném počítači by několik kilobajtů pravděpodobně nikoho příliš netrápilo.
ESP32 je ale jiné prostředí.
Zbytečně velká stack alokace může být rozdílem mezi stabilním chováním a problémem, který se projeví až při určité kombinaci volání.
Po úpravě se podařilo velikost příslušného compiled initializer frame snížit přibližně z:
Před úpravou6016 B ██████████████████████████████
Po úpravě448 B ██
Následná validace na skutečném hardwaru ukázala výrazně zdravější rezervu stacku a testování proběhlo bez watchdog resetu, panic stavu nebo stack overflow.
Je to zároveň dobrá ukázka toho, proč „funguje mi menu“ ještě neznamená, že je implementace robustní.
Test nestačí spustit jednou
S postupem projektu se proto změnil i způsob, jakým jednotlivé změny přijímám.
Nestačí mi, že firmware zkompiluje.
Nestačí ani to, že zařízení jednou nabootuje.
Pro důležitější změny dnes používám několik vrstev validace.
Host testy
Část logiky lze testovat mimo samotné zařízení.
To je rychlé a hlavně opakovatelné. Pokud později do stejné oblasti zasáhne jiná změna, regresní test může okamžitě upozornit, že jsme porušili dříve ověřené chování.
Build produkčního firmware
Každá změna samozřejmě musí projít také skutečným PlatformIO buildem pro cílovou konfiguraci.
Kromě samotného výsledku sleduji také využití RAM a flash paměti.
Test na skutečném T5S3
A nakonec přichází hardware.
Právě zde se ukazují věci, které se velmi obtížně simulují:
- skutečné GPIO vstupy,
- timing tlačítek,
- reset,
- deep sleep a wakeup,
- inicializace SD karty,
- USB,
- chování displeje,
- nebo kombinace několika těchto stavů zároveň.
Teprve po této validaci označuji danou část jako přijatou.
U Phase 1A.6 tak prošla celá připravená validační sada chybových scénářů a tato část projektu mohla být uzavřena.
Co se tedy změnilo pro běžného uživatele?
Velká část popsaných změn není na první pohled vůbec vidět.
A to je vlastně správně.
Robustní bootstrap nemá uživatele každý den upozorňovat na svou existenci.
Má být neviditelný, dokud ho není potřeba.
Výsledek dosavadního hardeningu je především v tom, že firmware dnes mnohem lépe rozlišuje mezi normálním a chybovým stavem.
Boot bez SD karty už není stejný scénář jako poškozená konfigurace.
Recovery není náhodná odbočka uvnitř běžného startu.
A invalidní persistentní data nemusí automaticky znamenat, že zařízení nebude schopné pokračovat.
To jsou změny, které možná nevypadají tak efektně jako nové menu nebo nová aplikace.
Pro zařízení, které bych chtěl skutečně používat jako čtečku, jsou pro mě ale podstatně důležitější.
Tím ale hardening zdaleka neskončil!
Po uzavření bootu, storage bootstrapu a recovery části jsem měl chvíli pocit, že ta nejhorší část základního firmwaru je za mnou.
Nebyla.
Při dalších testech se totiž ukázalo, že další velmi zajímavá skupina problémů čeká v power managementu.
Tentokrát už nešlo jen o neobvyklý chybový stav.
Zařízení dokázalo za určité kombinace událostí během vypínání kompletně zamrznout.
První podezření směřovala k obsluze USB, deep sleep a jednotlivým mutexům.
Skutečná příčina ale nakonec vedla mnohem hlouběji – ke dvěma souběžným cestám měnícím frekvenci procesoru a k deadlocku, který bylo potřeba zachytit přímo za běhu.
Ale o tom zase příště.
T5S3 Open E-Reader tak už pro mě není jen projekt, ve kterém měním vzhled nebo přidávám několik funkcí.
Postupně se z něj stal reliability-oriented fork, jehož cílem je systematicky projít jednotlivé vrstvy zařízení:
boot → storage → persistence → power → reader engine → recovery → UX
a každou z nich dostat do stavu, kterému můžu při každodenním používání důvěřovat.
Čím hlouběji se přitom do původního firmwaru dostávám, tím více se ukazuje, že u embedded zařízení bývají ty nejzajímavější problémy často právě ty, které na displeji vůbec nejsou vidět.