Chapter 150: SD Card and SDIO Interface

Chapter Objectives

Upon completing this chapter, you will be able to:

  • Understand different types of SD cards (SD, SDHC, SDXC) and their specifications.
  • Differentiate between SD mode (using the SDMMC peripheral) and SPI mode for SD card communication.
  • Learn about the FAT filesystem and its relevance to SD cards.
  • Initialize and mount SD cards using ESP-IDF drivers and the Virtual File System (VFS).
  • Perform standard file operations (create, read, write, delete, list directories) on an SD card.
  • Understand the role of sdmmc_host_tsdmmc_slot_config_t (for SD mode), and sdspi_device_config_t (for SPI mode).
  • Identify which ESP32 variants support the SDMMC host peripheral versus those limited to SPI mode.
  • Troubleshoot common problems encountered when interfacing SD cards with ESP32 devices.

Introduction

In many embedded applications, the need for persistent data storage that exceeds the internal flash capacity of a microcontroller is common. Whether it’s for logging sensor data over extended periods, storing configuration files, hosting web server content, or saving images and audio, external storage becomes essential. Secure Digital (SD) cards offer a convenient, removable, and high-capacity solution for these needs.

This chapter explores how to integrate SD cards with ESP32 microcontrollers using the ESP-IDF. We will cover the two primary communication modes—SPI mode and the faster SD mode (via the SDMMC peripheral on compatible ESP32 variants)—and learn how to manage files using the FAT filesystem through ESP-IDF’s Virtual File System (VFS) layer. Practical examples will guide you through initializing SD cards and performing common file operations.

Theory

SD Card Family

SD cards come in various types, primarily differing in capacity and the underlying filesystem they are typically formatted with:

  • SD (Standard Capacity): Capacities up to 2GB. Usually formatted with FAT16.
  • SDHC (High Capacity): Capacities from over 2GB up to 32GB. Usually formatted with FAT32.
  • SDXC (Extended Capacity): Capacities from over 32GB up to 2TB (and theoretically beyond). Usually formatted with exFAT. ESP-IDF’s FatFs library primarily supports FAT16/FAT32; exFAT support might require additional configuration or libraries.
  • microSD Cards: Smaller form factor versions of the above, commonly used with ESP32 development boards and modules.
  • Speed Classes: Indicate minimum sequential write speeds (e.g., Class 2, 4, 6, 10; UHS Class 1, 3). Higher class numbers mean faster minimum write speeds.
Feature SD (Standard Capacity) SDHC (High Capacity) SDXC (Extended Capacity)
Capacity Range Up to 2GB >2GB up to 32GB >32GB up to 2TB (theoretically more)
Typical Filesystem (Default) FAT16 FAT32 exFAT
Addressing Mode Byte Addressing Block Addressing (512 bytes/block) Block Addressing (512 bytes/block)
Primary ESP-IDF FatFs Support Yes (FAT16) Yes (FAT32) FAT32 (if formatted as such within 32GB limit for some tools).
Native exFAT support in ESP-IDF might require extra configuration or specific FatFs versions/settings. Default FatFs often focuses on FAT16/FAT32.
Common Use Older devices, very small storage needs. Most common for general-purpose embedded storage, logging, configurations. Large media files, extensive data logging, high-capacity needs.
Notes Less common now. Widely supported and good balance for ESP32 projects. Ensure your application and ESP-IDF setup can handle exFAT if used, or reformat to FAT32 if capacity allows and exFAT is problematic.

Communication Modes

ESP32 devices can communicate with SD cards using two primary modes:

  • SPI Mode:
    • Uses a standard Serial Peripheral Interface (SPI) bus.
    • Requires fewer pins: MOSI (Master Out Slave In), MISO (Master In Slave Out), SCLK (Serial Clock), and CS (Chip Select).
    • Simpler to implement in terms of hardware connections and supported by virtually all microcontrollers with an SPI peripheral, including all ESP32 variants.
    • Generally slower than SD mode.
    • ESP-IDF uses the sdspi_host.h driver for this mode.

  • SD Mode (SDIO – Secure Digital Input Output):
    • Uses a dedicated SD/MMC host controller peripheral (if available on the ESP32 variant).
    • Offers higher data transfer rates than SPI mode.
    • Can operate in 1-bit or 4-bit data bus modes:
      • 1-bit SD mode: Uses CLK, CMD, and DAT0 lines.
      • 4-bit SD mode: Uses CLK, CMD, and DAT0-DAT3 lines for higher throughput. This is the most common high-performance mode.
    • ESP-IDF uses the sdmmc_host.h driver for this mode.
