Debugging with a Debug Probe: JTAG/SWD Basics
What a Debug Probe Actually Does
Beyond just flashing firmware (covered in the ST-Link flashing guide), a proper debug session lets you set breakpoints, step through code line by line, inspect variable values and memory in real time, and catch crashes at the exact instruction that caused them — genuinely different from log-based debugging, and often much faster for tracking down subtle bugs.
Hardware: Same ST-Link, Different Software Mode
The same ST-Link V2 probe used for flashing (see that guide for wiring) also handles live debugging — the difference is which software you connect with and how you use it, not different hardware.
Software Options
- OpenOCD + GDB: the traditional, fully command-line-driven approach — more setup friction but works everywhere and integrates with almost any editor via GDB's standard interface.
- STM32CubeIDE: ST's own IDE, free, with integrated debugging built on the same OpenOCD/GDB stack underneath but wrapped in a proper GUI — generally the easier on-ramp if you're not already comfortable with raw GDB command syntax.
- VS Code + Cortex-Debug extension: a good middle ground if you're already developing in VS Code — wraps OpenOCD/GDB with a proper visual debugging UI (breakpoints, variable inspection, call stack) inside your existing editor.
Basic OpenOCD + GDB Session
# Terminal 1: start OpenOCD, connecting to the ST-Link and target chip openocd -f interface/stlink.cfg -f target/stm32wbx.cfg # Terminal 2: connect GDB to OpenOCD's debug server arm-none-eabi-gdb your_firmware.elf (gdb) target remote localhost:3333 (gdb) monitor reset halt (gdb) break main (gdb) continueThis halts execution at main(), from which you can step through (next/step), inspect variables (print variable_name), and set additional breakpoints anywhere in your code.
Practical Debugging Workflow
- Build your firmware/app with debug symbols included (most build configs do this by default in dev builds — check you're not accidentally using a stripped release build).
- Set a breakpoint at the function where you suspect the issue lives, or at the very start if you're not sure yet.
- Step through, watching variable values change, until behavior diverges from what you expect.
- Once you find the divergence point, that's your actual bug location — often several steps upstream from where a crash or wrong-output symptom actually appeared.
When This Beats Log-Based Debugging
The CLI's log command (covered in the CLI guide) is faster to set up and fine for many bugs, but a proper debug probe session is the right tool when: the bug is intermittent/timing-sensitive (adding log statements can itself change timing enough to hide the bug — a classic "heisenbug"), or you need to inspect memory/register state at a precise point rather than whatever you thought to log ahead of time.
Related Guides
- How to Use the Flipper Zero GPIO for Hardware Hacking: UART, SPI, I2C, and Debugging
- Using Flipper Zero as a USB-to-UART Serial Bridge
- Flipper CLI Over USB: Serial Console, Commands, Debugging
- Flashing Firmware via ST-Link/SWD: Recovery & Custom Builds
- How to Use a Logic Analyzer for Digital Signal Debugging: Saleae, Sigrok, and Protocol Decoding
- Debugging ESP32 Firmware with JTAG: OpenOCD, GDB, and ESP-IDF
- How to Use an Oscilloscope for Electronics Projects: Complete Beginner Guide
- How to Install Custom Firmware and Develop Apps for the Flipper Zero