Skip to content

storage

Wouter Van de Wiele edited this page Aug 6, 2026 · 2 revisions

Storage — NVS & SPIFFS

NVS (NvsStore, namespace rocat)

lib/NVS_STORE/ wraps Preferences with a recursive mutex (httpd task and main-loop task call it concurrently). Each accessor opens/closes the namespace per call. On first boot (nvsInit absent) it seeds the default station list and RSS feeds.

Key reference

Key Type Default Meaning
nvsInit bool first-boot seed marker
st_count int seeded number of stations
st_def int seeded how many leading stations are built-in (non-removable via dashboard)
st<N>_n / st<N>_u string seeded station N name / URL (N = 0…19, name ≤31 chars, URL ≤127)
rss_count, rss<N>_u int, string 1 feed (VRT NWS) RSS feeds (max 10, URL ≤127)
pb_st int 0 last station index
pb_play bool false was playing at last save
pb_vol float 1.0 last driver volume
ntp_srv string pool.ntp.org NTP server
tz string CET-1CEST,M3.5.0,M10.5.0/3 POSIX timezone
set_motors bool false motors master gate
led_mode uchar 0 (off) LED subsystem mode: 0=off, 1=auto, 2=manual (NvsStore::loadLedMode/saveLedMode)
set_led_fx int 0 selected LED effect index — an independent selector, not a master gate
set_accel_wk bool false tap-to-wake
mot_pwm_mul float 1.0 motor PWM calibration multiplier (0.70–1.30)
led_fx_br uchar 100 LED effect brightness %, stored as the actual 30–100 value (UI slider is 0–100)
led_eye_dim uchar 0 eye-pixel (0/1) dim offset %, subtracted from led_fx_br for the eyes only
boot_mode uchar 0 (STREAM) 0 = Web Radio, 1 = Web Server
bl_always_on bool false backlight never times out
mood_snd_en bool false mood engine may trigger sound clips
al_led_fx int 11 (Matrix) LED effect while the alarm rings
al_dance_mv int 5 (Twist) dance move while the alarm rings
al_aud_en bool true alarm plays audio
al_station int 0 station index the alarm streams (falls back to backup clip if unavailable)
al_backup_snd int 2 (Calling) local backup sound clip if streaming fails
col0col4 18-byte blob zeros saved LED color slots (6× r,g,b)
wx_cache blob WebFetcher's last weather + location + age

rss_count/rss<N>_u storage is live code with no live consumer: NvsStore still seeds one feed on first boot (nvs_store.cpp:57-66) and implements loadRssFeeds/saveRssFeeds/addRssFeed/removeRssFeed, but the dashboard's rss_add/rss_remove handlers are commented out (web_dashboard_idf.cpp:510-517) and the RSS-feed load block in main.cpp is commented out too (main.cpp:590-598) — nothing in the running firmware currently reads or writes these keys.

Not in NVS at all — alarm on/off and alarm hour/minute live in the PCF8523 RTC chip itself (TimeManager::setAlarm/getAlarm/isAlarmEnabled), not NVS — they survive power loss because the RTC is battery-backed, same as the clock.

Deliberately RAM-only (reset every boot, by design):

  • dance_move — a motor move must never resume after a power cycle.
  • power_off_min / power_off_start — the explicit power-off countdown.

The GEM menu's "LED Mode" item (MenuElement::_cbLedMode, menu_element.cpp:645-652) and the web dashboard's led_mode command (WebDashboardIdf::_h_cmd, web_dashboard_idf.cpp:360-367) both write the same Blackboard::led_mode field and persist it via NvsStore::saveLedMode() — there is no separate "manual control" flag. The dashboard's <select id="ledMode"> (data/www/app.js's sendLedMode()) just posts {cmd:"led_mode", value: 0|1|2} for off/auto/manual, the same enum the menu uses, and both surfaces read the persisted value back on load/open.

GEM menu ↔ storage map

Every settings item in the LCD menu (lib/GUI_Menu/menu_element.cpp), with where its value actually lives:

