From 21b5ccd9221ebb20de7ac4893e664f6a54085eb9 Mon Sep 17 00:00:00 2001 From: Rob Roy Date: Fri, 6 Sep 2024 10:55:07 +1000 Subject: [PATCH 1/3] Add live Debug Level switching Listen to the same serial port used for debug output to switch Debug Level. Enter V,D,I,W,E or VERBOSE, DEBUG, INFO, WARNING, or ERROR terminated by a new line/Line feed ('\n) character. CRLF ('\r\n') works as well. NOTE: Check your serial monitor or terminal settings is configured to send a line ending of new line/Line feed ('\n' or '\r\n') at the end of the string. --- src/Arduino_DebugUtils.cpp | 76 +++++++++++++++++++++++++++++++++++--- src/Arduino_DebugUtils.h | 3 +- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/Arduino_DebugUtils.cpp b/src/Arduino_DebugUtils.cpp index 270927b..5a87c66 100644 --- a/src/Arduino_DebugUtils.cpp +++ b/src/Arduino_DebugUtils.cpp @@ -21,6 +21,13 @@ #include "Arduino_DebugUtils.h" +/****************************************************************************** + INPUT BUFFER + ******************************************************************************/ + +#define COMMAND_BUFFER_SIZE 50 // Define a reasonable size for the input buffer +char commandBuffer[COMMAND_BUFFER_SIZE]; + /****************************************************************************** CONSTANTS ******************************************************************************/ @@ -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() { @@ -125,6 +132,65 @@ void Arduino_DebugUtils::print(int const debug_level, const __FlashStringHelper va_end(args); } +void Arduino_DebugUtils::processDebugUpdateLevelCommand() +{ + 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 + { + _debug_io_stream->println("Invalid command. Use V,D,I,W,E or VERBOSE, DEBUG, INFO, WARNING, or ERROR."); + } + + // 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 ******************************************************************************/ @@ -147,9 +213,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 @@ -193,7 +259,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) @@ -211,7 +277,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 diff --git a/src/Arduino_DebugUtils.h b/src/Arduino_DebugUtils.h index 73e6287..771807f 100644 --- a/src/Arduino_DebugUtils.h +++ b/src/Arduino_DebugUtils.h @@ -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 processDebugUpdateLevelCommand(); private: @@ -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(); From c8af05d13e9616cf547a0a59e1b744e4cfff411b Mon Sep 17 00:00:00 2001 From: Rob Roy Date: Fri, 6 Sep 2024 13:05:46 +1000 Subject: [PATCH 2/3] Add runtime updates of debug config Listen to the same serial port used for debug output to switch Debug Level and other config. Use VERBOSE, DEBUG, INFO, WARNING, ERROR, NONE or V,D,I,W,E,N. to change debug level, or LABEL, TIMESTAMP, NEWLINE or L,T,N to toggle config settings. Terminate command with a Line feed ('\n) or CRLF ('\r\n') --- README.md | 95 +++++++++++++++++-- .../Arduino_Debug_Advance.ino | 1 + .../Arduino_Debug_Basic.ino | 1 + src/Arduino_DebugUtils.cpp | 67 +++++++------ src/Arduino_DebugUtils.h | 2 +- 5 files changed, 129 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 23ea200..5bad25f 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. @@ -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`. @@ -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 @@ -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 <= ( that has been set using `setDebugLevel()` function). @@ -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) \ No newline at end of file diff --git a/examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino b/examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino index dbc704c..1253381 100644 --- a/examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino +++ b/examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino @@ -26,5 +26,6 @@ int i = 0; void loop() { DEBUG_VERBOSE("i = %d", i); i++; + Debug.processDebugConfigCommand(); delay(1000); } diff --git a/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino b/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino index 1806e90..db97036 100644 --- a/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino +++ b/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino @@ -10,5 +10,6 @@ int i = 0; void loop() { DEBUG_INFO("i = %d", i); i++; + Debug.processDebugConfigCommand(); delay(1000); } diff --git a/src/Arduino_DebugUtils.cpp b/src/Arduino_DebugUtils.cpp index 5a87c66..242da5b 100644 --- a/src/Arduino_DebugUtils.cpp +++ b/src/Arduino_DebugUtils.cpp @@ -25,7 +25,7 @@ INPUT BUFFER ******************************************************************************/ -#define COMMAND_BUFFER_SIZE 50 // Define a reasonable size for the input buffer +#define COMMAND_BUFFER_SIZE 30 // Define a reasonable size for the input buffer char commandBuffer[COMMAND_BUFFER_SIZE]; /****************************************************************************** @@ -96,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; @@ -113,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; @@ -132,8 +130,7 @@ void Arduino_DebugUtils::print(int const debug_level, const __FlashStringHelper va_end(args); } -void Arduino_DebugUtils::processDebugUpdateLevelCommand() -{ +void Arduino_DebugUtils::processDebugConfigCommand(){ static size_t bufferIndex = 0; // Index to track buffer position // Check if the stream is available and has data @@ -146,42 +143,56 @@ void Arduino_DebugUtils::processDebugUpdateLevelCommand() commandBuffer[bufferIndex] = '\0'; // Null-terminate the string // Compare C-strings for each command - if (strcmp(commandBuffer, "V") == 0 || strcmp(commandBuffer, "VERBOSE") == 0) - { + 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) - { + } 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) - { + } 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) - { + } 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) - { + } else if (strcmp(commandBuffer, "E") == 0 || strcmp(commandBuffer, "ERROR") == 0) { setDebugLevel(DBG_ERROR); _debug_io_stream->println("Debug level set to ERROR."); - } - else - { - _debug_io_stream->println("Invalid command. Use V,D,I,W,E or VERBOSE, DEBUG, INFO, WARNING, or 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, "N") == 0 || strcmp(commandBuffer, "NEWLINE") == 0) { + 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 VERBOSE, DEBUG, INFO, WARNING, ERROR, NONE or V,D,I,W,E,N. LABEL, TIMESTAMP, NEWLINE or L,T,N."); } // Clear the buffer for the next command bufferIndex = 0; commandBuffer[0] = '\0'; - } - else if (incomingChar != '\r') - { + } else if (incomingChar != '\r') { // Add the character to the buffer if it's not a carriage return if (bufferIndex < COMMAND_BUFFER_SIZE - 1) { diff --git a/src/Arduino_DebugUtils.h b/src/Arduino_DebugUtils.h index 771807f..aca34b2 100644 --- a/src/Arduino_DebugUtils.h +++ b/src/Arduino_DebugUtils.h @@ -70,7 +70,7 @@ class Arduino_DebugUtils { void print(int const debug_level, const char * fmt, ...); void print(int const debug_level, const __FlashStringHelper * fmt, ...); - void processDebugUpdateLevelCommand(); + void processDebugConfigCommand(); private: From 2620270f7c22be9bf66965e84eac5391fba4723a Mon Sep 17 00:00:00 2001 From: Rob Roy Date: Fri, 6 Sep 2024 14:54:30 +1000 Subject: [PATCH 3/3] Change shortcut for NEWLINE command Change shortcut for NEWLINE command from N to C (N used for debug level NONE) --- src/Arduino_DebugUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Arduino_DebugUtils.cpp b/src/Arduino_DebugUtils.cpp index 242da5b..4a24930 100644 --- a/src/Arduino_DebugUtils.cpp +++ b/src/Arduino_DebugUtils.cpp @@ -169,7 +169,7 @@ void Arduino_DebugUtils::processDebugConfigCommand(){ timestampOn(); _debug_io_stream->println("TIMESTAMPS set to ON."); } - } else if (strcmp(commandBuffer, "N") == 0 || strcmp(commandBuffer, "NEWLINE") == 0) { + } 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."); @@ -186,7 +186,7 @@ void Arduino_DebugUtils::processDebugConfigCommand(){ _debug_io_stream->println("DEBUG LABEL set to ON."); } } else { - _debug_io_stream->println("Invalid command. Use VERBOSE, DEBUG, INFO, WARNING, ERROR, NONE or V,D,I,W,E,N. LABEL, TIMESTAMP, NEWLINE or L,T,N."); + _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