Come usare la scheda SD con l’esp32 – 2
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.
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.
Pin Number | Pin Name | In SD Mode | In SPI Mode |
1 | DAT2/X | Connector Data line 2 | No use |
2 | DAT3/CS | Connector Data line 3 | Chip Select |
3 | CMD/DI | Command / Response Line | Data Input |
4 | VDD/VDD | Power supply (+3.3V) | Power supply (+3.3V) |
5 | CLK/SCLK | Clock | Serial Clock |
6 | VSS/VSS | Ground | Ground |
7 | DAT0/D0 | Connector Data line 0 | Data Out |
8 | DAT1/X | Connector Data line 1 | No use |
Per prima cosa interfacciamo la scheda SD.
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.
esp32
Qui lo schema di connessione sulla breadboard.
E qui lo schema elettronico.
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
Qui i collegamenti per l’adattatore SD.
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.
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?
Ciao Moreno,
non ho quel tipo di display e non mi è mai capitato di dover riprodurre video, ma può essere un buono spunto per un articolo futuro.
Ciao Renzo
Ciao Renzo, allora spero in un prossimo articolo. Grazie.
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
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
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?
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
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:
Grazie mille anche solo per il disturbo.
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:
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:
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
Grazie mile