Feature SPI Mode SD Mode (SDIO via SDMMC)
Primary Interface Standard SPI Bus Dedicated SD/MMC Bus (SDIO protocol)
Typical Pins Required 4 pins: MOSI, MISO, SCLK, CS
  • 1-bit mode: 3 pins (CLK, CMD, DAT0)
  • 4-bit mode: 6 pins (CLK, CMD, DAT0-DAT3)
Data Transfer Speed Slower (typically up to 20-25 MHz SPI clock) Faster (typically up to 40-50 MHz SD clock, or higher with UHS modes on capable cards/hosts)
Complexity Simpler hardware, widely supported. More complex protocol, requires SDMMC host peripheral. Pin requirements and pull-ups need care.
ESP-IDF Driver sdspi_host.h, esp_vfs_fat_sdspi_mount() sdmmc_host.h, esp_vfs_fat_sdmmc_mount()
ESP32 Variant Support All ESP32 variants (with SPI peripheral). ESP32 (original), ESP32-S3 (variants with SDMMC host).
Not available on ESP32-S2, C3, C6, H2.
Pull-up Resistors Generally not strictly required by SPI protocol itself, but often recommended on CS and MISO for stability, or if sharing bus. Crucial: Typically required on CMD and DAT0-3 lines (10k-50kΩ to 3.3V) for reliable operation, especially at higher speeds.
Pros
  • Uses fewer dedicated pins if SPI bus is shared.
  • Simpler to implement on any MCU with SPI.
  • Universally supported by SD cards.
  • Significantly higher performance (throughput).
  • More efficient for large data transfers.
  • Native SD card interface.
Cons
  • Lower data transfer rates.
  • Can be a bottleneck for high-speed logging or media.
  • Requires dedicated SDMMC peripheral on MCU.
  • More pins for 4-bit mode.
  • More sensitive to signal integrity and pull-up requirements.

FAT Filesystem (File Allocation Table)

SD cards are typically pre-formatted with a FAT filesystem (FAT16, FAT32, or exFAT). This filesystem allows operating systems and microcontrollers to organize data into files and directories.

  • FAT16: Used for smaller capacity cards (typically < 2GB).
  • FAT32: Most common for SDHC cards (2GB-32GB). Offers support for larger volumes and files than FAT16.
  • exFAT: Designed for larger capacity SDXC cards (>32GB).

ESP-IDF integrates the FatFs library, a generic FAT/exFAT filesystem module for small embedded systems. This library enables reading from and writing to FAT-formatted SD cards.

Virtual File System (VFS) in ESP-IDF

ESP-IDF includes a Virtual File System (VFS) component. The VFS provides an abstraction layer that allows standard C file I/O functions (like fopen()fread()fwrite()fclose()opendir()readdir()stat(), etc.) to work seamlessly with different underlying filesystems and storage devices.

