Come usare la scheda SD con l’esp32 – 2

Spread the love

Quando si utilizza un microcontrollore, una funzionalità importante è l’archiviazione dei dati, per il logging o salvare impostazioni, o magari per gestire un server Web oppure per mostrare un’immagine.

Come utilizzare l'adattatore per scheda SD su esp32
Come utilizzare l’adattatore per scheda SD su esp32

La soluzione migliore è una scheda SD, perché è un dispositivo semplice, piccolo e di bassa potenza.

Protocollo

La scheda SD ha un’interfaccia host nativa oltre alla modalità SPI per la comunicazione con i dispositivi master. L’interfaccia nativa utilizza quattro linee per il trasferimento dei dati ed il microcontrollore ha un modulo controller separato e per utilizzarlo richiede una licenza. Poiché SPI è un protocollo ampiamente utilizzato ed è disponibile nella maggior parte dei microcontrollori a basso costo, la modalità SPI è un’interfaccia ampiamente utilizzata nei sistemi embedded a basso costo. La tensione di lavoro della famiglia SD va da 2,7 V a 3,6 V e questo è indicato nel registro delle condizioni operative (OCR). Esiste una scheda SD a bassa potenza che funziona a 1,8 V, ma non è così utilizzata.

Pinout

Esistono vari fattori di forma, ma la piedinatura di base è la stessa.

MMC SD miniSD microSD pins and size factor
Pin NumberPin NameIn SD ModeIn SPI Mode
1DAT2/XConnector Data line 2No use
2DAT3/CSConnector Data line 3Chip Select
3CMD/DICommand / Response LineData Input
4VDD/VDDPower supply (+3.3V)Power supply (+3.3V)
5CLK/SCLKClockSerial Clock
6VSS/VSSGroundGround
7DAT0/D0Connector Data line 0Data Out
8DAT1/XConnector Data line 1No use

Per prima cosa interfacciamo la scheda SD.

SD pinout

Cablaggio

La tensione operativa dell’esp32 ci semplifica lo schema rispetto ad un Arduino a 5v. Per interfacciare la scheda SD uso un adattatore SD per micro SD, e il risultato è questo.

Vista frontale dei pins dell’adattatore SD
SD adapter pins back

esp32

ESP32 DOIT DEV KIT v1 piedinatura
ESP32 DOIT DEV KIT v1 piedinatura

Qui lo schema di connessione sulla breadboard.

esp32 SD card adapter on breadboard
esp32 SD card adapter on breadboard

E qui lo schema elettronico.

esp32 SD Card Adapter schema
esp32 SD Card Adapter schema

RICORDA!! puoi usare solo i file con pattern 8.3, ad esempio un file come config.txt è accettato ma configuration.text no, perché la lunghezza massima del file è di 8 caratteri e l’estensione 3.

Moduli

Esistono vari moduli per interfacciare il tuo microcontrollore con il tuo dispositivo ed in pratica sono fatti esattamente come lo schema di connessione Arduino per adattatore 5v e come lo schema di connessione esp8266 per 3.3v. Quando ne acquisti uno, devi prestare attenzione alla tensione di lavoro.

Esistono alcune varianti che supportano 3.3v e 5v, come quella qui collegata.

Puoi trovare il modulo per SD e MicroSD qui su AliExpress

esp32 connessione dell'adapter per scheda SD
esp32 connessione dell’adapter per scheda SD

Qui i collegamenti per l’adattatore SD.

ESP32 collegamento adattatore SD su breadboard
ESP32 collegamento adattatore SD su breadboard

Comandi

Classe SD

SD.begin() SD.begin(uint8_t ssPin=SS, SPIClass &spi=SPI, uint32_t frequency=4000000, const char * mountpoint=”/sd”, uint8_t max_files=5)
Inizializza la libreria e la scheda SD. Questo inizializza l’uso del bus SPI e del pin di selezione del chip, il cui valore predefinito è il pin SS dell’hardware. Restituisce vero in caso di successo; falso in caso di fallimento.

SD.cardType()
Ritorna il tipo di SD inserita

  • CARD_NONE
  • CARD_MMC
  • CARD_SD
  • CARD_SDHC
  • CARD_UNKNOWN

SD.exists(filename)
Verifica se esiste un file o una directory sulla scheda SD. Restituisce vero se esiste il file o la directory, falso in caso contrario.

