Arduino Debugger  1.0
A C++ Library to add debugging features via the Arduino IDE's Serial Moniter
Arduino Debugger

Introduction

The ArduinoDebugger library provides debugging functionality for the Arduino IDE via the Serial Monitor so that a programmer can view/update the state of variables and hardware pins at runtime. This document will detail how to:

  1. Create a debugger object
  2. Add breakpoints to your program
  3. Track your variables
  4. View/Update hardware pins

Importing the ArduinoDebugger Library

To use the ArduinoDebugger library, you'll need to add it to your Arduino IDE. In the Arduino IDE, select the "Sketch > Include Library > Add .ZIP Library ". A file browser will pop up. Navigate to where you saved the ArduinoDebugger.zip file and select it. Once added, you will now be able to include the ArduinoDebugger library to your program.

Arduino IDE


Arduino IDE Add Zip

Example Program

To start using the ArduinoDebugger, open the provided example "1_Basic_Setup". You can find it under "Files > Examples > ArduinoDebugger > 1_Basic_Setup".

Arduino IDE - File tab


Arduino IDE - Select Example

The code for the example is also included below:

//the intialize method determines if the debugger uses 8-bit or 32-bit registers
//and whether or not it can handle floats.
// intialize(usingFloats, is8Bit);
// Floats are generally not recommended because they use a lot of memory space.
Debugger debugger = ArduinoDebugger::initialize(NO_FLOATS, BITS_8);//Adafruit Circuit Playground Classic
//Debugger debugger = ArduinoDebugger::initialize(NO_FLOATS, BITS_32);//Adafruit Circuit Playground Express
byte d_pins[] = {LED_BUILTIN, 10, 9};//Subset of digital pins
byte a_pins[] = {A0, A1};//Subset of analog pins
byte counter = 0;
void setup() {
Serial.begin(9600);
while(!Serial) {}
pinMode(LED_BUILTIN, OUTPUT);
debugger.setSubsetPins(d_pins, 3, a_pins, 2);//Set subset of pins
debugger.add(&counter, BYTE, "counter");//Add to variable watch
}
void loop() {
if(counter %2 == 0)
{
digitalWrite(LED_BUILTIN, HIGH);
debugger.print("LED HIGH");//Displays information, no pausing
//debugger.breakpoint("LED HIGH");//Pauses & displays menu for updating variables & hardware pins
}
else
{
digitalWrite(LED_BUILTIN, LOW);
debugger.print("LED LOW");//Displays information, no pausing
//debugger.breakpoint("LED LOW");//Pauses & displays menu for updating variables & hardware pins
}
delay(500);
counter++;
}

Using Breakpoints

With the Debugger::breakpoint() method, a programmer can pause a running Arduino program and then view the current state of the system. This means the programmer can check or update the current value of variables and hardware pins via the Serial Monitor. Two versions of the breakpoint() method can be used:

debugger.breakpoint();//No parameter
debugger.breakpoint("Identifier Here");//List an identifier

The first version requires no parameters. This option is recommended if only 1 breakpoint is being used in the program.

Breakpoint Menu


If multiple breakpoints are used, it is necessary to know which one the program has stopped at. To help differentiate between breakpoints, the second version requires a char[] to identify the breakpoint. Here is an example using multiple breakpoints:

int state = digitalRead(10);
if(state == HIGH)
{
debugger.breakpoint("10 HIGH");
digitalWrite(13, HIGH);
}
else
{
debugger.breakpoint("10 LOW");
digitalWrite(13, LOW);
}
Breakpoint menu - HIGH


Breakpoint menu - LOW

Warning
The breakpoint method is not "timer safe". This means that timers, like those used for millis() or pwm, are still running while the program is "paused".

Adding Variables

The debugger cannot automatically track the variables in a program. Instead, they must be manually added to the debugger using the Debugger::add() method.

debugger.add(void* var_ptr, Type type, char var_name[]);

The add() method requires three parameters, which are as follows:

  • void* var_ptr (memory address) :
    The first parameter requires the memory address of the variable being added. Since we are working in C++, you can do this by adding an ampersand (&) in front of your variable's name (ex. &counter or &var_name). With the address, the debugger is able to view and update the variable.
  • Type type (enum) :
    The second parameter defines what type of data is being referenced by the variable. This is necessary for the debugger to properly display and update the variable. The second parameter must be one of the values for the enum Type provided by the ArduinoDebugger. The potential values are:

    Data primitives: BYTE, INT, LONG, FLOAT**, CHAR, BOOL
    Arrays: BYTE_ARRAY, INT_ARRAY, LONG_ARRAY, FLOAT_ARRAY**, CHAR_ARRAY, BOOL_ARRAY
    
    Warning
    Floats take up a large memory footprint, so they are generally discouraged when writing programs for Arduino microcontrollers. To include them in the debugger you must set the usingFloats boolean to true when initializing the debugger. Otherwise the debugger will not be able to display or update float variables.
  • char name[ ] :
    The last parameter is a character array containing the variable's name. For data primitives, you will just have to list the variable's name, ex. "counter", but if you are dealing with an array you'll need to also provide the size of the array. The format for array names is "name_SIZE". For example, the array int values[3] would list its name as "values_3".
Note
To preserve memory space, the debugger is limited to tracking up to 10 variables. This limit can be modified by updating var_watch[10], which is declared as a member in the Debugger.h file "Variable var_watch[10];"
Warning
The debugger is not aware of "variable scope". It just tracks the memory locations that have been added. To help prevent memory errors, you may need to use the Debugger::remove() method once a variable has left scope.

Modifying Hardware Pins

