Tip: Easier debug log toggling

Hi, I'm new on this forum and don't really know where this message belongs, but this folder seemed most appropriate.

Many of us use Serial.print() to debug our code and when everything is running fine we want to disable it. People have come up with various #ifdef DEBUG approaches as some mentioned here : Arduino Forum.
But these all seem tedious and some even go to the extend of creating a library for this which adds more memory constraints. And they don't toggle the Serial.begin() you have in your setup().

I've come up with a much easier way of working. Add the following at the top of your sketch:

#include "Arduino.h"   // replace this by "WProgram.h" when your IDE is older then 1.0
 
#define DEBUG true // flag to turn on/off debugging
#define Serial if(DEBUG)Serial

Advantages over the #ifdef DEBUG approaches:

  • Now you can keep using Serial.print() statements. So no code changes needed.
  • It also turns on/off the Serial.begin() statement.
  • Because Serial.begin() is also avoided this means that finally no Serial code will be referenced and compiled any more. This is a big benefit for SRAM tight sketches. Avoiding a Serial.begin() as well saves 922 bytes in sketch size and 179 bytes SRAM usage!! :astonished:

(measurements done on Arduino0022 with the Blink sketch with default rx_buffer size (Arduino Hacks | Arduino Tips, Tricks, and Techniques | Adafruit Learning System) and SRAM measured as described at Arduino RAM Overflow.

Note that the include line on top is needed before the defines otherwise you get a compilation error.

Nice approach!

In Cosa, an object-oriented platform for programming the Arduino, I use a traditional Linux, syslog, approach with macros that are redefined depending on the symbol NDEBUG (not debug).

https://github.com/mikaelpatel/Cosa/blob/master/Cosa/Trace.hh

There is a special trace output stream which is used for debugging. As you mention reducing the amount of SRAM consumed by output strings is important. All string in the above Cosa trace are in program memory, i.e., use PSTR() and the _P version of functions.

There is also support for maskable trace levels. The syslog style trace log message are always with the function name, file line number and level as prefix. This helps quickly locate the statement that generated the output.

Most fun is the macro TRACE(expr) which will print the expression and the result.

These are all common tricks you can play with the C/C++ preprocessor and macro symbols.

Cheers!

Off topic:
I'm still quite new to Arduino but are people really using (object oriented) layers on top of the Arduino lingo?
I find that once you're working on a serious project you quickly need to optimize and tweak very low level core and library things (like the rx_buffer) to make sure your project fits in the atmega, both memory wise and io wise. Adding layers on top of Arduino would have as purpose to abstract Arduino technicalities. So this would thus complicate lowe level tweaking.
Yet, it's the serious projects that would benefit most from those more programmer-friendly layers...
So nice paradox you've got there :roll_eyes:

I'm for instance working on a project involving 12 tlc5940's, a max7219, interrupt based IR, an RTC, 1-wire temperature sensor and some buttons.... Any tweak you can do then on interrupts, timers and memory used by yourself, by core arduino and by the libraries you use is then not only nice but vital.

Cosa totally replaces the Arduino/Wiring function library with a smaller, more compact and many times faster pure C++ implementation. Cosa uses a lot of techniques that allows the compiler do more optimizations than possible in a C-function library.

I would argue that done correctly you can reduce the memory footprint of a program more with C++ than C. This is due to virtual methods and reuse of code in a somewhat different approach. Take for instance the Arduino/Wiring function digitalRead(). Cosa uses an object-oriented approach and the function is reduces to only a few inlined instructions. Actually not much larger than the call to digitalRead(). The performance gain is X4-X10 not just a few procent. Where Arduino can do a software SPI write at less than 60 Kbps. Cosa can achieve approx. 250 kbps without assembly code.

But all this is somewhat of an Occams razor as going to far with object-orientation will end up with more code and more run-time memory requirements. This is the major experience from large scale usage of C++ in the late 90-ies.

Most important is maybe using name spaces and strong data typing if we are to build larger systems by integrating work from many sources. Arduino code has a long way here. It is cluttered with #defines and variables in global space.

Anyway you are welcome to test Cosa and see what you think. GitHub - mikaelpatel/Cosa: An Object-Oriented Platform for Arduino/AVR

Cheers!

BW: Cosa is a very rich set of classes and not just a replacement for Wiring/Arduino. Also AVR GCC-lib, Aduino and Cosa can co-exist. This allows stepwise migration and more libraries to select from.

roeln:

#include "Arduino.h"   // replace this by "WProgram.h" when your IDE is older then 1.0

#define DEBUG true // flag to turn on/off debugging
#define Serial if(DEBUG)Serial

Thank you very much! This is beautiful and should be part of official tutorials.

It's a nice trick, but it won't work if you use the SoftwareSerial library

lesept:
It's a nice trick, but it won't work if you use the SoftwareSerial library

Why not?

PaulS:
Why not?

In my case the whole program slow down to under 1/4 of the execute time.
I don't know why(as the serial is disabled) , but it's is just very slow executing anything.

Also you have to comment things like "Serial.Read" or "if (Serial.available() > 0)" inside your program else you will see this error:

Hi.

This doesn't work for me with ESP8266. It looks like the compiler has problem with replacing the object.
Instead I used these macros:

#define DEBUG false // flag to turn on/off debugging
#define debug_begin(...) do { if (DEBUG) { Serial.begin(__VA_ARGS__); while(!Serial); }} while (0)
#define debug_print(...) do { if (DEBUG) Serial.print(__VA_ARGS__); } while (0)
#define debug_println(...) do { if (DEBUG) Serial.println(__VA_ARGS__); } while (0)
#define debug_printf(...) do { if (DEBUG) Serial.printf(__VA_ARGS__); } while (0)

Only disadvantage is, that you must use debug_print etc. in the code instead of Serial.print