База знаний · Датчики и модули
SD-карта и логгер данных на Arduino
Четыре провода, формат FAT32, имена по восемь символов — и половина памяти Uno, которой больше нет.
У Arduino Uno две тысячи байт оперативной памяти и килобайт EEPROM. Этого хватает на настройки, но не на журнал измерений: температура раз в минуту за сутки — это уже больше, чем плата может удержать. Карта microSD решает вопрос радикально: гигабайты, обычные файлы и возможность вынуть карту и открыть журнал в Excel. Платить за это приходится памятью — и как раз про эту цену обычно не пишут.
Куда вообще можно писать
| Место | Объём | Сколько живёт | Для чего |
|---|---|---|---|
| Переменные (ОЗУ) | 2 КБ у Uno | исчезает при выключении | рабочие данные прямо сейчас |
| EEPROM | 1 КБ у Uno | около 100 000 перезаписей ячейки | настройки, которые меняются редко |
| Карта microSD | гигабайты | пока карта жива | журналы измерений, звуки, картинки |
| Флеш ESP (LittleFS) | от 128 КБ до нескольких МБ | пока не перепрошьёте раздел | настройки и странички веб-сервера |
Правило простое: то, что меняется каждую секунду, EEPROM убьёт — у неё около ста тысяч перезаписей на ячейку, и при записи раз в секунду это меньше полутора суток. Карта такого ограничения не знает: в ней есть свой контроллер, который сам раскидывает записи по микросхеме. Подробнее про память платы — в статье «Память Arduino: флеш, ОЗУ и EEPROM».
Четыре провода и питание
Карта разговаривает по шине SPI: такт (SCK), «плата говорит» (MOSI), «карта отвечает» (MISO) и выбор устройства (CS). Первые три жёстко привязаны к железу платы и сменить их нельзя, а вот номер CS вы назначаете сами — это обычный вывод, который скетч дёргает вниз перед разговором. Отсюда и первое правило отладки: если карта не находится, меняйте местами MOSI и MISO, а не всё подряд.
Второе, что стоит понять сразу: сама карта питается от 3,3 В, пятивольтовые уровни для неё смертельны. Поэтому на модуле стоят две детали — стабилизатор на 3,3 В и буфер, который пересчитывает уровни. Стабилизатору нужен запас по напряжению: у ходового AMS1117 паспортное падение около 1,2 В, гарантированное — не больше 1,3 В. Отсюда неочевидное следствие, на котором спотыкаются все, кто переходит с Uno на ESP: модуль нельзя питать от вывода 3,3 В — карте достанется около двух вольт, и она просто не ответит. Модулю нужен вывод 5 В, даже если сама плата трёхвольтовая.
В карточке нашего SPI-адаптера карт microSD — 176 ₽ так и написано: питание 4,5…5,5 В, ток 0,2…200 мА, уровни сигналов 3,3 или 5 В, карты до 32 ГБ. Есть и модуль под полноразмерную карту SD — 126 ₽, но у него в паспорте карты только до 2 ГБ и только FAT16 — для журнала этого хватит, для чего-то большего берите первый. Двести миллиампер — это не опечатка: в момент записи карта берёт заметно больше, чем в покое, и на слабом питании появляется самая обидная неисправность — «от компьютера пишет, от батарейки нет».
Первый запуск: карта на месте?
Начинать нужно не с логгера, а с проверки. Этот скетч ищет карту, пишет в файл строку и тут же читает её обратно — если в мониторе появился размер файла и текст, дальше можно ничего не бояться. Библиотека SD уже встроена в Trema IDE, ставить ничего не нужно.
#include <SPI.h>
#include <SD.h>
// Вывод выбора карты (CS). На Uno, Nano и Pro Mini — 10, на Mega — 53,
// на готовых шилдах часто 4. Остальные три провода менять нельзя:
// они жёстко привязаны к аппаратному SPI платы.
const int PIN_CS = 10;
void setup() {
Serial.begin(9600);
Serial.println("Ищем карту...");
if (!SD.begin(PIN_CS)) {
Serial.println("Карта не найдена.");
Serial.println("Проверьте: питание 5 В, четыре провода, номер вывода CS,");
Serial.println("формат карты FAT16 или FAT32 (exFAT библиотека не читает).");
return;
}
Serial.println("Карта на месте.");
// Пишем строку и тут же читаем её обратно — это и есть проверка.
File f = SD.open("TEST.TXT", FILE_WRITE);
if (!f) {
Serial.println("Файл не открылся: проверьте имя (8 символов, точка, 3).");
return;
}
f.println("proverka");
f.close();
f = SD.open("TEST.TXT");
Serial.print("Размер файла, байт: ");
Serial.println(f.size());
while (f.available()) {
Serial.write(f.read());
}
f.close();
}
void loop() {
}
Скорость шины библиотека выбирает сама и довольно осторожно: знакомство с картой идёт на 250 кГц, а рабочая скорость — 4 МГц, то есть четверть тактовой частоты Uno. Если хочется быстрее (и провода короткие), у SD.begin есть форма с двумя аргументами: сначала частота, потом вывод CS.
Формат карты: FAT32 и ничего другого
Библиотека читает только FAT16 и FAT32 — так исторически устроены все простые файловые системы для микроконтроллеров. Проверка в коде буквальная: если в загрузочном секторе не написано «512 байт на сектор», том считается неподходящим и SD.begin возвращает ложь. Именно в это упирается самая частая история «купил карту на 64 ГБ — плата её не видит»: все карты от 64 ГБ приходят с завода в формате exFAT, а это как раз другой загрузочный сектор.
Что делать: отформатировать карту в FAT32 или, что проще и надёжнее, взять карту не больше 32 ГБ — для журнала измерений это несопоставимо много (строка в CSV весит десятки байт, гигабайта хватит на годы записи раз в секунду). Тип тома, кстати, определяется не объёмом карты, а числом кластеров: меньше 4 085 — FAT12, меньше 65 525 — FAT16, больше — FAT32.
Имя файла: восемь символов, точка, три
Это не прихоть библиотеки, а формат коротких имён из времён DOS. Внутри есть функция, которая превращает ваше имя в одиннадцать байт записи каталога, и она отвергает всё, что не помещается: больше восьми символов до точки, вторую точку, пробел, кириллицу и полтора десятка служебных знаков. Строчные буквы она молча переводит в заглавные — поэтому log.csv и LOG.CSV для карты один и тот же файл.
Коварство в том, что никакой ошибки не будет. Скетч соберётся, карта найдётся, а SD.open просто вернёт «файл не открылся» — и человек ищет проблему в проводах, хотя дело в имени izmereniya.csv.
Сколько памяти на это уходит
Вот главная цена, о которой стоит знать заранее. Библиотека держит в оперативной памяти буфер целого блока карты — 512 байт, четверть всей памяти Uno. Плюс структуры открытого файла, плюс ваши переменные. Мы собрали скетчи из этой статьи и посмотрели, что показывает компилятор:
| Скетч | ОЗУ, байт (из 2 048) | Флеш, байт (из 32 256) |
|---|---|---|
| Пустые setup и loop | 9 | 444 |
| Проверка карты (скетч ниже) | 1 249 | 11 276 |
| Та же проверка, подсказки в F() | 847 | 11 312 |
| Логгер (скетч ниже) | 970 | 13 164 |
| Логгер плюс часы DS3231 | 1 055 | 13 788 |
| Логгер на библиотеке SdFat | 896 | 18 778 |
Две строки здесь стоит перечитать. Первая: проверочный скетч съедает больше логгера — и виноваты не карта и не библиотека, а длинные русские подсказки. Каждая строка в кавычках при старте копируется в оперативную память; если обернуть их в F(), расход падает с 1 249 до 847 байт, то есть возвращается пятая часть всей памяти платы. Вторая: библиотека SdFat экономит немного ОЗУ, но занимает почти вдвое больше флеша — она умеет длинные имена и exFAT, и за это надо платить.
Практический вывод: логгер на Uno — это уже тесно. Если в проекте кроме карты будет дисплей и пара датчиков, берите плату с запасом памяти (Mega, ESP32) или считайте байты. Что происходит, когда памяти не хватает, разобрано в статье про память: плата не выдаёт ошибку, она просто начинает вести себя странно.
Логгер: пишем таблицу для Excel
Обычный формат журнала — CSV: одна строка на измерение, столбцы через разделитель. Мы берём точку с запятой, потому что русский Excel считает запятую дробной частью числа и всю строку укладывает в одну ячейку.
#include <SPI.h>
#include <SD.h>
const int PIN_CS = 10;
const int PIN_SENSOR = A0;
const unsigned long PERIOD = 2000; // как часто писать строку, мс
// Имя строго по правилу 8.3: заглавные буквы, без пробелов и кириллицы.
const char FILE_NAME[] = "LOG.CSV";
unsigned long lastWrite = 0;
bool cardOk = false;
void setup() {
Serial.begin(9600);
cardOk = SD.begin(PIN_CS);
if (!cardOk) {
Serial.println("Карты нет — пишу только в монитор.");
return;
}
// Заголовок таблицы пишем один раз, если файла ещё не было.
// Разделитель — точка с запятой: так русский Excel сразу разложит
// строку по столбцам, а запятая останется дробной частью.
if (!SD.exists(FILE_NAME)) {
File f = SD.open(FILE_NAME, FILE_WRITE);
if (f) {
f.println("sec;raw;volt");
f.close();
}
}
}
void loop() {
if (millis() - lastWrite < PERIOD) {
return;
}
lastWrite = millis();
int raw = analogRead(PIN_SENSOR);
float volt = raw * 5.0 / 1024.0;
Serial.print(millis() / 1000);
Serial.print(" c, ");
Serial.print(volt, 2);
Serial.println(" В");
if (!cardOk) {
return;
}
// Открыли, дописали строку, закрыли. Закрытие — обязательная часть:
// пока файл открыт, последняя порция лежит в памяти платы, а не на карте.
File f = SD.open(FILE_NAME, FILE_WRITE);
if (!f) {
Serial.println("Файл не открылся.");
return;
}
f.print(millis() / 1000);
f.print(';');
f.print(raw);
f.print(';');
f.println(volt, 3);
f.close();
}
Три вещи в этом скетче важнее остальных.
Файл открывается и закрывается на каждую запись. Так медленнее, зато после каждой строки данные уже на карте. Пока файл открыт, последняя порция лежит в буфере платы, и выдернутое питание её уносит. Если пишете часто и вам дорога скорость — оставляйте файл открытым, но вызывайте flush хотя бы раз в несколько секунд.
Заголовок пишется один раз. Проверка SD.exists нужна, потому что FILE_WRITE у этой библиотеки означает «дописать в конец», а не «создать заново». Файл после перезагрузки платы продолжится, а не обнулится — это удобно, но про это забывают и потом удивляются, откуда в таблице десять заголовков.
Время в журнале — от millis. Это секунды с момента включения, а не время суток: после перезагрузки счёт начнётся заново. Как только журнал должен ложиться на календарь, нужны часы с батарейкой — про них есть отдельная статья «Часы реального времени». Плата с часами и картой — это и есть классический логгер.
Прочитать файл и посмотреть, что на карте
#include <SPI.h>
#include <SD.h>
const int PIN_CS = 10;
void setup() {
Serial.begin(9600);
if (!SD.begin(PIN_CS)) {
Serial.println("Карта не найдена.");
return;
}
// Второй аргумент не нужен: без него файл открывается на чтение.
File f = SD.open("LOG.CSV");
if (!f) {
Serial.println("Файла LOG.CSV на карте нет.");
return;
}
Serial.print("Размер, байт: ");
Serial.println(f.size());
while (f.available()) {
Serial.write(f.read());
}
f.close();
// Что ещё лежит на карте — просто список имён в корне.
File dir = SD.open("/");
while (true) {
File item = dir.openNextFile();
if (!item) {
break;
}
Serial.print(item.name());
Serial.print(" ");
Serial.println(item.size());
item.close();
}
dir.close();
}
void loop() {
}
Тот же SD.open без второго аргумента открывает файл на чтение, а openNextFile перебирает содержимое папки. Обратите внимание на Serial.write вместо print: мы отдаём в монитор байты как есть, ничего не превращая в текст.
Когда на шине не одна карта
SPI рассчитана на несколько устройств: общие такт и данные, а выбор — своим проводом к каждому. На практике это место, где ломается больше всего проектов.
Во-первых, у плат AVR есть особый вывод — 10 на Uno, 53 на Mega. Если сделать его входом и притянуть к земле, контроллер решит, что он сам стал ведомым, и шина замолчит. Библиотека это знает и на всякий случай объявляет вывод выходом при старте, но если вы потом отдадите его кнопке — получите загадочное молчание карты.
Во-вторых, у части дешёвых адаптеров линия ответа не освобождается после снятия выбора: карта продолжает держать провод, и соседнее устройство на той же шине не может ответить. Проверяется просто: отключите модуль карты — если сосед ожил, дело в этом. Лечится адаптером с честным буфером или разнесением устройств на разные выводы.
Если у вас ESP: карта не всегда нужна
У ESP8266 и ESP32 флеш-памяти на плате гораздо больше, чем нужно скетчу, и часть её можно отдать под файлы. Тогда никаких проводов вообще не нужно.
| Хранилище | Где лежит | Сколько | Чем хорошо | Чем плохо |
|---|---|---|---|---|
| LittleFS | во флеше самой платы | обычно 1—2 МБ | ничего не подключать, имена файлов любой длины | объём мал, при перепрошивке раздел можно потерять |
| SPIFFS | там же | то же самое | старые примеры написаны под неё | объявлена устаревшей, папок не понимает — берите LittleFS |
| Карта microSD | снаружи, по проводам | гигабайты | карту можно вынуть и вставить в компьютер | четыре провода, питание и капризы при плохом контакте |
Выбор между ними простой. Настройки, страницы веб-сервера, небольшой журнал — LittleFS, и никаких модулей. Многочасовая запись, звук, картинки или желание вынуть носитель и отнести к компьютеру — карта. SPIFFS сегодня брать не стоит: в ядре ESP8266 её служебные символы уже помечены как устаревшие, папок она не понимает, а LittleFS делает то же самое лучше.
Что делать, если не работает
| Симптом | Почему | Что делать |
|---|---|---|
| «Карта не найдена» сразу после включения | не тот вывод CS или перепутаны MOSI и MISO | сверьте четыре провода со схемой выше и номер CS в скетче |
| Карта не находится, хотя провода верные | формат exFAT — так продают все карты от 64 ГБ | отформатируйте в FAT32; проще всего взять карту не больше 32 ГБ |
| Работает от компьютера, глохнет от батарейки | просадка питания: карта в момент записи берёт до сотен мА | питание 5 В с запасом, конденсатор 100 мкФ рядом с модулем |
| SD.begin проходит, а файл не открывается | имя не по правилу 8.3 — пробел, длинное слово или кириллица | переименуйте в LOG.CSV; проверьте на схеме с именами выше |
| Файл есть, но он пустой или обрывается | плату выключили, не закрыв файл | close после каждой записи — или хотя бы flush |
| С картой всё хорошо, но соседний модуль замолчал | у части адаптеров линия MISO остаётся занятой и после снятия выбора | разведите устройства по времени или поставьте адаптер с честным буфером |
| Плата перестала стартовать после подключения модуля | на ESP8266 вывод CS повесили на D8 — это GPIO15, вывод режима загрузки | перенесите CS на D3 или D4 и укажите новый номер в скетче |
И общий совет напоследок: проверяйте карту на компьютере перед тем, как искать беду в схеме. Карты из старых телефонов часто уже переписаны до предела и ведут себя непредсказуемо — читаются, но не пишут. Чистая карта на 8—16 ГБ, отформатированная в FAT32, снимает половину вопросов ещё до начала.
Частые вопросы
Почему Arduino не видит SD-карту?
Три причины по частоте. Формат: все карты от 64 ГБ приходят с завода в exFAT, а библиотека читает только FAT16 и FAT32. Провода: чаще всего перепутаны MOSI и MISO или указан не тот вывод CS. Питание: модулю нужны 5 В, от вывода 3,3 В его стабилизатору не хватает напряжения и карте достаётся около двух вольт.
Какие карты памяти подходят для Arduino?
Любые SD и microSD объёмом до 32 ГБ, отформатированные в FAT16 или FAT32. Карты больше 32 ГБ работают только после переформатирования в FAT32. Для журнала измерений хватает самой маленькой карты: строка в CSV весит десятки байт.
Как называть файлы на SD-карте?
По правилу 8.3: до восьми символов, одна точка, до трёх символов расширения. Только латиница и цифры, без пробелов. Строчные буквы библиотека молча делает заглавными. Неверное имя не вызывает ошибку — файл просто не откроется.
Сколько памяти занимает библиотека SD?
Много: она держит буфер целого блока карты — 512 байт, четверть всей памяти Arduino Uno. Мы собрали скетчи из статьи под Uno: проверка карты заняла 1 249 байт ОЗУ из 2 048, логгер — 970, логгер с часами DS3231 — 1 055. На Uno это уже тесно.
Можно ли питать модуль SD-карты от 3,3 В?
Нет. На модуле стоит стабилизатор 3,3 В, которому нужен запас по напряжению около 1,2 В — то есть на вход требуется не меньше 4,5 В. При питании от 3,3 В карта получит примерно два вольта и не ответит. Модулю нужен вывод 5 В даже на трёхвольтовой плате.
Как перенести данные с карты в Excel?
Писать в файл с расширением CSV и разделять столбцы точкой с запятой: русский Excel считает запятую дробной частью числа и без разделителя укладывает всю строку в одну ячейку. Файл открывается двойным щелчком, ничего конвертировать не нужно.
Нужна ли SD-карта для ESP8266 и ESP32?
Часто нет: у этих плат во флеш-памяти есть место под файлы, которое подключается библиотекой LittleFS — без единого провода. Карта нужна, когда данных много или носитель хочется вынуть и отнести к компьютеру. SPIFFS сегодня брать не стоит, она объявлена устаревшей.
Читайте также
Обновлено: 25 августа 2026
