Руководство по работе с SD-картами на Arduino
Подробный разбор примеров чтения и записи данных на SD-карту с помощью Arduino и библиотеки SD.
Статья обновлена 18.11.2021 автором Karl Söderby.
Это руководство собирает совместимое оборудование и практические примеры кода для тех, кто хочет начать работать с картами Secure Digital (SD) на Arduino.
Все примеры взяты из SD Library, которая изначально основана на SdFat Уильяма Гримана (William Greiman).
Необходимое оборудование и ПО
*Платы и шилды со встроенным слотом для SD-карты:
- MKR Zero
- MKR IoT Carrier
- MKR MEM Shield
- MKR SD Proto Shield
- MKR ENV Shield
- MKR Ethernet Shield
- Arduino Education Shield
Практические замечания перед началом
- SD-карту нужно предварительно отформатировать в FAT16 или FAT32. Большинство карт из магазина уже отформатированы, но если возникают проблемы — переформатируйте вручную.
- Имена файлов должны соответствовать формату 8.3 (не более 8 символов до точки и 3 — после). Длинные имена вроде
mylogfile_2024.txtработать не будут — используйтеlog.txtилиdata001.txt. - Обязательно указывайте правильный пин Chip Select (CS). По умолчанию это пин 4, но для MKR Zero используйте 28 или константу SDCARD_SS_PIN.
- Всегда закрывайте файл после записи с помощью
myFile.close()— иначе данные могут не сохраниться.
Схема подключения
Ниже показано, как вставить SD-карту в слот платы MKR Zero. Ни один из примеров не требует дополнительных компонентов или проводов.

