diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..101edae --- /dev/null +++ b/.codespellrc @@ -0,0 +1,7 @@ +# See: https://github.com/codespell-project/codespell#using-a-config-file +[codespell] +# In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: +ignore-words-list = , +check-filenames = +check-hidden = +skip = ./.git diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..fa738ec --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file +version: 2 + +updates: + # Configure check for outdated GitHub Actions actions in workflows. + # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot + - package-ecosystem: github-actions + directory: / # Check the repository's workflows under /.github/workflows/ + schedule: + interval: daily + labels: + - "topic: infrastructure" diff --git a/.github/workflows/check-arduino.yml b/.github/workflows/check-arduino.yml new file mode 100644 index 0000000..e818685 --- /dev/null +++ b/.github/workflows/check-arduino.yml @@ -0,0 +1,28 @@ +name: Check Arduino + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + pull_request: + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Arduino Lint + uses: arduino/arduino-lint-action@v2 + with: + compliance: specification + library-manager: update + # Always use this setting for official repositories. Remove for 3rd party projects. + official: true + project-type: library diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml new file mode 100644 index 0000000..ac696fc --- /dev/null +++ b/.github/workflows/compile-examples.yml @@ -0,0 +1,153 @@ +name: Compile Examples + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/compile-examples.yml" + - "examples/**" + - "src/**" + pull_request: + paths: + - ".github/workflows/compile-examples.yml" + - "examples/**" + - "src/**" + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + build: + name: ${{ matrix.board.fqbn }} + runs-on: ubuntu-latest + + env: + SKETCHES_REPORTS_PATH: sketches-reports + + strategy: + fail-fast: false + + matrix: + board: + - fqbn: arduino:avr:nano + platforms: | + - name: arduino:avr + softwareserial: true + artifact-name-suffix: arduino-avr-uno + - fqbn: arduino:avr:mega + platforms: | + - name: arduino:avr + softwareserial: true + artifact-name-suffix: arduino-avr-mega + - fqbn: arduino:avr:leonardo + platforms: | + - name: arduino:avr + softwareserial: true + artifact-name-suffix: arduino-avr-leonardo + - fqbn: arduino:megaavr:nona4809 + platforms: | + - name: arduino:megaavr + softwareserial: true + artifact-name-suffix: arduino-megaavr-nona4809 + - fqbn: arduino:sam:arduino_due_x_dbg + platforms: | + - name: arduino:sam + softwareserial: false + artifact-name-suffix: arduino-sam-arduino_due_x_dbg + - fqbn: arduino:samd:mkrzero + platforms: | + - name: arduino:samd + softwareserial: false + artifact-name-suffix: arduino-samd-mkrzero + - fqbn: arduino:mbed_portenta:envie_m7:target_core=cm4 + platforms: | + - name: arduino:mbed_portenta + softwareserial: false + artifact-name-suffix: arduino-mbed_portenta-envie_m7-target_core-cm4 + - fqbn: arduino:mbed_portenta:envie_m7 + platforms: | + - name: arduino:mbed_portenta + softwareserial: false + artifact-name-suffix: arduino-mbed_portenta-envie_m7 + - fqbn: arduino:mbed_nano:nano33ble + platforms: | + - name: arduino:mbed_nano + softwareserial: false + artifact-name-suffix: arduino-mbed_nano-nano33ble + - fqbn: arduino:mbed_nano:nanorp2040connect + platforms: | + - name: arduino:mbed_nano + softwareserial: false + artifact-name-suffix: arduino-mbed_nano-nanorp2040connect + - fqbn: arduino:mbed_nicla:nicla_vision + platforms: | + - name: arduino:mbed_nicla + softwareserial: false + artifact-name-suffix: arduino-mbed_nicla-nicla_vision + - fqbn: arduino:mbed_opta:opta + platforms: | + - name: arduino:mbed_opta + softwareserial: false + artifact-name-suffix: arduino-mbed_opta-opta + - fqbn: arduino:mbed_giga:giga + platforms: | + - name: arduino:mbed_giga + softwareserial: false + artifact-name-suffix: arduino-mbed_giga-giga + - fqbn: arduino:renesas_portenta:portenta_c33 + platforms: | + - name: arduino:renesas_portenta + softwareserial: false + artifact-name-suffix: arduino-renesas_portenta-portenta_c33 + - fqbn: arduino:renesas_uno:unor4wifi + platforms: | + - name: arduino:renesas_uno + softwareserial: false + artifact-name-suffix: arduino-renesas_uno-unor4wifi + - fqbn: arduino:esp32:nano_nora + platforms: | + - name: arduino:esp32 + softwareserial: false + artifact-name-suffix: arduino-esp32-nano_nora + + # Make board type-specific customizations to the matrix jobs + include: + - board: + # Boards with a SoftwareSerial library + softwareserial: true + # Compile these sketches in addition to the ones defined by env.UNIVERSAL_SKETCH_PATHS + sketch-paths: | + - examples/Arduino_Debug_Advance + - board: + softwareserial: false + sketch-paths: "" + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Compile examples + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fqbn: ${{ matrix.board.fqbn }} + platforms: ${{ matrix.board.platforms }} + libraries: | + # Install the library from the local path. + - source-path: ./ + # Additional library dependencies can be listed here. + # See: https://github.com/arduino/compile-sketches#libraries + sketch-paths: | + - examples/Arduino_Debug_Basic + ${{ matrix.sketch-paths }} + enable-deltas-report: true + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + + - name: Save sketches report as workflow artifact + uses: actions/upload-artifact@v4 + with: + if-no-files-found: error + path: ${{ env.SKETCHES_REPORTS_PATH }} + name: sketches-report-${{ matrix.board.artifact-name-suffix }} diff --git a/.github/workflows/report-size-deltas.yml b/.github/workflows/report-size-deltas.yml new file mode 100644 index 0000000..39e2a0a --- /dev/null +++ b/.github/workflows/report-size-deltas.yml @@ -0,0 +1,24 @@ +name: Report Size Deltas + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/report-size-deltas.yml" + schedule: + # Run at the minimum interval allowed by GitHub Actions. + # Note: GitHub Actions periodically has outages which result in workflow failures. + # In this event, the workflows will start passing again once the service recovers. + - cron: "*/5 * * * *" + workflow_dispatch: + repository_dispatch: + +jobs: + report: + runs-on: ubuntu-latest + steps: + - name: Comment size deltas reports to PRs + uses: arduino/report-size-deltas@v1 + with: + # Regex matching the names of the workflow artifacts created by the "Compile Examples" workflow + sketches-reports-source: ^sketches-report-.+ diff --git a/.github/workflows/spell-check.yml b/.github/workflows/spell-check.yml new file mode 100644 index 0000000..ef7d894 --- /dev/null +++ b/.github/workflows/spell-check.yml @@ -0,0 +1,22 @@ +name: Spell Check + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + pull_request: + schedule: + # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + spellcheck: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Spell check + uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml new file mode 100644 index 0000000..53a9f54 --- /dev/null +++ b/.github/workflows/sync-labels.yml @@ -0,0 +1,138 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md +name: Sync Labels + +# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/sync-labels.ya?ml" + - ".github/label-configuration-files/*.ya?ml" + pull_request: + paths: + - ".github/workflows/sync-labels.ya?ml" + - ".github/label-configuration-files/*.ya?ml" + schedule: + # Run daily at 8 AM UTC to sync with changes to shared label configurations. + - cron: "0 8 * * *" + workflow_dispatch: + repository_dispatch: + +env: + CONFIGURATIONS_FOLDER: .github/label-configuration-files + CONFIGURATIONS_ARTIFACT: label-configuration-files + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download JSON schema for labels configuration file + id: download-schema + uses: carlosperate/download-file-action@v2 + with: + file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json + location: ${{ runner.temp }}/label-configuration-schema + + - name: Install JSON schema validator + run: | + sudo npm install \ + --global \ + ajv-cli \ + ajv-formats + + - name: Validate local labels configuration + run: | + # See: https://github.com/ajv-validator/ajv-cli#readme + ajv validate \ + --all-errors \ + -c ajv-formats \ + -s "${{ steps.download-schema.outputs.file-path }}" \ + -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" + + download: + needs: check + runs-on: ubuntu-latest + + strategy: + matrix: + filename: + # Filenames of the shared configurations to apply to the repository in addition to the local configuration. + # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels + - universal.yml + + steps: + - name: Download + uses: carlosperate/download-file-action@v2 + with: + file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} + + - name: Pass configuration files to next job via workflow artifact + uses: actions/upload-artifact@v4 + with: + path: | + *.yaml + *.yml + if-no-files-found: error + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + + sync: + needs: download + runs-on: ubuntu-latest + + steps: + - name: Set environment variables + run: | + # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable + echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" + + - name: Determine whether to dry run + id: dry-run + if: > + github.event_name == 'pull_request' || + ( + ( + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' + ) && + github.ref != format('refs/heads/{0}', github.event.repository.default_branch) + ) + run: | + # Use of this flag in the github-label-sync command will cause it to only check the validity of the + # configuration. + echo "::set-output name=flag::--dry-run" + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download configuration files artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + path: ${{ env.CONFIGURATIONS_FOLDER }} + + - name: Remove unneeded artifact + uses: geekyeggo/delete-artifact@v5 + with: + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + + - name: Merge label configuration files + run: | + # Merge all configuration files + shopt -s extglob + cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" + + - name: Install github-label-sync + run: sudo npm install --global github-label-sync + + - name: Sync labels + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # See: https://github.com/Financial-Times/github-label-sync + github-label-sync \ + --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ + ${{ steps.dry-run.outputs.flag }} \ + ${{ github.repository }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..687f872 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c1073db..0000000 --- a/.travis.yml +++ /dev/null @@ -1,65 +0,0 @@ -language: generic -env: - global: - - CLI_VERSION=latest -matrix: - include: - - env: - - BOARD="arduino:samd:mkr1000" - - env: - - BOARD="arduino:samd:mkrwifi1010" - - env: - - BOARD="arduino:samd:mkrgsm1400" - - env: - - NAME=Code Formatting Check - # must define an empty before_install phase, otherwise the default one is used - before_install: true - install: - # install Artistic Style code formatter tool: http://astyle.sourceforge.net - - | - mkdir "${HOME}/astyle"; - wget --no-verbose --output-document="${HOME}/astyle/astyle.tar.gz" "https://iweb.dl.sourceforge.net/project/astyle/astyle/astyle%203.1/astyle_3.1_linux.tar.gz"; - tar --extract --file="${HOME}/astyle/astyle.tar.gz" --directory="${HOME}/astyle"; - cd "${HOME}/astyle/astyle/build/gcc"; - make; - export PATH=$PWD/bin:$PATH; - cd "$TRAVIS_BUILD_DIR" - # download Arduino's Artistic Style configuration file - - wget --directory-prefix="${HOME}/astyle" https://raw.githubusercontent.com/arduino/Arduino/master/build/shared/examples_formatter.conf - script: - # check code formatting - - find . -regextype posix-extended -path './.git' -prune -or \( -iregex '.*\.((ino)|(h)|(hpp)|(hh)|(hxx)|(h\+\+)|(cpp)|(cc)|(cxx)|(c\+\+)|(cp)|(c)|(ipp)|(ii)|(ixx)|(inl)|(tpp)|(txx)|(tpl))$' -and -type f \) -print0 | xargs -0 -L1 bash -c 'if ! diff "$0" <(astyle --options=${HOME}/astyle/examples_formatter.conf --dry-run < "$0"); then echo "Non-compliant code formatting in $0"; false; fi' - - env: - - NAME=Spell Check - language: python - python: 3.6 - # must define an empty before_install phase, otherwise the default one is used - before_install: true - install: - # https://github.com/codespell-project/codespell - - pip install codespell - script: - - codespell --skip="${TRAVIS_BUILD_DIR}/.git" --ignore-words="${TRAVIS_BUILD_DIR}/extras/codespell-ignore-words-list.txt" "${TRAVIS_BUILD_DIR}" -# default phases -before_install: - - wget http://downloads.arduino.cc/arduino-cli/arduino-cli-$CLI_VERSION-linux64.tar.bz2 - - tar xf arduino-cli-$CLI_VERSION-linux64.tar.bz2 - - mkdir -p "$HOME/bin" - - mv arduino-cli-*-linux64 $HOME/bin/arduino-cli - - export PATH="$PATH:$HOME/bin" - - arduino-cli core update-index - - if [[ "$BOARD" =~ "arduino:samd:" ]]; then - arduino-cli core install arduino:samd; - fi - - buildExampleSketch() { arduino-cli compile --warnings all --fqbn $BOARD $PWD/examples/$1; } -install: - - mkdir -p $HOME/Arduino/libraries - - ln -s $PWD $HOME/Arduino/libraries/. -script: - - buildExampleSketch Arduino_Debug_Basic -notifications: - webhooks: - urls: - - https://www.travisbuddy.com/ - on_success: never - on_failure: always diff --git a/LICENSE b/LICENSE index cd4defc..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1,17 +1,3 @@ -This file includes licensing information for Arduino_DebugUtils. - -Copyright (c) 2018 ARDUINO SA (www.arduino.cc) - -The software is released under the GNU General Public License, which covers the main body -of the Arduino_DebugUtils code. The terms of this license can be found at: -https://www.gnu.org/licenses/gpl-3.0.en.html - -You can be released from the requirements of the above licenses by purchasing -a commercial license. Buying such a license is mandatory if you want to modify or -otherwise use the software for commercial activities involving the Arduino -software without disclosing the source code of your own applications. To purchase -a commercial license, send an email to license@arduino.cc - GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -645,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - Arduino_DebugUtils encapsulates functionality useful for debugging code via printf statements. - Copyright (C) Arduino SA, 2019, Author: Alexander Entinger + + Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -666,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Arduino_DebugUtils Copyright (C) 2019, Arduino SA + Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/README.md b/README.md index 24fb18b..c59b804 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ 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) + This class provides functionality useful for debugging sketches via `printf`-style statements. # How-To-Use Basic @@ -20,10 +24,96 @@ Example: ```C++ int i = 1; float pi = 3.1459; -Debug.print(DBG_VERBOSE, "i = %d, pi = %f", i, pi); +DEBUG_VERBOSE("i = %d, pi = %f", i, pi); ``` +**Note**: The output of floating point numbers (`%f`) does NOT work on [ArduinoCore-avr](https://github.com/arduino/ArduinoCore-avr). -If desired timestamps can be prefixed to the debug message. Timestamp output can be enabled and disabled via `timestampOn` and `timestampOff`. +If desired, timestamps can be prefixed to the debug message. Timestamp output can be enabled and disabled via `timestampOn` and `timestampOff`. # How-To-Use Advanced 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. + +### 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`. + +Return type: void. + +Example: +```C++ +Debug.setDebugLevel(DBG_VERBOSE); +``` +### Debug.setDebugOutputStream(Stream * stream) : +By default, Output Stream is Serial. In advanced cases other objects could be other serial ports (if available), or can be a Software Serial object. + +Return type: void. + +Example: +```C++ +SoftwareSerial mySerial(10, 11); // RX, TX +Debug.setDebugOutputStream(&mySerial); +``` +### Debug.timestampOn() : +Calling this function switches on the timestamp in the `Debug.print()` function call; +By default, printing timestamp is off, unless turned on using this function call. + +Return type: void. + +Example: +```C++ +Debug.timestampOn(); +DBG_VERBOSE("i = %d", i); //Output looks like : [ 21007 ] i = 21 +``` + +### Debug.timestampOff() : +Calling this function switches off the timestamp in the `Debug.print()` function call; + +Return type: void. + +Example: +```C++ +Debug.timestampOff(); +DEBUG_VERBOSE("i = %d", i); //Output looks like : i = 21 +``` + +### 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 +Return type: void. + +Example: +```C++ +Debug.newlineOn(); +``` + +### Debug.newlineOff() : +Calling this function ensure that a newline will NOT be sent at the end of the `Debug.print()` function call; +By default a newline is sent. Call this to shut that functionality off. +Return type: void. + +Example: +```C++ +Debug.timestampOff(); +``` + + +### 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). + +Return type: void. + +Example: +```C++ +Debug.setDebugLevel(DBG_VERBOSE); +int i = 0; +DEBUG_VERBOSE("DBG_VERBOSE i = %d", i); +``` + +# License + +Arduino_DebugUtils is licensed under the GNU General Public License v3.0. + +You can be released from the requirements of the above license by purchasing a commercial license. Buying such a license is mandatory if you want to modify or otherwise use the software for commercial activities involving the Arduino software without disclosing the source code of your own applications. To purchase a commercial license, send an email to license@arduino.cc diff --git a/examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino b/examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino new file mode 100644 index 0000000..dbc704c --- /dev/null +++ b/examples/Arduino_Debug_Advance/Arduino_Debug_Advance.ino @@ -0,0 +1,30 @@ +/* + Advanced Debug can be helpful in embedded applications when + there are more than two microcontrollers connected serially + or a wireless sensor like XBee is connected to the serial port + that will send data wirelessly to other XBee nodes. + + In boards like Arduino Nano, UNO, or MEGA only one serial port is available, + therefore additional software serial ports can be made using the + SoftwareSerial library. +*/ + +#include "Arduino_DebugUtils.h" +#include + +SoftwareSerial mySerial(10, 11); // RX, TX + +void setup() { + mySerial.begin(9600); + Debug.setDebugOutputStream(&mySerial); + Debug.setDebugLevel(DBG_VERBOSE); + Debug.timestampOn(); +} + +int i = 0; + +void loop() { + DEBUG_VERBOSE("i = %d", i); + i++; + delay(1000); +} diff --git a/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino b/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino index ddfedfe..1806e90 100644 --- a/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino +++ b/examples/Arduino_Debug_Basic/Arduino_Debug_Basic.ino @@ -8,7 +8,7 @@ void setup() { int i = 0; void loop() { - Debug.print(DBG_INFO, "i = %d", i); + DEBUG_INFO("i = %d", i); i++; delay(1000); } diff --git a/extras/codespell-ignore-words-list.txt b/extras/codespell-ignore-words-list.txt deleted file mode 100644 index e69de29..0000000 diff --git a/keywords.txt b/keywords.txt index e5c2020..d175771 100644 --- a/keywords.txt +++ b/keywords.txt @@ -17,6 +17,11 @@ setDebugOutputStream KEYWORD2 timestampOn KEYWORD2 timestampOff KEYWORD2 print KEYWORD2 +DEBUG_ERROR KEYWORD2 +DEBUG_WARNING KEYWORD2 +DEBUG_INFO KEYWORD2 +DEBUG_DEBUG KEYWORD2 +DEBUG_VERBOSE KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/library.properties b/library.properties index fd1a3c6..b59e2da 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=Arduino_DebugUtils -version=0.1.2 +version=1.4.0 author=Arduino maintainer=Arduino sentence=Debugging module with different debug levels, timestamps and printf-style output. paragraph=This class provides functionality useful for debugging sketches via printf-style statements. category=Communication url=https://github.com/arduino-libraries/Arduino_DebugUtils -architectures=* \ No newline at end of file +architectures=* diff --git a/src/Arduino_DebugUtils.cpp b/src/Arduino_DebugUtils.cpp index 09c2c37..270927b 100644 --- a/src/Arduino_DebugUtils.cpp +++ b/src/Arduino_DebugUtils.cpp @@ -34,6 +34,9 @@ static Stream * DEFAULT_OUTPUT_STREAM = &Serial; Arduino_DebugUtils::Arduino_DebugUtils() { timestampOff(); + newlineOn(); + debugLabelOff(); + formatTimestampOff(); setDebugLevel(DEFAULT_DEBUG_LEVEL); setDebugOutputStream(DEFAULT_OUTPUT_STREAM); } @@ -46,10 +49,38 @@ void Arduino_DebugUtils::setDebugLevel(int const debug_level) { _debug_level = debug_level; } +int Arduino_DebugUtils::getDebugLevel() const { + return _debug_level; +} + void Arduino_DebugUtils::setDebugOutputStream(Stream * stream) { _debug_output_stream = stream; } +void Arduino_DebugUtils::newlineOn() { + _newline_on = true; +} + +void Arduino_DebugUtils::newlineOff() { + _newline_on = false; +} + +void Arduino_DebugUtils::debugLabelOn() { + _print_debug_label = true; +} + +void Arduino_DebugUtils::debugLabelOff() { + _print_debug_label = false; +} + +void Arduino_DebugUtils::formatTimestampOn() { + _format_timestamp_on = true; +} + +void Arduino_DebugUtils::formatTimestampOff() { + _format_timestamp_on = false; +} + void Arduino_DebugUtils::timestampOn() { _timestamp_on = true; } @@ -58,21 +89,40 @@ void Arduino_DebugUtils::timestampOff() { _timestamp_on = false; } -void Arduino_DebugUtils::print(int const debug_level, const char * fmt, ...) { - if (debug_level >= DBG_ERROR && - debug_level <= DBG_VERBOSE && - debug_level <= _debug_level) { - if (_timestamp_on) { - char timestamp[20]; - snprintf(timestamp, 20, "[ %lu ] ", millis()); - _debug_output_stream->print(timestamp); - } - - va_list args; - va_start(args, fmt); - vPrint(fmt, args); - va_end(args); - } +void Arduino_DebugUtils::print(int const debug_level, const char * fmt, ...) +{ + if (!shouldPrint(debug_level)) + return; + + if (_print_debug_label) + printDebugLabel(debug_level); + + if (_timestamp_on) + printTimestamp(); + + va_list args; + va_start(args, fmt); + vPrint(fmt, args); + va_end(args); +} + +void Arduino_DebugUtils::print(int const debug_level, const __FlashStringHelper * fmt, ...) +{ + if (!shouldPrint(debug_level)) + return; + + if (_print_debug_label) + printDebugLabel(debug_level); + + if (_timestamp_on) + printTimestamp(); + + String fmt_str(fmt); + + va_list args; + va_start(args, fmt); + vPrint(fmt_str.c_str(), args); + va_end(args); } /****************************************************************************** @@ -80,12 +130,93 @@ void Arduino_DebugUtils::print(int const debug_level, const char * fmt, ...) { ******************************************************************************/ void Arduino_DebugUtils::vPrint(char const * fmt, va_list args) { - static size_t const MSG_BUF_SIZE = 120; - char msg_buf[MSG_BUF_SIZE] = {0}; + + va_list args_copy; + va_copy(args_copy, args); + + // calculate required buffer length + int msg_buf_size = vsnprintf(nullptr, 0, fmt, args) + 1; // add one for null terminator +#if __STDC_NO_VLA__ == 1 + // in the rare case where VLA is not allowed by compiler, fall back on heap-allocated memory + char * msg_buf = new char[msg_buf_size]; +#else + char msg_buf[msg_buf_size]; +#endif + + vsnprintf(msg_buf, msg_buf_size, fmt, args_copy); + va_end(args_copy); + + if (_newline_on) { + _debug_output_stream->println(msg_buf); + } else { + _debug_output_stream->print(msg_buf); + } + +#if __STDC_NO_VLA__ == 1 + // remember to clean up memory + delete[] msg_buf; +#endif +} + +void Arduino_DebugUtils::printTimestamp() +{ + char timestamp[32]; + + if (_format_timestamp_on) + { + auto const msCount = millis(); + + uint16_t const milliseconds = msCount % 1000; // ms remaining when converted to seconds + uint16_t const allSeconds = msCount / 1000; // total number of seconds to calculate remaining values + + uint16_t const hours = allSeconds / 3600; // convert seconds to hours + uint16_t const secondsRemaining = allSeconds % 3600; // seconds left over + + uint16_t const minutes = secondsRemaining / 60 ; // convert seconds left over to minutes + uint16_t const seconds = secondsRemaining % 60; // seconds left over + + snprintf(timestamp, sizeof(timestamp), // "prints" formatted output to a char array (string) + "[ " + "%02d:" //HH: + "%02d:" //MM: + "%02d." //SS. + "%03d" //MMM + " ] ", + hours, + minutes, + seconds, + milliseconds + ); + } + else + { + snprintf(timestamp, sizeof(timestamp), "[ %lu ] ", millis()); + } - vsnprintf(msg_buf, MSG_BUF_SIZE, fmt, args); + _debug_output_stream->print(timestamp); +} + +void Arduino_DebugUtils::printDebugLabel(int const debug_level) +{ + static char const * DEBUG_MODE_STRING[5] = + { + "[DBG_ERROR ] ", + "[DBG_WARNING] ", + "[DBG_INFO ] ", + "[DBG_DEBUG ] ", + "[DBG_VERBOSE] ", + }; + + bool is_valid_debug_level = (debug_level >= DBG_ERROR) && (debug_level <= DBG_VERBOSE); + if (!is_valid_debug_level) + return; + + _debug_output_stream->print(DEBUG_MODE_STRING[debug_level]); +} - _debug_output_stream->println(msg_buf); +bool Arduino_DebugUtils::shouldPrint(int const debug_level) const +{ + return ((debug_level >= DBG_ERROR) && (debug_level <= DBG_VERBOSE) && (debug_level <= _debug_level)); } /****************************************************************************** @@ -93,6 +224,11 @@ void Arduino_DebugUtils::vPrint(char const * fmt, va_list args) { ******************************************************************************/ Arduino_DebugUtils Debug; + void setDebugMessageLevel(int const debug_level) { Debug.setDebugLevel(debug_level); -} \ No newline at end of file +} + +int getDebugMessageLevel() { + return Debug.getDebugLevel(); +} diff --git a/src/Arduino_DebugUtils.h b/src/Arduino_DebugUtils.h index 77bfe45..73e6287 100644 --- a/src/Arduino_DebugUtils.h +++ b/src/Arduino_DebugUtils.h @@ -38,6 +38,7 @@ static int const DBG_DEBUG = 3; static int const DBG_VERBOSE = 4; void setDebugMessageLevel(int const debug_level); +int getDebugMessageLevel(); /****************************************************************************** CLASS DECLARATION @@ -50,22 +51,39 @@ class Arduino_DebugUtils { Arduino_DebugUtils(); void setDebugLevel(int const debug_level); + int getDebugLevel() const; void setDebugOutputStream(Stream * stream); void timestampOn(); void timestampOff(); + void newlineOn(); + void newlineOff(); + + void debugLabelOn(); + void debugLabelOff(); + + void formatTimestampOn(); + void formatTimestampOff(); + void print(int const debug_level, const char * fmt, ...); + void print(int const debug_level, const __FlashStringHelper * fmt, ...); private: bool _timestamp_on; + bool _newline_on; + bool _print_debug_label; + bool _format_timestamp_on; int _debug_level; Stream * _debug_output_stream; void vPrint(char const * fmt, va_list args); + void printTimestamp(); + void printDebugLabel(int const debug_level); + bool shouldPrint(int const debug_level) const; }; @@ -75,4 +93,28 @@ class Arduino_DebugUtils { extern Arduino_DebugUtils Debug; -#endif /* ARDUINO_DEBUG_UTILS_H_ */ \ No newline at end of file +/************************************************************************************** + * DEFINE + **************************************************************************************/ + +#ifndef DEBUG_ERROR +# define DEBUG_ERROR(fmt, ...) Debug.print(DBG_ERROR, fmt, ## __VA_ARGS__) +#endif + +#ifndef DEBUG_WARNING +# define DEBUG_WARNING(fmt, ...) Debug.print(DBG_WARNING, fmt, ## __VA_ARGS__) +#endif + +#ifndef DEBUG_INFO +# define DEBUG_INFO(fmt, ...) Debug.print(DBG_INFO, fmt, ## __VA_ARGS__) +#endif + +#ifndef DEBUG_DEBUG +# define DEBUG_DEBUG(fmt, ...) Debug.print(DBG_DEBUG, fmt, ## __VA_ARGS__) +#endif + +#ifndef DEBUG_VERBOSE +# define DEBUG_VERBOSE(fmt, ...) Debug.print(DBG_VERBOSE, fmt, ## __VA_ARGS__) +#endif + +#endif /* ARDUINO_DEBUG_UTILS_H_ */