graph TD
    A[Start: Use SD Card] --> B{Choose Communication Mode};
    style A fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6
    style B fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E

    B -- SPI Mode --> C["Configure <b>spi_bus_config_t</b> <br> (MOSI, MISO, SCLK pins)"];
    C --> D["Initialize SPI Bus <br> <b>spi_bus_initialize()</b>"];
    D --> E["Configure <b>sdspi_device_config_t</b> <br> (CS pin, host_id)"];
    E --> F["Call <b>esp_vfs_fat_sdspi_mount()</b> <br> (mount_point, slot_config, mount_config, &card)"];
    style C fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style D fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style E fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style F fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF

    B -- SD Mode (SDMMC) --> G["Configure <b>sdmmc_host_t</b> <br> (flags for 1/4 bit, freq)"];
    G --> H["Configure <b>sdmmc_slot_config_t</b> <br> (CLK, CMD, DAT0-3 pins, width)"];
    H --> I["Call <b>esp_vfs_fat_sdmmc_mount()</b> <br> (mount_point, host_config, slot_config, mount_config, &card)"];
    style G fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style H fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style I fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF

    F --> J{Mount Successful?};
    I --> J;
    style J fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E

    J -- Yes --> K["SD Card Initialized & FAT Filesystem Mounted <br> e.g., at <b>/sdcard</b>"];
    K --> L["Access SD Card via VFS <br> using standard C file I/O: <br> <b>fopen(<b>/sdcard/file.txt, w</b>)</b> <br> <b>fread()</b>, <b>fwrite()</b>, <b>fclose()</b>, etc."];
    style K fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46
    style L fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF

    J -- No (Error) --> M["Error: Mount Failed <br> (Check wiring, power, card format, logs)"];
    style M fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B

    L --> N[Perform File Operations];
    N --> O{Finished with SD Card?};
    style N fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style O fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E

    O -- Yes --> P["Unmount Filesystem <br> <b>esp_vfs_fat_sdcard_unmount()</b>"];
    P --> Q["Deinitialize Bus (if SPI & not shared) <br> <b>spi_bus_free()</b>"];
    P --> R[End];
    style P fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style Q fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    style R fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46
    O -- No (Continue Operations) --> N;


    classDef LStartStyle fill:#EDE9FE,stroke:#5B21B6,stroke-width:1.5px,color:#5B21B6
    classDef LProcessStyle fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    classDef LDecisionStyle fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E
    classDef LSuccessStyle fill:#D1FAE5,stroke:#059669,stroke-width:1.5px,color:#065F46
    classDef LErrorStyle fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B

When an SD card is initialized and a FAT filesystem is detected, it can be “mounted” to a specific path in the VFS (e.g., /sdcard). After mounting, you can access files on the SD card using paths like /sdcard/mylog.txt.

The functions esp_vfs_fat_sdmmc_mount() (for SD mode) and esp_vfs_fat_sdspi_mount() (for SPI mode) handle the initialization of the SD card, FatFs, and registration with the VFS.

ESP-IDF SD Card Drivers

  • sdmmc_host.h: Provides APIs for configuring the SDMMC host peripheral (for SD mode). Key structure: sdmmc_host_t.
  • sdspi_host.h: Provides APIs for configuring an SPI peripheral to communicate with an SD card in SPI mode.
  • sdmmc_cmd.h: Contains definitions and helper functions for SD commands and data structures.
  • esp_vfs_fat.h: Provides helper functions to mount FAT filesystems from SD cards (using either SDMMC or SPI host) to the VFS.
Header File / Component Key Structures / Functions Purpose
sdmmc_host.h
(for SD Mode)
  • sdmmc_host_t
  • sdmmc_slot_config_t
  • SDMMC_HOST_DEFAULT()
  • SDMMC_SLOT_CONFIG_DEFAULT()
  • sdmmc_host_init()
  • sdmmc_host_init_slot()
  • sdmmc_card_init() (used internally by mount)
Configuration and initialization of the SDMMC host peripheral for communication with SD cards in native SD mode (1-bit or 4-bit). Defines host capabilities and slot pin configurations.
sdspi_host.h
(for SPI Mode)
  • sdspi_device_config_t
  • SDSPI_DEVICE_CONFIG_DEFAULT()
  • sdspi_host_init() (usually not called directly)
  • sdspi_host_init_device() (used internally by mount)
Configuration of an SPI peripheral to act as a host for an SD card operating in SPI mode. Defines SPI bus and CS pin for the SD card device.
driver/spi_common.h
(Used with SPI Mode)
  • spi_bus_config_t
  • spi_bus_initialize()
  • spi_bus_free()
General SPI bus initialization and deinitialization, used before configuring an SD card device on that bus in SPI mode.
esp_vfs_fat.h
(Filesystem Layer)
  • esp_vfs_fat_sdmmc_mount_config_t
  • esp_vfs_fat_sdmmc_mount()
  • esp_vfs_fat_sdspi_mount()
  • esp_vfs_fat_sdcard_unmount()
Provides high-level functions to initialize an SD card (either in SD or SPI mode), initialize the FatFs library, and register the FAT filesystem with the ESP-IDF Virtual File System (VFS) at a specified mount point.
sdmmc_cmd.h
(Common SD Definitions)
  • sdmmc_card_t
  • sdmmc_card_print_info()
  • Various SD command definitions and response types (used internally by drivers).
Contains common data structures (like sdmmc_card_t to hold card information) and helper functions (like printing card info) used by both SDMMC and SDSPI drivers. Also defines SD protocol level commands.

