В прошлой статье я сделал обзор популярных способов отладки embedded: GDB-клиент -> GDB-сервер (OpenOCD) -> программатор -> целевой МК. Тогда я сформулировал свои субъективные требования к “идеальному” программатору. И на тот момент у меня уже был минимально рабочий вариант такого программатора. И вот спешу представить к чему привели меня изыскания в этом направлении. Читать далее
Уровень сложностиСредний
Время на прочтение6 мин
Охват и читатели5.9K
В прошлой статье я сделал обзор популярных способов отладки embedded: GDB-клиент -> GDB-сервер (OpenOCD) -> программатор -> целевой МК. Тогда я сформулировал свои субъективные требования к “идеальному” программатору. И на тот момент у меня уже был минимально рабочий вариант такого программатора. И вот спешу представить к чему привели меня изыскания в этом направлении.
Немного контекстаВ прошлой статье я закончил списком того, чего хочу от отладочного инструмента:
Отлаживать устройства удаленно
Не запускать gdb-серверы руками
Не настраивать их и не писать конфигурационные файлы
Не ставить драйвера
Использовать кроссплатформенную легковесную IDE
Поддержка широкого спектра целей: ARM Cortex-M/A/R, RISC-V
Black Magic Probe закрывает почти все пункты: gdb-сервер уже внутри прошивки программатора, OpenOCD не нужен, драйвера не нужны. Да, в комметариях прошлой статьи мне написали что список целей нужно вшивать и они могу не поместиться. В ESP32 не поместиться? В общем поместились ;) Ну и по поводу IDE, можно использовать любую. Это слишком простые интерфейсы чтобы было что-то сложное. Беспроводное соединение устанавливается через TCP, которое поддерживает GDB, а проводное - обычный VCP который gdb тоже поддерживает (но об этом чуть позже).
В процессе разработки список хотелок только расширялся.
Тут и возможность отладиться по проводу (почему бы и нет)
И поддержка RTT логирования по тому же TCP (черерз telnet или nc) или VCP
Еще хочу serial чтобы подключиться к цели в uart и видеть это через telnet или nc. Тоже через TCP или VCP
Если из веб инетрфейса нельзя закинуть прошивку Drag & Drop чтобы даже кнопок нажимать не пришлось, то зачем это все вообще делать? [Сарказм] Камень определяется автоматически и выставлен дефолтный адрес который можно поменять на нужный.
Стереть тоже надо одним кликом
Запоминать wifi точки доступа чтобы удобно было везде
А если из известных вокруг ни одной не оказалось то расшарить свою с дефолтными кредами. Можно остаться в ней, но интернета не будет, а можно добавить нужную и ребутнуть.
Если бы я все это сделал и искал каждый раз ip устройства, то можно было бы удалить этот проект ибо смысла во всем этом нет. Так что добавил mDNS. Так что как только устройство подключилось к сети, то в этой сети можно зайти на страничку по имени blackmagic.local. Это же имя используется в telnet.
Прошивка поднимает на ESP32-C5 четыре сетевых сервиса:
Порт | Сервис | Назначение |
|---|---|---|
80 | HTTP | Веб-интерфейс: OTA, Wi-Fi, конфигурация пинов |
2345 | GDB remote serial protocol | GDB-сервер - основной сервис |
2346 | RTT console | Segger RTT-консоль цели (telnet/nc) |
2347 | Target serial | UART-мост к целевой плате (telnet/nc) |
Дальше я опишу как что-либо делать через GDB терминал, это может быть избыточно, я же себе жизнь наоборт упростить хотел. Так что если у вас IDE то она все сделает за вас.
GDB-сервер (порт 2345)Сердце проекта. Подключение из GDB выглядит так:
(gdb) target extended-remote blackmagic.local:2345
(gdb) monitor swdp_scan
(gdb) attach 1
То есть ровно те же команды monitor swdp_scan / attach 1, что и у классического BMP, только вместо /dev/ttyACM0 - host:port. Никакого OpenOCD, никаких конфигов.
Имя blackmagic.local можно использоавть и здесь и при подключении к RTT и т.д.
swdp_scan - это SWD. JTAG тоже поддерживается: у цели ESP32-C5 есть и SWD, и JTAG-режимы, все линии (TMS/SWDIO, TCK/SWCLK, TDI, TDO/TRACESWO, TRST) разведены на GPIO и переназначаются на лету через веб-интерфейс.
RTT (Real-Time Transfer) от Segger - способ вывода логов из цели в реальном времени через отладочный интерфейс, почти без нагрузки на CPU.
$ nc blackmagic.local 2346
или через telnet. Внутри GDB управление такое:
(gdb) monitor rtt enable
(gdb) monitor rtt status
Target serial (порт 2347)UART-мост: то, что целевая плата пишет в свой UART, доступно по TCP на порту 2347. Удобно, когда у цели есть лог в UART, а ты физически рядом не стоишь.
Честное признание: порт сейчас конфликтует с логами самой ESP32 (которые тоже валятся в UART0), так что сервис в статусе «работает, но с оговорками». Думаю что в релизной сборке логи не нужны.
Веб-интерфейс (порт 80)Тут три страницы:
1. OTA-прошивка. Просто перетаскиваешь .bin файл в браузер - и плата перепрошивается. Drag & drop, ничего кроме браузера не нужно. Если на телефоне есть бинарник, то и с телефона можно прошить.
2. Настройки Wi-Fi. Список SSID и пароль точкек доступа, куда плата должна подключаться. Сохраняется в NVS и перживает перезагрузку.
3. Конфигурация пинов. Какие GPIO платы отвечают за SWDIO/SWCLK/TDI/TDO/TRST. Тоже в NVS, тоже переживает перезагрузку. Меняешь на лету, без перепрошивки. Это я сделал на всякий случай если поменяется разводка платы.
Первый запускСборка - стандартный ESP-IDF:
idf.py set-target esp32c5
idf.py build
idf.py flash monitor
Фронтенд собирается автоматически через npm install && npm run build - CMake это делает сам, руками ничего запускать не надо.
После первой прошивки:
Плата пытается подключиться к сохраненному Wi-Fi
Если не получилось - поднимает свою точку доступа с SSID blackmagic и паролем blackmagic
Подключаешься к этой точке, открываешь blackmagic.local или http://192.168.4.1, вводишь SSID/пароль своей сети
Плата перезагружается и подключается к твоему Wi-Fi
Дальше она доступна как blackmagic.local
Wi-Fi - это основная фича, но провод тоже поддерживается. Прошивка умеет работать через USB-Serial-JTAG - встроенный в ESP32-Cx периферийный блок, который торчит в системе как CDC-устройство:
(gdb) target extended-remote /dev/ttyACM0 # Linux
(gdb) target extended-remote /dev/cu.usbmodem11101 # macOS
При этом логи/консоль остаются на UART0 (отдельные пины), чтобы USB-периферия была полностью отдана под GDB и не делилась с консолью. Все фичи (monitor swdp_scan, attach, RTT) работают и по USB, и по сути - код ядра BMP один и тот же, отличается только транспорт.
Собрать без USB-GDB тоже можно: в CMakeLists.txt есть флаги:
ENABLE_USB_GDB - включает в сборку отладку по USB
ENABLE_UART_SERIAL - пока оставил отдельное включение serial поскольку нужен лог от esp
ENABLE_RTT - этот флаг предоставляет сам BMP для включения RTT
Тут будет самая «техническая» часть статьи - те нюансы, которые не описаны в документации и которые пришлось выяснять экспериментально.
1. Раздел PARTITION_TABLE_SINGLE_APP_LARGE. Прошивка с BMP-ядром + Wi-Fi + HTTP-сервером + фронтендом не влезает в дефолтный 1 МБ app-раздел. Пришлось использовать “большой” раздел на 3 МБ.
2. WHOLE_ARCHIVE для компонента esp32-platform. В проекте есть target_probe.c со слабыми (weak) заглушками функций-зондов, а реальные реализации - в отдельных файлах. По умолчанию линкер выкидывает «неиспользуемые» объектные файлы, и сильные символы из отдельных файлов не переопределяют слабые заглушки - probe-функции просто не вызываются, отладчик не видит цели. WHOLE_ARCHIVE заставляет линкер взять весь архив целиком, и сильные символы побеждают. Не совсем понимаю почему это так работает, но это так.
3. Отключенные watchdog’и. Операции прошивки и отладки цели (flash erase, jtag-транзакции, длинные последовательности) могут превышать таймауты ESP_INT_WDT и ESP_TASK_WDT - система ребутается посреди прошивки цели. Поэтому в sdkconfig.defaults оба watchdog’а выключены:
CONFIG_ESP_INT_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
4. Консоль на UART0:
CONFIG_ESP_CONSOLE_UART_DEFAULT=y
CONFIG_ESP_CONSOLE_SECONDARY_NONE=y
чтобы USB-Serial-JTAG не перехватывал консоль и оставался целиком под GDB.
Все это уже в sdkconfig.defaults в репозитории - руками ничего настраивать не надо.
Ядро прошивки - это оригинальный Black Magic Probe (благодаря blackmagic сообществу), поэтому поддерживается весь спектр целей, которые умеет BMP: ARM Cortex-M, Cortex-A, Cortex-R и RISC-V, по SWD и по JTAG.
Распиновка по умолчанию (для ESP32-C5):
Сигнал | GPIO |
|---|---|
TMS / SWDIO | 26 |
TCK / SWCLK | 25 |
TDI | 23 |
TDO / TRACESWO | 24 |
TRST | 27 |
Как я уже говорил, переназначается через веб-интерфейс и сохраняется в NVS.
VS Code без конфиговБез настройки IDE не обойтись в любом случае. Я пользуюсь VS Code и тут сложностей нет потому что сообщество cortex-debug поддерживает BMP и конфиг написать достаточно просто. Я создал шаблон конфигурации: bm-vscode-configs - репозиторий с готовым launch.json для Cortex-Debug, включая RTT. Копируешь в свой проект, там будет шаблон файла settings.json в котором нужно прописать путь до .elf, путь до .svd если надо, и саму цель. Без этого минимума никак. Можно запускать отладку.
Цена решения - стоимость чипа ESP32-C5. Проект открыт: blackmagic-esp32-c5. Никакого дополнительного железа. Все мои хотелки закрыты.
Я конечно не сомневался, но многие сомневались в качестве беспроводного соединения. Мол все будет отваливаться и т.п. Возможно, мне повезло, возможно, я ни разу не столкнулся с плохой сетью в 2026 году, но у меня ни разу отладка так и не отвалилась просто так.
Плата: всё в одном USBПрошивка работает на голом модуле ESP32-C5, но чтобы пользоваться всеми описанными преимуществами связанными с проводной отладкой я развёл плату, которая собирает в себе всё, что нужно для отладки, и заводит это в один USB-разъём - хаб на борту.
Что на плате:
Устройство | Что даёт | Как видно в системе |
|---|---|---|
ESP32-C5 (USB-Serial-JTAG) | Прошивка esp и GDB-сервер | JTAG и CDC-устройство, |
USB-UART #1 | UART цели | второй CDC |
USB-UART #2 | RTT цели | третий CDC |

