Skip to content

esp32s3_zx3d50ce02s_usrc_4832 — Factory App: Storage Module

esp32s3_zx3d50ce02s_usrc_4832 — Factory App: Storage Module

Section titled “esp32s3_zx3d50ce02s_usrc_4832 — Factory App: Storage Module”

The Storage sub-module of the esp32s3_zx3d50ce02s_usrc_4832 factory application provides SD card access to the recovery firmware. It is implemented in a single source file:

boards/esp32s3_zx3d50ce02s_usrc_4832/Factory/main/sdcard.c

Its two public functions — sdcard_mount and sdcard_unmount — are the sole gateway between the factory application and the FAT filesystem mounted on the SD card. Every operation that reads from or writes to the SD card (firmware flash, UI resource flash, screen snapshots, file probing) goes through this module.

Parent context: See esp32s3_zx3d50ce02s_usrc_4832_factory_app for the complete factory application, and esp32s3_zx3d50ce02s_usrc_4832_bsp for board hardware configuration.


Module Placement in the Factory Application

Section titled “Module Placement in the Factory Application”

Factory Application - esp32s3_zx3d50ce02s_usrc_4832

sdcard_mount()

sdcard_unmount()

VFS path /sdcard/

main.c

(app_main)

Display

gfx.c / st7796_i80.c

Input

buttons / encoder / touch / buzzer

★ Storage

sdcard.c

Dev Tools

flash_all.py / flash_factory.py

ESP-IDF

esp_vfs_fat

sdspi_host

sdmmc_cmd

SD Card

(SPI)


hw_config.h - board pin constants

main.c - callers

sdcard.c

reads at compile time

sdcard_mount()

sdcard_unmount()

static sdmmc_card_t *card

static bool spi_bus_inited

probe_sd_files()

action_sd_update()

action_sd_update_res()

snapshot_take()

SD_MOSI / SD_MISO

SD_CLK / SD_CS

SD_SPI_HOST

SD_FREQ_KHZ

SD_MOUNT_POINT

ESP-IDF ComponentRole
driver/spi_common.hspi_bus_initialize() — initialises the shared SPI bus
driver/sdspi_host.hsdspi_device_config_t, SD-over-SPI host adapter
esp_vfs_fat.hesp_vfs_fat_sdspi_mount() / esp_vfs_fat_sdcard_unmount() — FAT VFS integration
sdmmc_cmd.hsdmmc_card_t, sdmmc_card_print_info()
esp_log.hESP_LOGE / ESP_LOGW for always-on error-level logging
factory_log.hFACTORY_LOGD — debug logging guarded by FACTORY_LOG_LEVEL

esp_err_t sdcard_mount(void);

Initialises the SPI bus (first call only) and mounts the SD card as a FAT VFS at the path defined by SD_MOUNT_POINT (typically /sdcard).

Idempotent: if the internal card pointer is non-NULL the function returns ESP_OK immediately without re-mounting.

Returns:

  • ESP_OK — card is mounted; the VFS path is accessible via standard POSIX calls (fopen, fread, fwrite, fclose, rename, remove).
  • Any esp_err_t error code — SPI bus init failed, or the card was not present / could not be mounted.

Behaviour on first call:

  1. Builds an spi_bus_config_t from hw_config.h pin constants and calls spi_bus_initialize().
  2. Configures sdspi_device_config_t with SD_CS and SD_SPI_HOST.
  3. Sets sdmmc_host_t.max_freq_khz = SD_FREQ_KHZ.
  4. Calls esp_vfs_fat_sdspi_mount() with format_if_mount_failed = false.
  5. Stores the resulting sdmmc_card_t * handle.
  6. Optionally prints card info to stdout when FACTORY_LOG_LEVEL is set.

Side effects:

  • Sets the module-level spi_bus_inited = true flag.
  • Populates the static card pointer.

void sdcard_unmount(void);

Unmounts the FAT VFS and clears the sdmmc_card_t handle.

Note: The SPI bus itself is intentionally kept initialised across unmount/re-mount cycles. The SPI peripheral is shared with, or lives next to, the TFT display driver (st7796_i80); de-initialising the bus could interfere with it. The bus is reused transparently on the next sdcard_mount() call.


power-on\n(card=NULL, spi_bus_inited=false)

