ESP32 Arduino – przykład obsługi karty SD, SPI i SD_MMC, kod, podłączenie, formatowanie FAT32

Pytanie

ESP32 , przykład użycia karty SD aruino

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

  • Najprostsza i najbardziej przenośna integracja karty microSD z ESP32 w Arduino IDE wykorzystuje interfejs SPI i bibliotekę SD.h.
  • Podłącz: CS=GPIO5, SCK=GPIO18, MISO=GPIO19, MOSI=GPIO23, VCC=3.3 V, GND=GND; sformatuj kartę jako FAT32; wywołaj SD.begin(CS), a następnie czytaj/pisz pliki przez API FS/SD.

Kluczowe punkty

  • ESP32 i karta SD muszą pracować na 3.3 V (uwaga na moduły z konwersją poziomów).
  • Najpierw przetestuj przykład SD_Test z Arduino IDE (Plik > Przykłady > SD(esp32)).
  • Dla ESP32‑CAM i aplikacji wymagających większej przepustowości rozważ SD_MMC (SDIO) zamiast SPI.

Szczegółowa analiza problemu

  • Interfejsy:
    • SPI (SDSPI): 4 przewody + CS. Uniwersalny, kompatybilny z typowymi modułami microSD dla Arduino. Łatwy do routingu na płytce stykowej, typowe prędkości 4–20 MHz.
    • SD_MMC (SDIO): natywny kontroler SD; 1‑bit lub 4‑bit. Szybszy i mniej obciążający CPU, ale wymaga dedykowanych pinów (na wielu płytkach są współdzielone) i staranniejszej topologii przewodów. W ESP32‑CAM jest to domyślna ścieżka.
  • Zasilanie i integralność sygnałowa:
    • Zapewnij stabilne 3.3 V (chwilowe pobory prądu karty SD sięgają kilkudziesięciu–>100 mA). Blisko gniazda dodaj 10–47 µF (elektrolit/tantal) + 100 nF.
    • Utrzymuj przewody SPI krótsze niż ~10–15 cm, prowadź wspólną masę i unikaj luźnych połączeń na płytce stykowej.
    • Jeśli używasz modułu „5 V Arduino” z konwerterami poziomów, zasilaj go 5 V, ale sygnały logiczne będą obniżone do 3.3 V. Czystsze rozwiązanie: moduł natywnie 3.3 V bez translatorów.
  • System plików:
    • FAT32 (zalecane). Duże karty SDXC (64–128 GB) działają po sformatowaniu na FAT32 (zewnętrzne narzędzia). exFAT zwykle nie jest obsługiwany przez domyślną SD.h.
  • Konflikty pinów:
    • Domyślne SPI (VSPI) w ESP32: SCK=18, MISO=19, MOSI=23, CS=5, ale można użyć innych i zainicjalizować je przez SPI.begin(sck,miso,mosi,cs).
    • Unikaj pinów z funkcjami rozruchowymi (np. GPIO12, GPIO0, GPIO2) w konfiguracjach, gdzie mogą zaburzać boot.

Praktyczne zastosowania

  • Datalogger (CSV), buforowanie danych z Wi‑Fi/BLE, zapis zdjęć (ESP32‑CAM), aktualizacje OTA z nośnika.

Aktualne informacje i trendy

  • Rdzeń Arduino‑ESP32 utrzymuje dwa równoległe fronty: SD (SDSPI/SDMMC) oraz alternatywnie SdFat (do wyższej wydajności i kontroli buforów). W większości projektów wystarcza SD.h; SdFat warto rozważyć przy dużych plikach i ograniczaniu fragmentacji.
  • W praktyce hobbystycznej dominuje SPI z uwagi na prostotę okablowania; w modułach kamer (ESP32‑CAM) standardem jest SD_MMC.

Wspierające wyjaśnienia i detale

  • Wydajność:
    • Startuj z mniejszą częstotliwością (np. 4 MHz), a po udanej inicjalizacji możesz zwiększyć do 10–20 MHz, zależnie od długości przewodów i modułu.
  • Niezawodność zapisu:
    • Używaj FILE_APPEND do logów, domykaj pliki jak najszybciej (file.close()) aby zminimalizować utratę danych przy zaniku zasilania.
    • Rozważ buforowanie w RAM i zapisy porcjami (np. co 100–500 ms).

Aspekty etyczne i prawne

  • Zwróć uwagę na prywatność danych (logi środowiskowe/lokalizacyjne). W projektach komercyjnych zapewnij zgodność z lokalnymi regulacjami ochrony danych.
  • Licencje bibliotek: SD.h (rdzeń ESP32) i ew. SdFat – przestrzegaj warunków licencji przy dystrybucji.

Praktyczne wskazówki

  • Test zasilania: jeśli Wi‑Fi/BLE pracuje równolegle, zapewnij wydajne źródło 5 V (≥1 A) i stabilizację do 3.3 V o niskiej impedancji.
  • Dla kart „kapryśnych” spróbuj mniejszej częstotliwości SPI (1–4 MHz) i klastra 32 kB przy formatowaniu.
  • Na płytkach prototypowych unikaj „pajęczynki” – skręć przewody sygnałowe z masą lub ułóż je równolegle z dobrą referencją GND.