Practical Examples

Prerequisites:

  • ESP-IDF v5.x installed and configured with VS Code.
  • An ESP32 development board.
  • A microSD card and a compatible SD card module/socket wired to the ESP32.
  • Ensure the microSD card is formatted with FAT32 for these examples.

Example 1: SD Card in SPI Mode

This example demonstrates initializing an SD card in SPI mode, mounting it, and performing basic file operations.

1. Connections (Example for SPI Mode):

  • SD Card MOSI (DI/CMD on some breakouts) -> ESP32 SPI MOSI pin (e.g., GPIO23 for VSPI)
  • SD Card MISO (DO) -> ESP32 SPI MISO pin (e.g., GPIO19 for VSPI)
  • SD Card SCLK (CLK) -> ESP32 SPI SCLK pin (e.g., GPIO18 for VSPI)
  • SD Card CS (Chip Select) -> Any ESP32 GPIO (e.g., GPIO5 for VSPI_CS)
  • VCC -> 3.3V (ESP32)
  • GND -> GND (ESP32)

Warning: Pin assignments can vary. Always check your ESP32 board’s documentation for default SPI pins. The example uses VSPI (SPI3_HOST on original ESP32, SPI2_HOST on S2/S3).

Flow:

sequenceDiagram
    participant App as User Application
    participant VFS as ESP-IDF VFS Layer
    participant FatFs as FatFs Library
    participant SDDriver as SD Card Driver (SPI/SDMMC)
    participant SDCard as SD Card Hardware

    App->>+VFS: fopen("/sdcard/log.txt", "w")
    VFS->>+FatFs: Resolve path, <br>request file open (f_open)
    FatFs->>+SDDriver: Read/Verify FAT, <br>directory entries (disk_read)
    SDDriver->>+SDCard: Send SD Commands (CMDx),<br>Read Data Blocks
    SDCard-->>-SDDriver: Data / Status
    SDDriver-->>-FatFs: Sector Data
    FatFs-->>-VFS: File handle / Error
    VFS-->>-App: FILE* fp / NULL

    alt File Opened Successfully
        App->>+VFS: fwrite(buffer, size, count, fp)
        VFS->>+FatFs: Request file write (f_write)
        FatFs->>+SDDriver: Write data to sectors, <br>update FAT (disk_write)
        SDDriver->>+SDCard: Send SD Commands (CMDx),<br> Write Data Blocks
        SDCard-->>-SDDriver: Status
        SDDriver-->>-FatFs: Write Status
        FatFs-->>-VFS: Bytes written / Error
        VFS-->>-App: Bytes written / Error
        
        App->>+VFS: fclose(fp)
        VFS->>+FatFs: Request file close (f_close)
        FatFs->>+SDDriver: Flush buffers, <br>update directory entry (disk_write)
        SDDriver->>+SDCard: Send SD Commands, <br>Write Data Blocks
        SDCard-->>-SDDriver: Status
        SDDriver-->>-FatFs: Status
        FatFs-->>-VFS: Success / Error
        VFS-->>-App: Success / Error
    else File Open Failed
        App->>VFS: Handle error (e.g., log, retry)
    end

2. Code (main/sd_spi_example_main.c):

C
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "driver/spi_common.h"
#include "driver/sdspi_host.h"
#include "sdmmc_cmd.h"

static const char *TAG = "SD_SPI_Example";

// Define the mount point for the SD card
#define MOUNT_POINT "/sdcard"

// SPI Bus configuration (VSPI_HOST is SPI3 on ESP32 classic, HSPI_HOST is SPI2)
// For ESP32-S2/S3, these are SPI2_HOST and SPI3_HOST respectively.
// For ESP32-C3, only SPI2_HOST is available.
#define SPI_BUS_HOST    SPI2_HOST // Example: Using SPI2 host (often referred to as HSPI on ESP32 classic)

// Pin Configuration for SPI mode (Update these to match your wiring)
// Note: Using default SPI pins for HSPI on ESP32 classic for this example.
// Check your board's schematic or ESP-IDF docs for correct pins for your chosen SPI_BUS_HOST.
#define PIN_NUM_MISO    12 // Default HSPI_MISO
#define PIN_NUM_MOSI    13 // Default HSPI_MOSI
#define PIN_NUM_CLK     14 // Default HSPI_SCLK
#define PIN_NUM_CS      15 // Default HSPI_CS

