Третий вечер я гонял один и тот же сбой: датчик раз в несколько минут выдавал выброс, и я уже оброс десятком Serial.print, каждый из которых требовал перепрошивки и пары минут на заливку. На четвёртый день поставил точку останова на ESP32 в IDE 2 — и увидел живое состояние: значения переменных в момент выброса, панель watch, всё как в большой студии. Разница между «печатаю и угадываю» и «стою внутри программы» стоила мне трёх вечеров, и её стоит разобрать по косточкам до того, как вы тоже обрастёте сотней print-ов.
Механику отладчика — панели, кнопки, список выражений — уже разбирали в уроке про точки останова и watch, начните с него, там база. Здесь — то, о чём тот урок говорит вскользь, а в жизни спотыкается каждый: на каких платах отладка вообще заведётся, почему классический Uno остаётся за бортом, и что происходит с таймерами, пока ядро стоит на точке. Плюс честный разговор, когда Serial-печать спасает лучше любого отладчика — обе методики свёл в уроке про Serial-отладку.
Ещё деталь, о которой спрашивают в комментариях: отладочная сборка толще обычной. В бинарник добавляются отладочные символы — прошивка моего тестового скетча на ESP32-S3 выросла примерно с трёхсот до четырёхсот килобайтов, скорость работы до первого стопа не меняется. Жить с постоянной отладочной сборкой можно, но перед релизом я пересобираю обычной: символы в полёте не участвуют, а флеш у классических плат — счёт на сотни килобайтов.
Где отладчик заводится, а где нет
Правило одно: отладчику нужен канал в ядро, и не всякая плата его тянет. ESP32-S2, S3 и C3 выигрывают безобразно легко — USB-JTAG вшит в чип, и обычный кабель к разъёму платы уже отладочный шнур, покупать нечего. Классический ESP32 с мостом CP2102 или CH340 так не умеет: ему нужен внешний адаптер вроде ESP-Prog, полдюжины проводов и вечер на расстановку — заработает, но настраивать придётся руками. SAMD-семейство — Zero, Nano 33 IoT — и новые Uno R4 Minima и WiFi несут SWD-контур на борту: после установки пакета платы IDE 2 цепляется к ним сама.
Классический Uno — самая грустная строка списка. У его ATmega328P теоретически есть debugWIRE, но это танцы вокруг линии сброса, потеря Serial на время сеанса и обязательный аппаратный программатор уровня Atmel-ICE за десятки тысяч: поддержки в списке плат IDE 2 нет, и руками я этот путь ни разу не добил до конца. С Nano и Mega та же история — точку останова ставят только внешними инструментами. Отсюда мой практический вывод: скетч для классики отлаживаю Serial-методикой, а всё сложное переношу на ESP32-S3, который стоит дешевле хорошего мультиметра, — одна точка останова окупает плату.
Числа для планирования бюджета: JTAG-адаптер стоит порядка одной-двух тысяч рублей, плата на ESP32-S3 целиком — часто дешевле, и канала для отладки не требует вовсе. Когда проект уже живёт на классическом WROOM, я завожу вторую плату с USB-C для отладочных экспериментов и не трогаю основной стенд: прошивки с точками останова не должны встречаться с боевой логикой на одном железе. Отладка — про честность наблюдения, а мешать её с боевой работой я отвык после первого же зависшего стенда.
Если ESP32 в среде ещё не появился, начните с установки пакета плат ESP32 — без пакета список отладочных конфигураций пуст, и кнопка с жучком будет серой. Плата в списке, порт выбран — а отладка всё равно молчит? Проверьте, что выбрана именно отладочная плата вроде S3 или C3: у классического WROOM канал в ядро физически отсутствует, никакая среда его не придумает.
Сеанс выглядит как в VS Code — IDE 2 унаследовала и кнопки, и привычки. F5 или кнопка с жучком запускает отладочную сборку и прошивку; она длиннее обычной, у меня первый заход на ESP32-S3 занял с минуту. Точка останова ставится кликом в желобок слева от строки — красный кружок, на нём исполнение замирает. Дальше Continue (F5), Step Over (F10) — шаг по строкам, не залезая внутрь функций, Step Into (F11) — внутрь, Step Out — обратно наружу. Панель Variables показывает локальные значения на момент стопа, watch принимает свои выражения: вписал sum * 2 или делишь дату на части — и оно пересчитывается на каждом шаге.
Условная точка — самый недооценённый инструмент: правый клик по кружку, условие вроде x > 900, и стоп случается только на выбросе, а не на каждой сотой итерации. Без условия отладка зацикленного скетча превращается в сотню нажатий Continue: я именно так ловил выброс датчика руками, пока не вписал порог в условие. Час ручных нажатий — та цена, которую заплатил за догадку, ставьте условие сразу.
Стек вызовов — третий житель панелей: на стопе видно, через какие функции пришло исполнение, и глубина честно отвечает на вопрос «откуда я тут». Рядом список выражений принимает не только переменные: millis() - t показывает, сколько прошло с последнего такта расписания, sizeof(buf) — сколько памяти съел буфер. Выражения с вызовами функций считаются не везде — числа и адреса работают всегда, вызовы проверяйте на своей сборке первым же прогоном.
// Na plate: potenciometr na A0, skorost portu 115200.// Tochku ostanova stavyat na stroku s sum = sum + x:// v Variables vidno x, sum i t na kazdom prohode.int x = 0;int sum = 0;unsigned long t = 0; void setup() { Serial.begin(115200);} void loop() { if (millis() - t >= 200) { t = millis(); x = analogRead(A0); sum = sum + x; // <- zdes tochka ostanova Serial.print("x="); Serial.print(x); Serial.print(" sum="); Serial.println(sum); if (sum > 20000) { sum = sum / 2; } // sbros nabora - vtoroe mesto stopa }}| Ситуация | Serial-печать | Отладчик |
|---|---|---|
| быстрый осмотр раз в секунду | да: println по таймеру, работает на любой плате | можно, но точка останова ради цифры — стрельба из пушки |
| ловля редкого выброса | поток строк, выброс тонет в шуме | условная точка остановит именно на выбросе |
| ШИМ, прерывания, живой сигнал | печать меняет тайминги и может лечить баг сама | ядро стоит — картина честная, без вмешательства |
| Uno, Nano, Mega без отладочного канала | единственный вариант | не заведётся без внешнего аппаратного программатора |
| вопрос «как я сюда попал» | история строк в логе отвечает сама | стек вызовов покажет путь, но не прошлые значения |
Когда точка останова, а когда Serial
Моё правило после пары сотен сеансов: отладчик — для логики, Serial — для сигналов. Точку останова ставят на счётчики, состояния и ветки, которые срабатывают не тогда; Serial оставляют тому, что живёт во времени — ШИМ, прерывания, обмен с датчиком. Печать на 115200 съедает почти миллисекунду на десяток символов, и для цикла в двести микросекунд это целая эпоха: печать не то что показывает такой баг, она его создаёт. Отладчик наоборот: стоит — и меньше всего вмешивается, обрезает только время.
Обратная сторона честная: отладчик не помнит историю. Он показывает «здесь и сейчас», а как вы сюда попали — значения, не вписанные в watch заранее, уже исчезли. Серийная печать пишет лог каждый такт, и беглый взгляд по нему после сбоя отвечает быстрее сеанса с точками; ведётся этот лог в панели монитора, про которую был разбор панели IDE 2. Мой тест на выбор простой: баг воспроизводится раз в минуту и зависит от времени — сначала лог; ловится сразу и упирается в значения переменных — сразу отладчик.
Про время в скетчах под отладку отдельное слово: любое расписание на millis() после пауз проверяйте заново — механика таймера на millis вместо delay страдает от точек останова первой. А команды из монитора остаются рабочим инструментом и во время сеанса: подать сигнал плате и посмотреть реакцию на шагах можно и через управление из монитора — два окна, Serial и отладчик, живут рядом без конфликтов.
Есть и класс ситуаций, где отладчик бессилен в принципе: всё, что зависит от внешнего мира в реальном времени. Прерывание раз в сто микросекунд, обмен с датчиком по таймингам даташита, звук на аппаратном таймере — ядро стоит, и мир уже не тот, каким был бы без вас. Такие куски я прочёсываю печатью по краям: снял статистику до и после, сравнил цифры — и никакого стопа внутри горячего участка.
- 1. Какая плата заведётся с отладчиком в IDE 2 без внешних адаптеров?
- 2. Что делает Step Over (F10)?
- 3. Ядро минуту стоит на точке останова на ESP32. Что с millis()?
- 4. Точку останова на классическом Uno IDE 2 не берёт. Почему?
- 5. Условие точки останова нужно, чтобы…
- Канал в ядро решает всё: ESP32-S2/S3/C3 — USB-JTAG из коробки, SAMD и Uno R4 — SWD, классический ESP32 — внешний JTAG, Uno и Nano — только аппаратный программатор.
- F5 запускает отладочную сборку, клик в желобок ставит точку, F10 шагает поверх функций, F11 — внутрь.
- Условная точка с порогом ловит редкий выброс вместо сотни нажатий Continue.
- Пока ядро стоит, таймеры живут: millis() после Continue прыгает вперёд, UART-буфер переполняется — после сеанса перезапуск.
- Отладчик — для логики и значений, Serial — для сигналов и истории: печать меняет тайминги и способна создать баг.
Частые вопросы по теме урока
- Какая плата заведётся с отладчиком в IDE 2 без внешних адаптеров?
- У S2/S3/C3 USB-JTAG вшит в чип: кабель к штатному разъёму — уже отладочный шнур. Классическому ESP32 нужен внешний JTAG-адаптер, Uno и Nano — аппаратный программатор. ESP32-S3 по обычному USB-кабелю
- Что делает Step Over (F10)?
- Step Over — шаг по строкам поверх вызовов; Step Into залезает внутрь функции, Step Out оттуда возвращается. Заливка — Ctrl+U, к шагам отношения не имеет. шагает по строкам, не залезая внутрь функций
- Ядро минуту стоит на точке останова на ESP32. Что с millis()?
- Аппаратные таймеры не останавливаются: millis() после продолжения уходит вперёд на всё время паузы, и расписания на millis глотают такты. После сеанса плату перезапускаю. после Continue прыгает вперёд на время паузы
- Точку останова на классическом Uno IDE 2 не берёт. Почему?
- Канала в ядро у классики нет: debugWIRE требует аппаратного программатора и в списке плат IDE 2 не поддержан. На такие платы — Serial-методика или внешние инструменты. плате нужен внешний аппаратный отладчик, поддержки нет
- Условие точки останова нужно, чтобы…
- Условие превращает точку в фильтр: стоп случается на пороге вроде x > 900, а не на каждой итерации — иначе сотня нажатий Continue за вечер. остановиться только при нужном значении переменной