Ewentualne zastrzeżenia lub uwagi dodatkowe

  • Fragmentacja FAT i częste małe zapisy skracają żywotność karty; lepsze są zapisy blokowe i pre‑alokacja pliku.
  • Nie wszystkie moduły microSD działają stabilnie przy 20 MHz na długich przewodach – dobieraj częstotliwość do topologii.

Sugestie dalszych badań

  • Przejście na SdFat z pre‑alokacją i buforami o rozmiarze wielokrotności sektora (512 B/4 kB).
  • SD_MMC w trybie 4‑bit z właściwymi pull‑up i krótkimi ścieżkami PCB dla maksymalnej przepustowości.
  • Wear‑leveling po stronie aplikacji (rotacja plików, pierścieniowy log).

Krótkie podsumowanie

  • Użyj SD.h po SPI – to najszybsza droga do działania na dowolnym DevKit ESP32. Zapewnij poprawne 3.3 V, krótkie przewody i FAT32. Dla ESP32‑CAM lub wyższej przepustowości – SD_MMC.

––––––––––––––––––––––––––––––––––––––––––––

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”.

Oceń odpowiedź:
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.

Pytanie

podaję konkretną płytkę ESP32-C6

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

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:

  • SCK = GPIO6
  • MISO = GPIO2
  • MOSI = GPIO7
  • CS = GPIO10
    oraz oczywiście 3V3 i GND. Te piny są fizycznie wyprowadzone na złączu płytki, a mapowanie 6/2/7/10 odpowiada typowej konfiguracji SPI dla ESP32-C6. (documentation.espressif.com)

Kluczowe punkty:

  • ESP32-C6 nie ma kontrolera SDMMC Host, więc dla karty SD jako pamięci masowej należy przyjąć, że pracujesz po SPI. Ma natomiast SDIO Slave Controller, co nie jest tym samym co host do obsługi karty pamięci. (docs.espressif.com)
  • Nie używaj GPIO24…GPIO30, bo są związane z magistralą flash. (docs.espressif.com)
  • Unikaj pinów strapujących: GPIO4, GPIO5, GPIO8, GPIO9, GPIO15, zwłaszcza na sygnały krytyczne przy starcie. (docs.espressif.com)
  • Na DevKitC-1 piny GPIO12/GPIO13 są powiązane z USB D-/D+, więc nie warto ich zajmować, jeśli korzystasz z natywnego USB/JTAG. (documentation.espressif.com)

Bezpośrednia odpowiedź na pytanie Szczegółowa analiza problemu

Najważniejsze jest rozróżnienie między:

  1. układem ESP32-C6,
  2. modułem (np. WROOM / MINI),
  3. konkretną płytką deweloperską.

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:

  • zapis logów,
  • odczyt plików,
  • zapis CSV,
  • buforowanie danych pomiarowych,

to na ESP32-C6 należy planować SDSPI, czyli kartę SD podłączoną jako urządzenie SPI. (docs.espressif.com)

Zalecane połączenie dla ESP32-C6-DevKitC-1

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)

Dlaczego akurat te piny?

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)

Minimalny, poprawny szkic Arduino

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:

  • wymusza konkretne piny,
  • nie polega na niejawnych „defaultach” wariantu,
  • startuje z umiarkowaną częstotliwością 4 MHz, co zwykle ułatwia pierwsze uruchomienie.
    To podejście jest zgodne z praktyką SDSPI w dokumentacji Espressif. (docs.espressif.com)

Aktualne informacje i trendy

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:

  • Wi‑Fi 6 w 2.4 GHz,
  • Bluetooth LE 5,
  • Zigbee/Thread
    w jednym SoC. To czyni go bardzo dobrym kandydatem do:
  • rejestratorów danych z kartą SD,
  • bramek IoT,
  • urządzeń bateryjnych z lokalnym logowaniem. (documentation.espressif.com)

Wspierające wyjaśnienia i detale

W przykładowych odpowiedziach były dwie istotne klasy błędów:

  1. 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)

  2. 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)


Aspekty etyczne i prawne

Od strony techniczno-prawnej najważniejsze są tu:

  • integralność danych na karcie,
  • ochrona danych osobowych, jeśli coś logujesz,
  • odporność na utratę zasilania w trakcie zapisu.

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.


Praktyczne wskazówki


Ewentualne zastrzeżenia lub uwagi dodatkowe

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.


Sugestie dalszych badań

Najlepszy kolejny krok:

  1. podaj pełny model płytki z nadruku,
  2. albo wyślij zdjęcie obu stron płytki,
  3. albo przepisz nazwy pinów z silk screenu.

Wtedy rozpiszę Ci:

  • dokładny pinout,
  • bezpieczne GPIO,
  • połączenie z kartą microSD,
  • gotowy szkic Arduino albo ESP-IDF,
  • oraz listę pinów, których nie wolno użyć na Twoim konkretnym boardzie.

Krótkie podsumowanie

Najkrócej:

  • „ESP32-C6” to jeszcze nie konkretny board.
  • Jeśli masz ESP32-C6-DevKitC-1, do microSD użyj SPI, najlepiej:
  • Nie planuj SDMMC host na C6; dla karty pamięci trzymaj się SPI. (docs.espressif.com)

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.

Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.

Zadaj dodatkowe pytanie Sztucznej Inteligencji

Czekaj (2min)...