Mục tiêu
- Kết nối ESP32-C3 SuperMini với module microSD SPI.
- Viết khung code theo style Feature-based:
setupFeatureSD(),runFeatureSD(). - Kiểm tra module thẻ nhớ hoạt động (card type, size, read/write file test).
- Liệt kê thư mục, tạo cấu trúc thư mục mẫu.
Ứng dụng thực tế
- Ghi log/cấu hình vào thẻ nhớ.
- Lưu dữ liệu cảm biến dạng CSV.
- Làm nền tảng cho Part 2 (đọc ảnh BMP) và Part 3 (WiFi gallery).
Yêu cầu phần cứng
- ESP32-C3 SuperMini (Tenstar Robot)
- Module microSD SPI (loại đã có ổn áp AMS1117-3.3, sẽ ấp nguồn 5v)
- Thẻ microSD 8–32GB, format FAT32
- Dây nối ngắn, nguồn 3.3V ổn định


Sơ đồ nối dây (SPI)
Bạn có thể đổi GPIO theo board bạn đang dùng. Dưới đây là mapping gợi ý (phổ biến) — chỉ cần đúng 4 dây SPI + CS là được.
- CS → GPIO7
- SCK → GPIO4
- MOSI → GPIO6
- MISO → GPIO5
- VCC → 3V3
- GND → GND
Lưu ý quan trọng:
- Ưu tiên cấp 3.3V cho module SD để ổn định.
- Nếu
SD.begin()fail: giảm tần số SPI (10MHz → 4MHz), dây ngắn lại, kiểm tra chân CS.
Chuẩn bị thẻ nhớ
- Format FAT32.
- Tạo sẵn thư mục:
/images(để Part 2/3 dùng).
Code convention (áp dụng cho cả series)
- Feature init:
setupFeatureX()trả vềbool. - Feature loop:
runFeatureX(). - Hằng số pin:
PIN_*. - Log thống nhất:
LOGI(),LOGE(). - Tránh
delay()dài trongloop(); nếu cần thì dùngmillis().
Code Part 1 (SD health check)
/*
Project: Test hoạt động module microSD SPI
Target : ESP32-C3 SuperMini (Tenstar Robot) + microSD SPI module
Hardware notes:
- Module SD trong ảnh có AMS1117-3.3 => có thể cấp VCC = 5V (ổn áp xuống 3.3V).
- ESP32-C3 I/O là 3.3V. SPI signals vẫn 3.3V là OK.
- Nếu bạn dùng TFT chung SPI: CS phải tách riêng (SD_CS != TFT_CS).
Wiring (ví dụ - đổi theo board bạn):
- SD_CS-> GPIO10
- SCK -> GPIO4
- MISO -> GPIO5
- MOSI -> GPIO6
- VCC -> 5V
- GND -> GND
*/
#include <SPI.h>
#include <SD.h>
static const int PIN_SD_CS = 10; // sửa theo dây CS của bạn
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("SD init...");
if (!SD.begin(PIN_SD_CS, SPI, 4000000)) {
Serial.println("[ERR ] SD.begin failed");
while (true) delay(1000);
}
Serial.println("[INFO] SD OK");
Serial.printf("Card size: %llu MB\n", SD.cardSize() / (1024ULL * 1024ULL));
File f = SD.open("/test.txt", FILE_APPEND);
if (f) { f.println("hello"); f.close(); }
f = SD.open("/test.txt", FILE_READ);
if (f) { while (f.available()) Serial.write(f.read()); f.close(); }
}
void loop() {}
Cách kiểm tra module SD hoạt động
- Mở Serial Monitor 115200
- Kỳ vọng thấy:
SD OK+ Card type + size
Code 2 (SD test read/write)
/*
Project: Test hoạt động module microSD SPI
Target : ESP32-C3 SuperMini (Tenstar Robot) + microSD SPI module
Hardware notes:
- Module SD trong ảnh có AMS1117-3.3 => cấp VCC = 5V (ổn áp xuống 3.3V) là hợp lý.
- ESP32-C3 I/O là 3.3V. SPI signals vẫn 3.3V là OK.
- Nếu dùng chung SPI với TFT: CS phải tách riêng (SD_CS != TFT_CS).
Wiring (đang dùng):
- SD_CS-> GPIO10
- SCK -> GPIO4
- MISO -> GPIO5
- MOSI -> GPIO6
- VCC -> 5V
- GND -> GND
Tested baseline:
- SD.begin(PIN_SD_CS, SPI, 4000000) OK
*/
#include <Arduino.h>
#include <SPI.h>
#include <SD.h>
#define LOGI(fmt, ...) Serial.printf("[INFO] " fmt "\n", ##__VA_ARGS__)
#define LOGE(fmt, ...) Serial.printf("[ERR ] " fmt "\n", ##__VA_ARGS__)
// ===== Config =====
static const int PIN_SD_CS = 10;
static const uint32_t SD_FREQ_HZ = 4000000;
static const char* FILE_TEXT = "/test.txt";
static const char* FILE_BIN = "/test.bin";
static const char* DIR_IMAGES = "/images";
// ===== Helpers =====
static const char* cardTypeToStr(uint8_t t) {
switch (t) {
case CARD_MMC: return "MMC";
case CARD_SD: return "SDSC";
case CARD_SDHC: return "SDHC";
default: return "UNKNOWN";
}
}
uint32_t simpleChecksum(const uint8_t* data, size_t len) {
// checksum đơn giản để verify đọc/ghi nhị phân
uint32_t s = 0;
for (size_t i = 0; i < len; i++) s = (s * 131) + data[i];
return s;
}
// ===== SD functions =====
bool sdInit() {
// Best practice: CS output + HIGH trước khi init
pinMode(PIN_SD_CS, OUTPUT);
digitalWrite(PIN_SD_CS, HIGH);
LOGI("SD init... CS=%d, freq=%lu", PIN_SD_CS, (unsigned long)SD_FREQ_HZ);
if (!SD.begin(PIN_SD_CS, SPI, SD_FREQ_HZ)) {
LOGE("SD.begin failed. Check wiring/CS/power.");
return false;
}
return true;
}
bool sdHealthCheck() {
uint8_t type = SD.cardType();
if (type == CARD_NONE) {
LOGE("No SD card detected.");
return false;
}
uint64_t sizeMB = SD.cardSize() / (1024ULL * 1024ULL);
LOGI("SD OK. Type=%s, Size=%llu MB", cardTypeToStr(type), sizeMB);
return true;
}
bool sdEnsureDir(const char* dirPath) {
if (SD.exists(dirPath)) {
LOGI("Dir exists: %s", dirPath);
return true;
}
bool ok = SD.mkdir(dirPath);
LOGI("mkdir %s => %s", dirPath, ok ? "OK" : "FAIL");
return ok;
}
void sdListDir(const char* dirPath, uint8_t levels = 1) {
File dir = SD.open(dirPath);
if (!dir || !dir.isDirectory()) {
LOGE("Not a directory: %s", dirPath);
return;
}
LOGI("Listing: %s", dirPath);
File f = dir.openNextFile();
while (f) {
if (f.isDirectory()) {
LOGI(" DIR : %s", f.name());
if (levels) sdListDir(f.name(), levels - 1);
} else {
LOGI(" FILE: %s (%lu bytes)", f.name(), (unsigned long)f.size());
}
f = dir.openNextFile();
}
}
bool sdWriteText(const char* path, const char* text) {
File f = SD.open(path, FILE_WRITE); // overwrite
if (!f) {
LOGE("Open write failed: %s", path);
return false;
}
f.print(text);
f.close();
LOGI("Write text OK: %s", path);
return true;
}
bool sdAppendLine(const char* path, const char* line) {
File f = SD.open(path, FILE_APPEND);
if (!f) {
LOGE("Open append failed: %s", path);
return false;
}
f.println(line);
f.close();
LOGI("Append line OK: %s", path);
return true;
}
bool sdReadText(const char* path) {
File f = SD.open(path, FILE_READ);
if (!f) {
LOGE("Open read failed: %s", path);
return false;
}
LOGI("----- READ %s -----", path);
while (f.available()) Serial.write(f.read());
Serial.println();
LOGI("--------------------");
f.close();
return true;
}
bool sdWriteBinary(const char* path, const uint8_t* data, size_t len) {
File f = SD.open(path, FILE_WRITE); // overwrite
if (!f) {
LOGE("Open bin write failed: %s", path);
return false;
}
size_t w = f.write(data, len);
f.close();
if (w != len) {
LOGE("Binary write short. wrote=%u expect=%u", (unsigned)w, (unsigned)len);
return false;
}
LOGI("Write binary OK: %s (%u bytes)", path, (unsigned)len);
return true;
}
bool sdReadBinary(const char* path, uint8_t* out, size_t len) {
File f = SD.open(path, FILE_READ);
if (!f) {
LOGE("Open bin read failed: %s", path);
return false;
}
size_t r = f.read(out, len);
f.close();
if (r != len) {
LOGE("Binary read short. read=%u expect=%u", (unsigned)r, (unsigned)len);
return false;
}
LOGI("Read binary OK: %s (%u bytes)", path, (unsigned)len);
return true;
}
// ===== Main test flow =====
void runAllTests() {
// 1) Text tests
sdWriteText(FILE_TEXT, "IoTLabs - Test hoạt động module microSD SPI\n");
sdAppendLine(FILE_TEXT, "Line 1: hello");
sdAppendLine(FILE_TEXT, "Line 2: read/write OK");
sdReadText(FILE_TEXT);
// 2) Directory tests
sdEnsureDir(DIR_IMAGES);
sdListDir("/", 2);
// 3) Binary tests (verify checksum)
const size_t N = 256;
uint8_t bufW[N];
uint8_t bufR[N];
for (size_t i = 0; i < N; i++) bufW[i] = (uint8_t)(i ^ 0x5A);
uint32_t c1 = simpleChecksum(bufW, N);
LOGI("Binary checksum (write): %lu", (unsigned long)c1);
if (sdWriteBinary(FILE_BIN, bufW, N) && sdReadBinary(FILE_BIN, bufR, N)) {
uint32_t c2 = simpleChecksum(bufR, N);
LOGI("Binary checksum (read) : %lu", (unsigned long)c2);
if (c1 == c2) LOGI("Binary verify: PASS ✅");
else LOGE("Binary verify: FAIL ❌");
}
}
void setup() {
Serial.begin(115200);
delay(300);
LOGI("Project start: Test hoạt động module microSD SPI");
if (!sdInit() || !sdHealthCheck()) {
LOGE("Stop due to SD init/health failure.");
while (true) delay(1000);
}
runAllTests();
LOGI("All tests done.");
}
void loop() {
// no loop needed for this test
}
Kiểm tra kết quả
- Mở Serial Monitor 115200
- Kỳ vọng thấy:
- File
/test.binđược ghi và đọc ra - [INFO] Project start: Test hoạt động module microSD SPI (Feature-based)
[INFO] Init SD… CS=10, freq=4000000
[INFO] SD OK. Type=SDSC, Size=1944 MB
[INFO] Binary checksum (write): 4291153024
[INFO] Text overwrite => OK
[INFO] Text append #1 => OK
[INFO] Text append #2 => OK
[INFO] —– READ /test.txt —–
IoTLabs – Test hoạt động module microSD SPI
Line 1: hello
Line 2: read/write OK
- File
Troubleshooting nhanh
SD.begin failed:- Đổi chân
PIN_SD_CS(hay nhầm nhất) - Hạ
SD_FREQxuống4*1000*1000 - Cấp 5V ổn định
- Format lại thẻ FAT32
- Dây ngắn, tránh jumper dài
- Đổi chân


