Arduino IDE Debugger Quick-Start Guide
2026-09-02 | By Maker.io Staff
As programs grow larger, bugs naturally start to sneak in. In embedded projects, tracking them down can get frustrating, especially when debug output statements are the only tool available. This is where debugging comes in, as it allows you to pause the program and step through the code directly on the microcontroller. Read on to learn more about the process of debugging and how to set up and get started with the Arduino IDE’s built-in debugger.

What is Debugging?
Bugs in software are faults in code that cause incorrect or unexpected behavior. Debugging is the process of finding these problems, understanding their cause, and removing them from the code. This process is a vital part of software development that directly impacts functionality, quality, and maintainability. But even without the presence of bugs, debugging can be valuable for understanding more complex programs written by other developers.
In simpler Arduino projects, developers sometimes use the serial console to see which parts of the code are executed or to inspect variable values while the program is running. However, that approach quickly becomes tedious in larger sketches. This is where a debugger helps developers inspect what a program is doing internally. This tool allows pausing the program execution, stepping through the code, inspecting variables and function calls, and sometimes even modifying values while the sketch is running.
On desktop systems, debugging usually only requires a software debugger that’s integrated into the IDE. Embedded development, however, often relies on additional hardware that acts as the communication bridge between the computer and the microcontroller. These hardware debuggers commonly use protocols such as JTAG or SWD to communicate with the MCU during debugging.
Debugging Support in Arduino IDE 2
The Arduino IDE includes a built-in debugger starting with version 2.3. It officially supports many popular SAMD-based Arduino boards, including the MKR WiFi 1010, Nano 33 IoT, and Arduino Zero. However, all boards except the Arduino Zero require an additional SWD hardware debugger, and the Arduino IDE only officially supports the Segger J-Link and Atmel-ICE. Other probes and boards may work as well, although official support is limited.
Setting Up The Arduino IDE for Debugging
Before getting started with debugging, you have to install the SAMD board support package through the Arduino IDE. Open the boards manager panel, search for SAMD, and select the official Arduino package from the results:
This screenshot explains how to install the SAMD board support files in the Arduino IDE.
Next, it’s recommended to turn off certain compiler optimizations to prevent the compiler from removing or rearranging parts of the code. Normally, these optimizations help reduce binary size and improve performance. During debugging, however, they can make the program behave differently from the source code shown in the IDE, resulting in the debugger skipping code or variables behaving unexpectedly. To prevent this from happening, choose “Optimize for Debugging” in the “Sketch” menu:
This image illustrates how to turn off certain compiler optimizations that could interfere with debugging in the Arduino IDE.
Don’t forget to turn off debugging optimizations again after debugging to restore normal performance and binary size.
Setting Up the Arduino Zero for Debugging
The Arduino Zero has on-board SWD and JTAG debugging support, and no additional steps are required to set it up for debugging in the Arduino IDE. However, only the bottom USB port can be used for debugging the board:
This image shows the various debugging headers for external probes and the USB port location on the Arduino Zero.
Although not strictly required, it’s also recommended to select “Atmel EDBG” in the IDE’s programmer settings when using the Arduino Zero:
Select the Atmel EDBG programmer option when using the Arduino Zero.
The Zero is ready for debugging once it appears in the list of available devices, the connection string shows that the programming port is selected, and the debug play button becomes available:
This image shows the Arduino IDE interface once the Arduino Zero is set up for debugging.
Using Segger J-Link in the Arduino IDE
External debugging probes require a more involved setup than the Arduino Zero’s on-board debugger. Start by finding the SWD pins on the development board and solder wires to the data, clock, ground, and reset pads:
Solder four wires to the board’s SWD pins.
Note that you can also add a pin header to the Arduino board if you want to easily remove and reconnect the wires later. Either way, connect the other end of the jumper wires to the debugging probe’s input pins. Then, download and install the official J-Link GDB Server software.
Once the installation completes, open the Arduino IDE, select the board from the list of available devices, and then choose “Segger J-Link” in the IDE’s programmer settings:
Select the option shown in this screenshot to debug with a Segger J-Link probe.
Next, create a custom debugging configuration file in the same folder as the sketch you want to debug. Name that file debug_custom.json, and add the following contents:
[
{
"configId": "XXXXXXXXX:programmer=jlink",
"server": "jlink",
"servertype": "jlink",
"device": "ATSAMD21G18",
"interface": "SWD",
"serverPath": "YYYYYYYY",
"serverpath": "YYYYYYYY"
}
]
Make sure to replace XXXXXXXXX and YYYYYYYY with the values that match your setup. You can find the configId for your specific board by turning on verbose compiler logs in the Arduino IDE settings:
Turn on verbose compilation logs in the Arduino IDE settings.
Then, compile the sketch (without uploading it to the board), and scroll up to the first line of the compilation log output to see the board identifier string:
The verbose logs contain the board identifier. In this example, it is arduino:samd:arduino_zero_edbg
You will likely also need to adjust the serverPath and serverpath (note the difference in capitalization) keys in the custom settings JSON file. Both have to point to the JLinkGDBServer executable that was installed in a previous step. Make sure to enclose the path in double quotation marks. For example, on Windows:
"serverPath": "C:/Program Files/SEGGER/JLink/JLinkGDBServerCL", "serverpath": "C:/Program Files/SEGGER/JLink/JLinkGDBServerCL"
Save the JSON file and return to the Arduino IDE. The debug play button to the right of the upload button should be active. Click the button and wait for the GDB Server debugging window to open. Keep this window open during debugging:
A debug window like the one shown in this screenshot will open once everything is set up correctly. Image courtesy of Arduino.
Debugging With the Arduino IDE
Once set up, the Arduino IDE is ready for debugging. Consider the following example sketch from this article that doesn’t behave as one would expect:
int brightness = 90;
void setup() {
Serial.begin(9600);
}
void loop() {
constrainBrightness(brightness++);
Serial.print("Brightness: ");
Serial.println(brightness);
delay(250);
}
void constrainBrightness(float brightness) {
brightness = constrain(brightness, 0, 100);
}
At first glance, it might not be obvious where the bug is hiding, which is when debugging becomes valuable. The debugger can pause the program at certain points, called breakpoints, which lets developers step through the code and observe which instructions it executes and how variables behave.
Before doing any debugging, however, make sure to always recompile the program and upload it so that the binary doesn’t go out of sync with the code in the editor. Otherwise, the debugger might miss breakpoints or incorrectly show which line of code is active:
The debug console shows a warning when the source code is out of sync with the binary on the development board.
Once the upload finishes, you can add breakpoints to the code by clicking next to the line number where you want the debugger to stop. A filled-in red circle on a line means that the breakpoint is active. A gray circle indicates that a breakpoint is set but not active. You can remove breakpoints by clicking the circle next to the line number. Right-clicking a breakpoint shows additional options, such as one for activating or deactivating the breakpoint. However, you can also turn breakpoints on or off in the IDE’s debug panel:
This image illustrates how to manage breakpoints in the Arduino IDE.
Set a breakpoint on lines eight and fifteen of the code snippet from before, and make sure that both are active. Then, click the debug button in the Arduino IDE to start debugging. The code will run until the debugger hits the first breakpoint in the loop method:
This image shows the Arduino IDE with the debugger stopped at the breakpoint in line eight.
The Arduino IDE highlights the line where the debugger paused in yellow, and the debug control buttons become active. These buttons let you reset the device, continue program execution, or stop the debugging process.
The most important debug controls are Continue, Step over, Step in, and Step out. Continue resumes the program until another breakpoint is hit. Step over executes the current line of code and then pauses again at the next line. When stepping over a function call, the debugger executes the function without entering it. Step in follows function calls, allowing you to investigate a function’s internal behavior line by line. Step out resumes execution until the current function returns.
This image illustrates the operation of the different debugging controls.
Tracking Variable Values With the Arduino IDE Debugger
Breakpoints alone did not help find the bug in this program, as the program flow is exactly as expected. The loop calls the helper function, which executes exactly one command before returning. However, the brightness still exceeds 100, which is not what the program should do. So why are updates to the brightness variable lost?
Adding a variable watcher in the Arduino IDE can help shed light on the issue. These watchers let you observe and track values, such as the variable values, across the program. Each time the debugger stops, the IDE shows the current value. Click the plus icon in the Watch category in the debugging panel to track a new expression:
Use the highlighted button to track a value during debugging.
Then, enter the expression to track. In this example, the expression is just brightness:
Enter the expression to watch, as shown in this screenshot.
Then, continue stepping through the program and observe how the brightness variable changes, especially before and after the constrainBrightness call. If the debugger keeps skipping the function despite optimization settings, try adding a console log call inside it to track the value.
This screenshot shows that the watch section contains a new entry.
Watching the variable helps verify that its value is correctly constrained between zero and 100 within the constrainBrightness function. However, as soon as that function returns, the variable reverts to the previous state in loop, before getting incremented again in line eight. Debugging helped us reveal two problems: First, updates inside the helper function only affect its local brightness parameter rather than the global brightness variable, meaning the modified value is lost once the function returns. Second, the postfix increment operator (brightness++) updates the variable after the function call, not before it. As a result, even if the helper function constrained the global variable correctly, the postfix increment would still add one to the constrained result, causing the maximum value to become 101 instead of 100.
There are various ways to fix this bug, but the easiest approach is to remove the parameter and update the brightness in the loop before calling the helper function:
This screenshot demonstrates one way of fixing the code from before. The output shows that the variable no longer exceeds 100.
Common Arduino IDE Debugging Problems
Luckily, most common issues that prevent the Arduino IDE debugger from working are relatively easy to resolve. Connectivity problems are often caused by incompatible boards or debug probes, incorrectly connected SWD lines, or using the wrong USB port on the Arduino Zero.
If the debugger skips lines or fails to hit breakpoints, ensure that the binary and source code are in sync by recompiling and re-uploading the sketch. Also, enable the “Optimize for Debugging” option to disable certain compiler optimizations that may remove or rearrange parts of the code.
Lastly, uploading code while a debugging session is still active can result in failed uploads. Stop the debugger before uploading a new sketch and restart it afterward if necessary.
Summary
Debugging is the process of inspecting a running program to better understand its behavior and find bugs. Instead of relying on serial output statements, a debugger allows pausing the program at specific points called breakpoints, stepping through the code line by line, inspecting variables and function calls, and observing how values change while the program runs on the microcontroller.
The Arduino IDE features built-in debugging support for many SAMD-based boards starting with version 2.3. While the Arduino Zero includes on-board debugging hardware, most other supported boards require an external SWD debugger, such as a Segger J-Link or Atmel-ICE. Once the IDE and hardware are configured correctly, debugging largely boils down to setting breakpoints and stepping through the code.
When debugging with the Arduino IDE, ensure that the uploaded binary matches the source code currently open in the editor, as mismatches can cause breakpoints to not function correctly or lines to appear skipped. It’s also recommended to enable the “Optimize for Debugging” option while debugging, because certain compiler optimizations may rearrange or remove parts of the code and make the debugger behave unexpectedly. Lastly, always stop the debugger before uploading code when uploads fail.

