SD Card

The espp::SdCard component brings up an SD / microSD card over SDSPI (any target) or the SDMMC (SDIO) peripheral (ESP32, ESP32-S3, ESP32-P4), and keeps card initialization and FAT mounting as two separate steps.

ESP-IDF’s convenience functions (esp_vfs_fat_sdspi_mount() / esp_vfs_fat_sdmmc_mount()) probe the card and mount its FAT volume in one call, and own the card for as long as it is mounted. SdCard instead probes the card in initialize() and mounts / unmounts the volume with mount() / unmount(), so the same card can be handed to another user – a USB host through espp::UsbDevice’s MSC function – and taken back without re-probing it.

Interface configuration

Config::interface is a std::variant of:

  • SpiConfig: the SPI host and chip-select pin; optionally the bus pins, when the component should initialize (and later free) the bus rather than share one the application or BSP already owns.

  • SdmmcConfig: the slot, 1- or 4-bit width, the pins (routed through the GPIO matrix on targets that support it), the bus clock, and an optional on-chip LDO channel that powers the card (the ESP32-P4 feeds its SD pads from LDO channel 4).

Both share the mount settings: mount_point, mount_on_initialize, format_if_mount_failed (off by default; format() is explicit), max_files, allocation_unit_size and disk_status_check.

Handing the card to a USB host

Initialize with mount_on_initialize = false (or unmount() first) and pass card() as espp::UsbDevice::MscMedium::sd_card; the MSC function then mounts the card at its own path while the application owns it and unmounts it while the PC has it. See the usb_device component’s msc_example and the SD card example README.

API Reference

Header File

Classes

class SdCard : public espp::BaseComponent

SD / microSD card over SDSPI or SDMMC (SDIO), with card initialization and FAT mounting as two separate steps.

ESP-IDF’s convenience functions (`esp_vfs_fat_sdspi_mount()` / `esp_vfs_fat_sdmmc_mount()`) initialize the card and mount its FAT volume in one call, and own the card for as long as it is mounted. That is fine for a board that only ever reads its own card, but not when something else needs the raw card: USB mass storage (`espp::UsbDevice`’s MSC function) hands the card to a PC, which must not happen while the firmware has the volume mounted.

`SdCard` therefore separates the two:

  • `initialize()` brings up the host (an SPI bus device, or an SDMMC slot) and probes the card: afterwards `card()` is a valid `sdmmc_card_t` usable for raw sector access or for handing to another owner. By default it also mounts.

  • `mount()` / `unmount()` register / unregister the card’s FAT volume at `Configmount_point`, so the card can move between the application’s VFS and another user (USB host) any number of times without re-probing it.

Both interfaces are configured through one `Config` and selected with a `std::variant`:

  • `SpiConfig`: the card on an SPI bus (any target). The bus may already be initialized by the application / BSP (shared with a display, a radio, …) or the component can initialize and later free it.

  • `SdmmcConfig`: the dedicated SDMMC peripheral (ESP32, ESP32-S3, ESP32-P4), 1- or 4-bit, with the pins routed through the GPIO matrix on targets that support it, and an optional on-chip LDO channel powering the card (ESP32-P4).

SdCard Example

  espp::SdCard::Config config;
  config.mount_point = "/sdcard";
#ifdef CONFIG_SDCARD_EXAMPLE_FORMAT_IF_MOUNT_FAILED
  config.format_if_mount_failed = true; // erases a card with no FAT filesystem
#endif
  config.log_level = espp::Logger::Verbosity::INFO;
#if CONFIG_SDCARD_EXAMPLE_INTERFACE_SDMMC
  espp::SdCard::SdmmcConfig sdmmc;
#ifdef CONFIG_SDCARD_EXAMPLE_SDMMC_BUS_WIDTH_1
  sdmmc.bus_width = 1;
#else
  sdmmc.bus_width = 4;
#endif
  sdmmc.clk = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SDMMC_CLK);
  sdmmc.cmd = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SDMMC_CMD);
  sdmmc.d0 = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SDMMC_D0);
  sdmmc.d1 = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SDMMC_D1);
  sdmmc.d2 = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SDMMC_D2);
  sdmmc.d3 = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SDMMC_D3);
  sdmmc.ldo_channel = CONFIG_SDCARD_EXAMPLE_SDMMC_LDO_CHANNEL;
  config.interface = sdmmc;
