Simple Debugging for Arduino Sketches

Bug_TrackingDespite the advent of source level debuggers for Arduino code, one of the most accessible ways to debug Arduino projects is still the Serial.print() statement. It is how most beginners will start when trying to debug their code.

But what do you do with all the print() statements sprinkled through the code once your application is working?

You certainly don’t want to delete the debug after all the hard work putting it in – after all, you may need this again – and commenting it out everywhere can be a real pain.

A very simple technique is to use the C++ preprocessor to do the work for you. This is an obvious method for experienced programmers, but not always so for beginners.

Debug Macros

Somewhere in the headers for most of my Arduino sketches and libraries is this macro block:

// Debugging switches and macros
#define DEBUG 0 // Switch debug output on and off by 1 or 0

#if DEBUG
#define PRINTS(s)   { Serial.print(F(s)); }
#define PRINT(s,v)  { Serial.print(F(s)); Serial.print(v); }
#define PRINTX(s,v) { Serial.print(F(s)); Serial.print(F("0x")); Serial.print(v, HEX); }
#else
#define PRINTS(s)
#define PRINT(s,v)
#define PRINTX(s,v)
#endif

If the serial port is not used in the application, setup() needs to open the port specifically for debugging, otherwise no output will be visible on the serial monitor:

void setup(void)
{
#if DEBUG
 Serial.begin(57600);
#endif
}

Debugging output is enabled or disabled by setting DEBUG to 1 or 0.

If debugging is turned on, each statement is expanded into a block of code invoking a sequence of Serial.print() statements.

With debug turned off, the PRINT statements are expanded to ‘nothing’.  This frees the final code of all debugging statements, whilst still retaining all the debug source code in place.

The PRINT statements are built for convenience and can be expanded to suit your debugging style. They also automatically put the constant string in PROGMEM by applying the F() macro to these strings, minimizing the amount of RAM that is used to support debugging.

Usage

  • There is no need for PRINTLN – simply put in the “\n” as part of the string.
  • PRINTS prints a string, as in
 PRINTS("\nError check");
  • PRINT prints a string and a value. This is a very common requirement and greatly simplifies this operation.
PRINT("\nCurrent state:", state);
  • PRINTX is a variation on PRINT that outputs the value parameter in hexadecimal.
  • Specialized PRINT statements can also be created. For example, I often have specific ones for Finite State Machine tracing:
#define PRINT_STATE(f) { Serial.print(F("\n")); Serial.print(F(f)); Serial.print(F(" fsm "));

Limitations

As with anything there are some limitations I have found when using this technique:

  1. You cannot use it in interrupt routines. Serial.print() needs interrupts to work so is not compatible with use in an ISR.
  2. The string parameters to the PRINT statements need to be constants for the F() macro to work. This is not usually a problem, but it does cause compiler errors. A different PRINT statement can be created if required, but I have never found the need.
  3. Conditional statements can cause errors under certain circumstances. For example
if (condition)
  PRINT("Value: ", variable);
else
  value++;

will be expanded to have a ‘;’ before the else. The PRINT statement needs to be inside braces to make it a block of code. This error can be very confusing the first time you see it happen!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s