STANNUM
Open the simulator

Tutorials / Storage

SD card

A micro SD module on SPI that stores real files — they survive a restart, and you can eject the card to test your error handling.

Parts in the example:
  • SD card
The SD card block as it appears on the canvas.

What it is

The micro SD module is the way a microcontroller keeps data when the power goes: logs, settings, images for a display. The card speaks SPI; the SD library mounts its file system and gives you open, read, write and directories, the way a computer would.

In the simulator

The block shows the socket with a card in it — a 16 MB card, already formatted (FAT16), with a small text file from the factory. The Card setting ejects it: with no card, SD.begin() returns false, which is the case your code must handle. What you write stays on the card between runs of the same project.

Pins

PinWhat it is
CSchip select — to a GPIO (5 in the example; whatever you pass to SD.begin())
MOSIdata to the card — to GPIO 23 (VSPI) or 13 (HSPI)
MISOdata from the card — to GPIO 19 (VSPI) or 12 (HSPI)
SCKclock — to GPIO 18 (VSPI) or 14 (HSPI)
VCCpower — to a 3V3 symbol
GNDground — to a GND symbol

Wiring

Build this circuit yourself: start a New project, add the parts from the catalog and wire them as in the table.

The wired circuit, as the lab draws it.
SD card pinGoes to
CSD5 on the board (GPIO 5)
MOSID23 on the board (GPIO 23)
MISOD19 on the board (GPIO 19)
SCKD18 on the board (GPIO 18)
VCCa 3V3 symbol
GNDa GND symbol

Settings

Double-click the block's title bar to open its card, then the Settings tab. Changes apply to the running simulation right away.

The Settings tab of the SD card.
SettingAcceptsDefaultNotes
CardInserted · Ejected (no card)InsertedEject it to test what your code does when the card is not there — SD.begin() returns false.

Code

sketch.cpp
#include <Arduino.h>
#include <SPI.h>
#include <SD.h>

// The card talks SPI. On the ESP32 the default bus (VSPI) is SCK 18, MISO 19,
// MOSI 23 — the chip select is whatever GPIO you pass to SD.begin().
const int CS = 5;
int line = 0;

void setup() {
  Serial.begin(115200);
  delay(300);
  if (!SD.begin(CS)) {
    Serial.println("no card found — is it inserted? are the four SPI wires right?");
    for (;;) delay(1000);
  }
  Serial.printf("card ready: %llu MB\n", SD.cardSize() / (1024 * 1024));

  // the card comes formatted, with a file already on it
  File root = SD.open("/");
  for (File f = root.openNextFile(); f; f = root.openNextFile())
    Serial.printf("  %s  (%u bytes)\n", f.name(), (unsigned)f.size());
}

void loop() {
  // APPEND: each run adds lines to the same file — like a real logger
  File log = SD.open("/log.txt", FILE_APPEND);
  if (log) {
    log.printf("line %d at %lu ms\n", ++line, millis());
    log.close();
  }

  // read it all back, so you can see the file grow
  File back = SD.open("/log.txt");
  Serial.printf("--- /log.txt (%u bytes) ---\n", (unsigned)back.size());
  while (back.available()) Serial.write(back.read());
  back.close();
  delay(3000);
}

SD.begin(CS) mounts the card on the default bus (VSPI: 18, 19, 23). The sketch lists the root directory, then appends a line to /log.txt every three seconds and reads the whole file back — the logger pattern, with the file opened in FILE_APPEND mode.

Run it

Press Build and run. The first build of a project takes a while; after that, only what changed is rebuilt.

The simulation running: the canvas reacts and the serial monitor shows what the code prints.

The serial monitor shows the card size, the factory file, and /log.txt growing by one line per cycle. Press restart: the lines are still there, and the count continues from where the file was. Eject the card in Settings and restart: “no card found”.

How the simulation models it

Try this

  1. Write a CSV header on the first run only (check whether the file exists with SD.exists()).
  2. Read a number from /config.txt at start-up and use it as the blink interval — a configuration file.

See also