SD.mkdir(filename)
Crea una directory sulla scheda SD. Questo creerà anche qualsiasi directory intermedia se non esiste già; per esempio. SD.mkdir("a/b/c") creerà a, b e c. Restituisce vero se la creazione della directory ha avuto successo, falso in caso contrario.

SD.open(filepath) SD.open(filepath, mode)
Apre un file sulla scheda SD. Se il file viene aperto per la scrittura, verrà creato se non esiste già (ma la directory che lo contiene deve già esistere). Parametro mode (opzionale): la modalità in cui aprire il file, il valore predefinito è FILE_READ – byte. uno di: FILE_READ: apre il file per la lettura, a partire dall’inizio del file. FILE_WRITE: apre il file per la lettura e la scrittura, a partire dalla fine del file. Restituisce un oggetto File che fa riferimento al file aperto; se non è possibile aprire il file, questo oggetto verrà valutato come falso in un contesto booleano, ovvero è possibile verificare il valore restituito con “if (f)“.

SD.remove(filename)
Rimuovere un file dalla scheda SD. Restituisce vero se la rimozione del file ha esito positivo, falso in caso contrario. (se il file non esisteva, il valore restituito non è specificato)

SD.rename(filenameFrom, filenameTo)
Renames file from pathFrom to pathTo. Paths must be absolute. Returns true if file was renamed successfully.

SD.rmdir(filename)
Rimuove una directory dalla scheda SD. La directory deve essere vuota. Restituisce vero se la rimozione della directory è riuscita, falso in caso contrario. (se la directory non esisteva, il valore restituito non è specificato)

SD.totalBytes()
Restituisce i byte totali abilitati su SD. Restituisce byte.

SD.usedBytes()
Restituisce il numero totale di byte utilizzati abilitati su SD. Restituisce byte.

SD.cardSize()
Restituisce la dimensione della SD. Restituisce byte

File class

file.name()
Restituisce il nome del file

file.available()
Controlla se ci sono byte disponibili per la lettura dal file. Restituisce il numero di byte.

file.close()
Chiudere il file e assicurarsi che tutti i dati scritti su di esso vengano salvati fisicamente sulla scheda SD.

file.flush()
Assicura che tutti i byte scritti nel file vengano fisicamente salvati sulla scheda SD. Questo viene fatto automaticamente alla chiusura del file.

file.peek()
Leggi un byte dal file senza passare a quello successivo. Cioè, le chiamate successive a peek() restituiranno lo stesso valore, così come la chiamata successiva a read().

file.position()
Ottieni la posizione corrente all’interno del file (ovvero la posizione in cui il prossimo byte verrà letto o scritto). Restituisce la posizione all’interno del file (unsigned long).

file.print(data) file.print(data, base)
Stampa i dati sul file (doveva essere aperto per la scrittura). Stampa i numeri come una sequenza di cifre, ognuna un carattere ASCII (ad es. Il numero 123 viene inviato come i tre caratteri ‘1’, ‘2’, ‘3’).
data: i dati da stampare (char, byte, int, long o string),
base (opzionale): la base in cui stampare i numeri: BIN per binario (base 2), DEC per decimale (base 10), PTOM per ottale (base 8), HEX per esadecimale (base 16). Restituisce il numero di byte scritti, sebbene la lettura di quel numero sia facoltativa.

file.println() file.println(data) file.println(data, base)
Come print ma con il return finale.

file.seek(pos)
Cerca una nuova posizione nel file, che deve essere compresa tra 0 e le dimensioni del file (incluso).
Parametri:
pos: la posizione in cui cercare (unsigned long). Restituisce vero per il successo, falso per il fallimento (booleano)

file.size()
Ottieni le dimensioni del file. Restituisce la dimensione del file in byte (unsigned long).

file.read()  file.read(buf, len)
Leggi dal file. Restituisce il byte (o il carattere) successivo o -1 se nessuno è disponibile.

file.write(data) file.write(buf, len)
Scrivi i dati nel file. Restituisce il numero di byte scritti, la lettura di quel numero è facoltativa

file.isDirectory()
Le directory (o cartelle) sono tipi speciali di file, questa funzione segnala se il file corrente è una directory o meno. Restituisce vero se è directory.

file.openNextFile()
Riporta il file o la cartella successivi in una directory. Restituisce il file o la cartella successivi nel percorso.