#else
  espp::SdCard::SpiConfig spi;
  spi.host = SPI2_HOST;
  spi.initialize_bus = true; // nothing else is on this bus
  spi.mosi = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SPI_MOSI);
  spi.miso = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SPI_MISO);
  spi.sclk = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SPI_SCLK);
  spi.cs = static_cast<gpio_num_t>(CONFIG_SDCARD_EXAMPLE_SPI_CS);
  config.interface = spi;
#endif

  espp::SdCard sdcard(config);
  std::error_code ec;
  if (!sdcard.initialize(ec)) { // probes the card and mounts it at /sdcard
    logger.error("SD card initialization failed: {}", ec.message());
    return;
  }

Public Types

enum class Interface : uint8_t

Which interface a configured card uses.

Values:

enumerator Spi
enumerator Sdmmc

Public Functions

explicit SdCard(const Config &config)

Construct the component. Does not touch hardware until initialize().

Parameters:

config – Configuration.

~SdCard()

Unmounts the volume (if mounted) and releases the card, host and, when the component initialized it, the SPI bus.

bool initialize(std::error_code &ec)

Bring up the host (SPI device / SDMMC slot, LDO), probe the card and, with Config::mount_on_initialize, mount its FAT volume.

Parameters:

ec[out] Set on failure: invalid configuration (`invalid_argument`), the host could not be initialized (`io_error`), no card answered (`no_such_device` &#8212; check the card, the wiring and the pull-ups), or a mount failure (see mount()). Nothing stays initialized on failure.

Returns:

true on success.

bool initialize()

Convenience overload of initialize() that ignores errors.

bool mount(std::error_code &ec)

Mount the card’s FAT volume at Config::mount_point.

Parameters:

ec[out] Set on failure: not initialized (`not_connected`), every FatFs drive slot in use (`device_or_resource_busy` &#8212; raise CONFIG_FATFS_VOLUME_COUNT), no FAT filesystem on the card and Config::format_if_mount_failed off (`no_such_device` &#8212; see format()), or the mount / VFS registration failed (`io_error`).

Returns:

true if the volume is mounted (also when it already was).

bool mount()

Convenience overload of mount() that ignores errors.

bool unmount(std::error_code &ec)

Unmount the FAT volume, releasing Config::mount_point. Files still open there become invalid. The card stays initialized: card() remains valid and mount() may be called again.

Parameters:

ec[out] Set on failure (`not_connected` if not initialized).

Returns:

true if the volume is unmounted (also when it already was).

bool unmount()

Convenience overload of unmount() that ignores errors.

bool format(std::error_code &ec)

Create a fresh FAT filesystem on the card (erasing everything on it), using Config::allocation_unit_size. The volume is unmounted first if it was mounted, and mounted again afterwards.

Parameters:

ec[out] Set on failure (`not_connected` if not initialized, else `io_error`).

Returns:

true if the card was formatted.

bool format()

Convenience overload of format() that ignores errors.

bool deinitialize(std::error_code &ec)

Release everything: unmount, detach the card from the host, delete the LDO handle and free the SPI bus if the component initialized it. The destructor calls this.

Parameters:

ec[out] Set on failure (the object is still deinitialized).

Returns:

true on success.

bool deinitialize()

Convenience overload of deinitialize() that ignores errors.

bool is_initialized() const

Whether initialize() succeeded (the card is probed and card() is valid).

bool is_mounted() const

Whether the FAT volume is currently mounted at mount_point().

Interface interface() const

The interface the card is configured on.

inline const std::string &mount_point() const

The VFS path the volume is (or would be) mounted at.

sdmmc_card_t *card() const

The initialized card, for raw sector access or to hand to another owner (e.g. `esppUsbDevice::MscMedium::sd_card`). Valid from a successful initialize() until deinitialize(); the SdCard keeps owning it. nullptr when not initialized.

Note

Whoever uses the card directly must do so while the volume is NOT mounted here (unmount() first): FatFs and a raw writer must not share the card.

std::optional<CardInfo> card_info() const

What the card reported at initialize(); nullopt if not initialized.

std::optional<VolumeInfo> volume_info() const

Total / free space on the mounted volume; nullopt if not mounted.

void print_info(FILE *out = stdout) const

Print the card’s properties (what `sdmmc_card_print_info()` prints).

Parameters:

out – Stream to print to (default stdout).

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

struct CardInfo

What the card reported about itself at initialize().

Public Members

std::string name

Product name from the card’s CID register.

uint64_t capacity_bytes = {0}

Total capacity.

uint32_t sector_size = {0}

Bytes per sector (512 for every SD card).

uint32_t sector_count = {0}

Number of sectors.

uint32_t frequency_khz = {0}

Bus clock actually in use.

uint8_t bus_width = {1}

Data lines in use (SDSPI: 1).

bool high_capacity = {false}

SDHC / SDXC (block addressing).

bool is_mmc = {false}

An (e)MMC device rather than an SD card.

Interface interface = Interface::Spi

The interface it is on.

struct Config

Configuration for the SdCard.

Public Members

std::variant<SpiConfig, SdmmcConfig> interface = SpiConfig{}

Which interface the card is on and how it is wired.

std::string mount_point = {"/sdcard"}

VFS path the FAT volume is mounted at.

bool mount_on_initialize = {true}

Mount the FAT volume at the end of initialize(). Leave false when the card is first going elsewhere (e.g. to a USB host) and call mount() later.

bool format_if_mount_failed = {false}

If the card has no FAT filesystem, create one when mounting (this erases whatever is on the card). Off by default: mount() then fails with `std::errc::no_such_device` and format() is available.

int max_files = {5}

Files the application may keep open at once.

size_t allocation_unit_size = {16 * 1024}

FAT allocation unit (cluster) size in bytes used when the card is formatted; 0 = FatFs picks one from the card size. Larger clusters make big files faster and small files wasteful.

bool disk_status_check = {false}

Ask the card for its status before every FAT operation, so a card removed while mounted is noticed instead of returning stale data; costs a command per operation.

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Logger verbosity.

struct SdmmcConfig

The card is on the SDMMC (SDIO) peripheral: ESP32, ESP32-S3, ESP32-P4.

Public Members

int slot = {1}

SDMMC slot. ESP32: slot 0 (8-bit capable, shares pins with flash on some modules) or slot 1 (4-bit, the usual choice); ESP32-S3 / -P4: any slot, pins are routed through the GPIO matrix.

uint8_t bus_width = {4}

Data bus width: 1 or 4.

gpio_num_t clk = {GPIO_NUM_NC}

Pins (targets with SOC_SDMMC_USE_GPIO_MATRIX only, e.g. ESP32-S3 / -P4; the ESP32 uses its fixed slot pins and ignores these). GPIO_NUM_NC keeps the slot’s default pin. d1..d3 are unused with bus_width 1.

int frequency_khz = {SDMMC_FREQ_HIGHSPEED}

Bus clock in kHz: SDMMC_FREQ_DEFAULT (20 MHz), SDMMC_FREQ_HIGHSPEED (40 MHz), or SDMMC_FREQ_PROBING (400 kHz) for marginal wiring.

gpio_num_t card_detect = {GPIO_NUM_NC}

Card-detect input, if wired.

gpio_num_t write_protect = {GPIO_NUM_NC}

Write-protect input, if wired.

int ldo_channel = {-1}

On-chip LDO channel that powers the card’s IO rail, or -1 if the card is powered externally. The ESP32-P4 feeds the SD pads from LDO_VO4 (channel 4): without it the bus floats and the card never answers.

struct SpiConfig

The card is attached to an SPI bus (SDSPI). Works on every target.

Public Members

spi_host_device_t host = {SPI2_HOST}

SPI peripheral the card is on.

gpio_num_t cs = {GPIO_NUM_NC}

Card chip-select pin.

bool initialize_bus = {false}

Initialize the SPI bus (mosi / miso / sclk below) in initialize() and free it in deinitialize(). Leave false when the application or BSP already owns the bus (e.g. it is shared with a display), in which case only `host` and `cs` are used.

gpio_num_t mosi = {GPIO_NUM_NC}

Bus MOSI (only with initialize_bus).

gpio_num_t miso = {GPIO_NUM_NC}

Bus MISO (only with initialize_bus).

gpio_num_t sclk = {GPIO_NUM_NC}

Bus SCLK (only with initialize_bus).

int max_transfer_size = {4092}

Largest transfer the bus will carry, in bytes (only with initialize_bus). Multi-sector reads need at least the sector size (512).

int frequency_khz = {SDMMC_FREQ_DEFAULT}

SPI clock while talking to the card, in kHz. SDSPI supports 400 kHz up to 20 MHz (SDMMC_FREQ_DEFAULT).

gpio_num_t card_detect = {GPIO_NUM_NC}

Card-detect input, if wired.

gpio_num_t write_protect = {GPIO_NUM_NC}

Write-protect input, if wired.

struct VolumeInfo

Space on the mounted FAT volume.

Public Members

uint64_t total_bytes = {0}

Volume size.

uint64_t free_bytes = {0}

Unallocated space.