Вставьте microSD-карту в слот на плате Arduino MKR Zero. Убедитесь, что карта вошла до щелчка.
Примеры
Чтение и запись (Read and Write)
Этот пример демонстрирует базовую операцию: запись строки на карту, а затем её чтение и вывод в Serial Monitor.
Как это работает:
- В
setup()создаётся новый файл черезSD.open()с именем"test.txt". ФлагFILE_WRITEоткрывает файл для чтения и записи, позиционируя курсор в конец. Если файл"test.txt"уже существует на карте — он будет открыт, а не перезаписан с нуля. - Экземпляр открытого файла называется
myFile. - После открытия используйте
myFile.println(), чтобы записать строку с переносом строки. Затем закройте файл. - Снова откройте файл через
SD.open(), прочитайте его содержимое с помощьюSD.read()и отправьте в Serial Monitor. По завершении чтения закройте файл командойSD.close().
Важно: пин 4 — это Chip Select (CS) по умолчанию для большинства плат. Для MKR Zero используйте 28 или константу SDCARD_SS_PIN.
/*
Чтение/запись SD-карты
Пример показывает, как читать и записывать данные в файл на SD-карте.
Схема подключения:
SD-карта подключена к шине SPI следующим образом:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (для MKRZero SD: SDCARD_SS_PIN)
создан ноябрь 2010
автор David A. Mellis
изменён 9 апр 2012
автор Tom Igoe
Код примера находится в открытом доступе.
*/
#include <SPI.h>
#include <SD.h>
File myFile;
void setup() {
// Открываем последовательный порт и ждём его готовности:
Serial.begin(9600);
while (!Serial) {
; // ожидание подключения к последовательному порту. Нужно только для плат с native USB
}
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() {
// после setup ничего не происходит
}
Типичные ошибки:
- Если
SD.open()возвращает0(false) — проверьте номер CS-пина и правильность форматирования карты. - Если в Serial Monitor появляются «кракозябры» — убедитесь, что скорость порта в мониторе совпадает со скоростью, заданной в коде.
---
Информация о карте (Card Information)
Этот пример считывает и выводит в Serial Monitor служебную информацию о SD-карте: тип файловой системы, объём свободного пространства и другие параметры.
/*
Тест SD-карты
Пример показывает, как использовать вспомогательные библиотеки, на которых основана
библиотека SD, чтобы получить информацию о SD-карте.
Очень полезно для проверки карты, когда не уверен в её работоспособности.
Номера пинов соответствуют пинам SPI по умолчанию для моделей Uno и Nano.
Схема подключения:
SD-карта подключена к шине SPI следующим образом:
** SDO - pin 11 на Arduino Uno/Duemilanove/Diecimila
** SDI - pin 12 на Arduino Uno/Duemilanove/Diecimila
** CLK - pin 13 на Arduino Uno/Duemilanove/Diecimila
** CS - зависит от вашего шилда или модуля SD-карты.
Здесь используется pin 10 для совместимости с другими примерами Arduino
создан 28 мар 2011
автор Limor Fried
изменён 24 июля 2020
автор Tom Igoe
*/
// подключаем библиотеку SD:
#include <SPI.h>
#include <SD.h>
// объявляем переменные с помощью функций вспомогательной библиотеки SD:
Sd2Card card;
SdVolume volume;
SdFile root;
// измените под свой шилд или модуль SD-карты;
// SPI по умолчанию на Uno и Nano: пин 10
// Arduino Ethernet Shield: пин 4
// Adafruit® SD shields и модули: пин 10
// SparkFun SD Shield: пин 8
// MKRZero SD: SDCARD_SS_PIN
const int chipSelect = 10;
void setup() {
// Открываем последовательный порт и ждём его готовности:
Serial.begin(9600);
while (!Serial) {
; // ожидание подключения к последовательному порту. Нужно только для плат с native USB
}
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");
}
// пробуем открыть раздел — он должен быть FAT16 или FAT32
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();
// выводим тип и размер первого FAT-раздела
uint32_t volumesize;
Serial.print("Volume type is: FAT");
Serial.println(volume.fatType(), DEC);
volumesize = volume.blocksPerCluster(); // кластеры — это группы блоков
volumesize *= volume.clusterCount(); // кластеров будет много
volumesize /= 2; // блоки SD-карты всегда 512 байт (2 блока = 1 КБ)
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);
root.close();
}
void loop(void) {
}
Обратите внимание: размер кластера задаётся при форматировании и может отличаться от стандартного. В примере используется стандартный размер блока — 512 байт. Это не размер кластера: размер кластера вычисляется как количество блоков на кластер. Подробнее — in this article.
---
Вывод содержимого файла (Dump File)
Этот пример показывает, как прочитать файл с SD-карты и передать его содержимое через Serial Monitor на компьютер.
На карте должен находиться файл "datalog.txt". В loop() файл открывается вызовом SD.open(). Для последовательной передачи данных используется Serial.print(), которая читает байты файла через SD.read().
Где пригодится: этот подход удобен для отладки логгеров данных — вы можете скачать накопленный лог прямо через Serial Monitor, не вынимая карту из устройства.
/*
Дамп файла с SD-карты
Пример показывает, как прочитать файл с SD-карты с помощью
библиотеки SD и передать его содержимое через последовательный порт.
Номера пинов соответствуют пинам SPI по умолчанию для моделей Uno и Nano.
Схема подключения:
SD-карта подключена к шине SPI следующим образом:
** SDO - pin 11
** SDI - pin 12
** CLK - pin 13
** CS - зависит от вашего шилда или модуля SD-карты.
Здесь используется pin 10 для совместимости с другими примерами Arduino
(для MKRZero SD: SDCARD_SS_PIN)
создан 22 декабря 2010
автор Limor Fried
изменён 9 апр 2012
автор Tom Igoe
Код примера находится в открытом доступе.
*/
#include <SD.h>
const int chipSelect = 10;
void setup() {
// Открываем последовательный порт и ждём его готовности:
Serial.begin(9600);
// ожидание подключения Serial Monitor. Нужно только для плат с native USB:
while (!Serial);
Serial.print("Initializing SD card...");
if (!SD.begin(chipSelect)) {
Serial.println("initialization failed. Things to check:");
Serial.println("1. is a card inserted?");
Serial.println("2. is your wiring correct?");
Serial.println("3. did you change the chipSelect pin to match your shield or module?");
Serial.println("Note: press reset or reopen this serial monitor after fixing your issue!");
while (true);
}
Serial.println("initialization done.");
// открываем файл. Учтите, что одновременно может быть открыт только один файл,
// поэтому перед открытием другого нужно закрыть этот.
File dataFile = SD.open("datalog.txt");
// если файл доступен — читаем из него:
if (dataFile) {
while (dataFile.available()) {
Serial.write(dataFile.read());
}
dataFile.close();
}
// если файл не открылся — выводим ошибку:
else {
Serial.println("error opening datalog.txt");
}
}
void loop() {
}
---
Управление файлами (File Management)
Этот пример показывает, как создать файл на SD-карте, убедиться в его существовании и затем удалить его.
В setup() создаётся новый файл через SD.open() с именем "example.txt". Флаг FILE_WRITE открывает файл для чтения и записи с позицией в конце. В этом примере файл сразу закрывается командой myFile.close() — нам важен сам факт его создания.
Далее наличие файла проверяется с помощью SD.exists(), после чего файл удаляется с карты командой SD.remove.
Практическое применение: такой подход полезен, когда нужно периодически очищать старые лог-файлы или управлять конфигурационными файлами прямо из скетча.
/*
Базовый пример работы с файлом на SD-карте
Пример показывает, как создать и удалить файл на SD-карте.
Номера пинов соответствуют пинам SPI по умолчанию
для моделей Uno и Nano:
SD-карта подключена к шине SPI следующим образом:
** SDO - pin 11
** SDI - pin 12
** CLK - pin 13
** CS - зависит от вашего шилда или модуля SD-карты.
Здесь используется pin 10 для совместимости с другими примерами Arduino
(для MKRZero SD: SDCARD_SS_PIN)
создан ноябрь 2010
автор David A. Mellis
изменён 24 июля 2020
автор Tom Igoe
Код примера находится в открытом доступе.
*/
#include <SD.h>
const int chipSelect = 10;
File myFile;
void setup() {
// Открываем последовательный порт и ждём его готовности:
Serial.begin(9600);
// ожидание подключения Serial Monitor. Нужно только для плат с native USB:
while (!Serial);
Serial.print("Initializing SD card...");
if (!SD.begin(10)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
} else {
Serial.println("example.txt doesn't exist.");
}
// создаём новый файл и сразу закрываем его:
Serial.println("Creating example.txt...");
myFile = SD.open("example.txt", FILE_WRITE);
myFile.close();
// проверяем, существует ли файл:
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
} else {
Serial.println("example.txt doesn't exist.");
}
// удаляем файл:
Serial.println("Removing example.txt...");
SD.remove("example.txt");
if (SD.exists("example.txt")) {
Serial.println("example.txt exists.");
} else {
Serial.println("example.txt doesn't exist.");
}
}
void loop() {
// после завершения setup ничего не происходит.
}
---
Список файлов (List Files)
Этот пример выводит список всех файлов и директорий, находящихся на SD-карте.
Основной loop() ничего не делает в цикле, потому что функция вывода содержимого директории "/" вызывается один раз — из setup(). Это сделано намеренно: список файлов нужно посмотреть один раз при старте, а не повторять бесконечно.
Функция printDirectory рекурсивно обходит все записи и выводит в Serial Monitor имя каждого файла и директории. Для файлов дополнительно отображается их размер в байтах.
Как проверить результат: откройте Serial Monitor сразу после загрузки скетча. Если карта подключена правильно и содержит файлы — вы увидите их список. Пустой вывод или сообщение об ошибке инициализации говорит о проблеме с CS-пином или форматом карты.
/*
Listfiles
Пример показывает, как вывести список файлов в директории
на SD-карте. Номера пинов соответствуют пинам SPI по умолчанию
для моделей Uno и Nano.
Схема подключения:
SD-карта подключена к шине SPI следующим образом:
** SDO - pin 11
** SDI - pin 12
** CLK - pin 13
** CS - зависит от вашего шилда или модуля SD-карты.
Здесь используется pin 10 для совместимости с другими примерами Arduino
(для MKRZero SD: SDCARD_SS_PIN)
создан ноябрь 2010
автор David A. Mellis
изменён 9 апр 2012
автор Tom Igoe
изменён 2 фев 2014
автор Scott Fitzgerald
изменён 24 июля 2020
автор Tom Igoe
Код примера находится в открытом доступе.
*/
#include <SD.h>
const int chipSelect = 10;
File root;
void setup() {
// Открываем последовательный порт и ждём его готовности:
Serial.begin(9600);
// ожидание подключения Serial Monitor. Нужно только для плат с native USB:
while (!Serial);
Serial.print("Initializing SD card...");
if (!SD.begin(chipSelect)) {
Serial.println("initialization failed. Things to check:");
Serial.println("1. is a card inserted?");
Serial.println("2. is your wiring correct?");
Serial.println("3. did you change the chipSelect pin to match your shield or module?");
Serial.println("Note: press reset or reopen this serial monitor after fixing your issue!");
while (true);
}
Serial.println("initialization done.");
root = SD.open("/");
printDirectory(root, 0);
Serial.println("done!");
}
void loop() {
// после завершения setup ничего не происходит.
}
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.println(entry.size(), DEC);
}
entry.close();
}
}