纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。 Co-authored-by: Cursor <[email protected]>
SPI NAND file transfer app
This is a standalone ESP32-P4 application. The existing application and
E:\PET\fin_1\branch_main\IntegratedV1 are not modified. Its /extflash
mount prefix and IntegratedV1's /nandflash prefix point to the same FAT
volume on the external GD5F1GM7: /extflash/eaf/a.eaf corresponds to
/nandflash/eaf/a.eaf. Neither prefix exists as a directory on the NAND.
Build and flash (PowerShell)
Use ESP-IDF 5.5.4 and the installed P4 toolchain. From this directory:
.\build_app.ps1
.\flash_app.ps1 -Port COM3 -AppBin .\build_release\nand_xfer.bin
flash_app.ps1 checks the parent partitions.csv for ota_0 = 0x70000, 10M, validates the app image, and invokes only
esptool write_flash 0x70000 <app.bin>. To restore the official app, run
the same script with its existing app binary:
.\flash_app.ps1 -Port COM3 -AppBin "E:\PET\fin_1\branch_main\IntegratedV1\build\<official-app>.bin"
Confirm which official binary to use before restoring. Neither script
flashes a bootloader, partition table, otadata, NVS, nor any data partition.
Do not use idf.py flash, erase-flash, or generated flash_args /
flasher_args.json: IDF generates entries for partitions outside the app.
The actual board must already boot ota_0 at 0x70000 and have a compatible
partition table; the script does not change the boot selection.
Transfer
Install pyserial once: py -3 -m pip install pyserial. Close other serial
programs, including idf.py monitor. Use the P4 USB-Serial/JTAG COM port;
baud rate is opened at 115200. After NAND initialization the device sends its
capacity and mount result, followed by NAND-XFER READY. No text follows
READY on that USB byte stream; binary requests also work if READY has already
been printed when the host opens the port.
py -3 nand_put.py COM3 "C:\files\demo.eaf" "/extflash/eaf/demo.eaf"
py -3 nand_put.py COM3 "C:\files\eaf" "/extflash/eaf" --recursive
py -3 nand_put.py COM3 --info
py -3 nand_put.py COM3 --list "/extflash/eaf"
py -3 nand_put.py COM3 --verify "/extflash/eaf/demo.eaf"
py -3 nand_put.py COM3 --mkdir "/extflash/empty"
The directory command uploads the directory contents under the destination and retains empty subdirectories. Completed files are closed, synced, read back and CRC-checked before reporting success. An interrupted current file may be incomplete; running the upload again overwrites it. Reconnecting cancels any unfinished open file, not previously completed files.
Mounts are never automatically formatted. An unformatted NAND reports
mounted=False and refuses file writes. Formatting requires this distinct
command and a typed confirmation:
py -3 nand_put.py COM3 --format
This erases all files on the NAND FAT volume. Do not accept a Windows
Explorer format prompt: its sector-size assumptions are incompatible with the
NAND page-backed FAT. Formatting is done only by the device using 16KB
clusters. SPI3 matches this repo's main/FLASH/flash.h: MOSI39, MISO41,
CLK40, CS42, SIO, mode 0, 40MHz; WP and HOLD are tied high, not GPIOs.
Validation
py -3 -m unittest -v test_nand_put.py
On the board: check capacity/READY, transfer a file and a nested directory,
compare reported size/CRC, reset and list again, reboot without --format,
then restore the official app at 0x70000. Its fopen check through
/nandflash/<relative-path> is pending board validation. As of October 8,
2026, IntegratedV1's board_config.h still contains the obsolete NAND GPIO
assignments; this transfer app does not modify that firmware. A stock
IntegratedV1 binary is therefore not guaranteed to mount this board's NAND.
Protocol: NXFR, version 1, command byte, two reserved zero bytes, LE uint32
sequence and LE uint32 payload length, payload (at most 4352 bytes), then
IEEE CRC32 over header and payload. Responses set command bit 7 and prefix
their payload with a status byte. HELLO (11) starts a new session; INFO (1),
LIST (2, LE uint16 cursor + UTF-8 path), MKDIR (3), BEGIN (4), BLOCK (5,
LE uint32 offset + up to 4096 bytes), END (6, LE uint64 size + uint32 CRC),
VERIFY (7), CANCEL (8), PREPARE (9), CONFIRM (10) operate in sequence.
PREPARE returns a 4-byte challenge, and CONFIRM requires the challenge plus
the PREPARE sequence within 30 seconds. The host requests confirmation
before PREPARE; it never automatically retries CONFIRM.