Better Debugging for Arduino IDE: SerialDebug Library (Part 1)

This tutorial was written by João Lopes and edited by Sara Santos.

The SerialDebug library allows you to improve and do better debugging in Arduino IDE. In this post, João Lopes (creator of the SerialDebug library) shows you how to use it in your projects.

Warning: The developer of this tool is no longer updating this project, but the code is still available on GitHub.

After programming the ESP32 using ESP-IDF, João noticed that debugging for Arduino IDE needed some improvement. So, he created the SerialDebug library to bring better debugging to Arduino IDE. He also created a desktop application called SerialDebugApp that adds a UI and other useful functionalities.

To show you all the useful features of the SerialDebug library and the SerialDebugApp, he created three tutorials dedicated to this subject:

Using Debug Levels

The ESP-IDF has excellent debug output: the ESP-IDF logging, it has debug levels and displays the debug messages on your serial monitor with different colors accordingly to its level.

However, we don’t have this feature on the Arduino IDE. This motivated me (João) to make a library for the Arduino IDE: the SerialDebug library, which enables debugging with levels and other functionalities.

The following figure shows the SerialDebugApp, companion app for SerialDebug library.

Why are debug levels important?

Debug levels allow you to set a level for each message according to its importance, and thus improve the debug. If we have a project full of “Serial.print“, it is difficult to see, because much is generated in the Serial monitor, and there isn’t a specific order in the debug messages.

However, using SerialDebug levels, we can have a project full of debug messages via the Serial Monitor or SerialDebugApp. You can set the debug to “Verbose” to have all messages printed, or to “Debug” to display only the important messages.

See this hypothetical example:

When the level gets higher, the number of messages decreases.

For projects with SerialDebug, we can use the Arduino IDE Serial Monitor and have something as shown in the following figure:

However, this doesn’t present colors. To make the messages more visible, we can assign different colors to each level of debug. ESP-IDF, Android studio and other development environments have this feature.

That’s why I’ve developed a desktop app, the SerialDebugApp. In this app, debug messages are displayed in color. For example, using the example in SerialDebug Advanced of SerialDebug library, we get something as follows when using the default debug level:

At the verbose level, we get the following:

Note: the level can be changed via the serial port (this feature is not even possible with ESP-IDF).

Debugger

With the SerialDebug library, you can have a simple and functional software debugger. In the Serial Monitor and using SerialDebug library, you can:

  • Call a function
  • Show and change values ​​of global variables
  • Add or change watches for global variables

SerialDebug Boards Compatibility

SerialDebug is compatible with any board supported by Arduino IDE. This library is optimized in speed and memory, and can be used in boards with low memory, like UNO with only 2k of RAM (without some features, due limit of hardware), up until modern boards, such as ESP32, Arduino MKRs, ARM MCU boards, as Teensy 3.x, with full features enabled.

Migrating Code to SerialDebug

Migrating the code to use SerialDebug is very simple. There is a converter that reads existing Arduino code and generates a new directory with the code converted and ready to use.

Installing SerialDebug Library

Follow the next steps to install the SerialDebug library:

  1. Click here to download the SerialDebug library
  2. You should have a .zip folder in your Downloads folder
  3. Unzip the .zip folder and you should get SerialDebug-master folder
  4. Rename your folder from SerialDebug-master to SerialDebug
  5. Move the SerialDebug folder to your Arduino IDE installation libraries folder

Or use Library manager of Arduino IDE.

  1. Open Arduino IDE
  2. Go to Sketch > Include library > Manage libraries
  3. Search for “SerialDebug” and click “Install”

How to use SerialDebug – Basic Example

In the Arduino IDE, go to File > Examples > SerialDebug and select the SerialDebug_basic example.

You should see the following code:

////////
// Libraries Arduino
//
// Library: SerialDebug - Improved serial debugging to Arduino, with simple software debugger
// Author: Joao Lopes
// GitHub: https://github.com/JoaoLopesF/SerialDebug
//
// Basic example to show how to use it.
//
// Example of use:
//
//   print macros:
//
//		printA(F("This is a always - var "));
//		printlnA(var);
//		printV(F("This is a verbose - var "));
//		printlnV(var);
//		printD(F("This is a debug - var "));
//		printlnD(var);
//		printI(F("This is a information - var "));
//		printlnI(var);
//		printW(F("This is a warning - var "));
//		printlnW(var);
//		printE(F("This is a error - var "));
//		printlnE(var);
//
//		printlnV("This not have args");
//
///////

