Перейти к основному содержимому

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

Датчик, контроллер и двунаправленный обмен с окном Serial Monitor

Светодиод показывает только одно состояние, а робот одновременно хранит показания датчиков, команды и результаты вычислений. 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++.

Функции отличаются положением следующего вывода:

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.

Пример: кнопка и журнал состояний

Кнопка с подтяжкой, контроллер и абстрактные строки Serial

На схеме оранжевая верхняя линия — условное логическое питание подтягивающего резистора. Кнопка меняет вход контроллера, синий путь несёт данные к компьютеру, а цветные полосы обозначают записи, не реальный интерфейс программы.

Для платы, поддерживающей внутреннюю подтяжку, внешнюю схему можно упростить и настроить 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 — средство наблюдения, а не доказательство исправности датчика.

Практика

  1. Выведите стартовую запись из setup() и счётчик проходов из loop(). Подберите период, при котором строки удобно читать.
  2. Сформируйте одну строку из трёх полей: сырое показание, значение с единицей и выбранное состояние. Проверьте стабильность порядка.
  3. Подключите кнопку по схеме, допустимой для вашей платы. Печатайте только момент смены состояния и исследуйте влияние дребезга.
  4. Реализуйте две однобайтовые команды для учебного режима. Проверьте варианты окончания строки в Serial Monitor и запишите, какие дополнительные байты приходят.

Проверьте себя

  1. Для чего вызывают Serial.begin()?
  2. Что произойдёт при несовпадающих скоростях скетча и монитора?
  3. Чем print() отличается от println()?
  4. Почему подписи и единицы полезнее потока голых чисел?
  5. Как слишком частая печать может влиять на цикл?
  6. Зачем вызывать Serial.available() перед чтением?
  7. Почему результат Serial.read() удобно хранить в int?
  8. Какие дополнительные символы может отправить Serial Monitor после команды?
Ориентиры для самопроверки

Сначала ответьте без подсказки. Ответ можно считать полным, если вы:

  • формулируете основную мысль своими словами;
  • называете важные условия, ограничения или меры безопасности;
  • для схемы, кода или расчёта показываете ход решения и ожидаемый результат.

Если один из пунктов объяснить не получается, найдите соответствующую главу статьи, перечитайте её и повторите ответ.

Словарь статьи

ТерминЗначение
последовательный обменпередача данных по очереди как потока битов и байтов
Serial Monitorокно для отправки и просмотра последовательных данных
скорость обменачисло передаваемых битов в секунду для настроенного интерфейса
байтединица данных, которую можно передать и прочитать из буфера
буферобласть временного хранения ещё не обработанных данных
Serial.print()печатный вывод без обязательного перевода строки
Serial.println()печатный вывод с завершением строки
Serial.available()число доступных для чтения входных байтов
Serial.read()чтение следующего байта из входного буфера
CR/LFсимволы возврата каретки и перевода строки

Связанные темы

Источники