When you use a microcontroller, an important feature is storing data, for logging or setting, for a web server, or to show images.
How to use SD Card Adapter esp8266 and Arduino
The better solution is an SD card because It’s a simple, small, and low-power device.
Protocol
SD card has a native host interface apart from the SPI mode for communicating with master devices. The native interface uses four lines for data transfer, where the microcontroller has an SD card controller module and needs a separate license to use it. Since the SPI is a widely used protocol and it is available in most low-cost microcontrollers, the SPI mode is the widely used interface in low-cost embedded systems. The working voltage range of the SD family is 2.7V to 3.6V, and this is indicated in the operation condition register (OCR). Exist a low-power SD Card that operates al 1.8V but isn’t so used.
Pinout
Exists various form factors, but the base pinout is the same.
MMC SD miniSD microSD pins and size factor
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
Now we are going to interface SD card for first.
SD pinout
Wiring
The operating voltage forces us to make 2 distinct connection schema based on the type of microcontroller. To interface the SD card, I use an SD adapter with micro SD, which results.
Vista frontale dei pins dell’adattatore SD
SD adapter pins back
Arduino UNO
As you know, Arduino UNO operates at 5V, so we must add a voltage divider in the input line to prevent the SD Card’s firing.
If you want more information about Voltage Divider you can read this article “Voltage divider: calculator and application “.
I use 2 resistance of 1K and 2K to get 3.3v from 5v, and I pull up the MISO pin to prevent noise.
Arduino wiring SD card adapter
In the schema is more simple to identify voltage dividers and connections.
Arduino SD Card adapter schema
I use standard pins of Arduino examples, so next we are going to use some code that you can find in Arduino IDE also.
Arduino breadboard SD Card adapter
esp8266
Here the voltage is the same, so all is more simple.
WeMos D1 mini breadboard with SD Card adapter
And the schema
SDCard adapter and WeMos D1 mini schema
We use D2 pin that identify pin 4 as Arduino SD examples .
WeMos D1 mini breadboard SD Card adapter
REMEMBER!! you can use only an 8.3 file, for example, a file like config.txt
is accepted but configuration. text
no, because the max file length is 8 characters and the extension 3.
But Remember the standard CS pin is D8 like this schema
WeMos D1 (esp8266) SD Card Adapter on breadboard
Modules
There are various modules to interface your microcontroller with your device, and It works exactly like the Arduino connection schema for the 5v adapter and the esp8266 connection schema for 3.3v. When you buy one, you must pay attention to the working voltage.
Exists some variant that supports 3.3v and 5v, like the one linked here.
You can find sd card module on AliExpress
WeMos D1 mini esp8266 SD adapter wiring
Commands
SD Class
sd.begin() sd.begin(cspin) Initializes the SD library and card. This begins the use of the SPI bus and the chip select pin, which defaults to the hardware SS pin. Returns true on success; false on failure.
sd.exists(filename) Test whether a file or directory exists on the SD card. Returns true if the file or directory exists, false if not.
sd.mkdir(filename) Create a directory on the SD card. This will also create any intermediate directories that don’t already exist; e.g. SD.mkdir(“a/b/c”) will create a, b, and c. Returns true if the creation of the directory succeeded, false if not.
sd.open(filepath) sd.open(filepath, mode) Opens a file on the SD card. If the file is opened for writing, it will be created if it doesn’t already exist (but the directory containing it must already exist). Parameter mode (optional ): the mode in which to open the file defaults to FILE_READ – byte . one of FILE_READ: open the file for reading, starting at the beginning of the file. FILE_WRITE: open the file for reading and writing, starting at the end of the file. Returns a File object referring to the opened file; if the file can’t be opened, this object will evaluate to false in a boolean context, i.e. you can test the return value with “if (f)”.
sd.remove(filename) Remove a file from the SD card. Returns true if the removal of the file succeeded, false if not. (if the file didn’t exist, the return value is unspecified)
rs.rmdir(filename) Remove a directory from the SD card. The directory must be empty. Returns true if the removal of the directory succeeded, false if not. (if the directory didn’t exist, the return value is unspecified)
File class
file.name() Returns the file name
file.available() Check if there are any bytes available for reading from the file. Returns the number of bytes.
file.close() Close the file, and ensure that any data written to it is physically saved to the SD card.
file.flush() Ensures that any bytes written to the file are physically saved to the SD card. This is done automatically when the file is closed.
file.peek() Read a byte from the file without advancing to the next one. That is, successive calls to peek() will return the same value, as will the next call to read().
file.position() Get the current position within the file (i.e. the location to which the next byte will be read from or written to). Returns the position within the file (unsigned long ).
file.print(data) file.print(data, base) Print data to the file, which must have been opened for writing. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters ‘1’, ‘2’, ‘3’). Parameter data: the data to print (char, byte, int, long, or string), BASE (optional): the base in which to print numbers: BIN for binary (base 2), DEC for decimal (base 10), OCT for octal (base 8), HEX for hexadecimal (base 16). Returns the number of bytes written, though reading that number is optional.
file.println() file.println(data) file.println(data, base) As print but with final return
file.seek(pos) Seek to a new position in the file, which must be between 0 and the size of the file (inclusive). Parameters: pos: the position to which to seek (unsigned long ). Returns true for success, false for failure (boolean )
file.size() Get the size of the file. Returns the size of the file in bytes (unsigned long ).
file .read() file.read(buf, len) Read from the file. Returns the next byte (or character), or -1 if none is available.
file .write(data) file .write(buf, len) Write data to the file. Returns the number of bytes written, though reading that number is optional
file.isDirectory() Directories (or folders) are special kinds of files, this function reports if the current file is a directory or not. Returns true if is directory.
file.openNextFile() Reports the next file or folder in a directory. Returns the next file or folder in the path.
file.rewindDirectory() Will bring you back to the first file in the directory, used in conjunction with openNextFile().
Examples for Arduino and esp8266 with core <= 2.4.2
In the Arduino IDE, you can find some examples that are very useful and well commented.
Here is a sketch that extracts all information about the SD card used.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include
<SPI.h>
#include
<SD.h>
Sd2Card card;
SdVolume volume;
SdFile root;
const
int
chipSelect
=
4
;
void
setup
() {
Serial.begin
(
9600
);
while
(
!
Serial) {
;
}
Serial.print
(
"\nInitializing SD card..."
);
if
(
!
card.init(SPI_HALF_SPEED, 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."
);
}
Serial.println
();
Serial.print
(
"Card type: "
);
switch
(card.type()) {
case
SD_CARD_TYPE_SD1:
Serial.println
(
"SD1"
);
break
;
case
SD_CARD_TYPE_SD2:
Serial.println
(
"SD2"
);
break
;
case
SD_CARD_TYPE_SDHC:
Serial.println
(
"SDHC"
);
break
;
default
:
Serial.println
(
"Unknown"
);
}
if
(
!
volume.init(card)) {
Serial.println
(
"Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card"
);
while
(
1
);
}
Serial.print
(
"Clusters: "
);
Serial.println
(volume.clusterCount());
Serial.print
(
"Blocks x Cluster: "
);
Serial.println
(volume.blocksPerCluster());
Serial.print
(
"Total Blocks: "
);
Serial.println
(volume.blocksPerCluster()
*
volume.clusterCount());
Serial.println
();
uint32_t volumesize;
Serial.print
(
"Volume type is: FAT"
);
Serial.println
(volume.fatType(), DEC);
volumesize
=
volume.blocksPerCluster();
volumesize
*
=
volume.clusterCount();
volumesize
/
=
2
;
Serial.print
(
"Volume size (Kb): "
);
Serial.println
(volumesize);
Serial.print
(
"Volume size (Mb): "
);
volumesize
/
=
1024
;
Serial.println
(volumesize);
Serial.print
(
"Volume size (Gb): "
);
Serial.println
((
float
)volumesize
/
1024.0
);
Serial.println
(
"\nFiles found on the card (name, date and size in bytes): "
);
root.openRoot(volume);
root.ls(LS_R
|
LS_DATE
|
LS_SIZE);
}
void
loop
(
void
) {
}
Here is an example of a read-write of an SD card.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include
<SPI.h>
#include
<SD.h>
File myFile;
void
setup
() {
Serial.begin
(
9600
);
while
(
!
Serial) {
;
}
Serial.print
(
"Initializing SD card..."
);
if
(
!
SD.begin(
4
)) {
Serial.println
(
"initialization failed!"
);
while
(
1
);
}
Serial.println
(
"initialization done."
);
myFile
=
SD.open(
"test.txt"
, FILE_WRITE);
if
(myFile) {
Serial.print
(
"Writing to test.txt..."
);
myFile.println(
"testing 1, 2, 3."
);
myFile.close();
Serial.println
(
"done."
);
}
else
{
Serial.println
(
"error opening test.txt"
);
}
myFile
=
SD.open(
"test.txt"
);
if
(myFile) {
Serial.println
(
"test.txt:"
);
while
(myFile.available()) {
Serial.write
(myFile.read());
}
myFile.close();
}
else
{
Serial.println
(
"error opening test.txt"
);
}
}
void
loop
() {
}
Examples esp8266 with core > 2.4.2
The latest esp8266 cores have replaced the standard SD library with an implementation that involves the use of SDFat, adding, in fact, support for filenames without limitations, management of save times, etc.
Additional commands via SDFS
SD.begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED) { SDFS.setConfig(SDFSConfig(csPin, cfg)) You can set more information and parameter that are Inherited from the SDFS class.
SDFS.format() Formats the file system. Returns true if the formatting was successful.
SDFS .open(path, mode) Opens a file. path should be an absolute path starting with a slash (e.g. /dir/filename.txt). mode is a string specifying access mode. It can be one of “r”, “w”, “a”, “r+”, “w+”, “a+”. Meaning of these modes is the same as for fopen C function. Returns File object. To check whether the file was opened successfully, use the boolean operator.
SDFS .exists(path) Returns true if a file with given path exists, false otherwise.
SDFS .openDir(path) Opens a directory given its absolute path. Returns a Dir object.
SDFS .remove(path) : Deletes the file given its absolute path. Returns true if file was deleted successfully.
SDFS .rename(pathFrom, pathTo) Renames file from pathFrom to pathTo. Paths must be absolute. Returns true if file was renamed successfully.
SDFS .info(fs_info) Fills FSInfo structure with information about the file system. Returns true is successful, false otherwise.
file.getCreationTime() Returns the creation date in epoch time.
file.getLastWrite() Returns the data of the last write/change in epoch time.
Code
So you are going to add the libraries
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include
<SPI.h>
#include
<SD.h>
const
int
chipSelect
=
4
;
void
printDirectory(File dir,
int
numTabs);
void
setup
() {
Serial.begin
(
9600
);
while
(
!
Serial) {
;
}
Serial.print
(
"\nInitializing SD card..."
);
if
(
!
SD.begin(SS)) {
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."
);
}
Serial.println
();
Serial.print
(
"Card type: "
);
switch
(SD.type()) {
case
0
:
Serial.println
(
"SD1"
);
break
;
case
1
:
Serial.println
(
"SD2"
);
break
;
case
2
:
Serial.println
(
"SDHC"
);
break
;
default
:
Serial.println
(
"Unknown"
);
}
Serial.print
(
"Cluster size: "
);
Serial.println
(SD.clusterSize());
Serial.print
(
"Blocks x Cluster: "
);
Serial.println
(SD.blocksPerCluster());
Serial.print
(
"Blocks size: "
);
Serial.println
(SD.blockSize());
Serial.print
(
"Total Blocks: "
);
Serial.println
(SD.totalBlocks());
Serial.println
();
Serial.print
(
"Total Cluster: "
);
Serial.println
(SD.totalClusters());
Serial.println
();
uint32_t volumesize;
Serial.print
(
"Volume type is: FAT"
);
Serial.println
(SD.fatType(), DEC);
volumesize
=
SD.totalClusters();
volumesize
*
=
SD.clusterSize();
volumesize
/
=
1000
;
Serial.print
(
"Volume size (Kb): "
);
Serial.println
(volumesize);
Serial.print
(
"Volume size (Mb): "
);
volumesize
/
=
1024
;
Serial.println
(volumesize);
Serial.print
(
"Volume size (Gb): "
);
Serial.println
((
float
)volumesize
/
1024.0
);
Serial.print
(
"Card size: "
);
Serial.println
((
float
)SD.size()
/
1000
);
FSInfo fs_info;
SDFS.info(fs_info);
Serial.print
(
"Total bytes: "
);
Serial.println
(fs_info.totalBytes);
Serial.print
(
"Used bytes: "
);
Serial.println
(fs_info.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) {
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
{
Serial.print
(
"\t\t"
);
Serial.print
(entry.size(), DEC);
time_t cr
=
entry.getCreationTime();
time_t lw
=
entry.getLastWrite();
struct tm
*
tmstruct
=
localtime(
&
cr);
Serial.printf(
"\tCREATION: %d-%02d-%02d %02d:%02d:%02d"
, (tmstruct
-
>tm_year)
+
1900
, (tmstruct
-
>tm_mon)
+
1
, tmstruct
-
>tm_mday, tmstruct
-
>tm_hour, tmstruct
-
>tm_min, tmstruct
-
>tm_sec);
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();
}
}
And the result will be
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Initializing SD card...Wiring is correct and a card is present.
Card type: SDHC
Cluster size: 32768
Blocks x Cluster: 64
Blocks size: 512
Total Blocks: 940
Total Cluster: 60184
Volume type is: FAT16
Volume size (Kb): 1972109
Volume size (Mb): 1925
Volume size (Gb): 1.88
Card size: 1972109.25
Total bytes: 1972109312
Used bytes: 1376256
System Volume Information/
SPLAS.H 49354 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
desktop.ini 130 CREATION: 2021-03-23 14:40:24 LAST WRITE: 2021-01-29 12:02:42
PROVA/
SOTTO/
1109.PDF 148872 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
WPSETT~1.DAT 12 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
PAGe.PHP 122 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
LOGO.JPG 0 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
barbie_singer_step_11.png 55829 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
PROVA2/
404.PHP 121 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
LOGO.JPG 0 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
bomb.png 44236 CREATION: 2021-03-23 14:40:14 LAST WRITE: 2019-01-02 16:34:28
SPLASk.H 24677 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
TESTe.TXT 32 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
logoBN1264.bmp 9338 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
1109.pdf 148872 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
barbie_singer_step_11.png 55829 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
ciao/
logoEncod.txt 6500 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
logo.jpg 46136 CREATION: 2021-03-23 14:40:18 LAST WRITE: 2019-10-23 13:15:28
logoSchermo.xcf 6473 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
nome.svg 8546 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
nome_vector_text.svg 20513 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
logoBN128x64.png 2213 CREATION: 2021-01-01 00:00:00 LAST WRITE: 2021-01-01 00:00:00
LOGOs.JPG 71112 CREATION: 2000-01-01 01:00:00 LAST WRITE: 2000-01-01 01:00:00
The second sketch also works on this version of the core.
Thanks
How to use SD card with esp8266 and Arduino How to use SD card with esp32 How to use SD card with stm32 and SdFat library