Go Down

Topic: Toggling Debug Code (Read 16097 times) previous topic - next topic

econjack

It may be that everyone here already knows this technique, but I'll risk a repeat here.

During development, we all use Serial.print() to help debug our code. When things seem stable, we go back and remove the debug code...only to find out a few days later that we need the debug code...again. My solution is to leave this "scaffolding code" in the source file, but toggle it out for the release version. To do this, add:

#define DEBUG 1

at the top of the source (sketch) file. Where you have debug code, surround it with another preprocessor directive:

#ifdef DEBUG
Serial.print("Some debug stuff follows");
// More debug code...
#endif

As long a DEBUG is define, the debug statements are compiled into the current sketch. However, changing the first line to:

//#define DEBUG 1

makes the preprocessor define for DEBUG disappear by commenting it out. Since it is no longer defined in the program, anything between the #ifdef and #endif is no longer part of the source file from the compiler's point of view. This makes it easy to add and remove debug code. On my most recent project, the code size was about 11.8K with the #define for DEBUG, and 8.2K when it was undefined (I seem to write a lot of buggy code).

I hope this might help some newbies...

Groove

#1
Apr 17, 2010, 05:17 pm Last Edit: Apr 17, 2010, 05:21 pm by GrooveFlotilla Reason: 1
You don't even need:
Code: [Select]
#define DEBUG 1 because:
Code: [Select]
#define DEBUG
will suffice with a "#ifdef"

Better yet:
Code: [Select]
#ifdef DEBUG
 #define DEBUG_PRINT(x)  Serial.println (x)
#else
 #define DEBUG_PRINT(x)
#endif


Then you can liberally scatter your code with "DEBUG_PRINT ("I think I'm here");",
all controlled by a simple "#define DEBUG"
Per Arduino ad Astra

econjack

Yep, that would work too. Question: Wouldn't this macro approach require the #ifdef-#else-#endif to surround every statement in a block of debug statements? If so, the macro approach would require more typing, right?

Groove

Quote
Wouldn't this macro approach require the #ifdef-#else-#endif to surround every statement in a block of debug statements?


No, all the if..else does is define or (more importantly) not define the DEBUG_PRINT.

The "DEBUG_PRINT("Now I'm here");" here stays in the code; if DEBUG is defined, it prints, if DEBUG isn't defined, the print simply isn't there.

Try it!   8-)
Per Arduino ad Astra

econjack

Okay...I see what you're saying now. However, suppose I have debug code like the following:

Serial.print("pressCounter = ");
Serial.print(pressCounter, DEC);

My solution looks like:

#ifdef DEBUG
Serial.print("pressCounter = ");
Serial.print(pressCounter, DEC);
#endif

What does your approach look like?

Groove

#5
Apr 17, 2010, 06:20 pm Last Edit: Apr 17, 2010, 06:27 pm by GrooveFlotilla Reason: 1
Well, the original was over-simplistic:
Code: [Select]
#ifdef DEBUG
 #define DEBUG_PRINT(x)     Serial.print (x)
 #define DEBUG_PRINTDEC(x)     Serial.print (x, DEC)
 #define DEBUG_PRINTLN(x)  Serial.println (x)
#else
 #define DEBUG_PRINT(x)
 #define DEBUG_PRINTDEC(x)
 #define DEBUG_PRINTLN(x)
#endif

would be a little closer, but in use

DEBUG_PRINT("pressCounter = ");
DEBUG_PRINTDEC(pressCounter);

is all that's needed.

I should point out that I've been using this approach (though not exactly these macros) for the last 15 years or so, in applications and systems software, from Linux device drivers to mobile phones.
The Linux macros were even more advanced, and allowed selective masking and unmasking of individual routines' debugs at run-time.

Per Arduino ad Astra

econjack


Groove

