База знаний · Датчики и модули

SD-карта и логгер данных на Arduino

Четыре провода, формат FAT32, имена по восемь символов — и половина памяти Uno, которой больше нет.

У Arduino Uno две тысячи байт оперативной памяти и килобайт EEPROM. Этого хватает на настройки, но не на журнал измерений: температура раз в минуту за сутки — это уже больше, чем плата может удержать. Карта microSD решает вопрос радикально: гигабайты, обычные файлы и возможность вынуть карту и открыть журнал в Excel. Платить за это приходится памятью — и как раз про эту цену обычно не пишут.

Куда вообще можно писать

МестоОбъёмСколько живётДля чего
Переменные (ОЗУ)2 КБ у Unoисчезает при выключениирабочие данные прямо сейчас
EEPROM1 КБ у Unoоколо 100 000 перезаписей ячейкинастройки, которые меняются редко
Карта microSDгигабайтыпока карта живажурналы измерений, звуки, картинки
Флеш ESP (LittleFS)от 128 КБ до нескольких МБпока не перепрошьёте разделнастройки и странички веб-сервера

Правило простое: то, что меняется каждую секунду, EEPROM убьёт — у неё около ста тысяч перезаписей на ячейку, и при записи раз в секунду это меньше полутора суток. Карта такого ограничения не знает: в ней есть свой контроллер, который сам раскидывает записи по микросхеме. Подробнее про память платы — в статье «Память Arduino: флеш, ОЗУ и EEPROM».

Четыре провода и питание

Модуль карты — это четыре провода и питаниеПЛАТАArduino Uno / NanoМОДУЛЬадаптер microSDmicroSD10CSвыбор карты13SCKтакт11MOSIплата говорит12MISOкарта отвечает5 Впитание и общая землямодулю нужно именно 5 В: его стабилизатору 3,3 В нужен запас по напряжениюВывод CS можно назначить любой — его номер вы пишете в скетче сами
Вывод CS можно назначить любой — его номер вы сами пишете в скетче. Остальные три сменить нельзя.

Карта разговаривает по шине 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.

Имя файла: восемь символов, точка, три

Имя файла на карте: восемь символов, точка, ещё триLOG.CSVТак можноТри символа до точки, три после, все заглавные латинские — образцовое имя.Правила зашиты в библиотеку: до 8 символов, одна точка, до 3 после неё,только латиница и цифры, без пробелов. Неверное имя — файл не откроется.
Самое обидное здесь — тишина: библиотека не ругается на плохое имя, она просто возвращает «файл не открылся». Скетч при этом собирается и запускается, а на карте ничего не появляется.

Это не прихоть библиотеки, а формат коротких имён из времён DOS. Внутри есть функция, которая превращает ваше имя в одиннадцать байт записи каталога, и она отвергает всё, что не помещается: больше восьми символов до точки, вторую точку, пробел, кириллицу и полтора десятка служебных знаков. Строчные буквы она молча переводит в заглавные — поэтому log.csv и LOG.CSV для карты один и тот же файл.

Коварство в том, что никакой ошибки не будет. Скетч соберётся, карта найдётся, а SD.open просто вернёт «файл не открылся» — и человек ищет проблему в проводах, хотя дело в имени izmereniya.csv.

Сколько памяти на это уходит

Оперативная память Arduino Uno — 2 048 байтСкетч проверки карты из этой статьизанято 1 249 байт (61 %)свободно 7991 249 из 2 0481 249 байт: 512 — буфер блока карты, ещё около 400 — русские подсказки в мониторе.Измерено сборкой под Uno: библиотека держит буфер целого блока карты — 512 байт.
Половина памяти Uno уходит ещё до того, как вы напишете хоть строчку своего кода. Отсюда и главная беда логгеров на Uno: скетч собирается, плата стартует — и через минуту начинает вести себя странно, потому что переменным просто некуда расти.

Вот главная цена, о которой стоит знать заранее. Библиотека держит в оперативной памяти буфер целого блока карты — 512 байт, четверть всей памяти Uno. Плюс структуры открытого файла, плюс ваши переменные. Мы собрали скетчи из этой статьи и посмотрели, что показывает компилятор:

СкетчОЗУ, байт (из 2 048)Флеш, байт (из 32 256)
Пустые setup и loop9444
Проверка карты (скетч ниже)1 24911 276
Та же проверка, подсказки в F()84711 312
Логгер (скетч ниже)97013 164
Логгер плюс часы DS32311 05513 788
Логгер на библиотеке SdFat89618 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