Монитор порта Arduino: скорость в бодах, отправка данных, плоттер
Как пользоваться монитором порта Arduino: Serial.begin и скорость в бодах, борьба с мусорными символами, конец строки, parseInt, плоттер по последовательному соединению, примеры для ESP32 и Pico.
Монитор порта Arduino (Serial Monitor) — это окно в Arduino IDE, через которое плата и компьютер обмениваются текстом по USB-кабелю. Здесь видно, что выводит каждая строка Serial.println() в скетче, и здесь же набираются команды, которые нужно отправить обратно в плату. Это самый быстрый способ узнать, что прямо сейчас измеряет датчик, до какого места дошла программа и действительно ли нажимается кнопка, поэтому монитор порта — инструмент отладки номер один в мире Arduino. Чтобы им пользоваться, достаточно строки Serial.begin(9600) в коде, правильного порта в списке выбора платы и той же скорости в мониторе, что и в коде.
В этом руководстве сначала откроем монитор порта, а затем разберём, что на самом деле означает скорость в бодах, как выводить числа в десятичном, шестнадцатеричном и двоичном виде с помощью print и println, как управлять количеством знаков после запятой и как собирать аккуратные строки через snprintf. На примерах скетчей, которые действительно компилируются, разберём все причины мусорных символов, влияние настройки конца строки на код, отправку чисел и текстовых команд с компьютера в плату (Serial.read, parseInt, readStringUntil), построение живых графиков в плоттере по последовательному соединению (Serial Plotter) и приёмы отладки, которые сильно упростят жизнь. В последних разделах показано, чем отличается вывод в порт на ESP32 и Raspberry Pi Pico (и в Arduino, и в MicroPython) и как решить частые проблемы вроде пустого монитора.

Что такое монитор порта Arduino и как он работает?
ATmega328P, «мозг» Arduino Uno, общается с внешним миром через блок последовательной связи под названием UART: он передаёт данные бит за битом по одной линии (TX) и принимает по другой (RX). В современных компьютерах такого последовательного порта уже нет, поэтому вторая микросхема на плате (ATmega16U2 на оригинальной Uno, на клонах обычно CH340) преобразует сигнал в USB. Компьютер видит плату как виртуальный последовательный порт: в Windows это что-то вроде COM3, в Linux — /dev/ttyACM0 или /dev/ttyUSB0, а в macOS — имя, начинающееся с /dev/cu.usbmodem или /dev/cu.usbserial. Монитор порта открывает этот порт, показывает входящие байты как текст и так же отправляет в плату то, что ты набираешь. По умолчанию Arduino использует формат 8N1: на каждый байт один стартовый бит, 8 бит данных, без бита чётности и один стоповый бит. Значит, один символ занимает на линии 10 бит; это число пригодится в расчётах скорости.
| Характеристика | Arduino Uno | ESP32 DevKit | Raspberry Pi Pico |
|---|---|---|---|
| Подключение по USB | Микросхема-мост ATmega16U2 или CH340 | Микросхема-мост CP2102 или CH340 | Собственный USB у RP2040 (USB CDC) |
| Пины последовательного порта | D0 (RX), D1 (TX) | GPIO 3 (RX), GPIO 1 (TX) | Пинов нет, напрямую по USB |
| Дополнительные аппаратные порты | Нет (программный порт через SoftwareSerial) | Serial1 и Serial2 (Serial2 часто используют на GPIO 16 RX, GPIO 17 TX) | Serial1 (GP0 TX, GP1 RX), Serial2 (GP8 TX, GP9 RX) |
| Типичная скорость | 9600 бод | 115200 бод | Не важна, работает на скорости USB |
| Сброс при открытии монитора | Да (линия DTR) | Зависит от платы и программы | Нет |
| Serial.printf() | Нет, используй snprintf | Есть | Есть |