void app_main(void)
{
    esp_err_t ret;

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false, // Set to true to format if mount fails
        .max_files = 5,                  // Max number of open files
        .allocation_unit_size = 16 * 1024
    };
    sdmmc_card_t *card;
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card in SPI mode");

    // Initialize SPI bus
    spi_bus_config_t bus_cfg = {
        .mosi_io_num = PIN_NUM_MOSI,
        .miso_io_num = PIN_NUM_MISO,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000, // Max transfer size in bytes
    };
    ret = spi_bus_initialize(SPI_BUS_HOST, &bus_cfg, SDSPI_DEFAULT_DMA);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret));
        return;
    }

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = SPI_BUS_HOST;

    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(mount_point, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set format_if_mount_failed = true.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        spi_bus_free(SPI_BUS_HOST); // Free SPI bus if mount fails
        return;
    }
    ESP_LOGI(TAG, "Filesystem mounted");

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);

    // --- Perform file operations ---
    const char *file_path = MOUNT_POINT "/hello.txt";
    ESP_LOGI(TAG, "Opening file %s for writing", file_path);
    FILE *f = fopen(file_path, "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        goto cleanup;
    }
    fprintf(f, "Hello from ESP32! SD Card via SPI.\n");
    fclose(f);
    ESP_LOGI(TAG, "File written successfully");

    // Check if file exists
    struct stat st;
    if (stat(file_path, &st) == 0) {
        ESP_LOGI(TAG, "File %s exists, size: %ld bytes", file_path, st.st_size);
    } else {
        ESP_LOGE(TAG, "File %s does not exist", file_path);
    }
    
    ESP_LOGI(TAG, "Opening file %s for reading", file_path);
    f = fopen(file_path, "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for reading");
        goto cleanup;
    }
    char line[128];
    fgets(line, sizeof(line), f);
    fclose(f);
    // Strip newline
    char *pos = strchr(line, '\n');
    if (pos) {
        *pos = '\0';
    }
    ESP_LOGI(TAG, "Read from file: '%s'", line);

    // --- Unmount and cleanup ---
cleanup:
    ESP_LOGI(TAG, "Unmounting filesystem");
    esp_vfs_fat_sdcard_unmount(mount_point, card);
    ESP_LOGI(TAG, "Card unmounted");

    // Deinitialize SPI bus
    spi_bus_free(SPI_BUS_HOST);
    ESP_LOGI(TAG, "SPI bus freed");
}

3. CMakeLists.txt (in main directory):

Plaintext
idf_component_register(SRCS "sd_spi_example_main.c"
                    INCLUDE_DIRS "."
                    REQUIRES esp_vfs_fat sdmmc) # sdmmc component provides common SD definitions

4. Build, Flash, and Observe Steps:

  1. Wire your SD card module to the ESP32 using the SPI pins defined.
  2. Insert a FAT32 formatted microSD card.
  3. Build and flash the project.
  4. Open the ESP-IDF Monitor. You should see logs indicating SD card initialization, mounting, file creation, reading, and unmounting.
  5. You can remove the SD card and check its contents on a PC to verify hello.txt.

Example 2: SD Card in SD Mode (4-bit using SDMMC)

This example is for ESP32 variants that have an SDMMC host peripheral (e.g., original ESP32, ESP32-S3).

1. Connections (Example for SD 4-bit Mode on ESP32 original – Slot 1):

  • SD Card CLK -> ESP32 GPIO14 (HS2_CLK)
  • SD Card CMD -> ESP32 GPIO15 (HS2_CMD)
  • SD Card DAT0 -> ESP32 GPIO2 (HS2_D0)
  • SD Card DAT1 -> ESP32 GPIO4 (HS2_D1)
  • SD Card DAT2 -> ESP32 GPIO12 (HS2_D2)
  • SD Card DAT3 -> ESP32 GPIO13 (HS2_D3)
  • VCC -> 3.3V
  • GND -> GND

Important: These pins (GPIOs 2, 4, 12, 13, 14, 15) are commonly used for SDMMC Slot 1 on ESP32-WROOM/WROVER modules. Always verify with your specific board’s schematic. Pull-up resistors (typically 10k-50k Ohm) are usually required on CMD and DAT0-3 lines for reliable SD mode operation, though some modules include them.