file.rewindDirectory()
Riporta al primo file nella directory, utilizzato insieme a openNextFile().

file.getLastWrite()
Ritorna la data dell’ultima scrittura/variazione in epoch time.

Esempi

Nell’IDE di Arduino puoi trovare alcuni esempi molto utili e ben commentati.

Qui uno schetck che estrae tutte le informazioni sulla scheda SD utilizzata.

/*
  SD card test for esp8266

  This example shows how use the utility libraries

  The circuit:
    SD card attached to SPI bus as follows:
		SS    = 5;
		MOSI  = 23;
		MISO  = 19;
		SCK   = 18;


   by Mischianti Renzo <https://mischianti.org>
 
   https://www.mischianti.org
*/
// include the SD library:
#include <SPI.h>
#include <SD.h>

// WeMos D1 esp8266: D8 as standard
const int chipSelect = SS;

void printDirectory(File dir, int numTabs);

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("\nInitializing SD card...");

  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card inserted?");
    Serial.println("* is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    while (1);
  } else {
    Serial.println("Wiring is correct and a card is present.");
  }

  // print the type of card
  Serial.println();
  Serial.print("Card type:         ");
  switch (SD.cardType()) {
    case CARD_NONE:
      Serial.println("NONE");
      break;
    case CARD_MMC:
      Serial.println("MMC");
      break;
    case CARD_SD:
      Serial.println("SD");
      break;
    case CARD_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknown");
  }

  // print the type and size of the first FAT-type volume
//  uint32_t volumesize;
//  Serial.print("Volume type is:    FAT");
//  Serial.println(SDFS.usefatType(), DEC);

  Serial.print("Card size:  ");
  Serial.println((float)SD.cardSize()/1000);

  Serial.print("Total bytes: ");
  Serial.println(SD.totalBytes());

  Serial.print("Used bytes: ");
  Serial.println(SD.usedBytes());

  File dir =  SD.open("/");
  printDirectory(dir, 0);

}

void loop(void) {
}

void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.print(entry.size(), DEC);
      time_t lw = entry.getLastWrite();
      struct tm * tmstruct = localtime(&lw);
      Serial.printf("\tLAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
    }
    entry.close();
  }
}


Ecco un esempio di lettura e scrittura di una scheda SD.

/*
  SD card read/write

  This example shows how to read and write data to and from an SD card file
  The circuit:
   SD card attached to SPI bus as follows:
		SS    = 5;
		MOSI  = 23;
		MISO  = 19;
		SCK   = 18;

  created   Nov 2010
  by David A. Mellis
  modified 9 Apr 2012
  by Tom Igoe

  This example code is in the public domain.

*/

#include <SPI.h>
#include <SD.h>

File myFile;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("Initializing SD card...");

  if (!SD.begin(SS)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test.txt", FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }

  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");

    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}

void loop() {
  // nothing happens after setup
}

Grazie

Nei prossimi articoli andremo a vedere come usare altri sistemi operativi con la nostra scheda SD, così da superare i limiti della libreria standard SD.

  1. Come usare la scheda SD con esp8266 e Arduino
  2. Come usare la scheda SD con l’esp32

Spread the love