Как открыть монитор порта и как им пользоваться
- Добавь строку Serial.begin(9600); в раздел setup() скетча и загрузи скетч в плату. Без этой строки плата ничего не пишет в последовательный порт.
- Выбери порт, к которому подключена плата, в списке выбора платы на верхней панели или в меню Инструменты › Порт (Tools › Port). Если порт не выбран, монитор порта показывает предупреждение Not connected. Select a board and a port to connect automatically (не подключено, выбери плату и порт).
- Нажми на значок лупы в правом верхнем углу, выбери меню Инструменты › Монитор порта (Tools › Serial Monitor) или нажми Ctrl+Shift+M (Cmd+Shift+M в macOS). В IDE 2 монитор открывается на нижней панели рядом с разделом «Вывод» (Output).
- В меню скорости справа на панели выбери ту же скорость в бодах, что и в коде; в этом примере 9600. Если скорость отличается, экран заполняется мусорными символами.
- Чтобы отправить данные в плату, набери сообщение в поле ввода вверху панели и нажми Enter. Конец строки выбирается в меню прямо рядом с ним; ниже объясняем, почему эта настройка важна.
- Кнопками в правом верхнем углу можно приостановить автопрокрутку (удобно, когда читаешь старые строки), включить отметки времени, которые добавляют время компьютера в начало каждой строки, и очистить вывод.
В старой Arduino IDE 1.8 монитор порта открывается в отдельном окне; элементы управления делают то же самое, только расположены иначе: флажки автопрокрутки и отметок времени внизу слева, меню конца строки и скорости внизу справа. Следить за последовательным портом можно и без IDE: то же самое умеют PuTTY или CoolTerm в Windows, команда screen /dev/ttyUSB0 115200 в Linux и macOS и arduino-cli monitor -p COM3 -c baudrate=115200 в любой системе. Помни одну вещь: последовательный порт одновременно может открыть только одна программа. Пока порт занят скриптом на Python или PuTTY, загрузить код из IDE не получится; установку IDE и остальной интерфейс мы разобрали в руководстве по установке Arduino IDE.
Serial.begin() и скорость в бодах: 9600 или 115200?
Serial.begin(скорость) запускает последовательный порт на заданной скорости и почти всегда вызывается один раз внутри setup(). Скорость задаётся в бодах — это число бит, передаваемых по линии за секунду. Так как каждый символ занимает 10 бит, 9600 бод означают максимум 960 символов в секунду; передача одного символа занимает около 1,04 миллисекунды. Строка из 30 символов на этой скорости передаётся 31 мс, так что, как только нужно выводить сотни измерений в секунду, упираешься в этот предел. На 115200 бод та же строка занимает всего 2,6 мс. Значение в скетче и значение в мониторе порта должны совпадать, потому что общего тактового сигнала нет и приёмник считывает биты только по времени.
| Скорость, бод | Символов в секунду | Время на символ | Где используется? |
|---|---|---|---|
| 9600 | 960 | 1,04 мс | Примеры Arduino; по умолчанию у модулей вроде HC-05 и GPS NEO-6M |
| 57600 | 5760 | 174 µs | Скорость загрузки у Nano со старым загрузчиком, некоторые модули |
| 74880 | 7488 | 134 µs | Загрузочные сообщения ESP8266 |
| 115200 | 11520 | 87 µs | ESP32, Pico и быстрая отладка; скорость загрузки нового загрузчика Uno |
| 250000 | 25000 | 40 µs | Быстрая запись данных; тактовая частота Uno 16 MHz даёт её без ошибки |
| 1000000 | 100000 | 10 µs | Очень быстрая передача данных; нужен короткий кабель |
Тактовая частота Uno 16 MHz не позволяет получить любую скорость точно: 115200 бод на деле отклоняются примерно на 2% и составляют 117647 бод. В мониторе порта эта погрешность проблем не вызывает, но при связи с другим микроконтроллером по длинному кабелю может приводить к ошибкам; в таких случаях надёжнее 57600 или 250000 бод, которые получаются без ошибки. Для начала используй 9600 для Uno и Nano и 115200 для ESP32 и Pico. Если две платы соединены проводами TX–RX, скорость с обеих сторон должна быть одинаковой, земли (GND) должны быть общими, а линию TX на 5 V перед входом RX на 3,3 V (ESP32, Pico) нужно понизить делителем напряжения (номиналы подскажет утилита делителя напряжения). Подробности — в статье про последовательную связь UART.
Serial.print() и Serial.println(): вывод и форматирование
Serial.print() выводит переданное значение и оставляет курсор на той же строке; Serial.println() после этого ещё добавляет символы конца строки (\r\n), поэтому следующий вывод начинается с новой строки. Чтобы показать несколько значений в одной строке, выводи первые части через print, а последнюю через println. Эти функции принимают почти любой тип: int, long, float, char, текст и String, а необязательный второй параметр задаёт формат. Для целых чисел можно выбрать DEC, HEX, BIN и OCT, а для чисел с плавающей точкой второй параметр — это количество знаков после запятой. По умолчанию float выводится с 2 знаками после запятой, с округлением. В коде ниже результат каждой строки записан в комментарии:
1// Biçimlendirme örnekleri: çıktılar yorumlarda / Formatting examples: output in the comments
2void setup() {
3 Serial.begin(9600);
4 int sayi = 78;
5 float pi = 3.14159;
6
7 Serial.println(sayi); // 78
8 Serial.println(sayi, HEX); // 4E
9 Serial.println(sayi, BIN); // 1001110
10 Serial.println(sayi, OCT); // 116
11 Serial.println(pi); // 3.14 (varsayılan 2 basamak / 2 decimals by default)
12 Serial.println(pi, 4); // 3.1416
13 Serial.write(65); // A (ham bayt / raw byte)
14 Serial.println(); // yalnız satır sonu / line break only
15
16 Serial.print("Sicaklik:\t"); // \t = sekme / \t = tab
17 Serial.print(23.46, 1); // 23.5
18 Serial.println(" C"); // Sicaklik: 23.5 C
19
20 char satir[32];
21 int saat = 9, dakika = 5;
22 snprintf(satir, sizeof(satir), "Saat %02d:%02d", saat, dakika);
23 Serial.println(satir); // Saat 09:05
24
25 char metin[10];
26 dtostrf(pi, 6, 2, metin); // Uno'da %f yerine / instead of %f on the Uno
27 Serial.println(metin); // " 3.14"
28}
29
30void loop() {
31}| Код | Что показывает монитор порта | Примечание |
|---|---|---|
| Serial.print(78); | 78 | По умолчанию десятичный вид (DEC) |
| Serial.print(78, HEX); | 4E | Шестнадцатеричный; для адресов, цветов и значений байтов |
| Serial.print(78, BIN); | 1001110 | Двоичный; ведущие нули не выводятся |
| Serial.print(3.14159); | 3.14 | У float по умолчанию 2 знака после запятой |
| Serial.print(3.14159, 4); | 3.1416 | 4 знака, с округлением |
| Serial.write(65); | A | Отправляет символ ASCII с кодом 65, а не число |
| Serial.print('A'); | A | Тип char выводится как символ |
| Serial.print(5e9); | ovf | На Uno числа float больше примерно 4,29 млрд вывести нельзя |
Вместо того чтобы выводить длинные строки по кусочкам, можно собрать одну строку с помощью snprintf(): %d — целое число, %02d — двузначное целое с ведущим нулём, %s — текст, а %lu — unsigned long, например значение millis(). Ради экономии памяти библиотека C на Uno не поддерживает %f: при попытке вывести float на его месте появится ?. Поэтому сначала преврати float в текст с помощью dtostrf(число, ширина, знаки, буфер). На ESP32 и Pico можно сразу использовать Serial.printf(). У Uno всего 2 КБ ОЗУ; постоянный текст, записанный через макрос F(), как в Serial.println(F("Hello")), хранится во флеш-памяти, а не в ОЗУ, и избавляет большие проекты от проблем нехватки памяти.
Мусорные символы в мониторе порта и как от них избавиться
Если на экране ⸮⸮⸮, � или куча случайных букв и значков («кракозябры»), данные до компьютера доходят, но расшифровываются неправильно. Поскольку у последовательной связи нет общего тактового сигнала, приёмник вычисляет середину каждого бита по своей скорости, начиная с момента, когда видит стартовый бит. Если скорость неверная или тактовая частота платы не та, что должна быть, биты считываются не в те моменты, и каждый байт превращается в другой символ. Обычно причина в одной-единственной настройке; в таблице ниже собраны самые частые случаи по симптомам. Для плат с разными вариантами тактовой частоты, например Pro Mini, загляни также в статью об Arduino Pro Mini.
| Симптом | Причина | Решение |
|---|---|---|
| Мусор от начала до конца | Скорость в мониторе порта отличается от значения в Serial.begin() | Выбери в меню справа скорость из своего кода |
| Искажена только одна строка при старте, остальное в порядке | ESP8266 на 74880 или ESP32 на 115200 бод выводит собственное загрузочное сообщение | Это нормально; чтобы его прочитать, переключи монитор на эту скорость |
| Вывод на Pro Mini всегда искажён | Вместо платы на 3,3 V / 8 MHz выбрана плата на 5 V / 16 MHz, поэтому реальная скорость вдвое меньше | Выбери реальную частоту платы в меню Инструменты › Процессор (Tools › Processor) |
| В обычный текст вклиниваются случайные символы | Модуль на пинах D0/D1 тоже передаёт по этой линии | Подключи модуль к другим пинам через SoftwareSerial |
| Странные значки вместо чисел | Serial.write(число) отправляет число как сырой байт | Используй Serial.print(), чтобы вывести число как текст |
| Данные между двумя платами искажаются | Нет общего GND, длинный кабель или разные скорости | Соедини GND, укороти кабель, снизь скорость |
| Одна и та же искажённая строка появляется снова и снова, плата перезапускается | Питания не хватает, и плата постоянно сбрасывается | Проверь источник питания и кабель, питай нагрузку отдельно |
Настройка конца строки: No Line Ending, New Line, Carriage Return, Both NL & CR
Когда набираешь текст в поле ввода и нажимаешь Enter, монитор порта отправляет не только набранные символы, но и один-два невидимых символа — в зависимости от выбранного конца строки. Если код их не ожидает, например обрабатывает каждый входящий байт как команду, то '\n' (значение 10) и '\r' (13) тоже читаются как команды. С другой стороны, если код ждёт конец строки, а выбрано No Line Ending, строка никогда не заканчивается: readStringUntil() ждёт, пока не истечёт таймаут. В таблице ниже показаны байты, которые получает плата, если набрать 42 и нажать Enter:
| Значение | Название в IDE | Добавляемый символ | Байты, отправляемые для 42 |
|---|---|---|---|
| Ничего не добавляется | No Line Ending | Нет | 52, 50 |
| Новая строка (LF) | New Line | \n (LF, 10) | 52, 50, 10 |
| Возврат каретки (CR) | Carriage Return | \r (CR, 13) | 52, 50, 13 |
| Оба (CR + LF) | Both NL & CR | \r\n (13, 10) | 52, 50, 13, 10 |
Практическое правило: для кода, который читает построчно, выбирай New Line и применяй trim() к прочитанному тексту; тогда код работает при любой настройке пользователя. Для односимвольных команд вроде 1, 0 или a самый чистый вариант — No Line Ending. IDE запоминает выбор, так что не забудь проверить его при переходе к другому проекту.
Отправка данных с компьютера в Arduino: Serial.read(), parseInt(), readStringUntil()
Байты, отправленные из монитора порта, встают в очередь в приёмном буфере платы (на Uno 64 байта). Serial.available() сообщает, сколько байтов в нём ждёт, Serial.read() забирает следующий байт и удаляет его из буфера (если данных нет, возвращает -1), а Serial.peek() смотрит на следующий байт, не удаляя его. Помни, что всё с клавиатуры приходит в виде символов ASCII: если набрать 7, плата получит не число 7, а 55 — код символа '7'. Пример простых команд одной клавишей есть в статье о командах Arduino; здесь разбираем многозначные числа и команды из слов. Если буфер переполнится, новые байты теряются, поэтому отсчёт времени через millis() вместо долгого ожидания delay() внутри loop() делает чтение порта ещё и надёжнее.
Serial.parseInt() извлекает из входящих символов первое целое число: пропускает буквы и пробелы в начале, собирает цифры и останавливается на первом символе, который не цифра; знак минуса она тоже понимает. Так текст «128» превращается в число 128. Для дробных чисел то же самое делает Serial.parseFloat(). У этих функций есть одна привычка, о которой нужно знать: если число не приходит, они по умолчанию ждут 1000 мс, а затем возвращают 0. Классическая ошибка выглядит так: при настройке New Line отправляется 42, parseInt читает 42, но '\n' остаётся в буфере. На следующем проходе loop() видит, что available() больше 0, и снова вызывает parseInt, которая ждёт секунду и возвращает 0. В итоге после каждого числа приходит 0, и непонятно, почему светодиод гаснет через секунду после каждого числа. Код ниже решает проблему в корне: отбрасывает следующий символ, если это не цифра, и сокращает ожидание с помощью setTimeout().
1// Seri Port Ekranı'ndan sayı al: LED parlaklığı 0-255
2// Read a number from the Serial Monitor: LED brightness 0-255
3const int LED_PIN = 9; // ~9 PWM pini + 220 ohm direnç / ~9 PWM pin + 220 ohm resistor
4
5void setup() {
6 pinMode(LED_PIN, OUTPUT);
7 Serial.begin(9600);
8 Serial.setTimeout(100); // parseInt en fazla 100 ms bekler / parseInt waits at most 100 ms
9 Serial.println(F("0-255 arasi bir sayi yaz / Type a number from 0 to 255"));
10}
11
12void loop() {
13 if (Serial.available() > 0) {
14 char c = Serial.peek(); // sıradakine bak, silme / look at the next byte, keep it
15 if (isDigit(c) || c == '-') {
16 int deger = Serial.parseInt(); // "128" -> 128
17 deger = constrain(deger, 0, 255); // aralığın dışına çıkma / stay within range
18 analogWrite(LED_PIN, deger);
19 Serial.print(F("Parlaklik / Brightness: "));
20 Serial.println(deger);
21 } else {
22 Serial.read(); // '\r', '\n' ve harfleri at / drop '\r', '\n' and letters
23 }
24 }
25}Для команд из слов проще всего прочитать всю строку как String. Serial.readStringUntil('\n') читает до прихода '\n', отбрасывает символ конца строки и возвращает остальное. Если выбрано Both NL & CR, в конце текста остаётся '\r', команда «on» превращается в «on\r», и сравнение не срабатывает. Поэтому сразу применяй к строке trim(): эта функция убирает пробелы в начале и в конце, а также символы '\r' и '\n'. toLowerCase() делает ON, On и on одинаковыми. Пример ниже понимает команды on, off, blink 250 и status; в мониторе порта выставь 115200 бод и New Line. Так как мигание сделано на millis(), а не на delay(), скетч мгновенно отвечает на новую команду в любой момент.
1// Metin komutları: on, off, blink 250, status
2// Text commands: on, off, blink 250, status
3const int LED_PIN = LED_BUILTIN; // Uno'da pin 13 / pin 13 on the Uno
4bool ledAcik = false; // LED'in şu anki durumu / current LED state
5bool yanipSon = false; // yanıp sönme modu / blink mode
6unsigned long aralik = 500; // yanıp sönme aralığı (ms) / blink interval (ms)
7unsigned long sonDegisim = 0; // LED'in son değiştiği an / last time the LED changed
8
9void ledYaz(bool acik) {
10 ledAcik = acik;
11 digitalWrite(LED_PIN, acik ? HIGH : LOW);
12}
13
14void komutuIsle(String komut) {
15 if (komut == "on") {
16 yanipSon = false;
17 ledYaz(true);
18 Serial.println(F("LED: on"));
19 } else if (komut == "off") {
20 yanipSon = false;
21 ledYaz(false);
22 Serial.println(F("LED: off"));
23 } else if (komut.startsWith("blink")) {
24 long ms = komut.substring(5).toInt(); // "blink 250" -> 250
25 aralik = (ms >= 50) ? ms : 500; // çok küçükse 500 ms / fall back to 500 ms
26 yanipSon = true;
27 Serial.print(F("Blink (ms): "));
28 Serial.println(aralik);
29 } else if (komut == "status") {
30 Serial.print(F("LED: "));
31 Serial.print(ledAcik ? F("on") : F("off"));
32 Serial.print(F(", blink: "));
33 Serial.println(yanipSon ? F("yes") : F("no"));
34 } else {
35 Serial.print(F("Bilinmeyen komut / Unknown command: "));
36 Serial.println(komut);
37 }
38}
39
40void setup() {
41 pinMode(LED_PIN, OUTPUT);
42 Serial.begin(115200);
43 Serial.setTimeout(100); // satır sonu gelmezse 100 ms sonra bırak / give up after 100 ms without a line ending
44 Serial.println(F("Komutlar / Commands: on, off, blink 250, status"));
45}
46
47void loop() {
48 if (Serial.available() > 0) {
49 String komut = Serial.readStringUntil('\n'); // Enter'a kadar oku / read up to Enter
50 komut.trim(); // '\r' ve boşlukları sil / strip '\r' and spaces
51 komut.toLowerCase(); // ON ile on aynı olsun / treat ON like on
52 if (komut.length() > 0) {
53 komutuIsle(komut);
54 }
55 }
56
57 if (yanipSon && millis() - sonDegisim >= aralik) { // delay() olmadan yanıp sön / blink without delay()
58 sonDegisim = millis();
59 ledYaz(!ledAcik);
60 }
61}| Функция | Что возвращает | Сколько ждёт? | Лучше всего подходит для |
|---|---|---|---|
| Serial.read() | Один байт (int), -1, если данных нет | Не ждёт | Односимвольных команд, собственного парсера |
| Serial.peek() | Следующий байт, не удаляя его | Не ждёт | Просмотра следующего байта перед принятием решения |
| Serial.parseInt() | Целое число long, 0 по таймауту | Время setTimeout (по умолчанию 1000 мс) | Ввода чисел: угол, скорость, яркость |
| Serial.parseFloat() | Число float, 0 по таймауту | Время setTimeout | Дробных настроек, например 2.5 |
| Serial.readStringUntil(c) | Текст до символа c (String) | Пока не придёт c или не истечёт время | Команд со словами и параметрами |
| Serial.readBytesUntil(c, буфер, n) | Число прочитанных байтов; записывает текст в массив char | Пока не придёт c, не будет прочитано n байтов или не истечёт время | Чтения без String, бережного к ОЗУ |
Живые графики в плоттере по последовательному соединению
Плоттер по последовательному соединению (Serial Plotter) превращает числа из последовательного порта в живой линейный график; он открывается значком осциллограммы в правом верхнем углу IDE или через меню Инструменты › Плоттер по последовательному соединению. Формат очень простой: раздели значения в строке запятыми, пробелами или табуляцией и заверши строку вызовом println. Каждая строка становится одним шагом по времени на графике, а каждое значение в строке — отдельной цветной линией. Если перед значениями поставить имя в виде метка:значение, линии получат эти названия, и их можно по одной скрывать и показывать флажками над графиком; без меток линии называются value 1, value 2 и так далее. Здесь тоже меню скорости должно совпадать с кодом. Код ниже рисует на одном графике сырое значение с A0 и его сглаженную версию:
1// Seri Çizici: ham değer ve yumuşatılmış değer / Serial Plotter: raw and smoothed value
2const int SENSOR_PIN = A0; // potansiyometre ya da LDR bölücü çıkışı / potentiometer or LDR divider output
3float yumusak = 0; // üstel ortalama / exponential moving average
4
5void setup() {
6 Serial.begin(115200);
7}
8
9void loop() {
10 int ham = analogRead(SENSOR_PIN); // 0-1023
11 yumusak = yumusak * 0.9 + ham * 0.1; // yeni ölçümün payı %10 / the new reading weighs 10%
12
13 Serial.print("Min:0,Max:1023,"); // dikey ekseni sabitler / keeps the y-axis fixed
14 Serial.print("Ham:");
15 Serial.print(ham);
16 Serial.print(",Yumusak:");
17 Serial.println(yumusak); // satırı bitir: bir ölçüm / end the line: one sample
18 delay(20); // saniyede yaklaşık 50 satır / about 50 lines per second
19}- Зафиксируй масштаб: плоттер постоянно перестраивает вертикальную ось под входящие значения, поэтому мелкие колебания выглядят огромными. Если добавить в каждую строку два постоянных значения вроде Min:0,Max:1023,, ось остаётся неподвижной.
- Без пробелов в метках: пробелы и запятые считаются разделителями, поэтому «Room Temp:24» разбивается на две части; используй имена с подчёркиванием, например Room_Temp.
- В строке только метки и числа: единицы измерения и описания вроде «Temp 24 C» портят график; описания для монитора порта выводи отдельным кодом.
- Подбери частоту отправки: delay(20) — это примерно 50 строк в секунду. Если отправлять слишком быстро, график мгновенно уезжает с экрана; если слишком медленно, линия становится похожей на лесенку.
- Посмотри на фильтр своими глазами: сырое значение и среднее на одном графике сразу показывают, насколько фильтр снижает шум; способы — в статье о фильтрации данных датчиков.
- Для записи данных нужен другой инструмент: плоттер не сохраняет данные в файл. Для долгих измерений выводи значения строками CSV и импортируй их в Excel или читай на Python.
Приёмы отладки с монитором порта
В Arduino Uno нет отладчика, который проходит код по шагам; эту работу выполняет монитор порта. Но строки вывода, раскиданные по коду как попало, быстро превращаются в нечитаемую кучу текста. Эти привычки помогут находить ошибки гораздо быстрее:
- Сначала выведи сообщение из setup(): если строка «Started» появляется один раз при каждом запуске платы, значит, плата работает. Если эта строка повторяется снова и снова, плата постоянно сбрасывается, и проблема скорее в питании, чем в коде; см. руководство «Arduino постоянно перезагружается».
- Выводи значения вместе с именами: вместо голого 512 пиши temp=24.5 hum=40; потом не придётся вспоминать, какое число к чему относится.
- Отмечай, где находишься: если программа где-то зависает, поставь перед важными шагами короткие метки вроде A, B, C. Последняя метка на экране покажет, где программа застряла.
- Выводи при изменении, а не на каждом проходе: loop() выполняется тысячи раз в секунду. Вывод на каждом проходе заливает экран и замедляет программу; выводи значение только тогда, когда оно меняется, или раз в секунду с помощью millis().
- Измеряй время: чтобы узнать, сколько длится операция, сохрани значение micros() до и после неё и выведи разницу. Так можно поймать медленное чтение датчика или слишком долгий цикл.
- Не выводи ничего внутри прерываний: вывод в порт занимает время; Serial.print внутри функции прерывания без нужды затягивает её и может привести к пропуску других событий. В прерывании просто установи переменную-флаг, а вывод делай в loop(); подробности — в статье о прерываниях Arduino.
- Спрячь отладочные строки за одним переключателем: с макросами ниже весь отладочный вывод полностью убирается из кода, если задать DEBUG равным 0; в готовом проекте это экономит и флеш-память, и время.
1// Hata ayıklama makroları: tek satırla aç ya da kapat
2// Debug macros: switch all debug output on or off with one line
3#define DEBUG 1 // 0 yaparsan debug satırları koddan çıkar / set to 0 to strip debug lines
4
5#if DEBUG
6 #define DBG(x) Serial.print(x)
7 #define DBGLN(x) Serial.println(x)
8#else
9 #define DBG(x)
10 #define DBGLN(x)
11#endif
12
13const int SENSOR_PIN = A0;
14const int ESIK = 800; // uyarı eşiği / warning threshold
15bool esikUstunde = false; // son durum / last state
16unsigned long sonRapor = 0;
17unsigned long turSayisi = 0; // saniyedeki loop() turu / loop() passes per second
18
19void setup() {
20 Serial.begin(115200);
21 DBGLN(F("Basladi / Started"));
22}
23
24void loop() {
25 turSayisi++;
26 int deger = analogRead(SENSOR_PIN);
27
28 bool simdi = deger > ESIK;
29 if (simdi != esikUstunde) { // yalnız değişince yaz / print only on change
30 esikUstunde = simdi;
31 DBG(F("[esik / threshold] "));
32 DBG(simdi ? F("ustunde / above") : F("altinda / below"));
33 DBG(F(", deger / value = "));
34 DBGLN(deger);
35 }
36
37 if (millis() - sonRapor >= 1000) { // saniyede bir özet / one summary per second
38 sonRapor = millis();
39 DBG(F("t = "));
40 DBG(sonRapor);
41 DBG(F(" ms, tur/s / loops/s = "));
42 DBGLN(turSayisi);
43 turSayisi = 0;
44 }
45}Помни, что у вывода в порт есть цена. На Uno буфер передачи тоже 64 байта; когда он заполнен, Serial.print() ждёт, пока освободится место, и loop() замедляется. Это легко измерить счётчиком циклов в коде выше: если добавить строку из 25 символов, которая выводится на каждом проходе, на 9600 бод программа падает примерно до сорока проходов в секунду, а вывод только при изменении сохраняет тысячи. Чтобы точно знать, что плата вывела прямо перед зависанием или сбросом, вызывай Serial.flush() после критической строки: эта функция ждёт, пока всё содержимое буфера не будет отправлено. В более сложных программах переход на структуру конечного автомата и вывод одной строки только при смене состояния сильно упрощают поиск ошибок.
Вывод в порт на ESP32
На платах ESP32 DevKit Serial — это UART0 на GPIO 1 (TX) и GPIO 3 (RX), подключённый к USB через микросхему CP2102 или CH340 на плате. Обычная скорость — 115200 бод, потому что ПЗУ внутри чипа при загрузке тоже выводит на этой скорости строки вроде rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT); если держать монитор на 115200, эти строки тоже становятся читаемыми. Ядро ESP32 для Arduino предлагает Serial.printf() и поддерживает вывод float через %f. Дополнительные порты Serial1 и Serial2 можно назначить на любые пины; при подключении модуля часто используют Serial2 на GPIO 16 и 17. На платах со встроенным USB, например ESP32-S3 и ESP32-C3, если монитор порта остаётся пустым, переключи Инструменты › USB CDC On Boot на Enabled. Чтобы видеть собственные предупреждения ядра, выбери Error, Warn, Info, Debug или Verbose в меню Инструменты › Core Debug Level, а в своём коде можно выводить записи журнала с именем файла и номером строки макросами log_e, log_w и log_i.
1// ESP32: Serial (USB), Serial2 (GPIO16/17), printf ve log
2// ESP32: Serial (USB), Serial2 (GPIO16/17), printf and logging
3const int POT_PIN = 34; // ADC1, yalnız giriş pini / ADC1, input-only pin
4unsigned long sonYazdirma = 0;
5
6void setup() {
7 Serial.begin(115200); // açılış mesajlarıyla aynı hız / same rate as the boot messages
8 delay(500);
9 Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX = GPIO16, TX = GPIO17
10
11 Serial.printf("Model: %s, CPU: %lu MHz\n", ESP.getChipModel(), (unsigned long)ESP.getCpuFreqMHz());
12 Serial.printf("Bos RAM / Free heap: %lu bayt / bytes\n", (unsigned long)ESP.getFreeHeap());
13 log_i("Core Debug Level en az Info ise gorunur / shown when Core Debug Level is Info or higher");
14}
15
16void loop() {
17 while (Serial2.available() > 0) { // Serial2'den geleni USB'ye aktar / forward Serial2 data to USB
18 Serial.write(Serial2.read());
19 }
20
21 if (millis() - sonYazdirma >= 1000) { // delay() yok, tampon taşmaz / no delay(), no buffer overflow
22 sonYazdirma = millis();
23 float volt = analogReadMilliVolts(POT_PIN) / 1000.0; // kalibre gerilim / calibrated voltage
24 Serial.printf("t=%lu ms, GPIO34=%.2f V\n", millis(), volt); // ESP32'de %f çalışır / %f works on ESP32
25 }
26}При проблемах с подключением к Wi-Fi, неожиданных сбросах и ошибках Guru Meditation монитор порта — первое место, куда нужно смотреть: здесь появляются тип ошибки, причина сброса и строки backtrace (трассировка стека). Как читать эти сообщения, мы объясняем в руководствах про brownout и Guru Meditation на ESP32 и «ESP32 не подключается к Wi-Fi». Если ESP32 в новинку, начни со статьи «Что такое ESP32?».
Вывод в порт на Raspberry Pi Pico: Arduino и MicroPython
У Raspberry Pi Pico нет микросхемы-моста USB–UART: RP2040 сама работает с USB и видна компьютеру напрямую как последовательный порт (USB CDC). В ядре Arduino-Pico от Earle Philhower Serial — это и есть этот USB-порт, и, поскольку данные идут на скорости USB, значение скорости в Serial.begin() игнорируется. Из этого следуют две вещи: какую скорость выбрать в мониторе, уже не важно, зато плата, в отличие от Uno, не сбрасывается при открытии монитора порта. Первые сообщения, выведенные в setup(), к моменту открытия монитора уже отправлены и потеряны, поэтому в коде стоит ненадолго подождать, пока компьютер откроет порт, как показано ниже. То же относится к платам на ATmega32U4, таким как Leonardo, Micro и Pro Micro. Настоящий UART у Pico на пинах GP0 и GP1 используется под именем Serial1.
1// Raspberry Pi Pico (Arduino-Pico çekirdeği): USB seri port
2// Raspberry Pi Pico (Arduino-Pico core): USB serial port
3void setup() {
4 Serial.begin(115200); // USB'de hız değeri önemsiz / the baud value is ignored over USB
5 unsigned long baslangic = millis();
6 while (!Serial && millis() - baslangic < 3000) {
7 delay(10); // port açılana kadar en çok 3 sn bekle / wait up to 3 s for the port
8 }
9 Serial1.begin(9600); // UART0: GP0 = TX, GP1 = RX
10 Serial.println("Pico hazir / Pico ready");
11}
12
13void loop() {
14 Serial.print("Sicaklik / Temperature (C): ");
15 Serial.println(analogReadTemp(), 1); // dahili sensör, yaklaşık / internal sensor, approximate
16 Serial1.println(millis()); // aynı anda UART0'a da yaz / also write to UART0
17 delay(1000);
18}В MicroPython всё ещё проще: всё, что выводится через print(), уходит в компьютер по USB и появляется в окне Shell в Thonny; скорость в бодах настраивать не нужно. Если открыть в Thonny меню Вид › Плоттер (View › Plotter), числа в строках превращаются в живой график — как в плоттере Arduino IDE. Для приёма данных с компьютера можно использовать input(), но input() останавливает программу до прихода строки. Чтобы слушать команды и при этом продолжать измерения, проверяй через select.poll(), ждут ли данные в stdin, как показано ниже; достаточно набрать on или off в Shell и нажать Enter. Pico и установку MicroPython мы разобрали в статье «Что такое Raspberry Pi Pico?».
1# Pico MicroPython: print() ile yazdır, programı durdurmadan komut al
2# Pico MicroPython: print with print(), read commands without blocking
3import sys
4import select
5import time
6from machine import ADC, Pin
7
8led = Pin(25, Pin.OUT) # Pico'da dahili LED; Pico W'de Pin("LED") / onboard LED; Pin("LED") on Pico W
9sensor = ADC(4) # dahili sıcaklık sensörü / internal temperature sensor
10poll = select.poll()
11poll.register(sys.stdin, select.POLLIN)
12
13while True:
14 volt = sensor.read_u16() * 3.3 / 65535
15 sicaklik = 27 - (volt - 0.706) / 0.001721
16 print("Sicaklik:{:.1f}".format(sicaklik)) # Thonny Plotter da çizer / Thonny's Plotter can graph it
17 if poll.poll(0): # bekleyen veri var mı? / is input waiting?
18 komut = sys.stdin.readline().strip() # satırı al, boşlukları sil / read the line, strip spaces
19 if komut == "on":
20 led.value(1)
21 elif komut == "off":
22 led.value(0)
23 print("Komut / Command:", komut)
24 time.sleep(1)Монитор порта пуст или порт занят: частые ошибки и решения
| Симптом | Причина | Решение |
|---|---|---|
| Экран совсем пустой | Нет Serial.begin() или выбран не тот порт | Добавь Serial.begin() в setup(), выбери правильный порт в списке выбора платы |
| «Not connected. Select a board and a port to connect automatically» (не подключено, выбери плату и порт) | В IDE не выбран порт или плата отключена от компьютера | Подключи плату и выбери её в меню Инструменты › Порт |
| «Serial port busy» или «Access is denied» (порт занят, доступ запрещён) | Порт открыт в другом окне IDE, в PuTTY или в скрипте на Python | Закрой программу, которая держит порт; порт одновременно может открыть только одна программа |
| Нет первых строк на Leonardo или Pico | Плата со встроенным USB не сбрасывается при открытии монитора | Ненадолго подожди в setup() с помощью while (!Serial) |
| На ESP32-S3/C3 ничего не появляется | USB CDC On Boot выключен, вывод идёт на пины UART | Инструменты › USB CDC On Boot: Enabled |
| После каждого числа появляется 0 | parseInt ждёт до таймаута из-за символа конца строки | Пропускай нецифровые символы или выбери No Line Ending |
| Сравнение команд никогда не срабатывает | В конце текста остаётся '\r' | Применяй trim() после чтения |
| Порт так и не появляется в списке | Кабель только для зарядки или не установлен драйвер CH340/CP210x | Используй кабель с передачей данных, установи драйвер |
Идеи проектов с последовательной связью
- Светодиод и реле с управлением с компьютера: расширь парсер команд из этой статьи, чтобы управлять реле или мотором командами on, off и blink.
- Регистратор данных датчика: выводи температуру и влажность строками CSV через запятую и превращай их на компьютере в таблицу и график; для начала хорошо подходит датчик DHT11.
- Живой график освещённости: следи за значением фоторезистора (LDR) или потенциометра в плоттере и смотри действие фильтра на том же графике.
- Настольная панель на Python: читай данные, которые отправляет Arduino, программой на Python и сделай интерфейс с кнопками; подробности — в статье о связи Arduino и Python по последовательному порту.
- Стенд для проверки модулей: модули GPS, Bluetooth и GSM тоже работают через последовательный порт; перенаправь сырые данные модуля в монитор порта, чтобы проверить, работает ли он (HC-05, GPS NEO-6M).
- Попробовать без платы: если платы под рукой нет, в Tinkercad тоже есть монитор порта; код из этой статьи можно запустить в симуляции Tinkercad.
Когда монитор порта станет привычным, поиск ошибок в Arduino перестанет быть гаданием и превратится в измерение. Следующим шагом прочитай статью о последовательной связи UART, чтобы связать две платы по TX и RX, и статью о преобразователе USB-TTL, чтобы программировать платы без USB. В приложении BlueGrays есть проекты, где расстояние, измеренное HC-SR04, выводится в монитор порта, освещённость считывается фоторезистором (LDR), температура и влажность измеряются DHT11, а две Arduino связываются по последовательной линии, — пошагово, со схемами подключения и готовым кодом.
Частые вопросы
Как открыть монитор порта Arduino?
После загрузки кода нажми на значок лупы в правом верхнем углу Arduino IDE, выбери меню Инструменты › Монитор порта или нажми Ctrl+Shift+M. В IDE 2 монитор открывается на нижней панели. В списке выбора платы должен быть выбран правильный порт, а значение в меню скорости справа должно совпадать со значением Serial.begin() в коде.
Почему в мониторе порта мусорные символы?
Самая частая причина — скорость в мониторе порта отличается от значения Serial.begin() в коде; как только они совпадут, текст станет читаемым. Если искажена только одна строка при старте, это собственное загрузочное сообщение ESP8266 или ESP32, и это нормально. Такой же симптом дают неверно выбранная тактовая частота на платах вроде Pro Mini или модуль, подключённый к пинам D0/D1.
Какую скорость выбрать для Arduino: 9600 или 115200 бод?
Работают обе; главное, чтобы в коде и в мониторе порта была одна и та же скорость. 9600 бод передают около 960 символов в секунду, и для простых проектов на Uno этого достаточно. Если выводится много данных или используется ESP32 или Pico, выбирай 115200: эта скорость в 12 раз выше, и вывод меньше замедляет программу.
Чем отличаются Serial.print и Serial.println?
Serial.print() выводит значение и оставляет курсор на той же строке; Serial.println() после вывода добавляет символы возврата каретки и перевода строки (\r\n). Чтобы показать несколько значений в одной строке, сначала используй print, а в конце println. Кроме того, строку нужно завершать вызовом println, чтобы плоттер считал каждую строку отдельным отсчётом.
Почему в мониторе порта ничего не отображается?
Сначала проверь, что в setup() есть Serial.begin() и что в списке выбора платы выбран правильный порт. На платах со встроенным USB, таких как Leonardo, Pico и ESP32-S3, первые сообщения из setup() могут уйти и потеряться ещё до открытия монитора; добавь короткое ожидание с помощью while (!Serial). На ESP32-S3 и C3 в меню «Инструменты» также должна быть включена настройка USB CDC On Boot (Enabled).
Почему Arduino перезагружается при открытии монитора порта?
На Uno, Nano и Mega линия DTR USB-микросхемы подключена к пину RESET через конденсатор; при открытии порта на этой линии появляется короткий импульс, и плата перезапускается. Эта схема нужна для удобной загрузки кода и гарантирует, что сообщения из setup() не потеряются. Если это не нужно, можно поставить конденсатор 10 µF между RESET и GND, но перед загрузкой его нужно убрать.
Что собрать после прочтения
Все проекты
Измерение расстояния HC-SR04 (вывод в порт)
Датчик HC-SR04 работает совсем как летучая мышь, которая находит дорогу в темноте: он посылает звук, которого ты не слышишь, слушает эхо и измеряет, насколько далеко предмет перед ним. Arduino каждые

