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:
- Part 1 – Using debug with levels (currently reading)
- Part 2 – Simple Software Debugger
- Part 3 – SerialDebugApp
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:
- Click here to download the SerialDebug library
- You should have a .zip folder in your Downloads folder
- Unzip the .zip folder and you should get SerialDebug-master folder
- Rename your folder from
SerialDebug-masterto SerialDebug - Move the SerialDebug folder to your Arduino IDE installation libraries folder
Or use Library manager of Arduino IDE.
- Open Arduino IDE
- Go to Sketch > Include library > Manage libraries
- 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
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
{
}
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();
}
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
I have got to try this out.
Very interesting for sure!
#define DEBUG_INITIAL_LEVEL DEBUG_LEVEL_VERBOSE
is not working, it only prints A and E
The Debug App is lost in space, http://ww1.joaolopesf.net/ is just a empty web page.
Hi.
Yes.
That’s true.
I’ll add a warning about that on this page.
Regards,
Sara