////// Includes

#include "Arduino.h"

// SerialDebug Library

// Disable all debug ? Good to release builds (production)
// as nothing of SerialDebug is compiled, zero overhead :-)
// For it just uncomment the DEBUG_DISABLED
//#define DEBUG_DISABLED true

// Define the initial debug level here (uncomment to do it)
// #define DEBUG_INITIAL_LEVEL DEBUG_LEVEL_VERBOSE

// Disable SerialDebug debugger ? No more commands and features as functions and globals
// Uncomment this to disable it 
//#define DEBUG_DISABLE_DEBUGGER true

// Disable auto function name (good if your debug yet contains it)
//#define DEBUG_AUTO_FUNC_DISABLED true

// Force debug messages to can use flash ) ?
// Disable native Serial.printf (if have)
// Good for low memory, due use flash, but more slow and not use macros
//#define DEBUG_USE_FLASH_F true

// Include SerialDebug

#include "SerialDebug.h" //https://github.com/JoaoLopesF/SerialDebug

////// Variables

// Time

uint32_t mTimeSeconds = 0;

// Buildin Led ON ?

boolean mLedON = false;

////// Setup

void setup() {

    // Initialize the Serial

    Serial.begin(115200); // Can change it to 230400, if you dont use debugIsr* macros

#ifdef __AVR_ATmega32U4__ // Arduino AVR Leonardo

    while (!Serial) {
        ; // wait for serial port to connect. Needed for Leonardo only
    }

#else

    delay(500); // Wait a time

#endif

  	// Debug

	// Attention:
    // SerialDebug starts disabled and it only is enabled if have data avaliable in Serial
    // Good to reduce overheads.
	// if You want debug, just press any key and enter in monitor serial

    // Note: all debug in setup must be debugA (always), due it is disabled now.

    printlnA(F("**** Setup: initializing ..."));

    // Buildin led

    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);

    // WiFi connection, etc ....

    // ...

    // End

    printlnA(F("*** Setup end"));

}

////// Loop

void loop()
{
	// SerialDebug handle
	// Notes: if in inactive mode (until receive anything from serial),
	// it show only messages of always or errors level type
	// And the overhead during inactive mode is very low
	// Only if not DEBUG_DISABLED

	debugHandle();

	// Blink the led

	mLedON = !mLedON;
	digitalWrite(LED_BUILTIN, (mLedON)?LOW:HIGH);

	// Debug the time (verbose level)

	printV(F("Time: "));
	printV(mTimeSeconds);
	printlnV(F(" seconds (VERBOSE)"));

	if (mTimeSeconds % 5 == 0) { // Each 5 seconds

		// Debug levels

		printlnV(F("This is a message of debug level VERBOSE"));
		printlnD(F("This is a message of debug level DEBUG"));
		printlnI(F("This is a message of debug level INFO"));
		printlnW(F("This is a message of debug level WARNING"));
		printlnE(F("This is a message of debug level ERROR"));

		// Functions example to show auto function name feature

		foo();

		bar();
	}

	// Time

	mTimeSeconds++;

	// Delay of 1 second

	delay(1000);
}


// Functions example to show auto function name feature

void foo() {

  uint8_t var = 1;

  printV(F("This is a debug - var "));
  printlnV(var);
}

void bar() {

  uint8_t var = 2;

  printD(F("This is a debug - var "));
  printlnD(var);

}

/////////// End

View raw code

Explaining the Basic Example

1. First, include the SerialDebug library:

#include "SerialDebug.h" // https://github.com/JoaoLopesF/SerialDebug

The SerialDebug library has several options that must be set before the “#include”. These are commented in the example, so that you can activate the options for your specific case:

// Disable all debug ? Good to release builds (production)
// as nothing of SerialDebug is compiled, zero overhead :-)
// For it just uncomment the DEBUG_DISABLED
//#define DEBUG_DISABLED true

// Define the initial debug level here (uncomment to do it)
// #define DEBUG_INITIAL_LEVEL DEBUG_LEVEL_VERBOSE

// Disable SerialDebug debugger ? No more commands and features as functions and globals
// Uncomment this to disable it 
//#define DEBUG_DISABLE_DEBUGGER true