2. Code (main/sd_sdmmc_example_main.c):

C
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"
#include "sdmmc_cmd.h"

static const char *TAG = "SD_SDMMC_Example";

#define MOUNT_POINT "/sdcard"

void app_main(void)
{
    esp_err_t ret;
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };
    sdmmc_card_t *card;
    const char mount_point[] = MOUNT_POINT;

    ESP_LOGI(TAG, "Initializing SD card in SDMMC mode (4-bit)");

    // Configure SDMMC host peripheral
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    // For 4-line SD mode, set bus width. For 1-line mode, use SDMMC_HOST_FLAG_1BIT.
    host.flags = SDMMC_HOST_FLAG_4BIT; 
    // host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; // Or SDMMC_FREQ_DEFAULT, SDMMC_FREQ_PROBING

    // Configure SDMMC slot
    // GPIOs for SDMMC slot 1 on ESP32: CLK=14, CMD=15, D0=2, D1=4, D2=12, D3=13
    // These are defaults for SDMMC_SLOT_CONFIG_DEFAULT() when using slot 1.
    // For ESP32-S3, check its specific default pins or configure manually.
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
    
    // For ESP32, SDMMC_SLOT_CONFIG_DEFAULT uses:
    // CMD:  GPIO15
    // CLK:  GPIO14
    // D0:   GPIO2
    // D1:   GPIO4 (for 4-bit/8-bit)
    // D2:   GPIO12 (for 4-bit/8-bit)
    // D3:   GPIO13 (for 4-bit/8-bit)
    // D4-D7: Not used by default for SD (used for eMMC 8-bit)
    // slot_config.width = 4; // Explicitly set 4-bit mode if not using default flags in host

    // For robust D0 line behavior, ensure it has a pull-up resistor if not internal.
    // slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // Enable internal pullups if needed and available

    ESP_LOGI(TAG, "Mounting filesystem");
    // Note: esp_vfs_fat_sdmmc_mount initializes the host internally.
    ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set format_if_mount_failed = true.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors if needed.", esp_err_to_name(ret));
        }
        return;
    }
    ESP_LOGI(TAG, "Filesystem mounted");
    sdmmc_card_print_info(stdout, card);

    // --- Perform file operations (same as SPI example) ---
    const char *file_path = MOUNT_POINT "/hello_sdmmc.txt";
    ESP_LOGI(TAG, "Opening file %s for writing", file_path);
    FILE *f = fopen(file_path, "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        goto cleanup_sdmmc;
    }
    fprintf(f, "Hello from ESP32! SD Card via SDMMC (4-bit).\n");
    fclose(f);
    ESP_LOGI(TAG, "File written successfully");
    
    // ... (add read and stat operations similar to SPI example) ...

cleanup_sdmmc:
    ESP_LOGI(TAG, "Unmounting filesystem");
    esp_vfs_fat_sdcard_unmount(mount_point, card);
    ESP_LOGI(TAG, "Card unmounted");
    
    // Host deinitialization is handled by esp_vfs_fat_sdcard_unmount
    // if it was initialized by esp_vfs_fat_sdmmc_mount.
}

3. CMakeLists.txt (in main directory):

Plaintext
idf_component_register(SRCS "sd_sdmmc_example_main.c"
                    INCLUDE_DIRS "."
                    REQUIRES esp_vfs_fat sdmmc)

4. Build, Flash, and Observe Steps:

Similar to the SPI example, but ensure your wiring matches the SDMMC pins for your ESP32 variant.

Variant Notes

