Files
roro9stack/docs/milestones/F1.md
T
twislaandClaude Opus 5.5 b7aed8e91c F1 steps 2-6: the Storage App, its viewers, and Maintenance moved in (#3)
The Storage App browses the SD card: folders first with sizes and dates,
three sorts, one item at a time with a clipboard (c, x, v), rename, delete
after counting what's inside, new folder, details. A listing holds 256
entries and says when a folder has more.

FileOps does the card's work for the App and the console alike, one
operation at a time on the storage task in turns of about 150 ms, so Logs
and Captures are still written during a long copy. A copy shows progress,
can be cancelled (what it wrote is taken back) and compares sizes after.
The read-only rules are checked there: the firmware's top-level folders,
/gemini/cache, and files being written (a Track, a Capture, an upload,
today's IRC Logs). A listing reads the folder straight from FatFs: through
the Arduino File, 329 entries took over two seconds.

Viewers by type: text read a screen at a time whatever the file's size
(logs open at the end), a hex dump, a Capture's packets as the LoRa Scanner
lists them, a Track's summary, and an Update File checked as an install
would check it, without writing anything. Tab shows any file as hex or text.

Settings > Storage is gone: usage, Storage Clean-up and Erase are the App's
Maintenance, behind a warning. The Storage Warning points there.

The Clock sets the system time whatever its source, so files are dated
correctly with a GNSS Fix alone (Q137).

Console: cp, mv, mkdir, du, cancel; rm takes folders and follows the rules;
ls shows dates; Debug Builds get `sd fill`.

424 host tests. Checked on the device: docs/milestones/F1.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-06 08:45:43 +02:00

9.4 KiB

F1 — Files and Notes

Status: in progress (branch f1): the Storage App (issue #3) is built and checked on the device, not merged yet. Notes (#19) and the card as a USB drive (#1) come after.

Goal: get at what's on the SD card from the device itself: browse it, look inside the files the firmware writes, copy, move, rename and delete, and keep notes. A side milestone, like G1 and S1; Files and Notes were M3's original second half (Q30, Q89).

The Storage App (issue #3)

Until now the card could be looked at only through the Debug Console (ls, get, put), and Settings > Storage could only delete whole categories by age.

On the card today: six top-level folders, irc, wifi, updates, gnss, gemini and captures. No notes yet; settings are in flash, not on the card.

Decisions (design round 2026-10-06)

# Decision
Q128 An App of its own, Storage, in the Launcher. Settings > Storage goes away: its usage figures, Storage Clean-up and "Erase SD card" move into the App, under Maintenance, behind a warning that these delete things for good.
Q129 A row shows the name, then the size or "folder", then the date modified. Folders first, then by name; s cycles the sort (name, date, size). The top line shows the path and the card's free space.
Q130 Nothing is hidden. Read-only: /gemini/cache; any file the firmware has open right now (today's IRC log, a Track or Capture being recorded, a file being received); and the top-level folders themselves, which can't be renamed or deleted though their contents can. Everything else, the user's own data included, can be renamed, moved or deleted, always after a confirmation.
Q131 One item at a time, with a clipboard: Enter opens; Back goes up, and leaves the App at the top; c copy, x cut, v paste into the current folder; r rename; d delete; n new folder; i details.
Q132 Copy, move and delete work on folders too, recursively. The confirmation says what's inside: "Delete saved and its 42 files?".
Q133 A copy is a job on the storage task in 4 KB pieces, with a progress Toast; Back cancels it. It checks free space first and asks before replacing anything. Afterwards the sizes are compared, not the contents: the driver is trusted since v0.6.1 (ADR 0007). A move within the card is a rename.
Q134 Viewers by type. Text (.txt, .log, .gmi, .csv, .gpx, and anything that looks like text): read from the card as you scroll, so size doesn't matter; logs open at the end. .pcap: the LoRa Scanner's packet list. .gpx: a summary (start, duration, points, distance), Tab for the text. .ota: version, size, whether the signature is valid; Enter installs through Update from SD. Anything else: a hex dump.
Q135 No editing: that comes with Notes (#19).
Q136 A listing holds up to 256 entries, packed, about 10 KB; a bigger folder shows the first 256 by name and says how many more there are. The App refuses to open below the memory floors (Q86).
Q137 The Clock also sets the system time, so files are dated correctly with GNSS alone and not only after NTP. A file dated before 2020 shows "-".
Q138 Console: cp, mv and mkdir, next to ls and rm.
Q139 Left out, each with its issue: selecting several items (#41), finding files by name (#42), opening a .gmi in the Gemini App (#43), a table view for .csv (#44), images (#45).
Q140 Ships as v0.9.0 when done and checked.

Done when

  • The Storage App lists any folder of the card with sizes and dates, sorted three ways, and says so when a folder has more than 256 entries.
  • A file can be copied, moved, renamed and deleted, and a folder too; a new folder can be made. Each destructive action asks first; a copy shows progress and can be cancelled.
  • The read-only rules of Q130 hold, with a reason given when something is refused.
  • Each viewer of Q134 opens its type, and a 1 MB text file scrolls without loading whole.
  • Maintenance shows the card's usage and does what Settings > Storage did, behind its warning; Settings no longer has a Storage row; the Storage Warning points at the Storage App.
  • A file written with only a GNSS Fix (no Wi-Fi) is dated correctly.
  • Free heap stays above the floors with the App open, Wi-Fi and IRC on TLS.

Work breakdown

  1. Model (host-tested): paths and names, the read-only rules, the packed listing and its sorts, file types, sizes and dates for display, the GPX summary.
  2. Card operations: listing a folder, copy, move, delete (recursive, counted), new folder, as storage jobs with progress and cancel; cp, mv, mkdir; the Clock sets the system time.
  3. The App: browsing, the clipboard, dialogs, details.
  4. Maintenance: usage, Clean-up and Erase moved in from Settings, with the warning.
  5. Viewers: text, hex, .pcap, .gpx, .ota.
  6. Checks on the device, recorded here.

As built

  • FileOps (src/services/file_ops) does the card's work for the App and for the console alike: list, count, copy, move, delete, new folder. One operation at a time on the storage task, in turns of about 150 ms that queue themselves again, so Log lines and a Capture are written in between. The rules of Q130 are checked there, whoever asks.
  • A listing reads the folder straight from FatFs. Through the Arduino File, every entry was looked up by name again for its size and again for its date: 329 entries took over two seconds. One pass now, and it's there before the screen has redrawn. Counting, copying and deleting still walk with File; they show progress and can be stopped.
  • A copy shows its progress in a box in the App, not a Toast (Q133): it has a bar and says Back cancels. A cancelled or failed copy deletes what it had written. The copy gets today's date, like cp.
  • The viewers (src/apps/file_viewer, models in lib/files): text through TextPager, which reads about a kilobyte around the screen and wraps at spaces, 38 columns; going back a line wraps the paragraph before again, so a file reads the same in both directions. A .pcap, a .gpx and an .ota are read through once by a storage job, in the same 150 ms turns. An Update File is fed to the installer's own parser with a sink that writes nothing, so "would it install" is the same answer an install gives.
  • Tab in a viewer shows the same file as hex, or as text (not in Q134).
  • Maintenance is the last row at the top of the card, and m anywhere in the App. It's the old Settings > Storage page behind a dialog.
  • The Storage Warning was only ever a Toast; "selecting it opens Storage Clean-up" (CONTEXT.md) was never built. It now reads "SD card over 80% full: see Storage".
  • Console: cp, mv, mkdir (Q138), and rm and du through the same code, so rm now takes folders and follows the rules; ls shows dates. Debug Builds: sd fill <folder> <count> makes test files.

Checks on the device (2026-10-06, v0.8.1-2 Debug Build)

All in a scratch folder, /f1test, removed afterwards.

Check Result
Host tests 424 pass (411 before the viewers' models)
Browsing Folders first, sizes and dates, the three sorts; a 300-file and a 329-file folder show "first 256 of 300" and "of 329"
New folder, rename, copy, cut and paste, delete Each works on a file and on a folder; a copy next to its original is named (2); a name in the way asks "Replace it?"
A folder of 11 files, 8.4 MB, copied 19.4 s, 435 KB/s, the bar moving; two Log lines queued meanwhile were written
The same copy cancelled at 1.8 MB "Cancelled: nothing was copied", and nothing was left behind
Delete 341 files in 9.7 s; the dialog had counted them first
Read-only rules /irc, /gnss (top-level folders), /, /gemini/cache and a folder made inside it, a folder into itself, a name with :; a Capture being recorded and the folder holding it; a folder under /irc while IRC runs. Each refused with its reason; the Capture could still be copied
Text A 1 MB log opens at its last line at once; top, pages, lines; a file without an extension that looks like text opens as text
Hex A 5 KB binary file; Tab from any other viewer
.pcap A LoRa Capture: 3 packets as the Scanner lists them, Enter shows the Meshtastic header and bytes
.gpx 400 points: start, 33 min 15 s, 4.68 km; Tab shows the text
.ota A signed file: version, "intact", "older than what's running", Enter asks to install (not confirmed). A tampered one: "image corrupted (hash mismatch)"
Maintenance The warning, then usage, Clean-up's categories and Erase (not run)
Date with GNSS only NTP pointed at an address that doesn't answer, restart: the Clock came from the Fix, and a folder made then is dated 2026-10-06 08:39. A Track from the day before, written the same way by v0.8.1, shows "-"
Memory IRC connected, the App open on 256 entries: 61 KB free (70 KB before opening). Lowest since boot 29.7 KB, during IRC's TLS handshake
Stacks storage 3.1 KB free of 6 KB at worst, loopTask 1.5 KB

Not checked by hand: how the keys feel on the device itself; everything above was driven through the Debug Console's key command and screenshots.

One slip during the checks: a scripted key sequence ran in the wrong folder and renamed /gemini/saved to saved2, then copied it to the top of the card. Both were put right at once (renamed back, the copy deleted; 7 files, 53,798 bytes, as before).