serial.println() string problem

I made the following function to handle all my Serial.print() and Serial.println() calls. Basically, I wanted to control the ability of printing in a headless system by defining DBG (#define DBG) or not.

// * *************** dbug - print stuff ***************
//
void dbug(String stuff = "") {
  //Function to test for the dbg flag, and if defined, print the 'stuff'
#ifdef DBG
  Serial.print(stuff);
#endif
}
void dbugln(String stuff = "") {
  //Function to test for the dbg flag, and if defined, println the 'stuff'
#ifdef DBG
  Serial.println(stuff);
#endif
}

So, in my code, instead of testing the #ifdef DBG around every Serial.print() statement in the sketch - which quickly gets very messy- I on;y test once in the above function.

In other words:

#ifdef DBG
  Serial.print(stuff);
#endif

becomes, in the sketch:

dbug(stuff);

This works for almost all of my statements in the code but for one:

void setup_wifi() {
  delay(10);
  //Connect to a WiFi network
  dbugln();
  dbug("Connecting to ");
  dbugln(mySSID);

  //Connect to WiFi network so we can reach the MQTT broker and publish messages to topics.
  WiFi.mode(WIFI_STA);
  WiFi.begin(mySSID, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    dbug(".");
  }
  dbugln("");
  dbug("WiFi connected - ESP IP address: ");
  dbugln(String(WiFi.localIP()));
  Serial.println(WiFi.localIP());
}

In this case the problem is in the last dbugln(). It prints a string of numbers: "553756864". The Serial.println() following the last dbugln() statement correctly prints an IP address: "192.168.1.33". (The Serial.println() statement is just there to debug this issue).

So, there's my question. Why does Serial.println(WiFi.localIP()); work correctly, and dbugln(String(WiFi.localIP())); not, and what can I do to fix it?

Thanks

There's a better system that only requires adding two lines:

#define DEBUG true  //set to true for debug output, false for no debug ouput
#define Serial if(DEBUG)Serial

Add this to setup() if you are using a Leonardo, Pro Micro, or other ATmega32U4 based board and don't want the program to run until the Serial Monitor has been opened while debug output is enabled:while (DEBUG && !Serial);

This can easily be extended to allow multiple levels of debug output, still with no overhead:

#define DEBUG_ERROR true
#define DEBUG_ERROR_SERIAL if(DEBUG_ERROR)Serial

#define DEBUG_WARNING true
#define DEBUG_WARNING_SERIAL if(DEBUG_WARNING)Serial

#define DEBUG_INFORMATION true
#define DEBUG_INFORMATION_SERIAL if(DEBUG_INFORMATION)Serial

void setup() {
  Serial.begin(9600);
  while (!Serial);
  DEBUG_ERROR_SERIAL.println("This is an error message");
  DEBUG_WARNING_SERIAL.println("This is a warning message");
  DEBUG_INFORMATION_SERIAL.print("The state of pin 5 is ");
  DEBUG_INFORMATION_SERIAL.println(digitalRead(5) ? "HIGH" : "LOW");
  Serial.println("This is standard program output");
}

void loop() {}

That can also be useful if you use Serial in your non-debug code and only want to turn debug output on/off, not all Serial output.

You can use the Boolean debug macros to switch on and off other parts of your code too.

Wow, thanks, Pert. That is certainly a better way- which is not unusual in my programs.
Can you take a minute to explain what's happening behind the curtain, and why do I not get a compile error if Serial is not defined?

Thanks again,
Steve

Let's say you have this code:

#define DEBUG true  //set to true for debug output, false for no debug ouput
#define Serial if(DEBUG)Serial
void setup() {
  Serial.begin(9600);
  Serial.print("Hello World")';
}

The C++ preprocessor replaces every instance of Serial with if(DEBUG)Serial and every instance of DEBUG with true. So after the preprocessor is done, it looks like this:

void setup() {
  if(true)Serial.begin(9600);
  if(true)Serial.print("Hello World")';
}

The if statement will always evaluate as true so that code is the exact equivalent of this:

void setup() {
  Serial.begin(9600);
  Serial.print("Hello World")';
}

Now if you define DEBUG as false the preprocessed code looks like this:

void setup() {
  if(false)Serial.begin(9600);
  if(false)Serial.print("Hello World")';
}

The if statement will always evaluate as false so the compiler will just optimize those lines away:

void setup() {
}

Does that make sense now?

This debug system has no overhead and is so easy to retrofit into code that you wrote with serial output that was solely for debugging. If you have debug and non-debug output it's a little more work but it's still nice because it adds the minimum of extra lines of code.

  dbugln(String(WiFi.localIP()));

What class is WiFi an instance of? If it is an instance of the WiFiClass class, as defined in the Wifi.h file that ships with the IDE, then localIP() is defined:

    IPAddress localIP();

Now, IPAddress is a class that derives from Printable (whatever that is). Do you see an instance of the String class that takes an IPAddress as input? I didn't.

Was this not Valid?

dbugln(WiFi.localIP().toString());