Монитор порта Serial

Светодиод показывает только одно состояние, а робот одновременно хранит показания датчиков, команды и результаты вычислений. Serial Monitor превращает эти невидимые данные в строки на компьютере. По ним можно выяснить, запускается ли программа, какие значения она читает и почему выбирает конкретное действие.
Serial.begin(скорость) запускает последовательный обмен, Serial.print() выводит данные без обязательного переноса строки, а Serial.println() завершает строку. Скорость в Serial Monitor должна совпадать со скоростью скетча. Для приёма сначала проверяют Serial.available(), затем читают пришедшие байты.
Зачем это нужно
Представьте робота, который неожиданно останавливается. Причина может быть разной: датчик действительно увидел препятствие, провод отошёл, измерение оказалось вне диапазона или программа сравнила не то значение. По неподвижному роботу этого не определить.
Диагностическая строка связывает внутреннее состояние с наблюдением:
distance_cm=18 sensor_ok=1 action=stop
Такой вывод позволяет проверить три этапа по отдельности: измерение, оценку достоверности и выбранную команду. Позже эти данные можно записывать в журнал или строить график, но сначала важно научиться получать короткий стабильный текст.
Serial также работает в обратную сторону. Компьютер может передать символ или строку, а скетч — изменить режим испытания. Это удобно для стенда: одна прошивка принимает команды «начать тест», «остановиться» и «показать состояние» без повторной компиляции.
Главная идея
Последовательный интерфейс передаёт данные как поток байтов. Отдельный байт не хранит готового понятия «расстояние» или «команда»: смысл создаёт программа. Она решает, какие символы отправить, где поставить разделитель и чем отметить конец сообщения.
данные программы -> преобразование в байты -> линия связи -> Serial Monitor
На hero синяя и мятная линии показывают противоположные направления последовательных данных, а графитовая — общую опорную связь в показанном не изолированном примере. Трёхконтактный датчик слева — условный источник данных, а не универсальная распиновка ультразвукового модуля.
| Направление | Действия скетча | Что видит пользователь |
|---|---|---|
| плата → компьютер | Serial.print() и Serial.println() | строки и значения в окне мониторинга |
| компьютер → плата | Serial.available() и Serial.read() | программа получает байты команды |
Запуск порта
Обычно Serial запускают один раз в setup():
void setup() {
Serial.begin(9600);
Serial.println("controller_ready");
}
void loop() {
}
Число 9600 задаёт скорость обмена в битах в секунду для этого последовательного интерфейса. В Serial Monitor выберите такое же значение. Если настройки не совпадают, на некоторых соединениях появляются нечитаемые символы или сообщения не декодируются корректно.
На платах с нативным USB объект Serial может представлять USB-подключение, а не отдельные физические UART-контакты. На других платах USB–serial преобразователь связан с аппаратным UART. Поэтому конкретные контакты, необходимость ожидания подключения и поведение при открытии монитора проверяют по документации платы.
На многих классических платах управляющие сигналы USB–serial соединения используются для автоматического сброса. Поэтому после открытия Serial Monitor стартовая строка может появиться снова. Это зависит от платы и подключения, а не является правилом языка C++.
print и println
Функции отличаются положением следующего вывода:
void setup() {
Serial.begin(9600);
Serial.print("distance_cm=");
Serial.print(27);
Serial.print(" state=");
Serial.println("clear");
}
void loop() {
}
Первые три вызова продолжают одну строку. println() дописывает значение и перевод строки. Следующее сообщение начнётся с новой строки.
| Вызов | Пример результата | Подходящее применение |
|---|---|---|
Serial.print(value) | значение без обязательного конца строки | собрать запись из нескольких полей |
Serial.println(value) | значение и перевод строки | закончить запись |
Serial.println() | только перевод строки | отделить группы вывода |
Serial.write(byteValue) | отправляется байт как двоичное значение | протокол, где важны байты, а не их печатная запись |
Serial.print(65) обычно печатает символы 6 и 5, а Serial.write(65) отправляет один байт со значением 65. Это разные задачи. Для понятной начинающему диагностики чаще подходят print и println.
Понятный формат сообщения
Поток чисел 417 419 103 0 трудно интерпретировать через день. Подписывайте поля, указывайте единицы и сохраняйте одинаковый порядок:
const int sensorValue = 417;
const int distanceCm = 23;
const bool obstacleDetected = true;
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.print("sensor_raw=");
Serial.print(sensorValue);
Serial.print(" distance_cm=");
Serial.print(distanceCm);
Serial.print(" obstacle=");
Serial.println(obstacleDetected);
delay(500);
}
Этот пример использует постоянные учебные значения. В реальном скетче они будут обновляться чтением датчика. Задержка ограничивает частоту сообщений, чтобы монитор оставался читаемым.
Разные виды сообщений удобно различать короткими устойчивыми полями:
| Вид записи | Что включить | Зачем |
|---|---|---|
| запуск | версию скетча, режим, факт завершения настройки | увидеть перезапуск и конфигурацию |
| измерение | имя величины, значение, единицу | проверить датчик и преобразование |
| решение | входные признаки и выбранное действие | проверить ветвление |
| ошибка | источник, код или состояние, безопасную реакцию | не спутать отказ с обычным режимом |
Не печатайте быстрее, чем нужно
Каждая передача занимает время и место в буфере. Если loop() генерирует данные быстрее, чем интерфейс успевает отправлять, вывод способен влиять на длительность цикла. Особенно это заметно на низкой скорости и при длинных сообщениях.
Для периодической диагностики удобно сравнивать millis() с моментом предыдущего сообщения:
unsigned long previousPrintMs = 0;
const unsigned long printIntervalMs = 500;
void setup() {
Serial.begin(9600);
}
void loop() {
const unsigned long nowMs = millis();
if (nowMs - previousPrintMs >= printIntervalMs) {
previousPrintMs = nowMs;
Serial.println("heartbeat");
}
}
В отличие от долгого delay(), такая проверка не требует останавливать весь цикл между сообщениями. Подробнее этот приём разбирается в уроке о циклах и времени.
Отладочный вывод стоит планировать как часть испытания. Перед запуском определите, какой вопрос должна прояснить каждая запись: «поступает ли команда», «валидно ли измерение», «какая ветвь выбрана». После ответа лишние строки убирают или отключают одним флагом. Иначе поток сообщений затрудняет поиск причины и способен незаметно изменить временные характеристики скетча. Для критичных по времени измерений сравнивайте поведение с диагностикой и без неё.
Не отправляйте в каждом проходе большие повторяющиеся описания. Постоянные подписи можно сделать короткими, а состояние печатать только при изменении или по редкому таймеру. Такой журнал легче сопоставить с движением робота и событиями на стенде.
Приём одной команды
Проверяйте наличие данных перед чтением:
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
const int receivedByte = Serial.read();
if (receivedByte == 's') {
Serial.println("stop_requested");
}
}
}
Serial.available() сообщает число байтов, доступных для чтения. Serial.read() возвращает первый байт входного буфера либо -1, если данных нет, поэтому результат хранится в int. Символьный литерал 's' — один символ в одинарных кавычках; строка "s" имеет другой тип.
Serial Monitor может добавлять к отправленной строке символы конца строки. Настройка «нет окончания строки», LF или CRLF влияет на следующие байты в буфере. Для однобайтовой команды выберите режим осознанно либо напишите обработчик, который пропускает \r и \n.
Пример: кнопка и журнал состояний

