Уезжая с дачи на неделю, я оставил станцию с DHT22 и картой на два гигабайта писать погоду. Вернулся к трём сценариям, знакомым теперь наизусть: файл кончился на второй день из-за лишней колонки, которую я не посчитал; карта жива, но половина недели числится потерянными кластерами после пары отключений света; и лучший вариант — всё работает, только непонятно, почему и надолго ли. Все три отличает не везение, а один параметр, который надо посчитать до отъезда: интервал записи. Расчёт занимает вечер один раз, а экономит и сезон, и карту: оба диагноза из моего списка — и переполнение, и рассыпавшаяся разметка — вскрылись бы на бумаге за полчаса.
Здесь — планирование автономной недели по цифрам. Суточный объём: сколько станция пишет за день и что это значит для карты на два гигабайта. Ресурс: сколько циклов стирания-записи переживают флеш-ячейки, что делает wear leveling и при каком интервале ресурс перестаёт быть страшилкой. Стратегия: пакетный flush, файл на сутки, ротация и короткий чек-лист перед отъездом. Питание и сон с батарейки — отдельная тема, её разобрал в уроке про даталоггер со сном; карту здесь интересует только то, как часто в неё пишут. Расчёт ниже устроен так, чтобы его можно было повторить на салфетке: длина строки, интервал, ёмкость, ресурс — четыре числа, больше не нужно.
Базовый логгер с DHT и шапкой CSV собран в уроке про DHT-логгер; штамп времени и имя файла по дате ставит отдельный урок про DS3231. Считаю, что станция пишет и штампует, и говорю только о бюджете: байтах, циклах и днях.
Суточная арифметика: строка за строкой
Посчитаем станцию из трёх показаний: температура, влажность, давление, всё со штампом. Строка CSV со штампом и тремя числами — около 40 байт с переводом строки. Интервал 60 секунд даёт 1440 строк в сутки, это 56 килобайт. Неделя — 0,4 мегабайта, месяц — 1,7. Карта на 2 ГБ при таком темпе заполнится лет через сто, и даже агрессивные десять секунд — 576 КБ в сутки, 17 МБ в месяц — оставляют запас на годы. Отсюда первый практический вывод: на классической погодной станции объём карты не проблема вовсе; проблема начинается там, где интервал падает в десятки миллисекунд или к логу подмешивают звук и картинки. Обратный пересчёт работает так же: карта на 8 ГБ при десяти секундах проживает по объёму лет двадцать — вопрос уже не в ёмкости, а в том, нужен ли файл, в котором найти что-то сложнее, чем собрать заново.
Арифметику делают до запуска, а не по факту: карта, заполненная под завязку, перестаёт принимать записи, и последняя строка лога молчит о причине так же, как молчала первая. Суточные объёмы для текстового и байтового форматов я считал в уроке про текст против бинарника — формулы те же, подставьте свою длину строки и свой интервал. И не забывайте про шапку: файл, открывающийся без заголовка, к весу в байтах добавляет минуту недоумения при каждом открытии.
Ресурс ячеек: числа вместо страшилок
Флеш-ячейка переживает ограниченное число циклов стирания-записи. По паспортам памяти порядок такой: TLC-ячейки, на которых сидят почти все потребительские карты, — порядка тысячи-трёх тысяч циклов, MLC — в разы больше, SLC — десятки тысяч. Пугающее «сто тысяч циклов» из старых статей относилось к SLC и ранним спецификациям; современная карта — это TLC плюс контроллер с износораспределением. Цифры у производителей плавают в разы, поэтому точное значение ищите в паспорте своей карты, а не в первой попавшейся статье. Честнее смотреть на класс карты, а не только на тип ячеек: одна и та же ёмкость живёт вдвое дольше у среднего бренда, чем у самой дешёвой полки.
Страх «логгер убьёт карту за год» снимается делением. Строка в секунду — это 86 400 секторных записей в сутки, около 31 миллиона за год. Карта на 2 ГБ — примерно четыре миллиона секторов по 512 байт; при тысяче циклов контроллер может распределить по ним порядка четырёх миллиардов записей. Деля одно на другое, получаем сотню с лишним лет. Wear leveling при этом не мешает, а помогает: контроллер распределяет износ по всему массиву, и append-only лог, который пишет всегда в конец, изнашивает карту ровнее любого другого сценария. Ресурс становится реальной заботой на интервалах в десятки миллисекунд — там счёт уже на месяцы. Проверьте расчёт на своей карте за сутки теста: расхождение с этой арифметикой в разы означает, что в строке сидит поле, которое вы не посчитали.
Стратегия недели: пакет, сутки, ротация
Интервал задаёт не только объём, но и защищённость записи. Пакетный режим копит строки в оперативке и выталкивает их на карту разом: одна операция записи на дюжину строк вместо дюжины отдельных — меньше окно, в котором обрыв портит хвост файла, и меньше лишних секторных перезаписей. Механику и цену подхода разобрал в уроке про пакетную запись; для недели без человека это первый пункт плана. Окно уязвимости считается элементарно: длина пакета умножить на интервал — при минутном интервале и пакете в дюжину строк худший случай стоит двенадцати минут погоды, и не более. Компромисс честный: сбой между flush-ами стоит до дюжины строк — на погоде с минутным интервалом это невидимо.
Второй пункт — файл на сутки с именем из даты, как в уроке про DS3231-штампы: сбой одного дня не тянет за собой весь архив, а поиск «что было в четверг вечером» превращается в открытие одного файла. Третий — ротация по размеру из урока про циклическую запись: карта не переполняется молча, старое уступает место новому по вашему правилу. Таймеры всего этого собираются на millis без единого delay, как в уроке про millis вместо delay — delay в логгере скрадывает и интервал, и проверку переполнения, и реакцию на пропажу карты. Порядок приёмов не случайный: пакет защищает строку, сутки изолируют день, ротация страхует объём — три независимых пояса обороны.
#include <SPI.h>#include <SD.h> const int CS_PIN = 10;const unsigned long INTERVAL = 60000; // строка раз в минутуconst int PAKET = 12; // выталкивание раз в 12 строк File logFile;unsigned long lastRow = 0;int inBuffer = 0;int rows = 0; void setup() { Serial.begin(9600); if (!SD.begin(CS_PIN)) { Serial.println("карта не поднялась"); while (true) delay(500); } logFile = SD.open("wlog.csv", FILE_WRITE); if (logFile) logFile.println("sec,lux");} void loop() { unsigned long ms = millis(); if (ms - lastRow < INTERVAL) return; lastRow = ms; rows = rows + 1; inBuffer = inBuffer + 1; logFile.print(ms / 1000); logFile.print(","); logFile.println(analogRead(A1)); if (inBuffer >= PAKET) { logFile.flush(); // пакет уехал: окно уязвимости закрыто inBuffer = 0; Serial.print("строк: "); Serial.print(rows); Serial.println(", пакет уехал на карту"); }}В планировщике крутите pot и жмите кнопку: при интервале в секунду неделя наберёт 605 тысяч строк и 24 мегабайта, при минуте — десять тысяч строк и 400 килобайт. Финальная строка после седьмого дня — та самая расплата: карта заполнится через сотни суток, ресурс ячеек кончится через столетия. При интервале в секунду ведите журнал глазами: восемьдесят шесть тысяч строк в сутки — уже не журнал, а сырьё для программы; минута возвращает файлу человеческий размер. Цифры грубые, паспорт карты их не отменяет, но порядок величин держат, а этого достаточно, чтобы назначить интервал спокойно и не возвращаться к вопросу посреди недели.
Граница метода: расчёт предполагает append-only лог и честное износораспределение контроллера. Журнал, который перезаписывается на месте через seek в середину, изнашивает карту заметно быстрее, а у noname-карты с уценённым контроллером заявленный ресурс — лотерея. Брендовая карта нижней ценовой полки дешевле, чем сезон без архива, — на этом моя щедрость к случайным картам заканчивается.
| Интервал | Строк в сутки | Объём за неделю | Записей за год |
|---|---|---|---|
| 10 минут | 144 | 40 КБ | 52 560 |
| 1 минута | 1440 | 400 КБ | 525 600 |
| 10 секунд | 8640 | 2,4 МБ | 3,2 млн |
| 1 секунда | 86 400 | 24 МБ | 31,5 млн |
- 1. Строка лога 40 байт, интервал 60 секунд. Суточный объём?
- 2. Пугающее «сто тысяч циклов записи» из старых статей относилось к...
- 3. Строка в секунду на карте 2 ГБ. Когда кончится ресурс ячеек?
- 4. Пакетная запись с редким flush нужна, чтобы...
- 5. Файл на сутки с именем из даты нужен, чтобы...
- Суточный объём = длина строки умножить на 86400 и разделить на интервал: 40 байт и минута дают 56 КБ в сутки.
- TLC-ячейки потребительских карт переживают порядка тысячи-трёх тысяч циклов; бюджет 2 ГБ карты — миллиарды секторов.
- Append-only лог с износораспределением контроллера — самый щадящий сценарий; ресурс реален только на интервалах в десятки миллисекунд.
- Пакетный flush, файл на сутки, ротация — три приёма недели без человека, таймеры на millis без delay.
- Чек-лист перед отъездом: сутки теста, файл с карты на ПК, запас места, штамп RTC не на 2000-01-01.
Частые вопросы по теме урока
- Строка лога 40 байт, интервал 60 секунд. Суточный объём?
- 86400 / 60 = 1440 строк по 40 байт — это 57 600 байт, то есть около 56 КБ в сутки. Неделя такой записи весит меньше половины мегабайта. Около 56 КБ
- Пугающее «сто тысяч циклов записи» из старых статей относилось к...
- Современные потребительские карты строятся на TLC с ресурсом порядка тысячи-трёх тысяч циклов на ячейку; запас longevity даёт контроллер с износораспределением, а не цифра из паспорта SLC. SLC и ранним спецификациям
- Строка в секунду на карте 2 ГБ. Когда кончится ресурс ячеек?
- 31 миллион записей в год против миллиардов, которые контроллер может распределить по четырём миллионам секторов. Объём карты кончится в сотни раз раньше ресурса ячеек. Через сотню лет и больше
- Пакетная запись с редким flush нужна, чтобы...
- Один flush на дюжину строк закрывает окно обрыва быстрее и реже дёргает карту. Цена честная: сбой между выталкиваниями стоит до дюжины строк. Сузить окно уязвимости и снизить лишние перезаписи хвоста файла
- Файл на сутки с именем из даты нужен, чтобы...
- Суточные файлы локализуют потерю и поиск: четверг — это один файл, а не позиция в общем журнале. Имя из даты даёт RTC, а лимит 8.3 соблюдается форматом LOG0115.CSV. Сбой дня не тянул весь архив, а поиск шёл по одному файлу