Измерение освещённости с LDR
Хочешь увидеть яркость своей комнаты в виде числа? LDR — маленькая деталь, реагирующая на свет, — измеряет освещённость вокруг, а Arduino каждые полсекунды выводит значение на экран компьютера. Закрой

Температура и влажность с DHT11
Насколько тепло в твоей комнате и влажный ли воздух или сухой? Датчик DHT11 измеряет температуру и влажность, а Arduino выводит значения на двухстрочный LCD-экран и обновляет их каждые пару секунд. Ес

Измерение освещённости модулем LDR на ESP32
Наблюдение в мониторе порта в реальном времени за сырым значением ADC модуля LDR и относительным процентом освещённости, вычисленным по опорным значениям темноты и света. Этот процент — не измерение в люксах. Учит логике калибровки модуля. Идеально выполнить его перед проектом 10, потому что он нужен для определения порога.

Чтение кодов ИК-пульта
Каждая кнопка на пульте от телевизора отправляет секретный код светом, которого ты не видишь. ИК-приёмник ловит этот свет, а Arduino выводит на экран компьютера код каждой нажатой кнопки. Узнав эти ко

Чтение значения RFID
В брелоке от подъезда или карте доступа спрятан секретный идентификационный номер, и этот проект его читает. Поднеси карту к считывателю RFID, и Arduino выведет номер карты на экран компьютера. Первый

Односторонняя связь между двумя Arduino
Ты заставляешь две Arduino разговаривать по одному проводу: нажми кнопку на одной, и на другой загорится светодиод, нажми снова — погаснет. Плата-передатчик сообщает о нажатии кнопки коротким сообщени

Двусторонняя связь между двумя Arduino
Две Arduino соединены проводами и разговаривают друг с другом: нажми кнопку на одной, и на другой загорится свет, нажми снова — погаснет. На обеих платах одна и та же программа, поэтому каждая и отпра
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