Плата
То есть не нужно три отдельных кабеля и три отдельных переходника. Воткнул один провод - получил и беспроводной BMP, и проводной GDB, и оба UART цели. Порядок /dev/ttyACM* при этом стабильный, потому что устройства на одном хабе и не переподключаются.
Wi-Fi - основная фича, но провод остаётся нужен:
первый запуск чтобы прошить esp32;
банально - питание если у цели его нет;
если по каким-то причинам wifi полосы забиты и соединение нестабильно (у меня пока такого не было);
любые другие причины по которым нужно подключиться по проводу.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Разработка аппаратной платформы для соревнований по Embedded программированию | 0 | 7.62 | 20-05-2026 |
| 2 | Смотрим ILA без программатора на Zynq SoC | 0 | 6.85 | 06-10-2026 |
| 3 | Подножка для AI в виде UTF-8 | 0 | 10.07 | 14-10-2025 |
| 4 | Путь к миллиону точек: как я переписывал плоттер три раза, прежде чем он перестал лагать | 2 | 6 | 22-06-2026 |
| 5 | IllegalMonitorStateException. Красим код в поисках монитора | 0 | 12.6 | 29-07-2026 |
| 6 | Как мы ускоряли прошивку ESP (спойлер: совы не то, чем кажутся) | 0 | 9.66 | 23-07-2026 |
| 7 | Как мы ускоряли прошивку ESP (спойлер: совы не то, чем кажутся) | 0 | 9.66 | 23-07-2026 |
| 8 | Проект eMMC-emu | 0 | 8.99 | 06-08-2026 |
| 9 | Формула «идеального enterprise» для open-source | 0 | 18.47 | 12-08-2026 |