// Disable auto function name (good if your debug yet contains it)
//#define DEBUG_AUTO_FUNC_DISABLED true

// Force debug messages to can use flash ) ?
// Disable native Serial.printf (if have)
// Good for low memory, due use flash, but more slow and not use macros
//#define DEBUG_USE_FLASH_F true

Here’s a brief explanation of each option:

  • DEBUG_DISABLED: Disable the full library. With this option, nothing of SerialDebug will be compiled and debug outputs will not be generated. This is good to release a production version.
  • DEBUG_INITIAL_LEVEL: This is used to set initial level – the default is Debug level.
  • DEBUG_DISABLE_DEBUGGER: This disables the simple software debugger. This is good to reduce memory usage in low memory boards.
  • DEBUG_AUTO_FUNC_DISABLED: This disables the auto function name feature. This is good if your code already has that function in the debug messages.

2. For this example, no code is needed for SerialDebug in setup() function.

3. In the loop() function, we’re using print macros to debug.

printV(F("Time: "));
printV(mTimeSeconds);
printlnV(F(" seconds (VERBOSE)"));

if (mTimeSeconds % 5 == 0) { // Each 5 seconds
  // Debug levels

  printlnV(F("This is a message of debug level VERBOSE"));
  printlnD(F("This is a message of debug level DEBUG"));
  printlnI(F("This is a message of debug level INFO"));
  printlnW(F("This is a message of debug level WARNING"));
  printlnE(F("This is a message of debug level ERROR"));

  // Functions example to show auto function name feature

  foo();

  bar();
}

Print macros

SerialDebug has several print macros to debug outputs:

  • printV/printlnV: message of level verbose.
  • printD/printlnD: message of level debug
  • printI/printlnI: message of level information
  • printW/printlnW: message of level warning
  • printE/printlnE: message of level error – always shown
  • printA/printlnA: message of level always – always shown

These macros are a replacement of standard Serial.print commands.

Note: all routines to show debug are a C/C++ precompiler macros, so there is no need for extra functions calls, only Serial.print and Serial.println (except when use printf formatter for boards that don’t have it native).

Take the following code as an example:

// Example from ladyada.net/learn/arduino/lesson4.html

int a = 5;
int b = 10;
int c = 20;

void setup()                    // run once, when the sketch starts
{
  Serial.begin(9600);           // set up Serial library at 9600 bps

  Serial.println("Here is some math: ");

  Serial.print("a = ");
  Serial.println(a);
  Serial.print("b = ");
  Serial.println(b);
  Serial.print("c = ");
  Serial.println(c);

  Serial.print("a + b = ");       // add
  Serial.println(a + b);

  Serial.print("a * c = ");       // multiply
  Serial.println(a * c);
  
  Serial.print("c / b = ");       // divide
  Serial.println(c / b);
  
  Serial.print("b - c = ");       // subtract
  Serial.println(b - c);
}

void loop()                     // we need this to be here even though its empty
{
}

View raw code

This code is migrated to SerialDebug by making the following changes:

  • replace Serial.println to printlnD
  • replace Serial.print to printD

D refers to debug level. Instead of D, you can use V to verbose.

As mentioned previously, SerialDebug has a converter to help you migrate Arduino codes from Serial.prints to this library. We recommend using the converter to avoid unnecessary errors. Take a look at the SerialDebugConverter on the following link:

After running the code on the converter, a new file is generated on the source code folder. If you try the previous example, you’ll get the following:

// Example from ladyada.net/learn/arduino/lesson4.html

int a = 5;
int b = 10;
int c = 20;

// SerialDebug Library

// Disable all debug ? Good to release builds (production)
// as nothing of SerialDebug is compiled, zero overhead :-)
// For it just uncomment the DEBUG_DISABLED
//#define DEBUG_DISABLED true

// Disable SerialDebug debugger ? No more commands and features as functions and globals
// Uncomment this to disable it 
//#define DEBUG_DISABLE_DEBUGGER true

// Define the initial debug level here (uncomment to do it)
//#define DEBUG_INITIAL_LEVEL DEBUG_LEVEL_VERBOSE

// Disable auto function name (good if your debug yet contains it)
//#define DEBUG_AUTO_FUNC_DISABLED true

// Include SerialDebug
#include "SerialDebug.h" // Download SerialDebug library: https://github.com/JoaoLopesF/SerialDebug