#7
Apr 17, 2010, 08:11 pm Last Edit: Apr 17, 2010, 08:15 pm by GrooveFlotilla Reason: 1
It's worth spending some time on a simple debug header file that you can use anywhere.
With multi-line macros (don't forget the backslash) you can do all sorts of stuff.
On a memory-limited Arduino, this may not be so useful, but you can print lots of useful info, so a simple:

DEBUG_PRINT ("I'm here");

could get printed as:

"my_program.c::myFn line 1451 @ 1121 milliseconds I'm here"

by using __FILE__, __FUNCTION__, __LINE__ and "millis" and a few prints.

Have fun!
Per Arduino ad Astra

mromani

FWIW, I've put together a macro for this.

DebugUtils.h:
Code: [Select]

/*
* Utility functions to help debugging running code.
*/

#ifndef DEBUGUTILS_H
#define DEBUGUTILS_H

#include <WProgram.h>

#define DEBUG_PRINT(str) \
   Serial.print(millis()); \
   Serial.print(": "); \
   Serial.print(__FUNCTION__); \
   Serial.print("() in "); \
   Serial.print(__FILE__); \
   Serial.print(':'); \
   Serial.print(__LINE__); \
   Serial.print(' '); \
   Serial.println(str);

#endif


Save this as sketchbook/libraries/DebugUtils/DebugUtils.h and restart Arduino IDE. You can then click on Tools -> Import library.

The __FILE__ constant doesn't print just the file name, but also the relative path to it. This is something that should be addressed, but I don't know how at the moment.

HTH

mromani

Slightly modified  version:

Code: [Select]

/*
* Utility functions to help debugging running code.
*/

#ifndef DEBUGUTILS_H
#define DEBUGUTILS_H


#define DEBUG_PRINT(str)        \
   Serial.print(millis());     \
   Serial.print(": ");         \
   Serial.print(__PRETTY_FUNCTION__); \
   Serial.print(' ');          \
   Serial.print(__FILE__);     \
   Serial.print(':');          \
   Serial.print(__LINE__);     \
   Serial.print(' ');          \
   Serial.println(str);

#endif

mromani

Here's a sketch that makes use of the macro in DebugUtils.h:

Code: [Select]

#include <DebugUtils.h>


long lastMillis;
long interval;


void setup() {
   Serial.begin(115200);
   
   interval = 1000;
   DEBUG_PRINT("interval");
   DEBUG_PRINT(interval);

   lastMillis = millis();
}


void loop() {
   if (millis() - lastMillis > interval) {
       lastMillis = millis();
       DEBUG_PRINT("one second");
   }
}



HTH

Fabio Varesano

Building on the above comments, here's my improved version which supports the DEBUG flag.

DebugUtils.h:
Code: [Select]

/*
DebugUtils.h - Simple debugging utilities.
Copyright (C) 2011 Fabio Varesano <fabio at varesano dot net>

Ideas taken from:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1271517197

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#ifndef DEBUGUTILS_H
#define DEBUGUTILS_H

#include <WProgram.h>

#ifdef DEBUG
#define DEBUG_PRINT(str)    \
   Serial.print(millis());     \
   Serial.print(": ");    \
   Serial.print(__PRETTY_FUNCTION__); \
   Serial.print(' ');      \
   Serial.print(__FILE__);     \
   Serial.print(':');      \
   Serial.print(__LINE__);     \
   Serial.print(' ');      \
   Serial.println(str);
#else
#define DEBUG_PRINT(str)
#endif

#endif



In your code, you'll have to insert

Code: [Select]
#define DEBUG
#include "DebugUtils.h"


Then simply use DEBUG_PRINT("message"); to print your debugging message.

Or if you don't want the debug messages to appear simply comment out the DEBUG definition

Code: [Select]
//#define DEBUG
#include "DebugUtils.h"


And you won't need to comment out any DEBUG_PRINT calls.

robtillaart

@Fabio,
the lines
Code: [Select]
#include <WProgram.h>
#ifdef DEBUG


can be reversed

Code: [Select]
#ifdef DEBUG
#include <WProgram.h>

as there is noneed to include WProgram.h either

By the way your copyright notice is a year ahead or did I miss newyear? :)
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Fabio Varesano

:-) yeah.. that file became part of a project I'm working on which won't be public until the next year. That's why my text editor added that lines. I wouldn't claim any copyright on something I just copy and pasted from here.

Anyway, thanks for your suggestion.

arbarnhart

I have something I can describe but not post (code ownership issues). I have functions to do diagnostic printing and I sprinkle them all over the place and leave them there.  I monitor the serial port for a command to turn diagnostics on. I also have a command that can be received via xBee on a different serial port (on a MEGA) to turn them on. When turned on, they begin printing to the port they were enabled from. So I might be running code and wonder why it is acting oddly, run a terminal emulator connected to a USB explorer with an xBee and send the Arduino a command to start giving me diagnostic output. A major advantage to this approach is that I can enable diagnostics after I get it in some odd state. I also have a verbose switch, so some o my diagnostics I change to only show when in verbose mode. I also implemented some general purpose diagnostic commands to read or set any pin or get current values from some of my modules. It took some work to get this operational and it pretty much requires a MEGA as the development board, but it makes life so much easier.

Go Up