10 Risposte

  1. Moreno ha detto:

    Buonasera, ho un file in formato AVi all’interno di una scheda SD e vorrei che venisse trasmesso ad un display ST7789. Mi puoi aiutare a scrivere il codice per un modulo ESP32?

  2. Lorenzo Tosoni ha detto:

    Ciao Renzo, una domanda su una cosa strana con una micro sd, ho motato un lettore sd in un esp32 mi inizializza la scheda ma non mi apre il file!! Cosa puo essere? Ho collegato i pin come da schema ma non capisco cosa ho sbagliato
    ciao e grazie

    • Renzo Mischianti ha detto:

      Ciao Lorenzo,
      così su due piedi ti direi di formattare la scheda SD se non hai provveduto, oppure verifica che il tuo adattatore funzioni a 3v3 o 5v e collega il giusto voltaggio.
      Ciao RM

  3. Gabriele ha detto:

    Non si capisce la massima capacità di SD che può gestire Esp32, alcuni post parlano di 4GB , altri di 16 e altri ancora 32
    Dipende forse dalla libreia che si usa?

    • Renzo Mischianti ha detto:

      Ciao,
      si dipende da che filesystem usa la libreria, ad esempio con l’SdFat ho usato SD da 32Gb senza problemi, naturalmente il Microcontrollore deva avere abbastanza memoria per usare questa libreria, ma con l’esp32 non hai problemi.
      Ciao RM

  4. Daniele ha detto:

    Ciao, io utilizzo una esp32 c3 super mini, il primo esempio funziona bane il secono invece no.
    Da errore la myFile = SD.open(“test.txt”, FILE_WRITE); perchè non trova il file. Dopo qualche tentativo scopro che funziona solo con il “/” prima del none della directory myFile = SD.open(“/test.txt”, FILE_WRITE); <- (così funziona).
    Poichè l'SD mi serve per scriverci sopra dei dati ho cambiato la file.printf in myFile.println("testing %d", i); dove i è un int. Il grosso problema si riscontra quando midice che non può scrivere un dati diverso da cost char.

    Questo è l'errore di arduino:

    C:\Users\cpoll\AppData\Local\Temp\.arduinoIDE-unsaved2024518-34380-7zap0v.2tyxv\sketch_jun18a\sketch_jun18a.ino:50:20: error: invalid conversion from 'const char*' to 'long long int' [-fpermissive]
    myFile.println("testing %d", i);
    ^~~~~~~~~~~~
    In file included from C:\Users\cpoll\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.15\cores\esp32/Stream.h:26,
    from C:\Users\cpoll\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.15\cores\esp32/Arduino.h:189,
    from C:\Users\cpoll\AppData\Local\Temp\arduino\sketches\A19D31CD5B03AC5F344C85E28C30B13A\sketch\sketch_jun18a.ino.cpp:1:
    C:\Users\cpoll\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.15\cores\esp32/Print.h:106:12: note: candidate: 'size_t Print::println(long long unsigned int, int)'
    size_t println(unsigned long long, int = DEC);
    ^~~~~~~
    C:\Users\cpoll\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.15\cores\esp32/Print.h:106:12: note: conversion of argument 1 would be ill-formed:
    C:\Users\cpoll\AppData\Local\Temp\.arduinoIDE-unsaved2024518-34380-7zap0v.2tyxv\sketch_jun18a\sketch_jun18a.ino:50:20: error: invalid conversion from ‘const char*’ to ‘long long unsigned int’ [-fpermissive]
    myFile.println(“testing %d”, i);
    ^~~~~~~~~~~~
    Multiple libraries were found for “SD.h”
    Used: C:\Users\cpoll\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.15\libraries\SD
    Not used: C:\Users\cpoll\AppData\Local\Arduino15\libraries\SD
    exit status 1
    
    Compilation error: no matching function for call to ‘println(const char [11], int&)’
    

    Grazie mille anche solo per il disturbo.

    • Renzo Mischianti ha detto:

      Ciao,
      ci sono due questioni principali che stai affrontando: l’accesso ai file sulla scheda SD e la scrittura dei dati nel file.

      Accesso ai file sulla scheda SD:
      Hai già scoperto che aggiungere “/” all’inizio del nome del file risolve il problema:

      myFile = SD.open("/test.txt", FILE_WRITE);
      

      Questo è un comportamento comune nei filesystem dove il percorso assoluto è richiesto.

      Scrittura dei dati nel file:
      L’errore che stai vedendo è dovuto al fatto che println non supporta la formattazione delle stringhe come printf. Per risolvere questo problema, puoi utilizzare sprintf per formattare la stringa prima di scriverla nel file. Ecco un esempio di come fare:

      // Definisci un buffer per la stringa formattata
      char buffer[50];
      int i = 123; // esempio di variabile intera
      
      // Format the string
      sprintf(buffer, "testing %d", i);
      
      // Apri il file
      myFile = SD.open("/test.txt", FILE_WRITE);
      if (myFile) {
        // Scrivi la stringa formattata nel file
        myFile.println(buffer);
      
        // Chiudi il file
        myFile.close();
      } else {
        // Gestisci l'errore di apertura del file
        Serial.println("Errore nell'apertura del file");
      }
      

      Questo metodo utilizza sprintf per creare una stringa formattata e poi usa println per scrivere quella stringa nel file. Questo dovrebbe risolvere il problema dell’errore di conversione di tipo che hai visto.

      Ciao RM

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *