Камера дозревания томатов держит два числа сразу: 22-26 градусов и 60-75 процентов влажности. Мой первый журнал был честным — две колонки чисел в мониторе, строка в секунду. Прокручивая вечер, я не видел ничего: корреляция между колонками глазами не читается, а именно она была всей сутью — при провале температуры влажность скакала вверх от конденсата, и по паре чисел это видно, а по столбику — нет.
Плоттер мы уже открывали: в уроке про Serial Plotter были вкладка симулятора, формат «метка=значение» и один датчик с двумя рядами одного происхождения — сырой и масштабированный. Здесь шаг дальше: два разных физических сигнала, у которых разные шкалы, разные размахи и разный характер. Кривая температуры ходит на четыре градуса, влажности — на пятнадцать процентов, и на общей оси младший ряд прижимается к полу и превращается в прямую.
План такой: собрать строку с двумя подписанными рядами t и h, договориться о разделителях — запятая, пробел или таб, — развести масштабы множителем и выбрать частоту выборки, при которой график остаётся читаемым, а канал не захлёбывается. Отдельная ловушка — печать в цикле с delay(1): плоттер заливается тысячей строк в секунду, и кривые превращаются в кашу. В симуляторе — DHT с ползунками температуры и влажности и вкладка «Плоттер» рядом с монитором.
Формат строки: подписи и разделители
Плоттер читает те же строки, что монитор: сколько чисел в строке — столько рядов сдвинутся на шаг. Голые числа через запятую — «24.3,46.2» — дают два ряда с именами 1 и 2; через пробел или таб работает так же. Как только кривых становится две, имена нужны: формат «подпись:значение» даёт рядам человеческие названия — строка «t:24.3,h:46.2» рисует ряды t и h прямо в легенде. Разделителем между парами служит запятая, пробел или таб — плоттеру безразлично, важно, чтобы пары не слипались и каждая несла своё двоеточие. В Arduino IDE 2 пары подпись:значение принято разделять пробелом или табом; в симуляторе на сайте работает и запятая — проверено на вкладке «Плоттер», и она же экономит пару байтов строки.
Закрывает строку один println: печать собирается Serial.print по кускам — подпись, число, разделитель, вторая подпись, число — и println в конце переводит строку. Обе величины обязаны ехать в одной строке: если печатать t отдельным println, а h следующим, ряды поедут с шагами вразнобой, и ось времени соврёт — события, случившиеся одновременно, окажутся в разных тактах. Это то же правило склейки, что у монитора, только цена промаха выше: график притворяется измерением.
Проверка канала стоит пяти секунд: волна из синуса на millis — идеальный тест без железа; волна пришла — порт и скорость в порядке, и если дальше каша, виноват формат строки. Вторая проверка — проза среди чисел: строка с комментариями разбирается плоттером непредсказуемо, буквы он пропустит, а числа по соседству могут разъехаться по чужим рядам. Строка для плоттера — только числа и подписи, без прозы.
| Строка в порт | Ряды плоттера | Когда беру |
|---|---|---|
| 24.3,46.2 | два ряда «1» и «2» | быстрая проверка, имена не нужны |
| t:24.3,h:46.2 | два подписанных ряда t и h | постоянный прибор на столе |
| t=24.3 h=46.2 | то же, разделитель пробел | привычка из урока про метка=значение |
| println(t) и println(h) подряд | два ряда со сдвигом по времени | так не делаю: ось времени врёт |
// Два сигнала в одной строке плоттера: t и h от DHT22 на D2.// Период 500 мс - честно для DHT; множитель KK разводит кривые с разными размахами.#include <DHT.h>DHT dht(2, DHT22); const float KK = 1.0; // поставьте 10, чтобы развести кривые t и h unsigned long tPechat = 0; void setup() { Serial.begin(9600); dht.begin();} void loop() { if (millis() - tPechat < 500) return; // планировщик: печать без delay tPechat += 500; float t = dht.readTemperature(); float h = dht.readHumidity(); Serial.print("t:"); // подпись:значение - ряд получит имя t Serial.print(t * KK, 1); Serial.print(","); // разделитель пар: запятая, пробел или таб Serial.print("h:"); Serial.println(h, 1); // один println закрывает строку}Масштабы — главная причина, почему два сигнала на одном графике выглядят хуже, чем поодиночке. Ось у плоттера общая: при температуре 22-26 и влажности 60-75 оба ряда видны, но у температуры остаётся четверть экрана, и её динамика — плоская линия с дрожью. Множитель решает арифметически: деление размаха старшего ряда на размах младшего даёт стартовую цифру, дальше по глазам. Температуру с размахом в четыре градуса я умножаю на десять: кривая ходит на сорок единиц рядом с влажностью на пятнадцать, и обе читаются.
Плата множителя — подписи на оси: ряд t10 показывает не градусы, а десятки долей градуса, и это надо помнить или подписывать в легенде. Альтернатива — вычитание базы: (t - 21) * 10 переносит кривую в диапазон 0-50 без изменения масштаба делений. Моё мнение: для двух рядов множитель дешевле отдельного окна; когда рядов три и у каждого своя шкала, я разъезжаюсь на два плоттера или увожу данные в Processing — визуализацию на живом порту собирали в уроке про Processing, там у каждого ряда своя ось. Двух окон плоттера на одной Uno не бывает — порт один; второй график значило бы вторую плату или запись в файл с построением отдельно.
DHT вносит поправку в частоту: по даташиту DHT22 отвечает не чаще раза в две секунды, DHT11 — раза в секунду, и опрашивать их быстрее — значит читать одни и те же значения. Для t и h честная частота — 0,5-1 Гц, кривая превращается в медленную волну, и это правильно: температура в камере не дрожит. Датчик подключается как в уроке про DHT11 — сигнальный провод на D2 и подтяжка между сигналом и питанием.
Частота выборки и окно истории
Частота выборки — компромисс между читаемостью и каналом. Для быстрых сигналов — сырой АЦП, фоторезистор, отклик фильтра — берите 20-50 Гц: кривая становится гладкой линией, а не ломаной по трём точкам. Арифметика на 9600 бод: строка в пятнадцать байт занимает 15,6 мс, значит потолок канала — около шестидесяти строк в секунду; двадцать Гц с такой строкой занимают треть канала, пятьдесят Гц — уже три четверти, и на остальное канал не остаётся. При 115200 потолок поднимается до семисот строк, но историю режет не канал — окно графика.
Окно истории у плоттера конечное: около 240 последних точек на ряд. При двадцати Гц это двенадцать секунд — хватает, чтобы разглядеть переходный процесс, не хватает, чтобы увидеть ночной тренд. Для медленных сигналов частоту специально занижают: температура камеры на 0,5-1 Гц даёт окно в несколько минут, и волна читается. Печать держится на планировщике из урока про millis вместо delay — период меняется одной константой, без переделки цикла.
Когда график нужно сохранить, плоттер — не архив: закрыли окно, история ушла. В симуляторе вкладка «Плоттер» выгружает ряды в CSV одной кнопкой — те же данные дальше едут в журнал с кнопкой записи из урока про CSV-лог. Если двух рядов стало мало и нужна своя ось каждому — данные везут в Processing; а при плотной печати помните про буфер приёма на 64 байта, та же цифра, что в уроке про буфер UART: полный буфер — порезанные строки и каша на графике.
Что именно читают в паре кривых — покажу на той же камере. Ночь: температура провалилась с 24 до 21 градуса, и через полминуты влажность рванула с 62 на 70 процентов — холодный воздух держит меньше пара, влага осела на стенках, датчик увидел скачок. По одной кривой это выглядело бы странным поведением каждой величины в отдельности, на паре — диагноз: щель в уплотнителе дверцы, камера тянет уличный холод. График сделал за один взгляд то, чего журнал не делал месяц.
Второе применение — настройка регулятора. Меняю уставку на градус — на графике видно, как кривая идёт к новому значению и насколько перелетает: перелёт в три градуса говорит, что гистерезис мал и реле щёлкает чаще, чем нужно. Вялый подход без перелёта — наоборот, гистерезис великоват, камера дольше выходит на режим. Одна правка — один взгляд: плоттер превращает настройку из гадания в измерение с немедленной обратной связью.
И правило, к которому я пришёл после пары споров с самим собой: лог — для цифр, плоттер — для формы. Числа в журнале отвечают, какой была влажность в 2:04; кривая отвечает, как система к этому значению пришла — плавно или рывком, с перелётом или вялым подходом. Для настройки нужна форма, для протокола испытания — числа, и оба инструмента питаются одним и тем же println.
- 1. Строка «t:24.3,h:46.2» — что нарисует плоттер?
- 2. t ходит в 21-24 градуса, h — в 40-60 процентов. Почему кривая t выглядит плоской?
- 3. Строка в 15 байт на 9600 бод — сколько таких строк вывозит канал за секунду?
- 4. Печать в цикле с delay(1) на 9600 бод — что случится?
- 5. Окно плоттера — 240 точек. Сколько истории при частоте 20 Гц?
- Один цикл — одна строка: t и h едут одновременно, один println закрывает кадр
- Формат подпись:значение (t:24.3,h:46.2) даёт рядам имена; голые числа через запятую — имена 1 и 2
- Разные размахи давят младший ряд в пол: множитель = размах старшего / размах младшего
- Строка в 15 байт на 9600 бод стоит 15,6 мс: потолок ~60 строк/с, зона читаемого графика 20-50 Гц
- Окно плоттера ~240 точек: при 20 Гц это 12 секунд истории; для трендов частоту занижают
Частые вопросы по теме урока
- Строка «t:24.3,h:46.2» — что нарисует плоттер?
- Пары подпись:значение дают именованные ряды — t и h появятся в легенде. Голые числа через запятую дали бы безымянные ряды 1 и 2. Два подписанных ряда: t и h
- t ходит в 21-24 градуса, h — в 40-60 процентов. Почему кривая t выглядит плоской?
- Масштаб у плоттера один на все ряды. Множитель подбирается делением размахов: размах старшего ряда на размах младшего. t печатается реже
- Строка в 15 байт на 9600 бод — сколько таких строк вывозит канал за секунду?
- 9600 бод — около 960 символов в секунду; пятнадцать байт — 15,6 мс. Потолок около шестидесяти строк, и двадцать Гц занимают треть канала. ~60
- Печать в цикле с delay(1) на 9600 бод — что случится?
- Тысяча строк в секунду против шестидесяти возможных: 64-байтный буфер переполняется, печать блокирует loop, строки режутся — график в каше. Буфер передачи переполнится, print начнёт ждать, loop растянется
- Окно плоттера — 240 точек. Сколько истории при частоте 20 Гц?
- 240 точек на двадцати герцах — двенадцать секунд: хватает для переходного процесса, для ночного тренда частоту занижают. 12 секунд