Go Down

Topic: Serial Debug Macro (Read 1 time) previous topic - next topic

tarek_taha

For debugging, it's handy to print debug messages to the serial port and track down problems. But if you want to trigger the debugging on and off it becomes annoying to comment, and uncomment those Serial.print and Serial.println statements. I wrote this macro that can help in triggering the debugging by simply changing the #define SERIAL_DEBUG_ENABLED  value to 0 or 1. The macro code is :
Code: [Select]

#define SERIAL_DEBUG_ENABLED 1

#define GET_NUM_ARGS(...) GET_NUM_ARGS_ACT(__VA_ARGS__, 5,4,3,2,1)
#define GET_NUM_ARGS_ACT(_1,_2,_3,_4,_5,N,...) N

#define macro_dispatcher(func, ...) \
            macro_dispatcher_(func, GET_NUM_ARGS(__VA_ARGS__))
#define macro_dispatcher_(func, nargs) \
            macro_dispatcher__(func, nargs)
#define macro_dispatcher__(func, nargs) \
            func ## nargs
           
#if SERIAL_DEBUG_ENABLED
  #define DebugPrint(...) macro_dispatcher(DebugPrint, __VA_ARGS__)(__VA_ARGS__)
  #define DebugPrintln(...) macro_dispatcher(DebugPrintln, __VA_ARGS__)(__VA_ARGS__) 
  #define DebugPrint2(str,modifier)  \
        Serial.print(millis());     \
        Serial.print(": ");    \
        Serial.print(__PRETTY_FUNCTION__); \
        Serial.print(' ');      \
        Serial.print(__LINE__);     \
        Serial.print(' ');      \
        Serial.print(str,modifier);
  #define DebugPrint1(str)  \
        Serial.print(millis());     \
        Serial.print(": ");    \
        Serial.print(__PRETTY_FUNCTION__); \
        Serial.print(' ');      \
        Serial.print(__LINE__);     \
        Serial.print(' ');      \
        Serial.print(str);       
  #define DebugPrintln2(str,modifier)  \
        Serial.print(millis());     \
        Serial.print(": ");    \
        Serial.print(__PRETTY_FUNCTION__); \
        Serial.print(' ');      \
        Serial.print(__LINE__);     \
        Serial.print(' ');      \
        Serial.println(str,modifier);
  #define DebugPrintln1(str)  \
        Serial.print(millis());     \
        Serial.print(": ");    \
        Serial.print(__PRETTY_FUNCTION__); \
        Serial.print(' ');      \
        Serial.print(__LINE__);     \
        Serial.print(' ');      \
        Serial.println(str);         
#else
  #define DebugPrint(...) macro_dispatcher(DebugPrint, __VA_ARGS__)(__VA_ARGS__)
  #define DebugPrintln(...) macro_dispatcher(DebugPrintln, __VA_ARGS__)(__VA_ARGS__) 
  #define DebugPrint1(str)
  #define DebugPrintln1(str)
  #define DebugPrint2(str,modifier)
  #define DebugPrintln2(str,modifier)
#endif


and in your code all you need to do is something like this:

Code: [Select]

DebugPrint("The value of x is:");
DebugPrintln(x,DEC);


I hope this helps, let me know if you have any suggestions.


Tarek

AWOL

Quote
let me know if you have any suggestions.

Use PROGMEM for the strings?

Code: [Select]
#if SERIAL_DEBUG_ENABLED
even simpler would be
Code: [Select]
#ifdef SERIAL_DEBUG_ENABLED
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

pH7_JP1

I really appreciated Tarek's posting, because it explained __VA_ARGS__ (to me) better than any other reference I found. The macros can be simplified a lot, so I thought I would post my solution, so anyone searching this topic would find this alternative. This accomplishes exactly the same thing as his more complex solution:

Code: [Select]
#ifdef SERIAL_DEBUG_ENABLED
  #define DebugPrint(...)  \
        Serial.print(millis());     \
        Serial.print(": ");    \
        Serial.print(__PRETTY_FUNCTION__); \
        Serial.print(' ');      \
        Serial.print(__LINE__);     \
        Serial.print(' ');      \
        Serial.print(__VA_ARGS__)
  #define DebugPrintln(...)  \
        Serial.print(millis());     \
        Serial.print(": ");    \
        Serial.print(__PRETTY_FUNCTION__); \
        Serial.print(' ');      \
        Serial.print(__LINE__);     \
        Serial.print(' ');      \
        Serial.println(__VA_ARGS__)
#else
  #define DebugPrint(...)
  #define DebugPrintln(...) 
#endif

I feel that the extra information clutters the Serial Monitor output a lot, so I use this even simpler version. Here is the complete header file:
Code: [Select]
/*
DebugUtils.h - Simple debugging utilities.
*/

#ifndef DEBUGUTILS_H
#define DEBUGUTILS_H

#ifdef DEBUG
  #define DEBUG_PRINT(...) Serial.print(__VA_ARGS__)
  #define DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
  #define DEBUG_PRINT(...)
  #define DEBUG_PRINTLN(...)
#endif

#endif


To use my simplified version, put the following line at the top of your code, to generate debug output:

#define DEBUG

or comment it out to compile without debug statements:

// #define DEBUG

Then just use DEBUG_PRINT or DEBUG_PRINTLN in place of Serial.print and Serial.println.

Go Up