Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add runtime update of Debug Level and display settings. #46

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 87 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Arduino_DebugUtils
==================

[![Check Arduino status](https://github.com/arduino-libraries/Arduino_DebugUtils/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_DebugUtils/actions/workflows/check-arduino.yml)
[![Compile Examples status](https://github.com/arduino-libraries/Arduino_DebugUtils/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_DebugUtils/actions/workflows/compile-examples.yml)
[![Spell Check status](https://github.com/arduino-libraries/Arduino_DebugUtils/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_DebugUtils/actions/workflows/spell-check.yml)
[![Check Arduino status](https://github.com/robroypt/Arduino_DebugUtils/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/robroypt/Arduino_DebugUtils/actions/workflows/check-arduino.yml)
[![Compile Examples status](https://github.com/robroypt/Arduino_DebugUtils/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/robroypt/Arduino_DebugUtils/actions/workflows/compile-examples.yml)
[![Spell Check status](https://github.com/robroypt/Arduino_DebugUtils/actions/workflows/spell-check.yml/badge.svg)](https://github.com/robroypt/Arduino_DebugUtils/actions/workflows/spell-check.yml)

This class provides functionality useful for debugging sketches via `printf`-style statements.

Expand All @@ -16,7 +16,7 @@ Arduino_DebugUtils has 6 different debug levels (described descending from highe
* `DBG_DEBUG` - more information
* `DBG_VERBOSE` - most information

The desired debug level can be set via `setDebugLevel(DBG_WARNING)`.
The desired debug level can be set in code via `setDebugLevel(DBG_WARNING)` or while the code is running using the Serial Monitor.

Debug messages are written via `print` which supports `printf`-style formatted output.

Expand All @@ -34,8 +34,35 @@ If desired, timestamps can be prefixed to the debug message. Timestamp output ca
Normally all debug output is redirected to the primary serial output of each board (`Serial`). In case you want to redirect the output to another output stream you can make use of `setDebugOutputStream(&Serial2)`.

# Documentation
### Debug :
Arduino_DebugUtils Object that will be used for calling member functions.
## Code setup

### Include the Library

Include the Arduino_DebugUtils library by adding
```C++
#include "Arduino_DebugUtils.h"
```
at the top of your code.

### Setup the Serial Port

By default, the Output Stream and input port for printing debug messages and receiving config commands is `Serial`.

For minimal setup, just create the Serial object with a specified baud rate.
```C++
Serial.begin(9600); // Set baud rate here. Make sure you match the same baud rate in your terminal or monitor
```
In advanced cases you can use other serial ports (if available), or a Software Serial object. You can also (optionally) set a different Debug Level, turn on timestamps and debug labels.

```C++
mySerial.begin(9600); // Set baud rate here. Make sure you match the same baud rate in your terminal or monitor
Debug.setDebugOutputStream(&mySerial);
Debug.setDebugLevel(DBG_VERBOSE);
Debug.timestampOn();
```

### Debug: object
Arduino_DebugUtils Object that will be used for calling member functions is automatically instantiated when you include the library.

### Debug.setDebugLevel(int const debug_level) :
Parameter debug_level in order of lowest to highest priority are : `DBG_NONE`, `DBG_ERROR`, `DBG_WARNING`, `DBG_INFO` (default), `DBG_DEBUG`, and `DBG_VERBOSE`.
Expand Down Expand Up @@ -79,6 +106,28 @@ Debug.timestampOff();
DEBUG_VERBOSE("i = %d", i); //Output looks like : i = 21
```

### Debug.debugLabelOn() :
Calling this function switches on the Debug Label in the `Debug.print()` function call;
By default, printing the debug label is off, unless turned on using this function call.

Return type: void.

Example:
```C++
Debug.debugLabelOn();
```

### Debug.debugLabelOff() :
Calling this function switches off the Debug Label in the `Debug.print()` function call;
By default, printing the debug label is off, unless turned on using this function call.

Return type: void.

Example:
```C++
Debug.debugLabelOff();
```

### Debug.newlineOn() :
Calling this function ensures that a newline will be sent at the end of the `Debug.print()` function call;
By default, a newline is sent
Expand All @@ -96,10 +145,9 @@ Return type: void.

Example:
```C++
Debug.timestampOff();
Debug.newlineOff();
```


### Debug.print(int const debug_level, const char * fmt, ...);
This function prints the message if parameter `debug_level` in the `Debug.print(debug_level, ...)` function call belongs to the range: DBG_ERROR <= debug_level <= (<DBG_LEVEL> that has been set using `setDebugLevel()` function).

Expand All @@ -111,3 +159,34 @@ Debug.setDebugLevel(DBG_VERBOSE);
int i = 0;
DEBUG_VERBOSE("DBG_VERBOSE i = %d", i);
```
## Set Debug Settings at Runtime

While your code is running you can
- set the current debug level
- toggle the display of the prefixed Timestamp at the beginning of the `Debug.print()` function call
- toggle the display of the debug label at the beginning of the `Debug.print()` function call
- toggle the sending of a newline at the end of the `Debug.print()` function call

### Setup

Modify your `loop()` function to call `Debug.processDebugConfigCommand()` each time.

Example:
```C++
int i = 0;

void loop() {
DEBUG_VERBOSE("i = %d", i);
i++;
Debug.processDebugConfigCommand();
delay(1000); // See note on timing below
}
```

### Timing
- If you have a delay in your `loop()` (as is the case with the example code) this will also delay action on any entered commands, so for faster response call `processDebugConfigCommand()` more often.

### Trouble-shooting
Check your monitor or terminal is configured:
- to send a LF or CRLF Line Ending at the end of a message sent via the serial port.
- to use the same baud rate specified when setting up the Serial object (9600 in the examples)
1 change: 1 addition & 0 deletions examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ int i = 0;
void loop() {
DEBUG_VERBOSE("i = %d", i);
i++;
Debug.processDebugConfigCommand();
delay(1000);
}
1 change: 1 addition & 0 deletions examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ int i = 0;
void loop() {
DEBUG_INFO("i = %d", i);
i++;
Debug.processDebugConfigCommand();
delay(1000);
}
95 changes: 86 additions & 9 deletions src/Arduino_DebugUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@

#include "Arduino_DebugUtils.h"

/******************************************************************************
INPUT BUFFER
******************************************************************************/

#define COMMAND_BUFFER_SIZE 30 // Define a reasonable size for the input buffer
char commandBuffer[COMMAND_BUFFER_SIZE];

/******************************************************************************
CONSTANTS
******************************************************************************/
Expand Down Expand Up @@ -54,7 +61,7 @@ int Arduino_DebugUtils::getDebugLevel() const {
}

void Arduino_DebugUtils::setDebugOutputStream(Stream * stream) {
_debug_output_stream = stream;
_debug_io_stream = stream;
}

void Arduino_DebugUtils::newlineOn() {
Expand Down Expand Up @@ -89,8 +96,7 @@ void Arduino_DebugUtils::timestampOff() {
_timestamp_on = false;
}

void Arduino_DebugUtils::print(int const debug_level, const char * fmt, ...)
{
void Arduino_DebugUtils::print(int const debug_level, const char * fmt, ...){
if (!shouldPrint(debug_level))
return;

Expand All @@ -106,8 +112,7 @@ void Arduino_DebugUtils::print(int const debug_level, const char * fmt, ...)
va_end(args);
}

void Arduino_DebugUtils::print(int const debug_level, const __FlashStringHelper * fmt, ...)
{
void Arduino_DebugUtils::print(int const debug_level, const __FlashStringHelper * fmt, ...){
if (!shouldPrint(debug_level))
return;

Expand All @@ -125,6 +130,78 @@ void Arduino_DebugUtils::print(int const debug_level, const __FlashStringHelper
va_end(args);
}

void Arduino_DebugUtils::processDebugConfigCommand(){
static size_t bufferIndex = 0; // Index to track buffer position

// Check if the stream is available and has data
if (_debug_io_stream && _debug_io_stream->available()) {
// Read each character from the stream
char incomingChar = _debug_io_stream->read();

// If it's a newline character, process the command
if (incomingChar == '\n') {
commandBuffer[bufferIndex] = '\0'; // Null-terminate the string

// Compare C-strings for each command
if (strcmp(commandBuffer, "V") == 0 || strcmp(commandBuffer, "VERBOSE") == 0) {
setDebugLevel(DBG_VERBOSE);
_debug_io_stream->println("Debug level set to VERBOSE.");
} else if (strcmp(commandBuffer, "D") == 0 || strcmp(commandBuffer, "DEBUG") == 0) {
setDebugLevel(DBG_INFO);
_debug_io_stream->println("Debug level set to DEBUG.");
} else if (strcmp(commandBuffer, "I") == 0 || strcmp(commandBuffer, "INFO") == 0) {
setDebugLevel(DBG_INFO);
_debug_io_stream->println("Debug level set to INFO.");
} else if (strcmp(commandBuffer, "W") == 0 || strcmp(commandBuffer, "WARNING") == 0) {
setDebugLevel(DBG_WARNING);
_debug_io_stream->println("Debug level set to WARNING.");
} else if (strcmp(commandBuffer, "E") == 0 || strcmp(commandBuffer, "ERROR") == 0) {
setDebugLevel(DBG_ERROR);
_debug_io_stream->println("Debug level set to ERROR.");
} else if (strcmp(commandBuffer, "N") == 0 || strcmp(commandBuffer, "NONE") == 0) {
setDebugLevel(DBG_NONE);
_debug_io_stream->println("Debug level set to NONE.");
} else if (strcmp(commandBuffer, "T") == 0 || strcmp(commandBuffer, "TIMESTAMP") == 0) {
if (_timestamp_on) {
timestampOff();
_debug_io_stream->println("TIMESTAMPS set to OFF.");
} else {
timestampOn();
_debug_io_stream->println("TIMESTAMPS set to ON.");
}
} else if (strcmp(commandBuffer, "C") == 0 || strcmp(commandBuffer, "NEWLINE") == 0) { // Shortcut is C for Carriage Return. N Already used for NONE
if (_newline_on) {
newlineOff();
_debug_io_stream->println("NEWLINE set to OFF.");
} else {
newlineOn();
_debug_io_stream->println("NEWLINE set to ON.");
}
} else if (strcmp(commandBuffer, "L") == 0 || strcmp(commandBuffer, "LABEL") == 0) {
if (_print_debug_label) {
debugLabelOff();
_debug_io_stream->println("DEBUG LABEL set to OFF.");
} else {
debugLabelOn();
_debug_io_stream->println("DEBUG LABEL set to ON.");
}
} else {
_debug_io_stream->println("Invalid command. Use V|VERBOSE, D|DEBUG, I|INFO, W|WARNING, E|ERROR, N|NONE or L|LABEL, T|TIMESTAMP, C|NEWLINE.");
}

// Clear the buffer for the next command
bufferIndex = 0;
commandBuffer[0] = '\0';
} else if (incomingChar != '\r') {
// Add the character to the buffer if it's not a carriage return
if (bufferIndex < COMMAND_BUFFER_SIZE - 1)
{
commandBuffer[bufferIndex++] = incomingChar;
}
}
}
}

/******************************************************************************
PRIVATE MEMBER FUNCTIONS
******************************************************************************/
Expand All @@ -147,9 +224,9 @@ void Arduino_DebugUtils::vPrint(char const * fmt, va_list args) {
va_end(args_copy);

if (_newline_on) {
_debug_output_stream->println(msg_buf);
_debug_io_stream->println(msg_buf);
} else {
_debug_output_stream->print(msg_buf);
_debug_io_stream->print(msg_buf);
}

#if __STDC_NO_VLA__ == 1
Expand Down Expand Up @@ -193,7 +270,7 @@ void Arduino_DebugUtils::printTimestamp()
snprintf(timestamp, sizeof(timestamp), "[ %lu ] ", millis());
}

_debug_output_stream->print(timestamp);
_debug_io_stream->print(timestamp);
}

void Arduino_DebugUtils::printDebugLabel(int const debug_level)
Expand All @@ -211,7 +288,7 @@ void Arduino_DebugUtils::printDebugLabel(int const debug_level)
if (!is_valid_debug_level)
return;

_debug_output_stream->print(DEBUG_MODE_STRING[debug_level]);
_debug_io_stream->print(DEBUG_MODE_STRING[debug_level]);
}

bool Arduino_DebugUtils::shouldPrint(int const debug_level) const
Expand Down
3 changes: 2 additions & 1 deletion src/Arduino_DebugUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class Arduino_DebugUtils {
void print(int const debug_level, const char * fmt, ...);
void print(int const debug_level, const __FlashStringHelper * fmt, ...);

void processDebugConfigCommand();

private:

Expand All @@ -78,7 +79,7 @@ class Arduino_DebugUtils {
bool _print_debug_label;
bool _format_timestamp_on;
int _debug_level;
Stream * _debug_output_stream;
Stream * _debug_io_stream;

void vPrint(char const * fmt, va_list args);
void printTimestamp();
Expand Down
Loading