sdcard_mount()\nspi_bus_initialize() OK\nspi_bus_inited=true

esp_vfs_fat_sdspi_mount() OK\ncard != NULL

sdcard_mount() called again\nearly return ESP_OK, no-op

sdcard_unmount()\ncard=NULL, bus kept alive

sdcard_mount()\nskips spi_bus_initialize()\ncard != NULL

sdcard_mount() fails\nbus init error

sdcard_mount() fails\ncard absent or FS error

Uninitialised

BusReady

Mounted


Internal Flash (OTA / Partition)SD Card (SPI)ESP-IDF VFS/FATsdcard.cmain.cInternal Flash (OTA / Partition)SD Card (SPI)ESP-IDF VFS/FATsdcard.cmain.cloop[read 1 KB chunks]SPI bus stays alive (shared with TFT)sdcard_mount()spi_bus_initialize() [first call only]esp_vfs_fat_sdspi_mount("/sdcard", ...)SPI - card init + FAT parseESP_OKESP_OKfopen("/sdcard/esp3dfw.bin", "rb")fread(buf, 1, 1024, fw_file)SPI readdataread_len bytesesp_ota_write(ota_handle, buf, read_len)draw_progress(percent)fclose(fw_file)rename("esp3dfw.bin", "esp3dfw.ok")sdcard_unmount()esp_vfs_fat_sdcard_unmount("/sdcard", card)voidesp_restart() VFS/FATsdcard.cmain.c (probe_sd_files)VFS/FATsdcard.cmain.c (probe_sd_files)sd_has_fw = (f != NULL)sd_has_res = (f != NULL)alt[mount succeeded]Menu items coloured by sd_has_fw / sd_has_ressdcard_mount()ESP_OK or errorfopen("/sdcard/esp3dfw.bin", "rb")FILE* or NULLfopen("/sdcard/ui_resources.bin", "rb")FILE* or NULLsdcard_unmount()

Screen Snapshot to SD Card (optional ENABLE_SNAPSHOT)

Section titled “Screen Snapshot to SD Card (optional ENABLE_SNAPSHOT)”
VFS/FATgfx.csdcard.cmain.c (snapshot_take)VFS/FATgfx.csdcard.cmain.c (snapshot_take)Every gfx_flush() also writes pixels to the open .raw fileSD stays mounted for next snapshot in this sessionsdcard_mount()ESP_OKgfx_snapshot_begin("/sdcard/snapNNN.raw")fopen(..., "wb")draw_menu() / draw_flashing_screen()gfx_snapshot_end()fclose(raw_file)

All pin assignments and bus parameters are board-specific constants resolved from hw_config.h at compile time. No raw GPIO numbers appear in sdcard.c itself.

ConstantRole
SD_MOSIGPIO — SPI MOSI to SD card
SD_MISOGPIO — SPI MISO from SD card
SD_CLKGPIO — SPI clock
SD_CSGPIO — SD card chip-select
SD_SPI_HOSTSPI peripheral index (SPI2_HOST or SPI3_HOST)
SD_FREQ_KHZMaximum SPI clock frequency in kHz
SD_MOUNT_POINTVFS mount path string, typically "/sdcard"

This board drives the ST7796 display panel over an Intel 8080 (i80) parallel interface — not SPI. The SD card is the only device on the SPI bus. The spi_bus_inited guard ensures the bus is initialised exactly once.


sdcard_unmount() deliberately omits any call to spi_bus_free():

void sdcard_unmount(void)
{
if (card) {
esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, card);
card = NULL;
}
/* Keep SPI bus initialized - freeing it can interfere with TFT SPI.
* The bus will be reused on next mount. */
FACTORY_LOGD(TAG, "SD card unmounted");
}

Rationale:

  • spi_bus_free() is destructive on a live peripheral that may be in use by another driver.
  • The bus is cheap to keep alive without an active card handle.
  • On re-mount, spi_bus_inited = true is detected and spi_bus_initialize() is skipped, reusing the existing bus registration at no cost.

esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false, // Never auto-format: protects the user's firmware binary
.max_files = 4, // Small ceiling: only a few files opened at a time
.allocation_unit_size = 0, // Use FAT default cluster size
.disk_status_check_enable = true, // Detect card removal between successive mounts
};

