ESP32 , przykład użycia karty SD aruino
Kluczowe punkty
Praktyczne zastosowania
––––––––––––––––––––––––––––––––––––––––––––
Przykład 1: SPI + SD.h (uniwersalny, zalecany na start)
#include <FS.h>
#include <SD.h>
#include <SPI.h>
// Domyślne piny VSPI w ESP32:
static constexpr int PIN_CS = 5; // można zmienić
static constexpr int PIN_SCK = 18;
static constexpr int PIN_MISO= 19;
static constexpr int PIN_MOSI= 23;
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
Serial.printf("Listing: %s\n", dirname);
File root = fs.open(dirname);
if(!root || !root.isDirectory()){
Serial.println("- failed to open dir");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
Serial.print(" DIR : "); Serial.println(file.name());
if(levels){ listDir(fs, file.name(), levels - 1); }
} else {
Serial.print(" FILE: "); Serial.print(file.name());
Serial.print(" SIZE: "); Serial.println(file.size());
}
file = root.openNextFile();
}
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){ Serial.println("- failed to open file"); return; }
if(file.print(message)) Serial.println("- file written");
else Serial.println("- write failed");
file.close();
}
void appendFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Appending to: %s\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){ Serial.println("- failed to open file"); return; }
if(file.print(message)) Serial.println("- message appended");
else Serial.println("- append failed");
file.close();
}
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if(!file){ Serial.println("- failed to open file"); return; }
while(file.available()) Serial.write(file.read());
Serial.println();
file.close();
}
void setup(){
Serial.begin(115200);
delay(200);
// Jeśli zmieniasz piny, jawnie zainicjalizuj SPI:
SPI.begin(PIN_SCK, PIN_MISO, PIN_MOSI, PIN_CS);
// Bezpieczna częstotliwość startowa 4 MHz (opcjonalnie):
if(!SD.begin(PIN_CS, SPI, 4000000)){
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){ Serial.println("No SD card attached"); return; }
Serial.print("Card type: ");
if(cardType == CARD_MMC) Serial.println("MMC");
else if(cardType == CARD_SD) Serial.println("SDSC");
else if(cardType == CARD_SDHC) Serial.println("SDHC/SDXC");
else Serial.println("UNKNOWN");
Serial.printf("Card size: %llu MB\n", SD.cardSize() / (1024ULL*1024ULL));
listDir(SD, "/", 1);
writeFile(SD, "/test.txt", "Hello ESP32 + SD!\n");
appendFile(SD, "/test.txt", "Second line.\n");
readFile(SD, "/test.txt");
}
void loop(){}
Przykład 2: SD_MMC (ESP32‑CAM i projekty wymagające większej przepustowości)
#include <FS.h>
#include <SD_MMC.h>
// Uwaga: w ESP32‑CAM piny SD_MMC są już predefiniowane na płytce.
// Tryb 1‑bit ułatwia okablowanie w innych projektach.
void setup(){
Serial.begin(115200);
delay(200);
// true = 1‑bit mode (D0,CMD,CLK). Dla ESP32‑CAM często używa się 4‑bit (false),
// ale 1‑bit bywa stabilniejszy na długich ścieżkach/przewodach.
if(!SD_MMC.begin("/sdcard", /*mode1bit=*/true)){
Serial.println("SD_MMC Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){ Serial.println("No SD card"); return; }
Serial.printf("Card size: %llu MB\n", SD_MMC.cardSize() / (1024ULL*1024ULL));
File f = SD_MMC.open("/log.csv", FILE_APPEND);
if(!f){ Serial.println("Open fail"); return; }
f.println("ts, value");
f.close();
}
void loop(){}
Przykład 3: Logger danych (CSV) co 5 s, z ochroną przed utratą danych
#include <FS.h>
#include <SD.h>
#include <SPI.h>
static constexpr int CS = 5;
static constexpr int ADC_PIN = 34;
static constexpr uint32_t LOG_INTERVAL_MS = 5000;
unsigned long lastLog = 0;
void setup(){
Serial.begin(115200);
SPI.begin(18,19,23,CS);
if(!SD.begin(CS, SPI, 4000000)){
Serial.println("SD fail");
while(1) delay(1000);
}
pinMode(ADC_PIN, INPUT);
// Nagłówek tylko gdy plik pusty/nie istnieje
File f = SD.open("/data.csv", FILE_READ);
bool needHeader = !f || (f.size()==0);
if(f) f.close();
f = SD.open("/data.csv", FILE_APPEND);
if(needHeader) f.println("ms,adc,voltage_V");
f.close();
}
void loop(){
unsigned long now = millis();
if(now - lastLog >= LOG_INTERVAL_MS){
lastLog = now;
int raw = analogRead(ADC_PIN);
float v = raw * (3.3f / 4095.0f);
// Minimalne okno krytyczne — otwieraj/ zapisuj/ zamykaj
File f = SD.open("/data.csv", FILE_APPEND);
if(f){
f.printf("%lu,%d,%.3f\n", now, raw, v);
f.flush(); // wymuś zapis do FAT (redukcja ryzyka utraty)
f.close();
Serial.println("logged");
} else {
Serial.println("log fail");
}
}
}
Powodzenia! Jeśli podasz konkretną płytkę (np. model DevKit, ESP32‑CAM, moduł SD), dobiorę pewne piny/prędkości i schemat podłączenia „1:1”.
podaję konkretną płytkę ESP32-C6
Samo określenie „ESP32-C6” nadal nie identyfikuje jednoznacznie płytki, bo to nazwa układu/rodziny, a nie konkretnego devboardu. Aby rozpisać piny 1:1, potrzebny jest pełny model z nadruku, np. ESP32-C6-DevKitC-1, ESP32-C6-DevKitM-1, Seeed XIAO ESP32C6 itd. Oficjalna płytka ESP32-C6-DevKitC-1 ma wyprowadzone większość GPIO i nadaje się do karty microSD przez SPI. (documentation.espressif.com)
Jeżeli chodzi właśnie o ESP32-C6-DevKitC-1, to do karty microSD w Arduino zalecam połączenie w SPI:
Kluczowe punkty:
Najważniejsze jest rozróżnienie między:
To właśnie płytka decyduje, które GPIO są wyprowadzone, które są zajęte przez LED, USB-UART, czujniki, przetwornice itp. Dlatego „mam ESP32-C6” to z punktu widzenia projektowego nadal za mało. Oficjalny ESP32-C6-DevKitC-1 jest oparty o ESP32-C6-WROOM-1(U) i wyprowadza większość dostępnych GPIO na goldpiny. (documentation.espressif.com)
Druga kluczowa sprawa: w przykładowych odpowiedziach pojawiły się nieścisłości. Oficjalny datasheet mówi, że ESP32-C6 jest układem RISC-V single-core, a nie dual-core, i obsługuje Wi‑Fi 6 tylko w paśmie 2.4 GHz, nie 5 GHz. Oprócz tego ma Bluetooth LE 5 i 802.15.4 dla Zigbee/Thread. (documentation.espressif.com)
W kontekście karty SD najważniejsza poprawka jest taka, że ESP32-C6 nie ma SDMMC Host controller. Oficjalna dokumentacja ESP-IDF stwierdza wprost, że ESP32-C6 może używać kart tylko przez SPI, a nie przez klasyczny host SDMMC. Jednocześnie datasheet wymienia SDIO Slave Controller, co oznacza, że C6 może zachowywać się jako urządzenie slave na magistrali SDIO wobec zewnętrznego hosta, ale to nie jest tryb „podłączam kartę microSD i montuję FAT”. (docs.espressif.com)
Z tego powodu, jeśli Twoim celem jest:
to na ESP32-C6 należy planować SDSPI, czyli kartę SD podłączoną jako urządzenie SPI. (docs.espressif.com)
Dla oficjalnej płytki ESP32-C6-DevKitC-1 proponuję:
| Funkcja SD | GPIO | Złącze na DevKitC-1 |
|---|---|---|
| SCK | GPIO6 | J1 pin 6 |
| MOSI | GPIO7 | J1 pin 7 |
| MISO | GPIO2 | J1 pin 12 |
| CS | GPIO10 | J1 pin 10 |
| 3.3 V | 3V3 | J1 pin 1 |
| GND | GND | J1 pin 15 lub J3 GND |
To zestaw praktyczny, spójny z dostępnymi wyprowadzeniami płytki i z typowym mapowaniem SPI dla C6. (documentation.espressif.com)
Bo:
Jednocześnie trzeba podkreślić, że ESP32-C6 ma GPIO Matrix, więc SPI można zmapować także na inne wyprowadzenia. To tłumaczy, dlaczego w różnych przykładach pojawiają się różne zestawy pinów. Na przykład GPIO18…23 są również wyprowadzone na DevKitC-1 i część z nich ma funkcje związane z SDIO lub dodatkowymi liniami FSPI/CS, więc można je wykorzystać w konfiguracji niestandardowej, ale wtedy trzeba jawnie wywołać SPI.begin(SCK, MISO, MOSI, CS). (docs.espressif.com)
Dla ESP32-C6-DevKitC-1 zacząłbym od takiej wersji:
#include <SPI.h>
#include <SD.h>
static constexpr int PIN_SCK = 6;
static constexpr int PIN_MISO = 2;
static constexpr int PIN_MOSI = 7;
static constexpr int PIN_CS = 10;
void setup() {
Serial.begin(115200);
delay(1000);
SPI.begin(PIN_SCK, PIN_MISO, PIN_MOSI, PIN_CS);
if (!SD.begin(PIN_CS, SPI, 4000000)) {
Serial.println("Blad inicjalizacji karty SD");
while (1) delay(1000);
}
Serial.println("Karta SD zainicjalizowana");
File f = SD.open("/test.txt", FILE_WRITE);
if (f) {
f.println("ESP32-C6 + microSD OK");
f.close();
Serial.println("Zapis OK");
} else {
Serial.println("Blad otwarcia pliku");
}
}
void loop() {
}
Taka konfiguracja jest bezpieczna, bo:
Oficjalna dokumentacja Arduino-ESP32 pokazuje, że ESP32-C6 jest obecnie wspierany w projekcie Arduino-ESP32, a instalacja odbywa się przez Menedżera płytek Espressif. Dokumentacja rozróżnia kanał stable i development, więc do pracy produkcyjnej zalecałbym kanał stabilny. (github.com)
Z rynkowego punktu widzenia ESP32-C6 jest interesujący nie dlatego, że jest „następcą klasycznego ESP32” w każdym aspekcie, lecz dlatego, że łączy:
W przykładowych odpowiedziach były dwie istotne klasy błędów:
Błąd architektury i radia
ESP32-C6 nie jest dual-core i nie ma 5 GHz Wi‑Fi. Oficjalnie: single-core RISC-V, Wi‑Fi 6 2.4 GHz. (documentation.espressif.com)
Błąd interpretacji SDIO
Stwierdzenie „obsługuje karty SD przez SDIO” jest na C6 mylące. Tak, układ ma SDIO Slave, ale dla karty pamięci po stronie hosta obowiązuje praktycznie SPI. Dokumentacja ESP-IDF stwierdza to wprost. (docs.espressif.com)
Dodatkowo, jeśli będziesz współdzielił magistralę SPI z innymi układami, pamiętaj, że po przełączeniu karty do SPI mode karta nie wraca do trybu SD aż do kolejnego cyklu zasilania. To ma znaczenie diagnostyczne przy testach i współdzieleniu magistrali. (docs.espressif.com)
Od strony techniczno-prawnej najważniejsze są tu:
Karta SD jest nośnikiem wyjmowalnym, więc jeżeli zapisujesz identyfikatory użytkowników, pomiary medyczne, dane lokalizacyjne lub logi dostępu, warto rozważyć szyfrowanie na poziomie aplikacji i procedurę bezpiecznego domykania plików. To nie wynika z ograniczeń samego C6, tylko z charakteru pamięci wymiennej.
Najważniejsze zastrzeżenie: w Twojej wiadomości nadal nie ma pełnego symbolu płytki. Dlatego moja odpowiedź jest:
Jeśli masz inną płytkę niż DevKitC-1, to sama rodzina układu nie wystarcza do gwarantowanego doboru pinów.
Najlepszy kolejny krok:
Wtedy rozpiszę Ci:
Najkrócej:
Jeżeli chcesz, w następnej wiadomości mogę Ci rozpisać to już ściśle pod Twoją konkretną płytkę — wystarczy, że podasz jej pełny symbol.