void setup()                    // run once, when the sketch starts
{
  Serial.begin(9600);           // set up Serial library at 9600 bps

  printlnA("Here is some math: ");

  printA("a = ");
  printlnA(a);
  printA("b = ");
  printlnA(b);
  printA("c = ");
  printlnA(c);

  printA("a + b = ");       // add
  printlnA(a + b);

  printA("a * c = ");       // multiply
  printlnA(a * c);
  
  printA("c / b = ");       // divide
  printlnA(c / b);
  
  printA("b - c = ");       // subtract
  printlnA(b - c);

#ifndef DEBUG_DISABLE_DEBUGGER

  // Add Functions and global variables to SerialDebug

  // Add functions that can called from SerialDebug

  //debugAddFunctionVoid(F("function"), &function); // Example for function without args
  //debugAddFunctionStr(F("function"), &function); // Example for function with one String arg
  //debugAddFunctionInt(F("function"), &function); // Example for function with one int arg

  // Add global variables that can showed/changed from SerialDebug
  // Note: Only global, if pass local for SerialDebug, can be dangerous

  debugAddGlobalInt(F("a"), &a);
  debugAddGlobalInt(F("b"), &b);
  debugAddGlobalInt(F("c"), &c);

#endif // DEBUG_DISABLE_DEBUGGER

}

void loop()                     // we need this to be here even though its empty
{

  // SerialDebug handle
  // Notes: if in inactive mode (until receive anything from serial),
  // it show only messages of always or errors level type
  // And the overhead during inactive mode is very low
  // Only if not DEBUG_DISABLED

  debugHandle();

}

View raw code

Debug macros

SerialDebug has 3 types of debug macros:

  • print? – Serial.print replacement
  • println? – Serial.println replacement
  • debug? – Serial.printf (with newline) replacement

The sufix “?” is the message level, for example: printV, printlnD, debugE.

Here’s an example:

printD("b - c = "); // subtract
printlnD(b - c);

Which generates the following output in serial monitor:

V p:^1000 loop C1) b - c = 2

Where:

  • V: is the level
  • p: is a profile elapsed time between this and previous debug
  • loop: is the function name where this debug message was triggered
  • C1: is the core that executed this debug message (only applies to the ESP32)
  • The remaining is the message

Uploading the basic example

Upload the SerialDebug_basic example:

Using the SerialDebug in the Arduino IDE Serial Monitor

Open the Serial Monitor.

The initial status of SerialDebug is inactive, where there isn’t normal debug outputs, and no CPU waste time for debugs. This is good for projects that are not always connected via USB (battery powered or external power supply, for example). The only messages that are processed and displayed are of type Error or Always (important ones).

After receiving the first command, SerialDebug becomes active:

The default level is Debug, but it can be changed. Note that the messages of level verbose are not being shown.

SerialDebug commands

SerialDebug has commands that can be executed by the Serial Monitor. For example:

  • ? – show help of commands
  • v – change level to verbose

If you type ? and press “Send”, it shows help commands:

If you want to change level to verbose (command v and “Send” button):

Note that the volume of messages displayed increases significantly. That’s why having different debug levels is important.

Video Demonstration

You can watch the following video about the SerialDebug library to see it in action:

Wrapping Up

This first article was an introduction to the SerialDebug library for Arduino IDE. With this library we can significantly improve the debugging for Arduino IDE. We’ve also shown you how to use debug levels with the library and how to interact with the Serial Monitor.

In part 2, you’ll discover SerialDebug library advanced features, such as:

  • simple software debugger – how to get it without hardware debugger
  • debug? macros – using the powerful printf to format

Continue Reading: Better Debugging for Arduino IDE using Software Debugger (Part 2)

Help me bring a better debug to the Arduino IDE using this library. Visit the GitHub page https://github.com/JoaoLopesF/SerialDebug, for more information, post issues and suggestions. Also, you can use the gitter chat room to share your feedback.

Thanks to Random Nerd Tutorials for the possibility of doing a post about SerialDebug library.

João Lopes

Random Nerd Tutorials has more than 200 free electronics projects and tutorials. Check them all in the next link: 200+ Electronics Projects and Tutorials



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

4 thoughts on “Better Debugging for Arduino IDE: SerialDebug Library (Part 1)”

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.