Pages: [1]   Go Down
Author Topic: Toggling Debug Code  (Read 13292 times)
0 Members and 1 Guest are viewing this topic.
Cincinnati, OH
Online Online
God Member
*****
Karma: 42
Posts: 704
I'm not bossy...I just know what you should be doing.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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...
Logged

UK
Offline Offline
Faraday Member
**
Karma: 17
Posts: 2884
Gorm deficient
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You don't even need:
Code:
#define DEBUG 1
because:
Code:
#define DEBUG
will suffice with a "#ifdef"

Better yet:
Code:
#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"
« Last Edit: April 17, 2010, 10:21:06 am by GrooveFlotilla » Logged

Per Arduino ad Astra

Cincinnati, OH
Online Online
God Member
*****
Karma: 42
Posts: 704
I'm not bossy...I just know what you should be doing.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

UK
Offline Offline
Faraday Member
**
Karma: 17
Posts: 2884
Gorm deficient
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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-)
Logged

Per Arduino ad Astra

Cincinnati, OH
Online Online
God Member
*****
Karma: 42
Posts: 704
I'm not bossy...I just know what you should be doing.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

UK
Offline Offline
Faraday Member
**
Karma: 17
Posts: 2884
Gorm deficient
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, the original was over-simplistic:
Code:
#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.

« Last Edit: April 17, 2010, 11:27:08 am by GrooveFlotilla » Logged

Per Arduino ad Astra

Cincinnati, OH
Online Online
God Member
*****
Karma: 42
Posts: 704
I'm not bossy...I just know what you should be doing.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Nice!!
Logged

UK
Offline Offline
Faraday Member
**
Karma: 17
Posts: 2884
Gorm deficient
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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!
« Last Edit: April 17, 2010, 01:15:12 pm by GrooveFlotilla » Logged

Per Arduino ad Astra

0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

DebugUtils.h:
Code:
/*
 * 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
Logged

0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Slightly modified  version:

Code:
/*
 * 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
Logged

0
Offline Offline
God Member
*****
Karma: 2
Posts: 596
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
#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
Logged

Torino, Italy
Offline Offline
Sr. Member
****
Karma: 2
Posts: 309
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

DebugUtils.h:
Code:
/*
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:
#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:
//#define DEBUG
#include "DebugUtils.h"

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

can be reversed

Code:
#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? smiley
Logged

Rob Tillaart

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

Torino, Italy
Offline Offline
Sr. Member
****
Karma: 2
Posts: 309
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

:-) 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.
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 186
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Pages: [1]   Go Up
Jump to: