Storage & Persistent Data
Two ways to keep data on the board, and one guarantee that covers both: values live in the board’s storage partition, not the application image, so they survive re-flashing the app. (Erasing the whole flash wipes them.)
Store— typed key/value settings for device state: counters, calibration, flags. One namespace per store.File— whole-file text I/O on littlefs for blobs a person might read or edit: notes, exported JSON, a boot log.
There is no session on either one. No begin(), no mount(), no open()/close(). littlefs mounts lazily on first use; the settings backend loads once at boot.
Store — key/value settings
Construct a store with a namespace; every read and write rides that namespace ('app' becomes the tc/app/ settings subtree), so several stores can coexist without colliding.
import { Store } from '@typecad/hal';
import { UART0 } from '@typecad/board';
const settings = new Store('app');
const boots = settings.getInt('boots', 1); // first boot reads the default: 1
if (boots === 1) {
UART0.writeLine('first run');
}
settings.setInt('boots', boots + 1);
settings.setString('ssid', 'MyNet');
settings.setFloat('offset', -1.25);
settings.setBool('calibrated', true);
UART0.writeLine(settings.getString('ssid', '(none)'));The get methods take a required default. There is no hidden zero or empty-string value for “absent” — an unset key reads back exactly the default you chose, which makes first-boot logic explicit.
settings.remove('ssid'); // delete one key
settings.clear(); // delete every tc/app/* key this program wroteFile — whole-file text I/O
Construct with the path; the four verbs map onto littlefs one-for-one.
import { File } from '@typecad/hal';
const log = new File('/log.txt');
log.write('boot\n'); // overwrite (creates the file when absent)
if (log.exists()) {
UART0.writeLine(log.read()); // 'boot\n'
log.remove();
}Things worth knowing:
read()returns""for a missing file and an empty file alike — checkexists()first when the difference matters.write()overwrites; there is no append. To append, read, concatenate, write back.write()creates the file when absent, but not parent directories — keep files at the root of the partition.- Return buffers are shim-owned until the next read — copy anything you need to keep.
A boot counter that survives re-flashing is two lines:
const boot = new File('/boot-count.txt');
boot.write(String(Number(boot.read() || '0') + 1));Which one to use
Store | File | |
|---|---|---|
| Shape | Many typed keys under a namespace | One whole text document per path |
| Backend | Zephyr settings / ZMS | littlefs |
| Best at | Counters, calibration, flags | Notes, exported JSON, logs a person reads |
| Writes | Per-key commits | Whole-file rewrites |
| Types | Typed pairs, required defaults | None — it’s text |
⚙️ Advanced details — where the data physically lives
Both backends live in the board’s storage_partition (internal flash). If the board’s devicetree ships no storage partition — most STM32 boards define only bootloader slots — Cuttlefish generates one near the top of flash when your program uses Store or File. Anything in flash is readable by anyone holding the board; neither store is a place for secrets.
On This Page