The debugger allows a programmer to view the current state of both Digital and Analog Pins. For Analog Pins, the debugger will display a value between 0-1023. Digital Pins have 3 potential states:

  • HIGH (Power In)
    • Receiving power in
  • HIGH
    • Providing power out
  • LOW
    • No Power

In addition to viewing Digital Pin states, the debugger allows the programmer to set a Digital Pin's state to either HIGH or LOW (via the Debugger::breakpoint() method).

Breakpoint Menu - Hardware Pins 1


Breakpoint Menu - Hardware Pins 2


Breakpoint Menu - Hardware Pins 3

Subset of Pins

Depending on the microcontroller, the full list of all Digital and Analog pins can be a bit overwhelming. It is possible to create a subset of pins to keep track of using the Debugger::setSubsetPins() method.

debugger.setSubsetPins(byte d_pins[], byte size_d, byte a_pins[], byte size_a);

The setSubsetPins method accepts 4 parameters which are as follows:

  • byte d_pins[] :
    d_pins contains a list of digital pins to keep track of
  • byte size_d :
    The number of pins listed in d_pins
  • byte a_pins[] :
    a_pins contains a list of analog pins to keep track of
  • byte size_a :
    The number of pins listed in d_pins
Note
The ArduinoDebugger can keep track of, at most, 10 digital pins and 10 analog pins. This limit is set when digital_pins and analog_pins were declared in Debugger.h.

Examples of setting subsets of pins:

byte d_pins = {3, 4, 5};
byte a_pins = {A0, A1};
//Include both digital and analog pins in the subset
debugger.setSubsetPins(d_pins, 3, a_pins, 2};
//Only have digital pins in the subset
debugger.setSubsetPins(d_pins, 3, {}, 0);
//Only have analog pins in the subset
debugger.setSubsetPins({}, 0, a_pins, 2);
Breakpoint Menu - Subset 1


Breakpoint Menu - Subset 2


Breakpoint Menu - Subset 3

Using Repeats

Breakpoints are great for viewing and updating variables and hardware pins, but pausing/continuing may make debugging more time consuming then necessary. To help display the state of the system in a human readable fashion, across multiple iterations, a programmer can instead use the Debugger::repeat() method.

The repeat method has a simple set of logic:

  1. Display debugging information
    • Variable values
    • Hardware pin states (subset of pins, if they've been set)
  2. Decrement repeat counter
  3. If counter has reached zero:
    • Reset counter
    • Pause program

To determine the number of times repeat() will be called before pausing, a programmer can add in a counter value (default is 3)

debugger.repeat(7);//7 iterations before pausing
debugger.repeat();//3 iterations before pausing (default)
//Example program using update
Debugger debugger = ArduinoDebugger::initialize(NO_FLOATS, BITS_8);//No Floats, uses 8Bit
byte counter = 0;
byte d_pins[] = {11, 12};
byte a_pins[] = {A0};
void setup() {
Serial.begin(9600);
while(!Serial) {}
debugger.setSubsetPins(d_pins, 2, a_pins, 1);//Set subset of pins
debugger.add(&counter, BYTE, "counter");//Add to variable watch
}
void loop() {
debugger.repeat();
counter++;
}
Repeat Output 1


Repeat Output 2

Only one call to repeat() is recommended per Arduino Program. If you want more than one repeat point, the Arduino Debugger provides 5 unique counters to work with. Calls to repeat() and repeat(byte value), use the default counter (id 0). To use the other counters, you will need to set call its specific id.

//debugger.repeat(byte id, byte value);
debugger.repeat(2, 4);//Set counter [2] to repeat 4 times

Example Program:

Debugger debugger = ArduinoDebugger::initialize(NO_FLOATS, BITS_8);//No Floats, uses 8Bit
byte counter = 0;
byte d_pins[] = {11, 12};
byte a_pins[] = {A0};
void setup() {
Serial.begin(9600);
while(!Serial) {}
debugger.setSubsetPins(d_pins, 2, a_pins, 1);//Set subset of pins
debugger.add(&counter, BYTE, "counter");//Add to variable watch
}
void loop() {
int i;
debugger.add(&i, INT, "i");
for(i = 0; i < 3; i++)
{
debugger.repeat(1, 3);//Set counter 1 to 3
}
debugger.repeat(2);//Set default counter (0) to 2
counter++;
}
Update Output 3


Update Output 4

Type
ArduinoDebugger::initialize
static Debugger initialize(bool usingFloat, bool is8Bit)
Constructor to determine if a debugger with or without float capabilities is initialized.
Definition: ArduinoDebugger.cpp:11
INT
Definition: Debugger.h:14
BYTE
Definition: Debugger.h:12
BITS_8
#define BITS_8
8 Bit microcontroller (true) A constant to set the debugger to work with 8 Bit microcontrollers
Definition: Debugger.h:69
Debugger::setSubsetPins
void setSubsetPins(byte digital_pins[], byte size_d, byte analog_pins[], byte size_a)
Set a subset of Digital/Analog pins to display to the user.
Definition: Debugger.cpp:305
Debugger::add
void add(void *var_ptr, Type type, char var_name[])
Add a variable to the debugger's watch list.
Definition: Debugger.cpp:127
Debugger
Definition: Debugger.h:72
Debugger::breakpoint
void breakpoint()
Set a breakpoint with no name.
Definition: Debugger.cpp:48
NO_FLOATS
#define NO_FLOATS
Definition: ArduinoDebugger.h:8
ArduinoDebugger.h
Debugger::repeat
void repeat()
Display the value of hardware pins and watched variables & update the default counter[0].
Definition: Debugger.cpp:1112
Debugger::print
void print()
Display the value of hardware pins and watched variables, only once.
Definition: Debugger.cpp:1196