Page Item Backed by
Settings Acc WK NVS set_accel_wk
Settings Backlight NVS bl_always_on
Settings Boot in NVS boot_mode (reboot to apply)
Settings WiFi Setup / Show IP / Test / Forget WiFi actions, no state
Effects Mot En NVS set_motors
Effects PWM x NVS mot_pwm_mul
Effects Dance RAM-only (dance_move)
Mood Audio NVS mood_snd_en
Mood LED Mode NVS led_mode
Mood Effect NVS set_led_fx
Mood Brightness NVS led_fx_br
Mood Eye Dim NVS led_eye_dim
Alarm Enable RTC (PCF8523), not NVS
Alarm LED NVS al_led_fx
Alarm Move NVS al_dance_mv
Alarm Sound NVS al_aud_en
Alarm Station NVS al_station
Alarm Backup NVS al_backup_snd
Alarm → Set Alarm Hour / Minute RTC, written on Apply
Power Timer RAM-only (power_off_min/power_off_start)
Power Restart / Shut Down actions, no state
Time Hour/Min/Sec/Day/Month/Year RTC, written on Apply
Games (per registered game) action, no state

Write-pattern notes

  • Playback state is saved with a 500 ms debounce in loop() (NVS commits stall both cores and starve the audio task) and force-flushed before shutdown.
  • saveStations/saveRssFeeds remove orphaned keys when the list shrinks.
  • loadBlob() only fills the buffer when the stored blob length matches exactly, so PODs can be reinterpreted safely across firmware changes.

Default station list (seeded on first boot)

MNM Hits, MNM, Radio 1, Radio 2, Studio Brussel, Q-music, Q-Foute Radio, Q-Allstars, Vivacité Bruxelles, La Première, WDR4, France Bleu — all plain MP3 icecast streams (the MultiDecoder currently only registers MP3, see Audio).

SPIFFS

Partition table: ESP32_4MB_1APP.csv (single app + SPIFFS). Image source is the data/ directory (pio run -t uploadfs, or enable the upload_fs_before_flash.py extra script to do it automatically before each flash).

Path Used by
/www/index.html, /www/app.js, /www/style.css web dashboard static assets
/jokes/jokes.bin joke-of-the-day text read by lib/GUI_Jokes/jokes_element.cpp (JOKES_PATH), generated by patches/apply_patches.py's generate_jokes_bin()

Notes:

  • Mood-sound clips are not read from SPIFFS at all — they're compiled directly into the firmware binary. lib/CAT_AUDIO/audio_driver.cpp:5-18 includes one header per clip (angry.h, purr.h, hissing.h, …), each holding a baked-in data_<name>_mp3[] byte array; the Sound enum (audio_driver.h:11-26) indexes into them — 14 members: Angry, Bored, Calling, Confused, CoolOpera, CoolWobble, Cute, Hissing, InnerAngry, Purr, Snore, SoftAngry, Trilling, Wink. The data/ directory currently has zero .mp3 files.
  • Mood sounds are available by default: setup() allocates audioMgr and wires it into MoodManager whenever boot mode is STREAM, the default (main.cpp:573-574,582) — it's only left null in WEB mode. Availability is gated solely by the mood_snd_en NVS toggle (default false), not by whether an AudioDriver/AudioManager exists.
  • data_old/ is an old, unflashed asset set — an older www/ plus many individually-numbered mood mp3s (angry_1.mp3, angry_2.mp3, bored_2.mp3, …).
  • data_sounds/ holds 14 mp3s (angry.mp3, bored.mp3, calling.mp3, confused.mp3, cool_opera.mp3, cool_wobble.mp3, cute.mp3, hissing.mp3, inner_angry.mp3, purr.mp3, snore.mp3, soft_angry.mp3, trilling.mp3, wink.mp3) matching the compiled-in Sound enum 1:1 — these look like the source files the CAT_AUDIO headers were generated from, not something flashed as SPIFFS.
  • .gitignore still lists a data2/ entry, but no such directory exists in the repo.
  • setup() lists all SPIFFS files on serial at boot — handy to verify what is actually on the device.

Boot-time settings restore

setup() reads NVS once and publishes into the blackboard (main.cpp:508-562): led_mode+led_effect, led_effect_brightness, led_eye_dim, accel wake, mood sounds enabled, backlight-always-on, alarm LED/dance/audio/station/backup sound, and motors gate + PWM multiplier. There's no separate "mood LED" restore — that field doesn't exist; led_mode covers it. From then on the blackboard is the live copy; NVS is only written on change (see Data Broker).

Clone this wiki locally