beta_100 downloaded the bin into PSRAM and wrote it to the card fine, then
judged each attempt with f.size() on the still-open File. On arduino-esp32
File::size() of a written file is stat(path), and FAT writes the size into
the directory entry only on sync/close, so a fresh file reads 0 bytes. The
check failed all three attempts on every card and board, right after the
progress reached 100%, and each "failed" copy was deleted.
BufferedFileCommit::FsSink now closes the file and reopens it to compare
the size (closeAndCheckSize), which also catches a tail lost in close()'s
final flush. The streamed fallback (not enough PSRAM for the whole image)
uses the same check instead of trusting close(), and its write error now
says how far it got ("SD write failed at N KB") so the two paths can be
told apart in a report.
Host test: test_buffered_file_commit drives FsSink over a fake FS that
follows VFSFileImpl's size()/stdio-buffer rules. The same sequence was also
run against real FatFs (ESP-IDF ff.c) behind a transcribed VFSFileImpl on
16 MB FAT12/FAT16 and 4/32 GB FAT32 images: the old check sees 0 0 0, the
new one commits and reads back identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bin was written to the card chunk by chunk as it arrived, on the core-0
tile worker next to the Wi-Fi driver. A FAT write that fails there leaves the
file failed for good (FatFs keeps the error on the file object), so the
per-chunk retry could never recover and the stream could not be replayed.
Map tiles never hit this: they are downloaded first and written after.
When the image fits in PSRAM (with 512 KB to spare) it is now downloaded
first, then written through an internal DMA bounce buffer, up to three whole-
file attempts (helpers/BufferedFileCommit.h, host test included). Boards
without the room stream as before.
The SD health tick also guarded the download with s_sdfw_request, which the
worker clears the moment it starts, so a probe or remount could SD.end()
under the open file. The guard and sdRuntimeLifecycleBusy() now use
s_sdfw_state == 1 as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>