На схеме оранжевая верхняя линия — условное логическое питание подтягивающего резистора. Кнопка меняет вход контроллера, синий путь несёт данные к компьютеру, а цветные полосы обозначают записи, не реальный интерфейс программы.
Для платы, поддерживающей внутреннюю подтяжку, внешнюю схему можно упростить и настроить INPUT_PULLUP:
const int buttonPin = 2;
bool previousPressed = false;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
const bool pressed = digitalRead(buttonPin) == LOW;
if (pressed != previousPressed) {
Serial.print("button_pressed=");
Serial.println(pressed);
previousPressed = pressed;
}
}
При INPUT_PULLUP отпущенная кнопка обычно читается как HIGH, а замкнутая на GND — как LOW, поэтому сравнение инвертировано. Пример сообщает только об изменении, а не печатает одно состояние тысячи раз. Электрическое подключение и допустимость внутренней подтяжки проверяют для конкретной платы.
Что запомнить о Serial Monitor
Serial.begin()и настройка монитора должны описывать совместимый режим обмена.print()продолжает запись,println()завершает строку.- Поля, единицы и стабильный порядок делают журнал проверяемым.
- Ограничение частоты защищает основной цикл от чрезмерного вывода.
- Перед
Serial.read()проверяютSerial.available(). - Окно Serial — средство наблюдения, а не доказательство исправности датчика.
Практика
- Выведите стартовую запись из
setup()и счётчик проходов изloop(). Подберите период, при котором строки удобно читать. - Сформируйте одну строку из трёх полей: сырое показание, значение с единицей и выбранное состояние. Проверьте стабильность порядка.
- Подключите кнопку по схеме, допустимой для вашей платы. Печатайте только момент смены состояния и исследуйте влияние дребезга.
- Реализуйте две однобайтовые команды для учебного режима. Проверьте варианты окончания строки в Serial Monitor и запишите, какие дополнительные байты приходят.
Проверьте себя
- Для чего вызывают
Serial.begin()? - Что произойдёт при несовпадающих скоростях скетча и монитора?
- Чем
print()отличается отprintln()? - Почему подписи и единицы полезнее потока голых чисел?
- Как слишком частая печать может влиять на цикл?
- Зачем вызывать
Serial.available()перед чтением? - Почему результат
Serial.read()удобно хранить вint? - Какие дополнительные символы может отправить Serial Monitor после команды?
Сначала ответьте без подсказки. Ответ можно считать полным, если вы:
- формулируете основную мысль своими словами;
- называете важные условия, ограничения или меры безопасности;
- для схемы, кода или расчёта показываете ход решения и ожидаемый результат.
Если один из пунктов объяснить не получается, найдите соответствующую главу статьи, перечитайте её и повторите ответ.
Словарь статьи
| Термин | Значение |
|---|---|
| последовательный обмен | передача данных по очереди как потока битов и байтов |
| Serial Monitor | окно для отправки и просмотра последовательных данных |
| скорость обмена | число передаваемых битов в секунду для настроенного интерфейса |
| байт | единица данных, которую можно передать и прочитать из буфера |
| буфер | область временного хранения ещё не обработанных данных |
Serial.print() | печатный вывод без обязательного перевода строки |
Serial.println() | печатный вывод с завершением строки |
Serial.available() | число доступных для чтения входных байтов |
Serial.read() | чтение следующего байта из входного буфера |
| CR/LF | символы возврата каретки и перевода строки |
Связанные темы
- Первая программа — запуск и загрузка скетча.
- Переменные — хранение выводимых измерений и состояний.
- Конструкция ветвления — реакция на принятую команду.
- Циклы — периодический вывод без потери основного алгоритма.
- digitalRead: обработка кнопки — чтение входа и дребезг.
- UART Bluetooth-модуль — обмен с внешним последовательным модулем.
Источники
- Arduino Language Reference:
Serial - Arduino Language Reference:
Serial.begin() - Arduino Language Reference:
Serial.print() - Arduino Language Reference:
Serial.println() - Arduino Language Reference:
Serial.available() - Arduino Language Reference:
Serial.read() - Arduino Built-in Example: Digital Read Serial