ESP32 Variant SDMMC Host Peripheral (for SD Mode) SPI Mode Support Typical Use & Notes
ESP32 (Original) Yes (2 slots)
Slot 0 (HS1_*) often for internal Flash/PSRAM.
Slot 1 (HS2_*) commonly for external SD.
Yes (Multiple SPI controllers) Supports both high-performance SD mode (4-bit recommended) and SPI mode. Ideal for applications needing fast SD access.
ESP32-S2 No Yes (Multiple SPI controllers) Limited to SPI mode for SD card communication. Performance will be lower than SD mode.
ESP32-S3 Yes (1 slot) Yes (Multiple SPI controllers) Supports high-performance SD mode (4-bit recommended) and SPI mode. Flexible GPIO mapping for SDMMC.
ESP32-C3 No Yes (Fewer SPI controllers than ESP32/S3) Limited to SPI mode. Resource constraints (RAM, CPU) might impact performance for intensive SD operations.
ESP32-C6 No Yes (Fewer SPI controllers) Limited to SPI mode. Primarily for IoT applications where SD card might be for basic logging or configuration.
ESP32-H2 No Yes (Fewer SPI controllers) Limited to SPI mode. Focused on low-power wireless; SD card use likely for minimal data storage.
  • ESP32 (Original):
    • Features two SDMMC host peripherals (Slot 0 and Slot 1).
    • Slot 1 (HS2_*) is commonly used for external SD cards. Default pins: CLK=14, CMD=15, D0=2, D1=4, D2=12, D3=13.
    • Slot 0 (HS1_*) is often connected to the integrated flash/PSRAM chip on WROVER modules and might not be available for external SD cards on such modules.
    • Can also use SPI mode on any of its SPI peripherals (SPI0-SPI3, though SPI0/1 are often for flash/PSRAM).
  • ESP32-S2:
    • No dedicated SDMMC host peripheral.
    • Must use SPI mode for SD card communication.
    • Has multiple SPI peripherals that can be used.
  • ESP32-S3:
    • Features one SDMMC host peripheral.
    • Can use SD mode (1-bit or 4-bit) for higher performance. Pin mapping for SDMMC is flexible via GPIO matrix.
    • Can also use SPI mode on any of its SPI peripherals.
  • ESP32-C3 / ESP32-C6 / ESP32-H2:
    • No dedicated SDMMC host peripheral.
    • Must use SPI mode for SD card communication.
    • Typically have fewer SPI peripherals compared to ESP32/S3. Resource constraints (RAM, CPU) might also be a factor for high-throughput SD card operations.

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
Incorrect Wiring / Pin Definitions
  • SD card not detected.
  • Mounting fails (e.g., ESP_ERR_NOT_FOUND, ESP_ERR_INVALID_STATE).
  • Intermittent errors during read/write.
  • Verify all connections:
    • SPI Mode: MOSI, MISO, SCLK, CS.
    • SD Mode: CLK, CMD, DAT0-3.
  • Check pin definitions in code: Ensure sdspi_device_config_t (SPI) or sdmmc_slot_config_t (SD mode) matches physical wiring.
  • Consult ESP32 variant datasheet and board schematic for correct default pins or GPIO matrix capabilities.
  • Look for shorts, opens, or poor solder joints.
Missing Pull-Up Resistors (SD Mode)
  • Card initialization fails, especially at higher speeds.
  • Unreliable communication, data corruption.
  • “Failed to initialize the card” errors.
  • Required for SD Mode: CMD and DAT0-3 lines usually need external pull-up resistors (10kΩ – 50kΩ to 3.3V).
  • Check if your SD card module includes them. If not, add them externally.
  • ESP32 internal pull-ups (slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP) might work for lower speeds but are generally weaker.
  • For SPI mode, pull-ups on CS and MISO are good practice.
Power Supply Issues
  • Card not detected or fails during operations.
  • ESP32 resets or becomes unstable.
  • “Brownout detector was triggered” in logs.
  • Stable 3.3V supply: SD cards can draw significant current (up to 100-200mA or more during writes).
  • Ensure your ESP32’s 3.3V regulator can handle the combined load of ESP32, SD card, and other peripherals.
  • Use decoupling capacitors (e.g., 10µF and 0.1µF) near the SD card’s VCC pin.
  • Avoid powering directly from a weak USB port if issues persist.
SD Card Not Formatted / Incompatible Filesystem
  • Mounting fails with errors like “Failed to mount filesystem.” or “No FAT partition found”.
  • esp_vfs_fat_sdmmc_mount or esp_vfs_fat_sdspi_mount returns ESP_FAIL.
  • Format card with FAT32: Use a PC to format the SD card. FAT16 is also supported. exFAT support in ESP-IDF’s default FatFs might be limited or require specific configuration.
  • Set format_if_mount_failed = true in esp_vfs_fat_sdmmc_mount_config_t to allow ESP-IDF to attempt formatting (Warning: This erases all data on the card!).
  • Ensure the card is not physically damaged or write-protected (if it’s a full-size SD card with a lock switch).
