Скетч с Serial.parseInt() вы вчерашним вечером проверяли сорок минут: в мониторе печатаете 42, сервопривод должен качнуться на 42 градуса, а плата отмалчивается или отвечает нулём. Код правильный — я его тоже сначала переписал дважды. Виноват дропдаун у правого края монитора порта: настройка «Нет конца строки» после вашего числа не досылает ни одного байта, а parseInt останавливается только на нецифровом символе. Без него разбор тянется до таймаута — по умолчанию до тысячи миллисекунд — и возвращает ноль. Молча, без единого намёка в мониторе. Флешка-приём «переписать код» я тогда перепробовал весь, а поиски вёл не в том месте: в меню, а не в дропдауне на два сантиметра ниже.
Разбираю здесь три вещи, которые этот дропдаун трогает: какие байты монитор досылает за вас в четырёх режимах line ending, как устроен таймаут разбора и почему несовпадение скоростей вываливает кракозябры. Базовые кнопки монитора я показывал в уроке про первый выход на связь, а панель настроек IDE 2 и её отличия от старой версии — в уроке про монитор порта в IDE 2; здесь же — уровень байтов, на котором видно, почему команды молчат.
Дропдаун из четырёх строк и два байта разницы
Вариантов в дропдауне четыре, и разница между ними — два байта максимум. «Нет конца строки» шлёт ровно то, что вы набрали: ни байта после. «Перевод строки» (NL, байт 10) добавляет десятый байт. «Возврат каретки» (CR, байт 13) — тринадцатый. «Оба NL и CR» — сначала 13, потом 10, два байта на каждую отправку. Плата получает эти байты честно, в приёмный буфер на 64 байта, а дальше всё зависит от скетча: собирать строку до перевода строки, вылавливать байт 13 или считать всё нецифровое признаком конца числа. В старых версиях IDE дефолтом стояло «Нет конца строки», сейчас свежие сборки обычно ставят NL — но проверьте свой дропдаун глазами, он помнит последний выбор.
Механику parseInt стоит проговорить вслух один раз, и все молчания перестанут быть мистикой. Функция пропускает нецифровые байты, собирает цифры в число и останавливается на первом нецифровом. Если буфер кончился, а конца числа всё нет, она ждёт: потолок ожидания в ядре Arduino — тысяча миллисекунд, меняется через Serial.setTimeout(). Отсюда два характерных симптома. Первый: число без хвоста разбирается с секундной паузой и нулём на выходе — таймаут выработался. Второй: между отправками проскакивает ровно секунда тишины, потому что каждая посылка доживает до потолка ожидания.
Тот же таймаут стоит за readStringUntil(10): не дождались байта 10 — через секунду функция отдаёт накопленное. Из-за этого команда on без конца строки срабатывает не в момент отправки, а когда вы пришлёте следующую строку: хвост догоняет голову, и «мёртвая» кнопка оживала сама. У меня она оживала ровно на второй команде, до того как я открыл дропдаун. Отдельная история — переполнение приёмного буфера: на Uno под приём стоит 64 байта, и посылки быстрее, чем скетч читает, обрезаются бесшумно. При 9600 бод канал наливает примерно 960 байт в секунду, значит скетч, который наведывается в приём раз в триста миллисекунд, успевает всё; плотный цикл без чтения переполняет буфер за сотую долю секунды. Диагностика простая: начало команды доходит, а конец нет — считайте байты между чтениями. Если шлёте числа пачкой, подряд и без разделителей, parseInt склеит их в одно — вставляйте между ними запятую или пробел: нецифровой байт закроет число раньше потолка ожидания. Глубже в приём по байтам копаю в уроке про available и read.
Вторая половина странных молчаний — кракозябры: вместо букв высыпает пепел из символов. UART не угадывает скорость: скетч говорит Serial.begin(9600), монитор крутит 115200 — кадры рассыпаются, потому что каждый байт едет кадром из десяти бит со старта и стопом, и слушатель отмеряет биты не там. Лечится сверкой двух цифр: что в begin, то и в дропдауне скорости. Классика жанра — скетч с чужого блога на 115200 при вашем мониторе на 9600: плата мигает, монитор печатает мусор, а вы греете подозрениями чип USB-UART, который ни при чём. Тот же мусор получается и на полуотжившем мосте USB-UART, но по полевой статистике девять случаев из десяти — скорость; начинайте с неё.
Поправка с границей применимости: платы с нативным USB — Leonardo, Micro, ESP32-S3 — скорость в мониторе игнорируют, там Serial едет по USB-кабелю как по шине, и 9600 против 115200 ничего не ломает. Для Uno, Nano и Mega с мостом USB-UART на борту скорость — договор двух сторон, и gibberish почти всегда её рассинхрон, а не сгоревшая микросхема. Какую плату и порт выбрал IDE, я вспоминал в уроке про выбор платы и порта.
// Управление ШИМ-строками: on, off, set <0-255>.// Терминатор - байт 10 (NL): readStringUntil берёт строку до перевода строки.// Присвоение после trim() срезает байт 13 (CR), если пришёл хвост от режима «Оба». String line = ""; void setup() { Serial.begin(9600); pinMode(12, OUTPUT); Serial.println("PWM-console: on, off, set <0-255>");} void loop() { if (Serial.available() > 0) { line = Serial.readStringUntil(10); // ждёт байт 10, без него - таймаут 1000 мс line = line.trim(); if (line == "on") { digitalWrite(12, HIGH); Serial.println("echo: on, LED=HIGH"); } else if (line == "off") { digitalWrite(12, LOW); Serial.println("echo: off, LED=LOW"); } else if (line.startsWith("set ")) { int v = line.substring(4).toInt(); if (v >= 0 && v <= 255) { Serial.print("echo: set "); Serial.println(v); } else { Serial.println("echo: diapazon 0-255"); } } else if (line.length() > 0) { Serial.print("echo: ne ponyal - "); Serial.println(line); } }}Сводка: что сломалось и какая настройка лечит
Сводка, к которой возвращаются через неделю, когда симптомы уже смешались в голове: строка не разобралась, буквы рассыпались, кнопка ожила сама. Каждая строка — пара «симптом и виновник», и лечится одной настройкой или одним фильтром в скетче. Подсказка по чтению: левый столбец — то, что видно глазами, правый — то, что крутится без перепрошивки, средний объясняет механику. Таблицу удобно держать открытой рядом с монитором в первый месяц, пока работа с дропдауном не стала рефлексом.
Как я теперь держу приём. Дропдаун — NL всегда, независимо от проекта: единый выбор значит единый набор граблей. Строку беру readStringUntil(10) и сразу переприсваиваю trim: команда целиком, хвосты срезаны. Числа достаю toInt() из готовой строки — у него нет таймаутных сюрпризов, и мусор честно превращается в ноль на глазах. parseInt оставляю для потоков, где числа идут одно за другим от машины, а не от человека: там он быстрее ручной сборки, а его молчаливый ноль предсказуем, если разделитель гарантирован.
И второй пункт: лог-формат, машинно-читаемый. Пары имя=значение через пробел потом распарсятся в блокноте или скетче-логгере, а фраза «сейчас жду датчик» парсится только глазами. Сырую длину посылки печатаю рядом со счётчиком строк: прилипший CR виден как лишний байт, и спор «кто приклеил хвост» решается одним взглядом на stat. Настроенный один раз приём перестаёт быть источником вечеров — остаётся следить, чтобы обе стороны кабеля договаривались об одних и тех же байтах.
| Симптом в мониторе | Кто виноват | Что крутить |
|---|---|---|
| parseInt вернул 0, между отправками секунда тишины | дропдаун «Нет конца строки»: конца числа нет, таймаут выработался | выбрать NL — или Serial.setTimeout(200) под свой протокол |
| Команда оживает только со второй посылкой | первая ушла без конца строки и ждала хвоста | NL в дропдауне или сборка строки до байта 10 |
| Пепел из символов вместо букв | скорости разошлись: кадры рубятся по чужим границам | одна цифра в begin и дропдауне: 9600 = 9600 |
| Сравнение строк молча проваливается | приклеился CR от режима «Оба NL и CR» | фильтр байта 13 при сборе строки, как в скетче выше |
- 1. В мониторе выбрано «Нет конца строки», вы отправляете число 42. Что вернёт Serial.parseInt()?
- 2. Сколько байтов досылает к каждой посылке режим «Оба NL и CR»?
- 3. Скетч стоит на 9600 бод, монитор крутит 115200. Что вы увидите?
- 4. Что меняет Serial.setTimeout(200)?
- 5. На какой плате скорость в мониторе можно не сверять со скетчем?
- Дропдаун line ending решает, какие байты монитор досылает: ничего, 10 (NL), 13 (CR) или оба посылкой
- parseInt останавливается на нецифровом байте или по таймауту: по умолчанию 1000 мс, дальше — ноль молча
- Команда без конца строки оживает со следующей посылкой: хвост догоняет голову
- Кракозябры — рассинхрон скоростей: кадр байта 10 бит, обе стороны обязаны совпасть
- Нативный USB (Leonardo, ESP32-S3) скорость игнорирует; для Uno и Nano цифры сверяйте
Частые вопросы по теме урока
- В мониторе выбрано «Нет конца строки», вы отправляете число 42. Что вернёт Serial.parseInt()?
- Без нецифрового байта на конце разбор тянется до таймаута потока — по умолчанию 1000 мс — и молча возвращает ноль. Меняйте дропдаун на NL или ужмите Serial.setTimeout(). 0 — после секундного ожидания выработался таймаут
- Сколько байтов досылает к каждой посылке режим «Оба NL и CR»?
- Режим «Оба» приклеивает байты 13 и 10: два на каждую отправку. Для parseInt это невидимо, а для сравнения строк — хвост, который фильтруют при сборе. два — сначала возврат каретки, потом перевод строки
- Скетч стоит на 9600 бод, монитор крутит 115200. Что вы увидите?
- Байт едет кадром из 10 бит со старта и стопа; при рассинхроне слушатель режет поток по чужим границам. Лечение — одинаковые цифры по обе стороны кабеля. кракозябры: кадры рассыпались, биты отмеряются не там
- Что меняет Serial.setTimeout(200)?
- Таймаут — свойство Serial-потока: сколько функции разбора ждут конца числа или строки. На скорость бодов он не влияет никак. потолок ожидания parseInt и readStringUntil — 200 мс вместо 1000
- На какой плате скорость в мониторе можно не сверять со скетчем?
- У плат с нативным USB (Leonardo, Micro, ESP32-S3) скорость декоративна. Для плат с мостом USB-UART цифры обязаны совпасть, иначе — мусор в мониторе. Leonardo — Serial едет по нативному USB