# SD Card Component for ESP-IDF
Simple SD card driver supporting both SDIO and SPI modes with hot-plug detection.
## Features
- **SDIO mode**: 4-bit and 1-bit modes
- **SPI mode**: For boards without SDIO, supports external SPI bus
- **Hot-plug detection**: Optional card detect GPIO with callbacks
- **Space monitoring**: Track total/free/used space
- **Simple file API**: Arduino-style wrappers that auto-prepend mount point
- **Error tracking**: Automatic status changes after repeated failures
## Installation
```bash
idf.py add-dependency "drfhaust/sd-card"
```
## Quick Start
### SDIO Mode (Default)
```c
#include "sd_card.h"
void app_main(void)
{
// Use defaults (SDIO 4-bit mode)
if (sd_card_init(NULL) == ESP_OK) {
// Write a file
FILE *f = sd_fopen("test.txt", "w");
if (f) {
fprintf(f, "Hello SD Card!\n");
sd_fclose(f);
}
}
}
```
### SPI Mode
```c
#include "sd_card.h"
void app_main(void)
{
sd_card_config_t cfg = SD_CARD_SPI_DEFAULT();
cfg.spi_mosi = 23;
cfg.spi_miso = 19;
cfg.spi_clk = 18;
cfg.spi_cs = 5;
sd_card_init(&cfg);
}
```
### SPI Mode with External Bus
If you already have an SPI bus configured (e.g., sharing with other devices):
```c
#include "sd_card.h"
void app_main(void)
{
// Your existing SPI bus init
spi_bus_config_t bus_cfg = { /* ... */ };
spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
// Use external SPI bus for SD card
sd_card_config_t cfg = SD_CARD_SPI_EXTERNAL(SPI2_HOST, GPIO_NUM_5);
sd_card_init(&cfg);
}
```
### Hot-Plug Detection (Optional)
Hot-plug detection requires `detect_gpio` to be configured. If not set (-1), the card is assumed to be always present.
```c
#include "sd_card.h"
void on_status_change(sd_card_status_t status, void *ctx)
{
switch (status) {
case SD_STATUS_AVAILABLE:
printf("Card ready!\n");
break;
case SD_STATUS_NO_CARD:
printf("Card removed\n");
break;
case SD_STATUS_ERROR:
printf("Card error!\n");
break;
}
}
void app_main(void)
{
sd_card_config_t cfg = SD_CARD_SDIO_DEFAULT();
cfg.detect_gpio = GPIO_NUM_4; // Enable hot-plug detection
cfg.detect_active_low = true; // LOW = card inserted
sd_card_on_status_change(on_status_change, NULL);
sd_card_init(&cfg);
// Only poll if hot-plug detection is available
if (sd_card_hotplug_available()) {
while (1) {
sd_card_check(); // Handles mount/unmount automatically
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
```
## Configuration
### SDIO Configuration
| Field | Description | Default |
|-------|-------------|---------|
| `sdio_clk` | CLK pin | 12 |
| `sdio_cmd` | CMD pin | 11 |
| `sdio_d0` | D0 pin | 13 |
| `sdio_d1` | D1 pin (-1 for 1-bit) | 14 |
| `sdio_d2` | D2 pin (-1 for 1-bit) | 9 |
| `sdio_d3` | D3 pin (-1 for 1-bit) | 10 |
### SPI Configuration
| Field | Description | Default |
|-------|-------------|---------|
| `spi_mosi` | MOSI pin | 23 |
| `spi_miso` | MISO pin | 19 |
| `spi_clk` | CLK pin | 18 |
| `spi_cs` | CS pin | 5 |
| `spi_host` | SPI host (-1 = create new) | -1 |
### Common Options
| Field | Description | Default |
|-------|-------------|---------|
| `detect_gpio` | Card detect pin (-1 = none) | -1 |
| `detect_active_low` | TRUE = LOW when inserted | true |
| `mount_point` | VFS mount path | "/sdcard" |
| `max_files` | Max open files | 5 |
| `freq_khz` | Bus frequency (0 = default) | 0 |
## API Reference
### Initialization
```c
esp_err_t sd_card_init(const sd_card_config_t *config); // NULL = SDIO defaults
void sd_card_deinit(void);
```
### Status
```c
sd_card_status_t sd_card_get_status(void); // Current status
bool sd_card_is_available(void); // Ready for use?
bool sd_card_is_inserted(void); // Physically inserted?
void sd_card_get_info(sd_card_info_t *info); // Full info
const char *sd_card_get_mount_point(void); // Get mount path
```
### Hot-Plug (Optional)
```c
bool sd_card_hotplug_available(void); // Check if detect_gpio is configured
bool sd_card_check(void); // Poll for changes (no-op if no detect pin)
void sd_card_on_status_change(sd_card_callback_t cb, void *ctx);
```
### File Operations
All paths are relative to mount point (auto-prepended):
```c
FILE *sd_fopen(const char *filename, const char *mode);
int sd_fclose(FILE *f);
size_t sd_fwrite(const void *ptr, size_t size, size_t count, FILE *f);
size_t sd_fread(void *ptr, size_t size, size_t count, FILE *f);
int sd_fflush(FILE *f);
int sd_fseek(FILE *f, long offset, int whence);
long sd_ftell(FILE *f);
int sd_remove(const char *filename);
bool sd_exists(const char *filename);
long sd_file_size(const char *filename);
esp_err_t sd_mkdir(const char *path);
```
### Error Tracking
```c
void sd_card_report_fail(void); // Report failure (3 = error state)
void sd_card_report_success(void); // Reset failure counter
esp_err_t sd_card_test_write(void); // Test write capability
```
## Status Codes
| Status | Description |
|--------|-------------|
| `SD_STATUS_NO_CARD` | No card detected |
| `SD_STATUS_AVAILABLE` | Card mounted and ready |
| `SD_STATUS_MOUNT_FAIL` | Card detected but mount failed |
| `SD_STATUS_WRITE_FAIL` | Mounted but write test failed |
| `SD_STATUS_ERROR` | Too many consecutive failures |
## License
MIT License - Olaifa Oluwadara Daniel
idf.py add-dependency "drfhaust/sd-card^1.0.0"