У теплицы на чердаке одно настраиваемое число — порог проветривания. Полгода я менял его перешивкой скетча, пока не вынес в JSON-конфиг: плата читает настройки, а при старте печатает их в монитор. Первую печать я сделал serializeJson — получил строку {"name":"terrasa","set":26,"mode":"avto"} и замучился её читать: где здесь порог, где режим, глаз цепляется за запятые. Второй вызов — serializeJsonPretty — развернул тот же документ лесенкой, и читать стало легко. Тогда же монитор показал и первую цену: 41 байт компактной печати против 54 у красивой.
Тринадцать байт кажутся мелочью, пока вы не шлёте пакет по каналу в 9600 бод и не вспомните, что EEPROM на плате — один килобайт. В этом уроке оба формата печати одного и того же документа, точный расклад по байтам и времени, и правило, по которому я выбираю формат не ощущениями, а адресатом: пакет нужен человеку в мониторе или программе на другом конце провода. От ответа зависит всё — от размера буфера до счёта за трафик сим-карты.
База уже была: как собрать свой документ присваиваниями — в уроке про сборку своего JSON, чем длина печати отличается от ёмкости документа — в уроке про measureJson. Здесь остаётся второй вызов библиотеки и его честная цена.
Как устроен pretty-вывод
serializeJsonPretty печатает тот же документ, что и serializeJson, но вставляет перевод строки после каждого члена и отступ — два пробела на уровень вложенности. У плоского конфига каждый член уезжает на собственную строку, скобки — тоже. Возьмите пакет с массивом {"readings":[20,21,22]}: компактная печать в 25 байт разворачивается в 58, потому что каждый элемент массива получает свою строку и четыре пробела — он на уровень глубже. Надбавка не константа, а функция вложенности: чем глубже структура, тем быстрее она растёт.
Возврат функции — число записанных байт: int n = serializeJsonPretty(doc, Serial); даст ровно длину pretty-версии. Это удобная привычка на тех местах, где меришь печать постоянно; в уроке про measureJson мы разбирали, что тот считает только компактный документ — про отступы и переносы он не знает вовсе. На плате я печатаю pretty прямо в Serial: печать идёт потоком, промежуточный буфер не нужен, SRAM платит только за сам документ.
Вторая сторона счёта — время. 9600 бод — это примерно 960 байт в секунду, около миллисекунды на байт с учётом стартового и стопового бита. Наш конфиг едет 43 мс компактно и 56 мс красиво. Для монитора разница ничтожна; для лупа, печатающего конфиг в каждом проходе без паузы, — уже способ занять канал всерьёз и надолго.
Скетч ниже собирает конфиг присваиваниями: имя площадки, порог с потенциометра на A0, режим. Дальше вилка по флагу: DEBUG_PRETTY включён — документ печатается лесенкой, выключен — компактно. Под печатью measureJson, чтобы счётчик байт был перед глазами в каждом цикле. Свет на A1 против порога зажигает светодиод на D12 — минимальная логика теплицы, чтобы конфиг не висел в воздухе.
Порог намеренно берётся с ручки, а не из константы: крутишь потенциометр — обе печати обновляются, а счётчик показывает, как длина колеблется вместе со значением. На плате это реальная ручка, в симуляторе — ползунок; постоянное хранение настроек между выключениями — тема урока про конфиг в EEPROM, а приём из этого урока печатает их человеком.
#include <ArduinoJson.h> const bool DEBUG_PRETTY = true; // выключите — печать станет компактной DynamicJsonDocument doc(768); // v7: JsonDocument doc; void setup() { Serial.begin(9600); pinMode(12, OUTPUT);} void loop() { int svet = analogRead(A1); int porog = map(analogRead(A0), 0, 1023, 5, 35); doc.clear(); doc["name"] = "terrasa"; doc["set"] = porog; doc["mode"] = "avto"; if (DEBUG_PRETTY) { int n = serializeJsonPretty(doc, Serial); Serial.println(); Serial.print("pretty bayt="); Serial.println(n); } else { serializeJson(doc, Serial); Serial.println(); Serial.print("kompakt bayt="); Serial.println(measureJson(doc)); } if (svet > porog * 20) digitalWrite(12, HIGH); else digitalWrite(12, LOW); delay(2000);}Где красота уместна
| Сценарий | Формат | Почему |
|---|---|---|
| Ответ HTTP серверу | компакт | Каждый байт — трафик; парсеру отступы не дают ничего |
| Конфиг в EEPROM или на SD | компакт | Место ограничено: в килобайте EEPROM лишние 13 байт — уже роскошь |
| Настройки с ноутбука, монитор | pretty | Читается глазами; байты и скорость здесь не критичны |
| Первый прогон нового протокола | pretty | Структура видна сразу; потом флаг выключается одной строкой |
Моё правило выглядит так: pretty живёт за флагом отладки и в человеческих конфигах, компакт — везде, где пакет пересекает границу платы. На этапе подбора порогов pretty окупается за один вечер — видно, что именно плата считает текущими настройками. На проде его нет: серверу отступы не нужны, а сим-карта в полевом узле выставляет счёт честно по байтам. Это не догма: если пакет уходит раз в час и весит сотню байт, споры о тринадцати лишних — трата времени; если раз в секунду с узла на батарейке — каждый байт в бюджете энергии на связи.
Граница применимости у pretty — человек. Как только читателя нет, формат теряет смысл: конфиг на SD-карте пишет компактно и читает компактно, файл с настройками не меняется человеком на лету. А конфиг, который вы правите в редакторе раз в месяц, наоборот, заслуживает лесенки: тринадцать байт в килобайте EEPROM против двадцати минут вглядывания в строку без отступов — не та экономия, ради которой стоит мучиться.
Ползунок против счётчика
В симуляторе тот же конфиг и та же вилка печати. Ползунок A0 — порог: от пяти до тридцати пяти, LED на D12 загорается выше двадцати четырёх. Двигайте ручку и смотрите на счётчик: bayt= показывает компактную длину и меняется, когда значение теряет или набирает разряд, а pretty-версия ниже всегда на десяток с лишним байт длиннее — перенос и отступ у каждого из трёх членов плюс свои строки у скобок.
Честная оговорка: на настоящей плате возврат serializeJsonPretty и measureJson дают вам обе цифры бесплатно, в симуляторе счётчик один — компактный, длину лесенки видно глазами. Обе печати идут в один и тот же Serial: строки перемежаются маркерами, чтобы в мониторе было видно, где чей формат. Именно так я и налаживаю конфиги на плате: сначала pretty для себя, потом выключаю флаг и оставляю плате компакт.
- 1. Компактный конфиг — 41 байт, три члена без вложенности. Сколько примерно отдаст serializeJsonPretty для того же документа?
- 2. Куда pretty-печать лучше не допускать?
- 3. Что вернёт measureJson для документа, который вы печатаете через serializeJsonPretty?
- 4. Pretty-пакет ушёл на ESP32, сервер разобрал без вопросов. Что изменилось для него?
- 5. Где на Uno pretty выходит дороже всего?
- serializeJsonPretty печатает тот же документ с переносами и отступом в два пробела на уровень: конфиг 41 байт превращается в 54
- Возврат функции — число записанных байт: им и меряйте pretty, measureJson знает только компакт
- Канал платит за красоту: 9600 бод — около 960 байт в секунду, лишний байт едет примерно миллисекунду
- Законные места pretty: монитор при наладке, человеческий конфиг, первый прогон протокола
- В проде pretty держат за флагом отладки: const bool решает формат печати одной строкой
Частые вопросы по теме урока
- Компактный конфиг — 41 байт, три члена без вложенности. Сколько примерно отдаст serializeJsonPretty для того же документа?
- Каждый член получает перенос строки и два пробела отступа, скобки уезжают на отдельные строки. Для трёх членов надбавка — десяток с небольшим байт; чем глубже вложенность, тем она заметнее. Около 54: перенос и два пробела у каждого члена, скобки на своих строках
- Куда pretty-печать лучше не допускать?
- Серверу красота ничего не даёт: канал и место в логах платят за переносы. Монитор, лог и человеческий конфиг — законные места pretty, там цена копеечная. В HTTP-пакет серверу раз в минуту
- Что вернёт measureJson для документа, который вы печатаете через serializeJsonPretty?
- measureJson всегда меряет только компактную печать. Отступы и переносы в цифру не входят — буфер под pretty берите с запасом или меряйте возвратом самой serializeJsonPretty. Длину компактной печати
- Pretty-пакет ушёл на ESP32, сервер разобрал без вопросов. Что изменилось для него?
- Любой JSON-парсер ест оба формата, слома нет. Но байты отступов — это трафик и место в логах без пользы: сервер не жалуется, просто платит за красоту, которую не просил. Тело запроса распухло: парсер съел байты отступов без пользы
- Где на Uno pretty выходит дороже всего?
- Печать идёт потоком и вторую копию не строит, флеш тоже не при чём. Канал честный: 9600 бод — примерно 960 байт в секунду, и надбавка в байтах превращается в миллисекунды на связи. В канале 9600 бод: каждый лишний байт едет около миллисекунды