format_if_mount_failed = false is a hard safety requirement: the factory app must never reformat an SD card that holds the user’s firmware or UI resource images.


All callers follow a strict mount → operate → unmount pattern. Each caller is responsible for calling sdcard_unmount() before returning to the main menu.

Pattern 1 — Transient probe (startup and post-failure)

Section titled “Pattern 1 — Transient probe (startup and post-failure)”
static void probe_sd_files(void)
{
sd_has_fw = false;
sd_has_res = false;
if (sdcard_mount() != ESP_OK) { return; }
FILE *f;
f = fopen(FW_FILENAME, "rb"); if (f) { sd_has_fw = true; fclose(f); }
f = fopen(RES_FILENAME, "rb"); if (f) { sd_has_res = true; fclose(f); }
sdcard_unmount();
// result drives menu item highlighting
}

Pattern 2 — Flash operation (user-initiated menu action)

Section titled “Pattern 2 — Flash operation (user-initiated menu action)”
// action_sd_update() / action_sd_update_res()
if (sdcard_mount() != ESP_OK) {
show_status("No SD card!", COLOR_RED);
return;
}
// ... open file, validate size, flash 1 KB chunks, draw progress bar ...
fclose(fw_file);
sdcard_unmount();
if (ok) { esp_restart(); } else { probe_sd_files(); draw_menu(); }

Pattern 3 — Screen snapshot (optional ENABLE_SNAPSHOT)

Section titled “Pattern 3 — Screen snapshot (optional ENABLE_SNAPSHOT)”
// snapshot_take() — triggered by BOOT button (GPIO0)
if (sdcard_mount() != ESP_OK) { return; }
// gfx_snapshot_begin() opens "/sdcard/snapNNN.raw"
// draw_menu() / draw_flashing_screen() writes pixels to both display and file
// gfx_snapshot_end() closes the raw file
// SD intentionally stays mounted for the next snapshot in the same session

Note: snapshot_take() is the only caller that does not immediately unmount after each operation. The card is left mounted to avoid SPI re-negotiation overhead between consecutive snapshots within the same boot session.


FilenameDescription
esp3dfw.binMain firmware binary to flash via OTA
esp3dfw.okRenamed from esp3dfw.bin after a successful flash
esp3dfw.badRenamed from esp3dfw.bin after a failed flash
ui_resources.binUI resources partition image (icons, fonts, theme palettes)
ui_resources.okRenamed from ui_resources.bin after a successful flash
ui_resources.badRenamed from ui_resources.bin after a failed flash
snapNNN.rawRaw screen captures (480×320, RGB565), numbered sequentially

The rename-on-success / rename-on-failure strategy lets the operator distinguish a successful update (*.ok) from a corrupted transfer (*.bad) on the next inspection of the card.


Error conditionDetection pointBehaviour
SPI bus init failuresdcard_mount() — spi_bus_initialize() returns errorESP_LOGE, return error code; caller shows red status on display
Card not present / mount failuresdcard_mount() — esp_vfs_fat_sdspi_mount() returns errorESP_LOGW, return error code; callers gate all file I/O on return value
Already mounted (card != NULL)sdcard_mount() — early guardSilent ESP_OK return — fully idempotent
File not found after successful mountCaller — fopen() returns NULLsdcard_unmount() then red status message on display
File size invalid or exceeds partitionCaller — range check before write beginssdcard_unmount() then red status message; write never starts
Read error mid-flashCaller — fread() returns 0ok = false; OTA aborted; binary renamed to *.bad; esp_restart() skipped

Relationship to Other Board Storage Modules

Section titled “Relationship to Other Board Storage Modules”

All boards in this repository share the same two-function sdcard_mount / sdcard_unmount interface. The only variation between boards is the hardware pin assignments resolved from each board’s hw_config.h. Functionally equivalent storage modules exist for:

BoardDocumentation
pibot_pendant_v1_0pibot_pendant_v1_0_factory_app_storage
esp32s3_8048s070cesp32s3_8048s070c_factory_app_storage
esp32s3_bzm_tft35_gt911esp32s3_bzm_tft35_gt911_factory_app_storage
esp32s3_hmi43v3esp32s3_hmi43v3_factory_app_storage

For the production firmware’s SD card subsystem (as opposed to this factory-time module), see the Storage & Configuration section and main/modules/filesystem/esp3d_sd.h.