Learning Goals 5 min
- Wire a microSD card module to the UNO over SPI (4 data wires + power) and explain at a high level what SPI is.
- Initialise the card with
SD.begin(CS_PIN), open a file withSD.open(...), write lines withfile.println(...), and close cleanly. - Inspect the resulting file by removing the card and plugging it into your laptop. Confirm the text actually got written.
Warm-Up 10 min
EEPROM gave you 1 KB. An SD card gives you 4 GB, or 16 GB, or 128 GB. That's 4 million to 100 million times more storage. With that much room you can log a sensor reading every second for years.
What SPI is, in one paragraph
SPI (Serial Peripheral Interface) is a 4-wire communication protocol Arduinos use to talk to lots of chips: SD cards, OLED displays, shift registers, accelerometers. Four signals: MOSI (master sends data to slave), MISO (slave sends data back), SCK (clock), CS (which slave we're talking to). The Arduino is the "master"; the SD card is the "slave". We'll cover SPI properly in L3-20; today we just use the SD library which hides the protocol.
New Concept · Wiring + library use 25 min
Pinout — UNO + microSD module
D11, D12, D13 and nowhere else. Only CS is yours to pick, and that is the whole idea of a bus: several devices share the same three wires, and CS is how you say which one you are talking to. Note MOSI and MISO are one direction each; that is why the card can answer.| SD module | UNO pin | SPI role |
|---|---|---|
| VCC | +5 V | — |
| GND | GND | — |
| MISO | D12 | data from card to Arduino |
| MOSI | D11 | data from Arduino to card |
| SCK | D13 | clock |
| CS | D10 | chip select (we picked it; any free pin works) |
Three of the four pins (MISO 12, MOSI 11, SCK 13) are hardware-fixed — those are the UNO's SPI hardware pins, you can't move them. CS is your choice — D10 is the conventional default. Six wires total counting power.
Card format
The SD library only reads FAT16 or FAT32 formatted cards. New cards usually arrive FAT32. If the library can't see your card, use your computer to reformat it: right-click → Format → FAT32. Don't use exFAT or NTFS.
The minimum-viable SD sketch
#include <SPI.h>
#include <SD.h>
const int CS = 10;
void setup() {
Serial.begin(9600);
while (!Serial) ;
Serial.print("Initialising SD card... ");
if (!SD.begin(CS)) {
Serial.println("FAILED.");
return;
}
Serial.println("OK.");
// Open or create a file in write mode
File f = SD.open("hello.txt", FILE_WRITE);
if (f) {
f.println("Hello from Arduino!");
f.close();
Serial.println("Wrote hello.txt.");
} else {
Serial.println("Could not open file.");
}
}
void loop() {}Four library calls:
SD.begin(CS)— initialise the SD library and the card. Returns true on success.SD.open(name, mode)— open a file.FILE_WRITEopens for appending (creates if missing). Returns aFileobject.f.println(string)— write a line. Same API as Serial.println.f.close()— flush and close. Always close, or your write may not actually land on the card.
FILE_WRITE vs FILE_READ
FILE_WRITEopens for appending — new content goes to the end of an existing file (or creates a fresh one).FILE_READopens for reading from the start. Usefile.read()orfile.readStringUntil('\n').
There's no "overwrite from scratch" mode in this library. To start fresh, delete the file first: SD.remove("hello.txt");
Worked Example · Hello world to SD 25 min
Step 1 — format the card
Plug the microSD into a card reader on your laptop. Right-click → Format → FAT32. Choose a small allocation unit (default is fine). Confirm. Takes a few seconds.
Step 2 — wiring
Connect the SD module as per the pin table above. Insert the formatted card.
Step 3 — upload the minimum sketch
Use the sketch from the New Concept section. Open Serial Monitor at 9600. Expected output:
Initialising SD card... OK. Wrote hello.txt.
If you see FAILED, double-check: card is FAT32, CS pin matches the wiring, all 4 SPI wires are connected correctly.
Step 4 — verify on your computer
Power off the Arduino. Eject and remove the SD card. Plug into your laptop. Open the card's file browser. You should see HELLO.TXT (8.3 names — old FAT limitation; long filenames work but are mangled).
Open the file. Inside:
Hello from Arduino!
One line. Reinsert the card into the Arduino, power on, and the sketch will run again — appending a second line. Now the file reads:
Hello from Arduino! Hello from Arduino!
Every boot adds one more line. That's persistence on the gigabyte scale.
Step 5 — sketch a real log
Modify the sketch to log a timestamp + pot reading every second:
#include <SPI.h>
#include <SD.h>
const int CS = 10;
const int POT = A0;
File log;
unsigned long lastLog = 0;
void setup() {
Serial.begin(9600);
if (!SD.begin(CS)) { Serial.println("SD fail");
return;
}
log = SD.open("data.csv", FILE_WRITE);
if (log) {
log.println("seconds,pot");
log.close();
}
}
void loop() {
if (millis() - lastLog < 1000) return;
lastLog = millis();
log = SD.open("data.csv", FILE_WRITE);
if (log) {
log.print(millis() / 1000);
log.print(",");
log.println(analogRead(POT));
log.close();
}
}Notice the pattern: open, write one line, close. For high-rate loggers this is wasteful (a lot of open overhead). For 1 Hz it's fine and means a power loss costs at most 1 line of data.
Step 6 — pull the card after a minute
Let the logger run for a minute or so. Pop the card. Open data.csv on your laptop:
seconds,pot 1,512 2,512 3,481 4,237 5,189 ...
One row per second, header on top. Drop into Excel → Insert Chart → line chart. Your first real persistent data logger. Cluster G's payoff in one sketch.
Basic 5 min
Goal: Use SD.remove("data.csv") at the top of setup to ensure a fresh file every boot. Compare with the appending behaviour.
Challenge 1 5 min
Goal: Use FILE_READ to open data.csv and print its first 20 lines to Serial. Useful for debugging without removing the card.
Challenge 2 5 min
Goal: Log to a NEW filename every boot — log001.csv, log002.csv, log003.csv. Use EEPROM (from L02-39) to remember the next log number.
Challenge 3 · Multi-channel logger 15 min
Take any 2–3 sensors you've used in L2 (LDR, pot, DHT11, HC-SR04, thermistor — your pick) and log them all to one CSV file. One row per sample interval, multiple columns, header on top.
Spec:
- Pick a sample interval that's appropriate for your slowest sensor (DHT11 = 2 s minimum).
- Header row:
seconds,sensor1,sensor2,sensor3 - One data row per sample. Use
sprintf+ a singleprintlnfor tidy formatting. - Handle invalid readings (NaN, -1) by writing an empty cell (e.g.
10,27.3,,512) so the CSV stays parseable. - Log for at least 5 minutes. Pop the card. Chart in spreadsheet.
It's done when:
- The CSV opens in Excel/Sheets without errors and has correct column count on every row.
- The chart from the data tells a coherent story (e.g. temperature stable, light dropping as you blocked the window, pot turned at one point).
- Pulling power mid-log loses at most 1–2 rows (not the whole file).
- You can describe in one sentence what each column represents.
Recap 5 min
An SD card module + the SD library + 6 wires gives your Arduino virtually unlimited persistent storage. The API mirrors Serial closely — file.println, file.print, and the file appears on a regular FAT-formatted microSD card you can pop into any computer. The big trade-offs are RAM (SD library eats ~500 bytes), speed (writes are slow), and the need to close() for data to actually land on disk. Combined with EEPROM (small, fast, on-chip) you now have the full Arduino persistence story. Tomorrow we use all of this to ship the proper Sensor Data Logger — Cluster G's capstone.
- SD card
- The removable memory card found in cameras, phones, IoT devices. Sizes from 4 GB to 1 TB. Mounts as a regular flash filesystem on a computer.
- microSD
- The physically smaller form factor (15 × 11 mm vs full-size 32 × 24 mm). Same protocol; an adapter lets a microSD fit a full-size slot.
- FAT32
- The filesystem format the Arduino SD library understands. Older but universally supported. Most blank cards arrive in this format.
- SPI
- Serial Peripheral Interface — the 4-wire protocol the Arduino uses to talk to the SD card (and many other chips). Covered in depth in L03-20.
- MOSI / MISO / SCK / CS
- The four SPI wires. MOSI = master to slave data; MISO = slave to master data; SCK = clock; CS = chip select. On UNO: D11, D12, D13, and your-choice-of-pin.
SD.begin(CS)- Initialises the SD library and the card hardware. Returns true on success. Call once in
setup(). SD.open(name, mode)- Opens or creates a file.
FILE_WRITEappends;FILE_READreads. Returns aFileobject that's falsy on failure. file.close()- Flushes buffered data to the card and releases the file handle. Always call after writing. Skipping it = silent data loss on power-cycle.
- 8.3 filenames
- The classic FAT short-filename limit: up to 8 chars + a 3-char extension (
data.csv,log001.txt). The SD library supports long names too but it's safer to stick with 8.3. - Card removal
- Always power down the Arduino before pulling the card to avoid corrupting the filesystem. (Phones & cameras have a software "eject" for the same reason.)
Extra Mission 5 min
Part 1 — Design a data question
Pick a question about an everyday place that data could answer. Ideas: "Does my room get colder at night?", "When is the hallway darkest?", "How bright is my desk after school?". On paper, design a logger to answer it.
Your design must include:
- The question, in one sentence.
- The sensors you need, from those used in Level 2.
- The sample interval, and how long the logger must run.
- The CSV header row and one example data row.
- Your prediction of what the data will show.
Part 2 — Make it
Build the logger with the microSD module, writing your header and one row per sample to a CSV file. Run it for at least 30 minutes in the real place. Open the file in a spreadsheet and make one chart.
Bring back next class: your design, your hw-l02-41.ino sketch, and a screenshot of your chart. Was your prediction right?