Mounting/Unmounting Issues
  • fopen() fails after supposedly successful mount.
  • Data corruption if card removed without unmounting.
  • “Too many open files” errors if max_files is too low.
  • Check mount point: Ensure you are using the correct path (e.g., “/sdcard/file.txt” if mounted at “/sdcard”).
  • Always unmount: Call esp_vfs_fat_sdcard_unmount() before removing the card or powering down to flush buffers and prevent corruption.
  • Adjust max_files in esp_vfs_fat_sdmmc_mount_config_t if you need to keep many files open simultaneously.
  • Check the return value of mount functions carefully.
SPI Bus Conflicts / Incorrect Host ID
  • SD card (SPI mode) doesn’t work if other SPI devices are on the same bus and CS lines are not handled properly.
  • Initialization fails if wrong host_id is used in sdspi_device_config_t.
  • Unique CS lines: If sharing an SPI bus, ensure each device has its own unique CS pin, and only one is active at a time.
  • Correct host_id: Match slot_config.host_id with the SPI_BUS_HOST used in spi_bus_initialize().
  • Ensure SPI bus is initialized before trying to mount the SD card in SPI mode.
SD Card Speed Issues / Timeouts
  • Operations are very slow.
  • Timeout errors during read/write.
  • Card works at lower speeds but fails at higher speeds.
  • Signal Integrity: Long wires, poor connections, or missing pull-ups (SD mode) can limit speed. Keep connections short.
  • Clock Speed: For SDMMC mode, try a lower host.max_freq_khz (e.g., SDMMC_FREQ_PROBING or SDMMC_FREQ_DEFAULT). For SPI, the clock is set during bus initialization.
  • Card Class: A slow SD card (e.g., Class 2/4) will naturally have lower performance.
  • Ensure allocation_unit_size in mount config is reasonable (e.g., 16KB or 32KB).

Exercises

  1. Directory Listing:
    • Create a few files and directories on your SD card using a PC.
    • Write an ESP32 program that mounts the SD card, then lists all files and directories in the root directory (/sdcard/) and prints their names (and types: file/directory) to the serial monitor. Use opendirreaddir, and stat or d_type from dirent.h.
  2. Data Logging with Timestamps:
    • Initialize the SNTP service (Chapter 98) to get the current time.
    • Modify the SPI or SDMMC example to create a log file (e.g., datalog.csv).
    • Periodically (e.g., every 10 seconds) write a new line to the file containing a timestamp and some dummy sensor data (e.g., “YYYY-MM-DD HH:MM:SS, Temperature: 25.5, Humidity: 45.0”).
  3. File Deletion and Renaming:
    • Write a program that:
      1. Creates a file named temp_file.txt.
      2. Writes some text into it.
      3. Closes it.
      4. Renames temp_file.txt to renamed_file.txt (use rename()).
      5. Verifies renamed_file.txt exists and temp_file.txt does not.
      6. Deletes renamed_file.txt (use unlink()).
  4. Performance Comparison (SPI vs. SD 4-bit on ESP32/S3):
    • If you have an ESP32 (original) or ESP32-S3, set up connections for both SPI mode and SD 4-bit mode (you might need to re-wire between tests or use different pins if available).
    • Write a test program that creates a moderately large file (e.g., 1MB or 5MB) by repeatedly writing a small buffer.
    • Measure the time taken to write the entire file in SPI mode and then in SD 4-bit mode.
    • Print the results to compare the throughput. Use esp_timer_get_time() for timing.

Summary

  • SD cards provide versatile external storage for ESP32 projects, supporting various capacities and speeds.
  • Communication can be achieved via SPI mode (simpler, fewer pins, slower) or SD mode (higher performance using SDMMC peripheral on compatible ESP32s).
  • ESP-IDF uses the FatFs library, integrated with its Virtual File System (VFS), to manage FAT16/FAT32 filesystems on SD cards.
  • Key ESP-IDF components include sdmmc_host.h (SD mode), sdspi_host.h (SPI mode), and esp_vfs_fat.h (mounting).
  • ESP32 (original) and ESP32-S3 have SDMMC host peripherals for SD mode. ESP32-S2, C3, C6, H2 are limited to SPI mode for SD cards.
  • Proper wiring, power supply, pull-up resistors (for SD mode), and card formatting are crucial for reliable operation.
  • Standard C file I/O functions can be used once the SD card is mounted to the VFS.

Further Reading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top