SD Card
Library: Arduino SD (included with ESP32 Arduino core)
Access: SD global object, via SPI bus
The M5Cardputer has a built-in microSD slot accessed over SPI. Use it for storing WAV recordings, log files, configuration, images, or any persistent data.
Pin Assignments
| Signal | GPIO |
|---|---|
| SCK | 40 |
| MISO | 39 |
| MOSI | 14 |
| CS | 12 |
These are pre-configured by M5Unified. You only need to pass CS to SD.begin().
Mount / Init
#include <SD.h>
void setup() {
M5Cardputer.begin();
SPI.begin(40, 39, 14, 12); // SCK, MISO, MOSI, CS
if (!SD.begin(12, SPI, 25000000)) { // CS, SPI bus, frequency (25 MHz)
M5Cardputer.Display.println("SD init failed!");
return;
}
uint8_t cardType = SD.cardType();
uint64_t cardSize = SD.cardSize() / (1024 * 1024); // MB
if (cardType == CARD_NONE) {
M5Cardputer.Display.println("No SD card");
} else {
M5Cardputer.Display.printf("SD Card: %llu MB\n", cardSize);
}
}
Card Type Constants
| Constant | Meaning |
|---|---|
CARD_NONE |
No card / unknown |
CARD_MMC |
MMC card |
CARD_SD |
SD card |
CARD_SDHC |
SDHC card (most common) |
File Operations
All file operations use fs::FS abstraction via the File class.
Open a File
File file = SD.open("/data.txt"); // Read mode (default)
File file = SD.open("/data.txt", FILE_WRITE); // Write mode (create/truncate)
File file = SD.open("/data.txt", FILE_APPEND); // Append mode
Read
// Read byte by byte
while (file.available()) {
char c = file.read();
// ...
}
// Read into buffer
uint8_t buf[256];
size_t bytesRead = file.read(buf, sizeof(buf));
// Seek to position
file.seek(44); // Skip WAV header (44 bytes)
Write
// Write text
file.print("Hello, SD!");
file.println(" line 2");
// Write binary
uint8_t data[] = {0x00, 0xFF, 0x55};
file.write(data, sizeof(data));
Close
File / Directory Management
// Check existence
if (SD.exists("/config.json")) { ... }
// Delete
SD.remove("/old_file.txt");
// Rename
SD.rename("/old.txt", "/new.txt");
// Create directory
SD.mkdir("/logs");
// Remove directory (must be empty)
SD.rmdir("/empty_dir");
Directory Listing
File dir = SD.open("/");
while (File entry = dir.openNextFile()) {
M5Cardputer.Display.printf("%s %u bytes %s\n",
entry.name(),
entry.size(),
entry.isDirectory() ? "[DIR]" : "");
entry.close();
}
dir.close();
Space Info
uint64_t total = SD.totalBytes();
uint64_t used = SD.usedBytes();
uint64_t free = total - used;
M5Cardputer.Display.printf("Total: %llu MB\n", total / 1024 / 1024);
M5Cardputer.Display.printf("Free: %llu MB\n", free / 1024 / 1024);
WAV Recording to SD
Combining Microphone, Speaker, and SD card — see the full example at:
Minimal WAV Write Snippet
struct WAVHeader {
char riff[4] = {'R','I','F','F'};
uint32_t fileSize = 0;
char wave[4] = {'W','A','V','E'};
char fmt[4] = {'f','m','t',' '};
uint32_t fmtSize = 16;
uint16_t audioFormat = 1; // PCM
uint16_t numChannels = 1; // Mono
uint32_t sampleRate = 16000;
uint32_t byteRate = 16000 * 2;
uint16_t blockAlign = 2;
uint16_t bitsPerSample = 16;
char data[4] = {'d','a','t','a'};
uint32_t dataSize = 0;
};
void saveWAV(const char* path, int16_t* samples, size_t count) {
File file = SD.open(path, FILE_WRITE);
WAVHeader header;
header.fileSize = 36 + count * sizeof(int16_t);
header.dataSize = count * sizeof(int16_t);
file.write((uint8_t*)&header, sizeof(WAVHeader));
file.write((uint8_t*)samples, count * sizeof(int16_t));
file.close();
}
Quick Example: Logging to SD
#include <SD.h>
#include <SPI.h>
void setup() {
M5Cardputer.begin();
SPI.begin(40, 39, 14, 12);
SD.begin(12, SPI, 25000000);
// Create log header once
if (!SD.exists("/log.csv")) {
File f = SD.open("/log.csv", FILE_WRITE);
f.println("timestamp,event");
f.close();
}
}
void loop() {
M5Cardputer.update();
if (M5Cardputer.Keyboard.isChange() && M5Cardputer.Keyboard.isPressed()) {
M5Cardputer.Keyboard.updateKeysState();
for (char c : M5Cardputer.Keyboard.keysState().word) {
File f = SD.open("/log.csv", FILE_APPEND);
f.printf("%lu,%c\n", millis(), c);
f.close();
}
}
}