diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 000000000..b10d11ce9 --- /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 = alocation,bu,wan +check-filenames = +check-hidden = +skip = ./.git,./extras/test/external,./src/cbor/lib/tinycbor,./src/tls/bearssl diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..03b0e93f9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# See: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#about-the-dependabotyml-file +version: 2 + +updates: + # Configure check for outdated GitHub Actions actions in workflows. + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/dependabot/README.md + # See: https://docs.github.com/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot + - package-ecosystem: github-actions + directory: /.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 000000000..ed673ea6d --- /dev/null +++ b/.github/workflows/check-arduino.yml @@ -0,0 +1,27 @@ +name: Check Arduino + +# See: https://docs.github.com/en/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 diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 28e48bb07..5eb4f66d9 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -22,155 +22,318 @@ jobs: # Install the ArduinoIoTCloud library from the repository - source-path: ./ - name: Arduino_ConnectionHandler + - name: ArduinoHttpClient - name: Arduino_DebugUtils - name: ArduinoMqttClient + - name: Arduino_SecureElement + - name: Arduino_CloudUtils # sketch paths to compile (recursive) for all boards UNIVERSAL_SKETCH_PATHS: | - examples/ArduinoIoTCloud-Advanced - examples/ArduinoIoTCloud-Basic + - examples/ArduinoIoTCloud-Callbacks - examples/utility/ArduinoIoTCloud_Travis_CI - ARDUINOCORE_MBED_STAGING_PATH: extras/ArduinoCore-mbed - ARDUINOCORE_API_STAGING_PATH: extras/ArduinoCore-API SKETCHES_REPORTS_PATH: sketches-reports strategy: fail-fast: false matrix: - board: [ - {"fqbn": "arduino:samd:mkr1000", "type": "mkr1000"}, - {"fqbn": "arduino:samd:mkrwifi1010", "type": "nina"}, - {"fqbn": "arduino:samd:nano_33_iot", "type": "nina"}, - {"fqbn": "arduino:samd:mkrwan1300", "type": "wan"}, - {"fqbn": "arduino:samd:mkrgsm1400", "type": "gsm"}, - {"fqbn": "arduino:samd:mkrnb1500", "type": "nb"}, - {"fqbn": "arduino:mbed:envie_m4", "type": "mbed"}, - {"fqbn": "arduino:mbed:envie_m7", "type": "mbed"}, - {"fqbn": "esp8266:esp8266:huzzah", "type": "esp8266"} - ] + board: + - fqbn: arduino:samd:mkr1000 + type: mkr1000 + artifact-name-suffix: arduino-samd-mkr1000 + - fqbn: arduino:samd:mkrwifi1010 + type: nina + artifact-name-suffix: arduino-samd-mkrwifi1010 + - fqbn: arduino:samd:nano_33_iot + type: nina + artifact-name-suffix: arduino-samd-nano_33_iot + - fqbn: arduino:samd:mkrwan1300 + type: wan + artifact-name-suffix: arduino-samd-mkrwan1300 + - fqbn: arduino:samd:mkrgsm1400 + type: gsm + artifact-name-suffix: arduino-samd-mkrgsm1400 + - fqbn: arduino:samd:mkrnb1500 + type: nb + artifact-name-suffix: arduino-samd-mkrnb1500 + - fqbn: arduino:mbed_portenta:envie_m7 + type: mbed_portenta + artifact-name-suffix: arduino-mbed_portenta-envie_m7 + - fqbn: esp8266:esp8266:huzzah + type: esp8266 + artifact-name-suffix: esp8266-esp8266-huzzah + - fqbn: esp32:esp32:esp32 + type: esp32 + artifact-name-suffix: esp32-esp32-esp32 + - fqbn: arduino:mbed_nano:nanorp2040connect + type: nina + artifact-name-suffix: arduino-mbed_nano-nanorp2040connect + - fqbn: arduino:mbed_nicla:nicla_vision + type: mbed_nicla + artifact-name-suffix: arduino-mbed_nicla-nicla_vision + - fqbn: arduino:mbed_opta:opta + type: mbed_opta + artifact-name-suffix: arduino-mbed_opta-opta + - fqbn: arduino:mbed_giga:giga + type: mbed_giga + artifact-name-suffix: arduino-mbed_giga-giga + - fqbn: arduino:renesas_portenta:portenta_c33 + type: renesas_portenta + artifact-name-suffix: arduino-renesas_portenta-portenta_c33 + - fqbn: arduino:renesas_uno:unor4wifi + type: renesas_uno + artifact-name-suffix: arduino-renesas_uno-unor4wifi + - fqbn: arduino:esp32:nano_nora + type: arduino_esp32 + artifact-name-suffix: arduino-esp32-nano_nora + - fqbn: arduino:mbed_edge:edge_control + type: mbed_edge + artifact-name-suffix: arduino-mbed_edge-edge_control + - fqbn: "rp2040:rp2040:rpipicow" + type: rp2040 + artifact-name-suffix: rp2040-rp2040-rpipicow + # make board type-specific customizations to the matrix jobs include: # MKR 1000 - board: - type: "mkr1000" + type: mkr1000 platforms: | - # Install Arduino SAMD Boards via Boards Manager for the toolchain + # Install samd platform via Boards Manager - name: arduino:samd libraries: | + - name: ArduinoBearSSL - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: WiFi101 + - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule - examples/utility/Provisioning - # MKR WiFi 1010, Nano 33 IoT + # MKR WiFi 1010, Nano 33 IoT, Nano RP2040 Connect - board: - type: "nina" + type: nina platforms: | - # Install Arduino SAMD Boards via Boards Manager for the toolchain + # Install samd and mbed_nano platform via Boards Manager - name: arduino:samd + - name: arduino:mbed_nano libraries: | + - name: ArduinoBearSSL - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: WiFiNINA - name: Arduino_JSON - - name: ArduinoBearSSL + - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | + - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule - examples/utility/Provisioning - examples/utility/SelfProvisioning - # LoRaWAN boards - board: - type: "wan" + type: wan platforms: | + # Install samd platform via Boards Manager - name: arduino:samd libraries: | - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: MKRWAN - sketch-paths: + - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git + sketch-paths: | + - examples/ArduinoIoTCloud-Notecard # GSM boards - board: - type: "gsm" + type: gsm platforms: | + # Install samd platform via Boards Manager - name: arduino:samd libraries: | + - name: ArduinoBearSSL - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: MKRGSM + - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule - examples/utility/Provisioning # NB boards - board: - type: "nb" + type: nb platforms: | + # Install samd platform via Boards Manager - name: arduino:samd libraries: | + - name: ArduinoBearSSL - name: ArduinoECCX08 + - name: Blues Wireless Notecard - name: RTCZero - name: MKRNB + - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git sketch-paths: | + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule - examples/utility/Provisioning # Portenta - board: - type: "mbed" + type: mbed_portenta + platforms: | + # Install mbed_portenta platform via Boards Manager + - name: arduino:mbed_portenta + libraries: | + - name: ArduinoBearSSL + - name: ArduinoECCX08 + - name: Arduino_Cellular + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule + - examples/utility/Provisioning + # Nicla Vision + - board: + type: mbed_nicla platforms: | - # Install Arduino mbed-Enabled Boards via Boards Manager for the toolchain - - name: arduino:mbed - # Overwrite the Arduino mbed-Enabled Boards release version with version from the tip of the master branch (located in local path because of the need to first install ArduinoCore-API) - - source-path: extras/ArduinoCore-mbed - name: arduino:mbed + # Install mbed_nicla platform via Boards Manager + - name: arduino:mbed_nicla libraries: | + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule + - examples/utility/Provisioning + # Opta + - board: + type: mbed_opta + platforms: | + # Install mbed_opta platform via Boards Manager + - name: arduino:mbed_opta + libraries: | + - name: ArduinoBearSSL + - name: ArduinoECCX08 + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule + - examples/utility/Provisioning + # GIGA + - board: + type: mbed_giga + platforms: | + # Install mbed_giga platform via Boards Manager + - name: arduino:mbed_giga + libraries: | + - name: ArduinoBearSSL - name: ArduinoECCX08 + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule + - examples/utility/Provisioning + # Portenta C33 + - board: + type: renesas_portenta + platforms: | + # Install renesas_portenta platform via Boards Manager + - name: arduino:renesas_portenta + libraries: | + - name: Arduino_Cellular + - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule - examples/utility/Provisioning + # UNO R4 WiFi + - board: + type: renesas_uno + platforms: | + # Install renesas_uno platform via Boards Manager + - name: arduino:renesas_uno + libraries: | + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule + # Nano ESP32 + - board: + type: arduino_esp32 + platforms: | + # Install arduino_esp32 platform via Boards Manager + - name: arduino:esp32 + libraries: | + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule + # Edge Control + - board: + type: mbed_edge + platforms: | + # Install mbed_edge platform via Boards Manager + - name: arduino:mbed_edge + libraries: | + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule # ESP8266 boards - board: - type: "esp8266" + type: esp8266 platforms: | # Install ESP8266 platform via Boards Manager - name: esp8266:esp8266 source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json + # Use the version currently installed in Arduino Cloud + version: 2.5.0 libraries: - sketch-paths: + sketch-paths: | + - examples/ArduinoIoTCloud-Schedule + # ESP32 boards + - board: + type: esp32 + platforms: | + # Install ESP32 platform via Boards Manager + - name: esp32:esp32 + source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + libraries: | + - name: Blues Wireless Notecard + sketch-paths: | + - examples/ArduinoIoTCloud-DeferredOTA + - examples/ArduinoIoTCloud-Notecard + - examples/ArduinoIoTCloud-Schedule + # PicoW + - board: + type: rp2040 + platforms: | + # Install rp2040 platform via Boards Manager + - name: rp2040:rp2040 + source-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json steps: - name: Checkout - uses: actions/checkout@v2 - - # it's necessary to checkout the platform before installing it so that the ArduinoCore-API dependency can be added - - name: Checkout ArduinoCore-mbed - # this step only needed when the Arduino mbed-Enabled Boards platform sourced from the repository is being used - if: matrix.board.type == 'mbed' - uses: actions/checkout@v2 - with: - repository: arduino/ArduinoCore-mbed - # the arduino/actions/libraries/compile-examples action will install the platform from this path - path: ${{ env.ARDUINOCORE_MBED_STAGING_PATH }} - - - name: Remove ArduinoCore-API symlink from Arduino mbed-Enabled Boards platform - # this step only needed when the Arduino mbed-Enabled Boards platform sourced from the repository is being used - if: matrix.board.type == 'mbed' - run: rm "${{ env.ARDUINOCORE_MBED_STAGING_PATH }}/cores/arduino/api" - - - name: Checkout ArduinoCore-API - # this step only needed when the Arduino mbed-Enabled Boards platform sourced from the repository is being used - if: matrix.board.type == 'mbed' - uses: actions/checkout@v2 - with: - repository: arduino/ArduinoCore-API - # as specified at https://github.com/arduino/ArduinoCore-mbed/blob/master/README.md#installation - ref: namespace_arduino - path: ${{ env.ARDUINOCORE_API_STAGING_PATH }} + uses: actions/checkout@v4 - - name: Install ArduinoCore-API - # this step only needed when the Arduino mbed-Enabled Boards platform sourced from the repository is being used - if: matrix.board.type == 'mbed' - run: | - mv "${{ env.ARDUINOCORE_API_STAGING_PATH }}/api" "${{ env.ARDUINOCORE_MBED_STAGING_PATH }}/cores/arduino" + - name: Install ESP32 platform dependencies + if: matrix.board.type == 'esp32' + run: pip3 install pyserial - name: Compile examples - uses: arduino/compile-sketches@main + uses: arduino/compile-sketches@v1 with: + github-token: ${{ secrets.GITHUB_TOKEN }} platforms: ${{ matrix.platforms }} fqbn: ${{ matrix.board.fqbn }} libraries: | @@ -179,7 +342,7 @@ jobs: sketch-paths: | ${{ env.UNIVERSAL_SKETCH_PATHS }} ${{ matrix.sketch-paths }} - enable-deltas-report: 'true' + enable-deltas-report: "true" sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} - name: Write data to size trends report spreadsheet @@ -193,7 +356,7 @@ jobs: - name: Save memory usage change report as artifact if: github.event_name == 'pull_request' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: - name: ${{ env.SKETCHES_REPORTS_PATH }} + name: sketches-report-${{ matrix.board.artifact-name-suffix }} path: ${{ env.SKETCHES_REPORTS_PATH }} diff --git a/.github/workflows/report-size-deltas.yml b/.github/workflows/report-size-deltas.yml index 12c162fa4..39e2a0ad2 100644 --- a/.github/workflows/report-size-deltas.yml +++ b/.github/workflows/report-size-deltas.yml @@ -1,14 +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: - - cron: '*/5 * * * *' + # 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@main + uses: arduino/report-size-deltas@v1 with: - # The name of the workflow artifact created by the "Compile Examples" workflow - sketches-reports-source: sketches-reports + # 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 index f77b7f9cc..4253ed878 100644 --- a/.github/workflows/spell-check.yml +++ b/.github/workflows/spell-check.yml @@ -1,6 +1,8 @@ name: Spell Check -on: [push, pull_request] +on: + - push + - pull_request jobs: build: @@ -8,10 +10,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Spell check - uses: arduino/actions/libraries/spell-check@master - with: - ignore-words-list: extras/codespell-ignore-words-list.txt - skip-paths: ./extras/test/external,./src/cbor/lib/tinycbor + 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 000000000..53a9f54f4 --- /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/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index e4288e97f..b2194c155 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -23,19 +23,34 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - uses: arduino/cpp-test-action@main with: - runtime-path: extras/test/build/bin/testArduinoIoTCloud + runtime-paths: | + - extras/test/build/bin/testArduinoIoTCloud coverage-exclude-paths: | - '*/extras/test/*' - '/usr/*' - - '*/src/cbor/lib/*' coverage-data-path: ${{ env.COVERAGE_DATA_PATH }} + # A token is used to avoid intermittent spurious job failures caused by rate limiting. + - name: Set up Codecov upload token + run: | + if [[ "${{ github.repository }}" == "arduino-libraries/ArduinoIoTCloud" ]]; then + # In order to avoid uploads of data from forks, only use the token for runs in the parent repo. + # Token is intentionally exposed. + # See: https://community.codecov.com/t/upload-issues-unable-to-locate-build-via-github-actions-api/3954 + CODECOV_TOKEN="47827969-3fda-4ba1-9506-e8d0834ed88c" + else + # codecov/codecov-action does unauthenticated upload if empty string is passed via the `token` input. + CODECOV_TOKEN="" + fi + echo "CODECOV_TOKEN=$CODECOV_TOKEN" >> "$GITHUB_ENV" + - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: file: "${{ env.COVERAGE_DATA_PATH }}" fail_ci_if_error: true + token: ${{ env.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 16e30d984..c71e1ae86 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,22 @@ *.orig .vs build +.idea/ +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +CMakeUserPresets.json + +# External projects +*-prefix/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/README.md b/README.md index bfd3a3ff5..4b248e784 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ `ArduinoIoTCloud` ================= + +[![Check Arduino status](https://github.com/arduino-libraries/ArduinoIoTCloud/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/ArduinoIoTCloud/actions/workflows/check-arduino.yml) [![Compile Examples](https://github.com/arduino-libraries/ArduinoIoTCloud/workflows/Compile%20Examples/badge.svg)](https://github.com/arduino-libraries/ArduinoIoTCloud/actions?workflow=Compile+Examples) [![Spell Check](https://github.com/arduino-libraries/ArduinoIoTCloud/workflows/Spell%20Check/badge.svg)](https://github.com/arduino-libraries/ArduinoIoTCloud/actions?workflow=Spell+Check) [![Unit Tests](https://github.com/arduino-libraries/ArduinoIoTCloud/workflows/Unit%20Tests/badge.svg)](https://github.com/arduino-libraries/ArduinoIoTCloud/actions?workflow=Unit+Tests) @@ -8,10 +10,12 @@ ### What? The `ArduinoIoTCloud` library is the central element of the firmware enabling certain Arduino boards to connect to the [Arduino IoT Cloud](https://www.arduino.cc/en/IoT/HomePage). The following boards are supported: -* **WiFi**: [`MKR 1000`](https://store.arduino.cc/arduino-mkr1000-wifi), [`MKR WiFi 1010`](https://store.arduino.cc/arduino-mkr-wifi-1010), [`Nano 33 IoT`](https://store.arduino.cc/arduino-nano-33-iot), [`Portenta H7`](https://store.arduino.cc/portenta-h7), `ESP8266` +* **WiFi**: [`MKR 1000`](https://store.arduino.cc/arduino-mkr1000-wifi), [`MKR WiFi 1010`](https://store.arduino.cc/arduino-mkr-wifi-1010), [`Nano 33 IoT`](https://store.arduino.cc/arduino-nano-33-iot), [`Portenta H7`](https://store.arduino.cc/portenta-h7), [`Nano RP2040 Connect`](https://store.arduino.cc/products/arduino-nano-rp2040-connect), [`Nicla Vision`](https://store.arduino.cc/products/nicla-vision), [`OPTA WiFi`](https://store.arduino.cc/products/opta-wifi), [`GIGA R1 WiFi`](https://store.arduino.cc/products/giga-r1-wifi), [`Portenta C33`](https://store.arduino.cc/products/portenta-c33), [`UNO R4 WiFi`](https://store.arduino.cc/products/uno-r4-wifi), [`Nano ESP32`](https://store.arduino.cc/products/nano-esp32), [`ESP8266`](https://github.com/esp8266/Arduino/releases/tag/2.5.0), [`ESP32`](https://github.com/espressif/arduino-esp32/releases/tag/2.0.5) * **GSM**: [`MKR GSM 1400`](https://store.arduino.cc/arduino-mkr-gsm-1400-1415) * **5G**: [`MKR NB 1500`](https://store.arduino.cc/arduino-mkr-nb-1500-1413) * **LoRa**: [`MKR WAN 1300/1310`](https://store.arduino.cc/mkr-wan-1310) +* **Ethernet**: [`Portenta H7`](https://store.arduino.cc/products/portenta-h7) + [`Vision Shield Ethernet`](https://store.arduino.cc/products/arduino-portenta-vision-shield-ethernet), [`Max Carrier`](https://store.arduino.cc/products/portenta-max-carrier), [`Breakout`](https://store.arduino.cc/products/arduino-portenta-breakout), [`Portenta Machine Control`](https://store.arduino.cc/products/arduino-portenta-machine-control), [`OPTA WiFi`](https://store.arduino.cc/products/opta-wifi), [`OPTA RS485`](https://store.arduino.cc/products/opta-rs485), [`OPTA Lite`](https://store.arduino.cc/products/opta-lite), [`Portenta C33`](https://store.arduino.cc/products/portenta-c33) + [`Vision Shield Ethernet`](https://store.arduino.cc/products/arduino-portenta-vision-shield-ethernet) +* **Notecard**: [Provides Cellular/LoRa/Satellite/Wi-Fi to any modern board/architecture](examples/ArduinoIoTCloud-Notecard/README.md) ### How? 1) Register your Arduino IoT Cloud capable board via [Arduino IoT Cloud](https://create.arduino.cc/iot) (Devices Section). @@ -66,8 +70,29 @@ void onLedChange() { ``` ### FAQ -* Device can not subscribe to `THING_ID` +#### Watchdog +The [1.0.0](https://github.com/arduino-libraries/ArduinoIoTCloud/releases/tag/1.0.0) release of this library adds watchdog functionality to all ATSAMD21G18 based cloud connected boards. A watchdog is simply an electronic timer counting down from a preset start value which, upon reaching zero, triggers a reset of the microcontroller. It can be used to automatically recover from temporary hardware faults or unrecoverable software errors. In order to avoid the watchdog from reaching zero the countdown timer needs to be regularly re-set to its start value. This is happening within `ArduinoCloud.update()` which is periodically called at the start of the `loop()` function. Although the watchdog is automatically enabled it can be disabled by setting the second parameter of `ArduinoCloud.begin(...)` to `false`: +```C++ +ArduinoCloud.begin(ArduinoIoTPreferredConnection, false). +``` + +Whatchdog is enabled by default using the following boards: [`MKR 1000`](https://store.arduino.cc/arduino-mkr1000-wifi), [`MKR WiFi 1010`](https://store.arduino.cc/arduino-mkr-wifi-1010), [`MKR GSM 1400`](https://store.arduino.cc/arduino-mkr-gsm-1400-1415), [`MKR NB 1500`](https://store.arduino.cc/arduino-mkr-nb-1500-1413), [`MKR WAN 1300/1310`](https://store.arduino.cc/mkr-wan-1310), [`Nano 33 IoT`](https://store.arduino.cc/arduino-nano-33-iot), [`Portenta H7`](https://store.arduino.cc/portenta-h7), [`Nano RP2040 Connect`](https://store.arduino.cc/products/arduino-nano-rp2040-connect), [`Nicla Vision`](https://store.arduino.cc/products/nicla-vision), [`OPTA WiFi`](https://store.arduino.cc/products/opta-wifi), [`OPTA RS485`](https://store.arduino.cc/products/opta-rs485), [`OPTA Lite`](https://store.arduino.cc/products/opta-lite), [`GIGA R1 WiFi`](https://store.arduino.cc/products/giga-r1-wifi) + +#### Device can not subscribe to `THING_ID` ``` ArduinoIoTCloudTCP::handle_SubscribeMqttTopics could not subscribe to /a/t/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/e/i ``` -In this case either the device has not been associated with the thing within the Arduino IoT Cloud GUI configuration or there's a typo in the thing id. +In this case either the device has not been associated with the thing within the Arduino IoT Cloud GUI configuration or there's a typo in the thing ID. + +#### OTA +OTA is supported by the following boards: +[`MKR WiFi 1010`](https://store.arduino.cc/arduino-mkr-wifi-1010), [`Nano 33 IoT`](https://store.arduino.cc/arduino-nano-33-iot), [`Portenta H7`](https://store.arduino.cc/portenta-h7), [`Nano RP2040 Connect`](https://store.arduino.cc/products/arduino-nano-rp2040-connect), [`Nicla Vision`](https://store.arduino.cc/products/nicla-vision), [`OPTA WiFi`](https://store.arduino.cc/products/opta-wifi), [`OPTA RS485`](https://store.arduino.cc/products/opta-rs485), [`OPTA Lite`](https://store.arduino.cc/products/opta-lite), [`GIGA R1 WiFi`](https://store.arduino.cc/products/giga-r1-wifi), [`Nano ESP32`](https://store.arduino.cc/products/nano-esp32), [`ESP32`](https://github.com/espressif/arduino-esp32/releases/tag/2.0.5) + +#### Authentication +Boards can authenticate to the ArduinoIoTCloud servers using 3 methods: + + * `DEVICE_LOGIN_NAME` and `DEVICE_KEY`. This values are defined in the `thingProperties.h` file and included in the Sketch. Boards that are using this method are: [`UNO R4 WiFi`](https://store.arduino.cc/products/uno-r4-wifi), [`Nano ESP32`](https://store.arduino.cc/products/nano-esp32), [`ESP8266`](https://github.com/esp8266/Arduino/releases/tag/2.5.0), [`ESP32`](https://github.com/espressif/arduino-esp32/releases/tag/2.0.5) + + * `DEVICE_CERTIFICATE` and `PRIVATE_KEY`. This values are stored inside the board secure element during the device provisioning phase. Boards that are using this method are: [`MKR 1000`](https://store.arduino.cc/arduino-mkr1000-wifi), [`MKR WiFi 1010`](https://store.arduino.cc/arduino-mkr-wifi-1010), [`MKR GSM 1400`](https://store.arduino.cc/arduino-mkr-gsm-1400-1415), [`MKR NB 1500`](https://store.arduino.cc/arduino-mkr-nb-1500-1413), [`Nano 33 IoT`](https://store.arduino.cc/arduino-nano-33-iot), [`Portenta H7`](https://store.arduino.cc/portenta-h7), [`Nano RP2040 Connect`](https://store.arduino.cc/products/arduino-nano-rp2040-connect), [`Nicla Vision`](https://store.arduino.cc/products/nicla-vision), [`OPTA WiFi`](https://store.arduino.cc/products/opta-wifi), [`OPTA RS485`](https://store.arduino.cc/products/opta-rs485), [`OPTA Lite`](https://store.arduino.cc/products/opta-lite), [`GIGA R1 WiFi`](https://store.arduino.cc/products/giga-r1-wifi), [`Portenta C33`](https://store.arduino.cc/products/portenta-c33) + + * `APP_EUI` and `APP_KEY`. This values are defined in the `thingProperties.h` file and included in the Sketch. Boards that are using this method are: [`MKR WAN 1300/1310`](https://store.arduino.cc/mkr-wan-1310) diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 000000000..e4825e2d6 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,504 @@ +## ArduinoCloud Class (Base) + +### `ArduinoCloud (Class)` + +#### Description + +`ArduinoIoTCloud` is a class for interfacing with the Arduino IoT Cloud. It provides an interface for managing IoT Cloud connectivity, updating data, and handling events. + +Depending on what type of connection is used (TCP/LPWAN), either the `ArduinoIoTCloudTCP` or `ArduinoIoTCloudLPWAN` base classes are initialized. This specification is done in the Arduino IoT Cloud's "Thing" interface, and is reflected in the automatically generated `thingProperties.h` file. + +The above classes are inherited from the `ArduinoIoTCloudClass` base class, with methods used depending on what connection is used. + +#### Syntax + +``` +ArduinoCloud.method() +``` + +### `push()` + +#### Description + +Pushes data to the IoT Cloud. + +#### Syntax + +``` +ArduinoCloud.push() +``` + +#### Parameters + +None. + +#### Returns + +Nothing. + +### `setTimestamp()` + +Sets a timestamp for a cloud variable. + +#### Syntax + +``` +ArduinoCloud.setTimestamp(cloudVariable, timestamp) +``` + +#### Parameters + +- `String` - property name +- `unsigned long const` - timestamp + + +### `getThingId()` + +Gets the Thing ID. + +#### Syntax + +``` +ArduinoCloud.getThingId() +``` + +#### Parameters +None. + +#### Returns +- `String` - Thing ID. + +### `setDeviceId()` + +Sets the Device ID. + +#### Syntax + +``` +ArduinoCloud.setDeviceId(deviceId) +``` + +#### Parameters + +- `String` - Device ID. + +### `getDeviceId()` + +Gets the Device ID. + +#### Syntax + +``` +ArduinoCloud.getDeviceId() +``` + +#### Parameters + +None + +#### Returns + +- `String` - Device ID. + +### `getConnection()` + +Gets the connection handler used. + +#### Syntax + +``` +ArduinoCloud.getConnection() +``` + +#### Parameters + +### `getInternalTime()` + +Gets the internal time. + +#### Syntax + +``` +ArduinoCloud.getInternalTime() +``` + +#### Parameters +None. + +#### Returns +- `unsigned long` - internal time + +### `getLocalTime()` + +Gets the local time (based on your time zone). + +#### Syntax + +``` +ArduinoCloud.getLocalTime() +``` + +#### Parameters +None + +#### Returns +- `unsigned long` - local time + + +### `addCallback()` + +Adds a callback function for IoT Cloud events. + +#### Syntax + +``` +ArduinoCloud.addCallback(ArduinoIoTCloudEvent::CONNECT, doThisOnConnect); +ArduinoCloud.addCallback(ArduinoIoTCloudEvent::SYNC, doThisOnSync); +ArduinoCloud.addCallback(ArduinoIoTCloudEvent::DISCONNECT, doThisOnDisconnect); +``` + +#### Parameters +- Event (CONNECT, SYNC, DISCONNECT) +- Callback function + +#### Example + +See the [ArduinoIoTCloud-Callbacks](https://github.com/arduino-libraries/ArduinoIoTCloud/blob/master/examples/ArduinoIoTCloud-Callbacks/ArduinoIoTCloud-Callbacks.ino) example. + + + +## ArduinoCloud Class (TCP) + +### `begin()` (TCP) + +#### Description + +Initialises the library with the connection handler specified in `thingProperties.h`. This is automatically generated based on the type of device. + +#### Syntax + +``` +ArduinoCloud.begin() +``` + +#### Parameters + +The `ArduinoIoTPreferredConnection` object is created automatically inside a `thingProperties.h` file when configuring a Thing and depends on what type of device you use. The connection handler classes are not part of the Arduino IoT Cloud, but exists inside the [Arduino_ConnectionHandler](https://github.com/arduino-libraries/Arduino_ConnectionHandler) library (listed as a dependency). + +For example, using the `WiFiConnectionHandler`, we use the `SSID`, `PASS` parameters which are defined in the `arduino_secrets.h` file, also generated based on what you input in the Arduino IoT Cloud interface. + +``` +WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS); +``` + +#### Returns +Nothing. + +### `update()` (TCP) + +#### Description + +**Note:** for LoRa devices, the `update()` function is implemented differently. See + +Responsible for updating the IoT Cloud connection. +- First it feeds a watchdog timer, making sure it is not stuck anywhere, +- then sends variable updates to the IoT Cloud (via MQTT client) +- finally, it checks for new data from the IoT Cloud (via the MQTT client) + +Any IoT Cloud sketch needs to continuously call the `update()` function, as it will otherwise time out and reset. The `update()` is by default placed inside of the `loop()`. + +**Note:** do not use the `delay()` function in the sketch, as the watchdog timer will cause the board to reset and attempt to reconnect. + +#### Syntax + +``` +ArduinoCloud.update() +``` + +#### Parameters +None. + +#### Returns +Nothing. + + +### `connected()` (TCP) + +#### Description + +Checks the connection status and returns an integer the connection status (int). + +#### Syntax + +``` +ArduinoCloud.connected() +``` + +#### Parameters +None. + +#### Returns +- `int` - status of connection + +### `printDebugInfo()` (TCP) + +#### Description + +Print any available debug information. + +#### Parameters +None + +#### Returns +Nothing. + +### `addProperty()` (TCP) + +#### Description + +Adds a variable/property with a set of parameters. + +#### Syntax +``` +ArduinoCloud.addProperty(cloudVariable, tag, permission, policy, callbackFunction) +``` + +#### Parameters +- `cloudVariable` - name of the variable/property. +- `permission` - can either be `READ` / `WRITE` or `READWRITE` +- `policy` - `ON_CHANGE` (whenever variable data changes) or ` * SECONDS`. `` is specified in the Thing configuration. +- `callBackFunction` - by default, a callback function is added to a variable with **WRITE** permissions. A variable called `test` will automatically be added as `onTestChange` which also is added to your sketch. + +#### Returns +Nothing. + +### `setBoardId()` + +#### Description + +This method is only enabled if you are using an ESP32/ESP8266 board, and sets the board/device id. + +Currently, the following official Arduino boards uses this method: +- [Arduino UNO R4 WiFi](https://store.arduino.cc/products/uno-r4-wifi) +- [Arduino Nano ESP32](https://store.arduino.cc/products/nano-esp32) + +#### Syntax + +``` +ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME); +``` + +### `setSecretDeviceKey()` + +#### Description + +This method is only enabled if you are using an ESP32/ESP8266 based board, and sets the secret device key for the device. The secret key is only obtainable from the Arduino IoT Cloud during device configuration. + +#### Syntax + +``` +ArduinoCloud.setSecretDeviceKey(DEVICE_KEY); +``` + +### `getBrokerAddress()` + +#### Description + +Get the MQTT broker address used. + +#### Syntax + +``` +ArduinoCloud.getBrokerAddress() +``` + +#### Parameters +None. + +#### Return +- `String` - the MQTT broker address. + +### `getBrokerPort()` + +#### Description + +Get the MQTT broker port used. + +#### Syntax + +``` +ArduinoCloud.getBrokerPort() +``` + +#### Parameters +None. + +#### Return +- `int` - the MQTT broker port. + + + +## ArduinoCloud Class (LPWAN) + +### `begin()` (LPWAN) + +### Description +This function initializes the ArduinoIoTCloudLPWAN library with the specified connection handler and retry settings. + +#### Parameters +- `ConnectionHandler` - object containing `APPEUI`, `APPKEY` and frequency band (e.g. `_lora_band::EU868`) +- `bool` - a boolean value that decides whether retry functionality is enabled (`true`) or disabled (`false`) + +#### Returns + +- `int` - returns `1` on successful initialization. + +### `update()` (LPWAN) + +#### Description + +This method handles the update between the board and the IoT Cloud. + +#### Syntax + +``` +ArduinoCloud.update() +``` + +#### Parameters +None. + +#### Returns +Nothing. + + +### `connected()` (LPWAN) + +#### Description + +Checks the connection status and returns an integer the connection status (int). + +#### Syntax + +``` +ArduinoCloud.connected() +``` + +#### Parameters +None. + +#### Returns +- `int` - status of connection + +### `printDebugInfo()` (LPWAN) + +#### Description + +Print any available debug information. + +#### Parameters +None. + +#### Returns +Nothing. + +### `addProperty()` (LPWAN) + +#### Description + +Adds a variable/property with a set of parameters. + +#### Syntax +``` +ArduinoCloud.addProperty(cloudVariable, tag, permission, policy, callbackFunction) +``` + +#### Parameters +- `cloudVariable` - name of the variable/property. +- `permission` - can either be `READ` / `WRITE` or `READWRITE` +- `tag` - matches the cloud and local variables with a number. E.g. adding a second variable will have the tag `2`. +- `policy` - `ON_CHANGE` (whenever variable data changes) or ` * SECONDS`. `` is specified in the Thing configuration. +- `callBackFunction` - by default, a callback function is added to a variable with **WRITE** permissions. A variable called `test` will automatically be added as `onTestChange` which also is added to your sketch. + +#### Returns +Nothing. + +### `setThingId()` + +Sets the Thing ID. + +#### Syntax + +``` +ArduinoCloud.setThingId(thingId) +``` + +#### Parameters +- `String` - your Thing's ID. Obtainable in the IoT Cloud interface. + + +### `isRetryEnabled()` + +#### Description +This method is used to check whether retry functionality is enabled. + +#### Parameters +None. + +#### Returns +- `bool` - returns `true` if retry functionality is enabled, otherwise returns `false`. + + +### `getMaxRetry()` + +#### Description +This method is used to retrieve the maximum number of retry attempts. + +#### Parameters +None. + +#### Returns +- `int` - returns the maximum number of retry attempts as an integer. + +### `getIntervalRetry()` + +#### Description +This method is used to retrieve the interval in milliseconds between the retry attempts. + +#### Parameters +None. + +#### Returns +- `long` - returns the interval between retry attempts in milliseconds. + +### `enableRetry()` + +#### Description +This method is used to enable or disable retry functionality. + +#### Parameters +- `bool` - if `true`, retry functionality is enabled; if `false`, retry functionality is disabled. + +#### Returns +Nothing. + +### `setMaxRetry()` + +Used to set the maximum number of retry attempts. + +#### Parameters +- `int` - the maximum number of retry attempts to set. + +#### Returns +Nothing. + +### `setIntervalRetry()` + +#### Description +Sets the interval in milliseconds between retry attempts. + +#### Parameters +- `long` - the interval between retry attempts in milliseconds to set. + +#### Returns +Nothing. \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 000000000..16c3cea7b --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,36 @@ +# ArduinoIoTCloud + +The ArduinoIoTCloud library is the central element of the firmware enabling certain Arduino boards to connect to the [Arduino IoT Cloud](https://create.arduino.cc/iot/). + +- To get started, check out the [official documentation](https://docs.arduino.cc/arduino-cloud/) +- For source code, check out the [GitHub repository](https://github.com/arduino-libraries/ArduinoIoTCloud) + +## thingProperties.h + +When you are create and configure a Thing in the [Arduino IoT Cloud interface](https://create.arduino.cc/iot/), the `thingProperties.h` file is generated automatically. + +This file adds all the variable/properties along with its specifications (e.g. update policy, read/write permissions), and the type of connection handler depending on what device you have attached. + +Methods such as `addProperty()` and `setThingId()` are implemented here, so there's no need for manually entering anything. + +We recommend that **you do not edit this file** as it is automatically updating whenever you make changes in the Arduino IoT Cloud. + +### Offline Editing + +If you have set up your sketch in an offline environment, make sure that whatever changes you make are reflected in this file, as it will be updated in the online environment. + +## TCP / LPWAN + +Depending on what type of connection is used (TCP/LPWAN), either the `ArduinoIoTCloudTCP` or `ArduinoIoTCloudLPWAN` base classes are initialized. This specification is done in the Arduino IoT Cloud's "Thing" interface, and is reflected in the automatically generated `thingProperties.h` file. + +- If a board is configured as a **TCP** device, it will automatically include the `ArduinoIoTCloudTCP.h` file, and methods of the `ArduinoIoTCloudTCP` class will be made available. This is the most common option. +- If a board is configured as an **LPWAN** device, it will instead include the `ArduinoIoTCloudLPWAN.h` file, and methods of the `ArduinoIoTCloudLPWAN` class will be made available. + +As a result, functions such as `begin()`, `update()`, `connected()` and `printDebugInfo()` are documented under each corresponding class. + +## Connection Handler Library + +When the library is initialized via the `begin()` function, it will choose the specified **connection handler**, which is automatically added to your `thingProperties.h` file when configuring a Thing. + +The connection handler is done via another library, [Arduino_ConnectionHandler](https://github.com/arduino-libraries/Arduino_ConnectionHandler), which supports Wi-Fi®, GSM, NB-IoT, LoRa® & Ethernet. + diff --git a/examples/ArduinoIoTCloud-AWS-Basic/ArduinoIoTCloud-AWS-Basic.ino b/examples/ArduinoIoTCloud-AWS-Basic/ArduinoIoTCloud-AWS-Basic.ino new file mode 100644 index 000000000..3213a3bf8 --- /dev/null +++ b/examples/ArduinoIoTCloud-AWS-Basic/ArduinoIoTCloud-AWS-Basic.ino @@ -0,0 +1,151 @@ +#include "arduino_secrets.h" +/* + This sketch demonstrates how to connect to ArduinoIoTCloud and AWS IoT core. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what +*/ + +#include "thingProperties.h" +#include "aws_secrets.h" + +Client& getDefaultClient() { + switch(ArduinoIoTPreferredConnection.getInterface()) { + +#ifdef BOARD_HAS_WIFI + case NetworkAdapter::WIFI: + static WiFiClient wclient; + return wclient; +#endif + +#ifdef BOARD_HAS_ETHERNET + case NetworkAdapter::ETHERNET: + static EthernetClient eclient; + return eclient; +#endif + + default: + Serial.println("Error: could not create default AWS client"); + break; + } +} + +unsigned long publishMillis = 0; +unsigned long connectMillis = 0; + +BearSSLClient sslClientAWS(getDefaultClient()); +MqttClient mqttClientAWS(sslClientAWS); + +void setup() { + /* Initialize serial and wait up to 5 seconds for port to open */ + Serial.begin(9600); + + /* Configure LED pin as an output */ + pinMode(LED_BUILTIN, OUTPUT); + + /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ + initProperties(); + + /* Initialize Arduino IoT Cloud library */ + ArduinoCloud.begin(ArduinoIoTPreferredConnection, true, "iot.arduino.cc"); + + setDebugMessageLevel(5); + ArduinoCloud.printDebugInfo(); + + /* Initialize AWS Client */ + ArduinoBearSSL.onGetTime(getTime); + sslClientAWS.setEccSlot(AWS_SLOT, AWS_CERTIFICATE); + + mqttClientAWS.setId("ArduinoAWSClient"); + mqttClientAWS.onMessage(onMessageReceived); + mqttClientAWS.setConnectionTimeout(10 * 1000); + mqttClientAWS.setKeepAliveInterval(30 * 1000); + mqttClientAWS.setCleanSession(false); +} + +void loop() { + ArduinoCloud.update(); + potentiometer = analogRead(A0); + seconds = millis() / 1000; + + if (!ArduinoCloud.connected()) { + return; + } + + if (!mqttClientAWS.connected()) { + if (millis() - connectMillis > 5000) { + connectMillis = millis(); + // MQTT client is disconnected, connect + if (!connectMQTT()) { + return; + } + } else { + return; + } + } + + // poll for new MQTT messages and send keep alive + mqttClientAWS.poll(); + + // publish a message roughly every 5 seconds. + if (millis() - publishMillis > 5000) { + publishMillis = millis(); + + publishMessage(); + } +} + +/* + * 'onLedChange' is called when the "led" property of your Thing changes + */ +void onLedChange() { + Serial.print("LED set to "); + Serial.println(led); + digitalWrite(LED_BUILTIN, led); +} + +void onMessageReceived(int messageSize) +{ + // we received a message, print out the topic and contents + Serial.print("Received a message with topic '"); + Serial.print(mqttClientAWS.messageTopic()); + Serial.print("', length "); + Serial.print(messageSize); + Serial.println(" bytes:"); + + for (int i = 0; i < messageSize; i++) { + const char c = mqttClientAWS.read(); + Serial.print(c); + } + Serial.println(); +} + +int connectMQTT() { + Serial.print("Attempting to connect to MQTT broker: "); + Serial.print(AWS_BROKER); + Serial.println(" "); + + if (!mqttClientAWS.connect(AWS_BROKER, 8883)) { + // failed, retry + Serial.print("."); + return 0; + } + Serial.println(); + + Serial.println("You're connected to the MQTT broker"); + Serial.println(); + + // subscribe to a topic + mqttClientAWS.subscribe("arduino/incoming"); + return 1; +} + +void publishMessage() { + Serial.println("Publishing message"); + + // send message, the Print interface can be used to set the message contents + mqttClientAWS.beginMessage("arduino/outgoing"); + mqttClientAWS.print("hello "); + mqttClientAWS.print(millis()); + mqttClientAWS.endMessage(); +} diff --git a/examples/ArduinoIoTCloud-AWS-Basic/arduino_secrets.h b/examples/ArduinoIoTCloud-AWS-Basic/arduino_secrets.h new file mode 100644 index 000000000..dee3e7210 --- /dev/null +++ b/examples/ArduinoIoTCloud-AWS-Basic/arduino_secrets.h @@ -0,0 +1,2 @@ +#define SECRET_SSID "" +#define SECRET_OPTIONAL_PASS "" diff --git a/examples/ArduinoIoTCloud-AWS-Basic/aws_secrets.h b/examples/ArduinoIoTCloud-AWS-Basic/aws_secrets.h new file mode 100644 index 000000000..07131d1da --- /dev/null +++ b/examples/ArduinoIoTCloud-AWS-Basic/aws_secrets.h @@ -0,0 +1,10 @@ +/* Fill in the hostname of your AWS IoT broker */ +#define AWS_BROKER "" + +#define AWS_SLOT 4 + +/* Fill in the boards public certificate */ +const char AWS_CERTIFICATE[] = R"( +-----BEGIN CERTIFICATE----- +-----END CERTIFICATE----- +)"; diff --git a/examples/ArduinoIoTCloud-AWS-Basic/thingProperties.h b/examples/ArduinoIoTCloud-AWS-Basic/thingProperties.h new file mode 100644 index 000000000..6717ea377 --- /dev/null +++ b/examples/ArduinoIoTCloud-AWS-Basic/thingProperties.h @@ -0,0 +1,21 @@ +// Code generated by Arduino IoT Cloud, DO NOT EDIT. + +#include +#include + +const char SSID[] = SECRET_SSID; // Network SSID (name) +const char PASS[] = SECRET_OPTIONAL_PASS; // Network password (use for WPA, or use as key for WEP) + +void onLedChange(); + +bool led; +int potentiometer; +int seconds; + +void initProperties() { + ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, Permission::Read).publishOnChange(1); +} + +WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_SSID, SECRET_OPTIONAL_PASS); diff --git a/examples/ArduinoIoTCloud-Advanced/ArduinoIoTCloud-Advanced.ino b/examples/ArduinoIoTCloud-Advanced/ArduinoIoTCloud-Advanced.ino index 181205776..f5f9b03b9 100644 --- a/examples/ArduinoIoTCloud-Advanced/ArduinoIoTCloud-Advanced.ino +++ b/examples/ArduinoIoTCloud-Advanced/ArduinoIoTCloud-Advanced.ino @@ -1,22 +1,30 @@ /* This sketch demonstrates how to use more complex cloud data types such as a colour or coordinates. - This sketch is compatible with: - - MKR 1000 - - MKR WIFI 1010 - - MKR GSM 1400 - - MKR NB 1500 - - MKR WAN 1300/1310 - - ESP 8266 + IMPORTANT: + This sketch works with WiFi, GSM, NB, Ethernet and Lora enabled boards supported by Arduino IoT Cloud. + On a LoRa board, if it is configured as a class A device (default and preferred option), + values from Cloud dashboard are received only after a value is sent to Cloud. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what */ -#include "arduino_secrets.h" #include "thingProperties.h" void setup() { /* Initialize serial and wait up to 5 seconds for port to open */ Serial.begin(9600); - for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime > 5000); ) { } + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ initProperties(); @@ -24,7 +32,6 @@ void setup() { /* Initialize Arduino IoT Cloud library */ ArduinoCloud.begin(ArduinoIoTPreferredConnection); - setDebugMessageLevel(DBG_INFO); ArduinoCloud.printDebugInfo(); } diff --git a/examples/ArduinoIoTCloud-Advanced/arduino_secrets.h b/examples/ArduinoIoTCloud-Advanced/arduino_secrets.h index fc0b0661e..4134e9377 100644 --- a/examples/ArduinoIoTCloud-Advanced/arduino_secrets.h +++ b/examples/ArduinoIoTCloud-Advanced/arduino_secrets.h @@ -1,18 +1,22 @@ #include -/* MKR1000, MKR WiFi 1010 */ +/* A complete list of supported boards with WiFi is available here: + * https://github.com/arduino-libraries/ArduinoIoTCloud/#what + */ #if defined(BOARD_HAS_WIFI) - #define SECRET_SSID "YOUR_WIFI_NETWORK_NAME" - #define SECRET_PASS "YOUR_WIFI_PASSWORD" + #define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" #endif -/* ESP8266 */ -#if defined(BOARD_ESP8266) +/* ESP8266 ESP32 */ +#if !defined(BOARD_HAS_SECURE_ELEMENT) #define SECRET_DEVICE_KEY "my-device-password" #endif -/* MKR GSM 1400 */ -#if defined(BOARD_HAS_GSM) +/* MKR GSM 1400 */ /* MKR NB 1500 */ /* Portenta CAT.M1/NB IoT GNSS Shield */ +/* Portenta H7 and C33 + Portenta Mid Carrier + 4G Module */ +#if defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) || \ + defined(BOARD_HAS_CATM1_NBIOT) || defined(BOARD_HAS_CELLULAR) #define SECRET_PIN "" #define SECRET_APN "" #define SECRET_LOGIN "" @@ -25,10 +29,10 @@ #define SECRET_APP_KEY "" #endif -/* MKR NB 1500 */ -#if defined(BOARD_HAS_NB) - #define SECRET_PIN "" - #define SECRET_APN "" - #define SECRET_LOGIN "" - #define SECRET_PASS "" +/* Portenta H7 + Ethernet shield */ +#if defined(BOARD_HAS_ETHERNET) + #define SECRET_OPTIONAL_IP "" + #define SECRET_OPTIONAL_DNS "" + #define SECRET_OPTIONAL_GATEWAY "" + #define SECRET_OPTIONAL_NETMASK "" #endif diff --git a/examples/ArduinoIoTCloud-Advanced/thingProperties.h b/examples/ArduinoIoTCloud-Advanced/thingProperties.h index 467e2e5e4..482b2991e 100644 --- a/examples/ArduinoIoTCloud-Advanced/thingProperties.h +++ b/examples/ArduinoIoTCloud-Advanced/thingProperties.h @@ -1,8 +1,18 @@ #include #include +#include "arduino_secrets.h" -#define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -#define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#if !(defined(HAS_TCP) || defined(HAS_LORA)) + #error "Please check Arduino IoT Cloud supported boards list: https://github.com/arduino-libraries/ArduinoIoTCloud/#what" +#endif + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +#if defined(HAS_LORA) + #define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif void onSwitchButtonChange(); void onColorChange(); @@ -12,28 +22,39 @@ CloudLocation location; CloudColor color; void initProperties() { -#if defined(BOARD_ESP8266) +#if defined(HAS_TCP) + ArduinoCloud.addProperty(switchButton, Permission::Write).onUpdate(onSwitchButtonChange); + ArduinoCloud.addProperty(location, Permission::Read).publishOnChange(0.0f); + ArduinoCloud.addProperty(color, Permission::ReadWrite).onUpdate(onColorChange); + +#if !defined(BOARD_HAS_SECURE_ELEMENT) ArduinoCloud.setBoardId(BOARD_ID); ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); #endif +#elif defined(HAS_LORA) + ArduinoCloud.addProperty(switchButton, 1, Permission::Write).onUpdate(onSwitchButtonChange); + ArduinoCloud.addProperty(location, 2, Permission::Read).publishOnChange(0.0f); + ArduinoCloud.addProperty(color, 3, Permission::ReadWrite).onUpdate(onColorChange); + ArduinoCloud.setThingId(THING_ID); -#if defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) - ArduinoCloud.addProperty(switchButton, WRITE, ON_CHANGE, onSwitchButtonChange); - ArduinoCloud.addProperty(location, READ, ON_CHANGE); - ArduinoCloud.addProperty(color, READWRITE, ON_CHANGE, onColorChange); -#elif defined(BOARD_HAS_LORA) - ArduinoCloud.addProperty(switchButton, 1, WRITE, ON_CHANGE, onSwitchButtonChange); - ArduinoCloud.addProperty(location, 2, READ, ON_CHANGE); - ArduinoCloud.addProperty(color, 3, READWRITE, ON_CHANGE, onColorChange); #endif } #if defined(BOARD_HAS_WIFI) - WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_SSID, SECRET_PASS); + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_WIFI_SSID, SECRET_WIFI_PASS); #elif defined(BOARD_HAS_GSM) GSMConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); #elif defined(BOARD_HAS_LORA) - LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, _lora_band::EU868, _lora_class::CLASS_A); + LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, _lora_band::EU868, NULL, _lora_class::CLASS_A); #elif defined(BOARD_HAS_NB) NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CATM1_NBIOT) + CatM1ConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_ETHERNET) + /* DHCP mode */ + //EthernetConnectionHandler ArduinoIoTPreferredConnection; + /* Manual mode. It will fallback in DHCP mode if SECRET_OPTIONAL_IP is invalid or equal to "0.0.0.0" */ + EthernetConnectionHandler ArduinoIoTPreferredConnection(SECRET_OPTIONAL_IP, SECRET_OPTIONAL_DNS, SECRET_OPTIONAL_GATEWAY, SECRET_OPTIONAL_NETMASK); +#elif defined(BOARD_HAS_CELLULAR) + CellularConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); #endif diff --git a/examples/ArduinoIoTCloud-Basic/ArduinoIoTCloud-Basic.ino b/examples/ArduinoIoTCloud-Basic/ArduinoIoTCloud-Basic.ino index e5a8e4c86..238b59f55 100644 --- a/examples/ArduinoIoTCloud-Basic/ArduinoIoTCloud-Basic.ino +++ b/examples/ArduinoIoTCloud-Basic/ArduinoIoTCloud-Basic.ino @@ -6,26 +6,33 @@ * When you flip the switch in the Cloud dashboard the onboard LED lights gets turned ON or OFF. IMPORTANT: - This sketch works with WiFi, GSM, NB and Lora enabled boards supported by Arduino IoT Cloud. - On a LoRa board, if it is configuered as a class A device (default and preferred option), values from Cloud dashboard are received - only after a value is sent to Cloud. - - This sketch is compatible with: - - MKR 1000 - - MKR WIFI 1010 - - MKR GSM 1400 - - MKR NB 1500 - - MKR WAN 1300/1310 - - ESP 8266 + This sketch works with WiFi, GSM, NB, Ethernet and Lora enabled boards supported by Arduino IoT Cloud. + On a LoRa board, if it is configured as a class A device (default and preferred option), + values from Cloud dashboard are received only after a value is sent to Cloud. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what */ -#include "arduino_secrets.h" #include "thingProperties.h" +#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32) +static int const LED_BUILTIN = 2; +#endif + void setup() { /* Initialize serial and wait up to 5 seconds for port to open */ Serial.begin(9600); - for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime > 5000); ) { } + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); /* Configure LED pin as an output */ pinMode(LED_BUILTIN, OUTPUT); @@ -36,7 +43,6 @@ void setup() { /* Initialize Arduino IoT Cloud library */ ArduinoCloud.begin(ArduinoIoTPreferredConnection); - setDebugMessageLevel(DBG_INFO); ArduinoCloud.printDebugInfo(); } diff --git a/examples/ArduinoIoTCloud-Basic/arduino_secrets.h b/examples/ArduinoIoTCloud-Basic/arduino_secrets.h index fc0b0661e..4134e9377 100644 --- a/examples/ArduinoIoTCloud-Basic/arduino_secrets.h +++ b/examples/ArduinoIoTCloud-Basic/arduino_secrets.h @@ -1,18 +1,22 @@ #include -/* MKR1000, MKR WiFi 1010 */ +/* A complete list of supported boards with WiFi is available here: + * https://github.com/arduino-libraries/ArduinoIoTCloud/#what + */ #if defined(BOARD_HAS_WIFI) - #define SECRET_SSID "YOUR_WIFI_NETWORK_NAME" - #define SECRET_PASS "YOUR_WIFI_PASSWORD" + #define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" #endif -/* ESP8266 */ -#if defined(BOARD_ESP8266) +/* ESP8266 ESP32 */ +#if !defined(BOARD_HAS_SECURE_ELEMENT) #define SECRET_DEVICE_KEY "my-device-password" #endif -/* MKR GSM 1400 */ -#if defined(BOARD_HAS_GSM) +/* MKR GSM 1400 */ /* MKR NB 1500 */ /* Portenta CAT.M1/NB IoT GNSS Shield */ +/* Portenta H7 and C33 + Portenta Mid Carrier + 4G Module */ +#if defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) || \ + defined(BOARD_HAS_CATM1_NBIOT) || defined(BOARD_HAS_CELLULAR) #define SECRET_PIN "" #define SECRET_APN "" #define SECRET_LOGIN "" @@ -25,10 +29,10 @@ #define SECRET_APP_KEY "" #endif -/* MKR NB 1500 */ -#if defined(BOARD_HAS_NB) - #define SECRET_PIN "" - #define SECRET_APN "" - #define SECRET_LOGIN "" - #define SECRET_PASS "" +/* Portenta H7 + Ethernet shield */ +#if defined(BOARD_HAS_ETHERNET) + #define SECRET_OPTIONAL_IP "" + #define SECRET_OPTIONAL_DNS "" + #define SECRET_OPTIONAL_GATEWAY "" + #define SECRET_OPTIONAL_NETMASK "" #endif diff --git a/examples/ArduinoIoTCloud-Basic/thingProperties.h b/examples/ArduinoIoTCloud-Basic/thingProperties.h index b2bfb5d67..8ad84c419 100644 --- a/examples/ArduinoIoTCloud-Basic/thingProperties.h +++ b/examples/ArduinoIoTCloud-Basic/thingProperties.h @@ -1,16 +1,18 @@ #include #include +#include "arduino_secrets.h" -#if defined(BOARD_HAS_WIFI) -#elif defined(BOARD_HAS_GSM) -#elif defined(BOARD_HAS_LORA) -#elif defined(BOARD_HAS_NB) -#else - #error "Arduino IoT Cloud currently only supports MKR1000, MKR WiFi 1010, MKR WAN 1300/1310, MKR NB 1500 and MKR GSM 1400" +#if !(defined(HAS_TCP) || defined(HAS_LORA)) + #error "Please check Arduino IoT Cloud supported boards list: https://github.com/arduino-libraries/ArduinoIoTCloud/#what" +#endif + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #endif -#define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -#define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#if defined(HAS_LORA) + #define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif void onLedChange(); @@ -19,28 +21,39 @@ int potentiometer; int seconds; void initProperties() { -#if defined(BOARD_ESP8266) +#if defined(HAS_TCP) + ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, Permission::Read).publishOnChange(1); + +#if !defined(BOARD_HAS_SECURE_ELEMENT) ArduinoCloud.setBoardId(BOARD_ID); ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); #endif +#elif defined(HAS_LORA) + ArduinoCloud.addProperty(led, 1, Permission::ReadWrite).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, 2, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, 3, Permission::Read).publishEvery(5 * MINUTES); + ArduinoCloud.setThingId(THING_ID); -#if defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) - ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange); - ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10); - ArduinoCloud.addProperty(seconds, Permission::Read).publishOnChange(1); -#elif defined(BOARD_HAS_LORA) - ArduinoCloud.addProperty(led, 1, READWRITE, ON_CHANGE, onLedChange); - ArduinoCloud.addProperty(potentiometer, 2, READ, ON_CHANGE); - ArduinoCloud.addProperty(seconds, 3, READ, 5 * MINUTES); #endif } -#if defined(BOARD_HAS_WIFI) - WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_SSID, SECRET_PASS); +#if defined(BOARD_HAS_ETHERNET) + /* DHCP mode */ + //EthernetConnectionHandler ArduinoIoTPreferredConnection; + /* Manual mode. It will fallback in DHCP mode if SECRET_OPTIONAL_IP is invalid or equal to "0.0.0.0" */ + EthernetConnectionHandler ArduinoIoTPreferredConnection(SECRET_OPTIONAL_IP, SECRET_OPTIONAL_DNS, SECRET_OPTIONAL_GATEWAY, SECRET_OPTIONAL_NETMASK); +#elif defined(BOARD_HAS_WIFI) + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_WIFI_SSID, SECRET_WIFI_PASS); #elif defined(BOARD_HAS_GSM) GSMConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); #elif defined(BOARD_HAS_LORA) - LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, _lora_band::EU868, _lora_class::CLASS_A); + LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, _lora_band::EU868, NULL, _lora_class::CLASS_A); #elif defined(BOARD_HAS_NB) NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CATM1_NBIOT) + CatM1ConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CELLULAR) + CellularConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); #endif diff --git a/examples/ArduinoIoTCloud-BlockForOTA/ArduinoIoTCloud-BlockForOTA.ino b/examples/ArduinoIoTCloud-BlockForOTA/ArduinoIoTCloud-BlockForOTA.ino new file mode 100644 index 000000000..d682ff2b3 --- /dev/null +++ b/examples/ArduinoIoTCloud-BlockForOTA/ArduinoIoTCloud-BlockForOTA.ino @@ -0,0 +1,97 @@ +/* + This sketch demonstrates how to optimize OTA in case of complex loop(). + + * Connect a potentiometer (or other analog sensor) to A0. + * When the potentiometer (or sensor) value changes the data is sent to the Cloud. + * When you flip the switch in the Cloud dashboard the onboard LED lights gets turned ON or OFF. + + IMPORTANT: + This sketch works with WiFi, GSM, NB, Ethernet and Lora enabled boards supported by Arduino IoT Cloud. + On a LoRa board, if it is configured as a class A device (default and preferred option), + values from Cloud dashboard are received only after a value is sent to Cloud. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what +*/ + +#include "thingProperties.h" + +#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32) +static int const LED_BUILTIN = 2; +#endif + +bool block_for_ota { false }; +bool ota_started { false }; + +bool onOTARequestCallback() { + block_for_ota = true; + ota_started = true; + return true; +} + +constexpr unsigned long printInterval { 1000 }; +unsigned long printNow { printInterval }; + +void setup() { + /* Initialize serial and wait up to 5 seconds for port to open */ + Serial.begin(9600); + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_VERBOSE); + + /* Configure LED pin as an output */ + pinMode(LED_BUILTIN, OUTPUT); + + /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ + initProperties(); + + /* Initialize Arduino IoT Cloud library */ + ArduinoCloud.begin(ArduinoIoTPreferredConnection); + + /* Setup OTA callback */ + ArduinoCloud.onOTARequestCb(onOTARequestCallback); + + ArduinoCloud.printDebugInfo(); +} + +void loop() { + // When OTA is available, stay there until it completes. + // The rest of the loop() does not run and the sketch + // restarts automatically at the end of the OTA process. + while (block_for_ota) { + ArduinoCloud.update(); + if (ota_started) { + Serial.print("Waiting for OTA to finish..."); + ota_started = false; + } + if (millis() > printNow) { + Serial.print("."); + printNow = millis() + printInterval; + } + } + + ArduinoCloud.update(); + potentiometer = analogRead(A0); + seconds = millis() / 1000; + + if (millis() > printNow) { + Serial.println(millis()); + printNow = millis() + printInterval; + } +} + +/* + * 'onLedChange' is called when the "led" property of your Thing changes + */ +void onLedChange() { + Serial.print("LED set to "); + Serial.println(led); + digitalWrite(LED_BUILTIN, led); +} diff --git a/examples/ArduinoIoTCloud-BlockForOTA/arduino_secrets.h b/examples/ArduinoIoTCloud-BlockForOTA/arduino_secrets.h new file mode 100644 index 000000000..4134e9377 --- /dev/null +++ b/examples/ArduinoIoTCloud-BlockForOTA/arduino_secrets.h @@ -0,0 +1,38 @@ +#include + +/* A complete list of supported boards with WiFi is available here: + * https://github.com/arduino-libraries/ArduinoIoTCloud/#what + */ +#if defined(BOARD_HAS_WIFI) + #define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" +#endif + +/* ESP8266 ESP32 */ +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define SECRET_DEVICE_KEY "my-device-password" +#endif + +/* MKR GSM 1400 */ /* MKR NB 1500 */ /* Portenta CAT.M1/NB IoT GNSS Shield */ +/* Portenta H7 and C33 + Portenta Mid Carrier + 4G Module */ +#if defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) || \ + defined(BOARD_HAS_CATM1_NBIOT) || defined(BOARD_HAS_CELLULAR) + #define SECRET_PIN "" + #define SECRET_APN "" + #define SECRET_LOGIN "" + #define SECRET_PASS "" +#endif + +/* MKR WAN 1300/1310 */ +#if defined(BOARD_HAS_LORA) + #define SECRET_APP_EUI "" + #define SECRET_APP_KEY "" +#endif + +/* Portenta H7 + Ethernet shield */ +#if defined(BOARD_HAS_ETHERNET) + #define SECRET_OPTIONAL_IP "" + #define SECRET_OPTIONAL_DNS "" + #define SECRET_OPTIONAL_GATEWAY "" + #define SECRET_OPTIONAL_NETMASK "" +#endif diff --git a/examples/ArduinoIoTCloud-BlockForOTA/thingProperties.h b/examples/ArduinoIoTCloud-BlockForOTA/thingProperties.h new file mode 100644 index 000000000..8ad84c419 --- /dev/null +++ b/examples/ArduinoIoTCloud-BlockForOTA/thingProperties.h @@ -0,0 +1,59 @@ +#include +#include +#include "arduino_secrets.h" + +#if !(defined(HAS_TCP) || defined(HAS_LORA)) + #error "Please check Arduino IoT Cloud supported boards list: https://github.com/arduino-libraries/ArduinoIoTCloud/#what" +#endif + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +#if defined(HAS_LORA) + #define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +void onLedChange(); + +bool led; +int potentiometer; +int seconds; + +void initProperties() { +#if defined(HAS_TCP) + ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, Permission::Read).publishOnChange(1); + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + ArduinoCloud.setBoardId(BOARD_ID); + ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); +#endif +#elif defined(HAS_LORA) + ArduinoCloud.addProperty(led, 1, Permission::ReadWrite).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, 2, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, 3, Permission::Read).publishEvery(5 * MINUTES); + + ArduinoCloud.setThingId(THING_ID); +#endif +} + +#if defined(BOARD_HAS_ETHERNET) + /* DHCP mode */ + //EthernetConnectionHandler ArduinoIoTPreferredConnection; + /* Manual mode. It will fallback in DHCP mode if SECRET_OPTIONAL_IP is invalid or equal to "0.0.0.0" */ + EthernetConnectionHandler ArduinoIoTPreferredConnection(SECRET_OPTIONAL_IP, SECRET_OPTIONAL_DNS, SECRET_OPTIONAL_GATEWAY, SECRET_OPTIONAL_NETMASK); +#elif defined(BOARD_HAS_WIFI) + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_WIFI_SSID, SECRET_WIFI_PASS); +#elif defined(BOARD_HAS_GSM) + GSMConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_LORA) + LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, _lora_band::EU868, NULL, _lora_class::CLASS_A); +#elif defined(BOARD_HAS_NB) + NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CATM1_NBIOT) + CatM1ConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CELLULAR) + CellularConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#endif diff --git a/examples/ArduinoIoTCloud-Callbacks/ArduinoIoTCloud-Callbacks.ino b/examples/ArduinoIoTCloud-Callbacks/ArduinoIoTCloud-Callbacks.ino new file mode 100644 index 000000000..3234def46 --- /dev/null +++ b/examples/ArduinoIoTCloud-Callbacks/ArduinoIoTCloud-Callbacks.ino @@ -0,0 +1,81 @@ +/* + This sketch demonstrates how to subscribe to IoT Cloud events and perform actions + The available events are + + CONNECT : Board successfully connects to IoT Cloud + SYNC : Data is successfully synced between Board and IoT Cloud + DISCONNECT : Board has lost connection to IoT Cloud + + You don't need any specific Properties to be created in order to demonstrate these functionalities. + Simply create a new Thing and give it 1 arbitrary Property. + Remember that the Thing ID needs to be configured in thingProperties.h + These events can be very useful in particular cases, for instance to disable a peripheral + or a connected sensor/actuator when no data connection is available, as well as to perform + specific operations on connection or right after properties values are synchronised. + + To subscribe to an event you can use the `addCallback` method and specify + which event will trigger which custom function. + One function per event can be assigned. + + IMPORTANT: + This sketch works with WiFi, GSM, NB, Ethernet and Lora enabled boards supported by Arduino IoT Cloud. + On a LoRa board, if it is configured as a class A device (default and preferred option), + values from Cloud dashboard are received only after a value is sent to Cloud. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what +*/ + +#include "thingProperties.h" + +void setup() { + /* Initialize serial and wait up to 5 seconds for port to open */ + Serial.begin(9600); + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); + + /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ + initProperties(); + + /* Initialize Arduino IoT Cloud library */ + ArduinoCloud.begin(ArduinoIoTPreferredConnection); + + /* + Invoking `addCallback` on the ArduinoCloud object allows you to subscribe + to any of the available events and decide which functions to call when they are fired. + + The functions `doThisOnConnect`, `doThisOnSync`, `doThisOnDisconnect` + are custom functions and can be named to your likings and for this example + they are defined/implemented at the bottom of the Sketch + */ + ArduinoCloud.addCallback(ArduinoIoTCloudEvent::CONNECT, doThisOnConnect); + ArduinoCloud.addCallback(ArduinoIoTCloudEvent::SYNC, doThisOnSync); + ArduinoCloud.addCallback(ArduinoIoTCloudEvent::DISCONNECT, doThisOnDisconnect); + + ArduinoCloud.printDebugInfo(); +} + +void loop() { + ArduinoCloud.update(); +} + +void doThisOnConnect(){ + /* add your custom code here */ + Serial.println("Board successfully connected to Arduino IoT Cloud"); +} +void doThisOnSync(){ + /* add your custom code here */ + Serial.println("Thing Properties synchronised"); +} +void doThisOnDisconnect(){ + /* add your custom code here */ + Serial.println("Board disconnected from Arduino IoT Cloud"); +} \ No newline at end of file diff --git a/examples/ArduinoIoTCloud-Callbacks/arduino_secrets.h b/examples/ArduinoIoTCloud-Callbacks/arduino_secrets.h new file mode 100644 index 000000000..4134e9377 --- /dev/null +++ b/examples/ArduinoIoTCloud-Callbacks/arduino_secrets.h @@ -0,0 +1,38 @@ +#include + +/* A complete list of supported boards with WiFi is available here: + * https://github.com/arduino-libraries/ArduinoIoTCloud/#what + */ +#if defined(BOARD_HAS_WIFI) + #define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" +#endif + +/* ESP8266 ESP32 */ +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define SECRET_DEVICE_KEY "my-device-password" +#endif + +/* MKR GSM 1400 */ /* MKR NB 1500 */ /* Portenta CAT.M1/NB IoT GNSS Shield */ +/* Portenta H7 and C33 + Portenta Mid Carrier + 4G Module */ +#if defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) || \ + defined(BOARD_HAS_CATM1_NBIOT) || defined(BOARD_HAS_CELLULAR) + #define SECRET_PIN "" + #define SECRET_APN "" + #define SECRET_LOGIN "" + #define SECRET_PASS "" +#endif + +/* MKR WAN 1300/1310 */ +#if defined(BOARD_HAS_LORA) + #define SECRET_APP_EUI "" + #define SECRET_APP_KEY "" +#endif + +/* Portenta H7 + Ethernet shield */ +#if defined(BOARD_HAS_ETHERNET) + #define SECRET_OPTIONAL_IP "" + #define SECRET_OPTIONAL_DNS "" + #define SECRET_OPTIONAL_GATEWAY "" + #define SECRET_OPTIONAL_NETMASK "" +#endif diff --git a/examples/ArduinoIoTCloud-Callbacks/thingProperties.h b/examples/ArduinoIoTCloud-Callbacks/thingProperties.h new file mode 100644 index 000000000..e5e89e2ee --- /dev/null +++ b/examples/ArduinoIoTCloud-Callbacks/thingProperties.h @@ -0,0 +1,45 @@ +#include +#include +#include "arduino_secrets.h" + +#if !(defined(HAS_TCP) || defined(HAS_LORA)) + #error "Please check Arduino IoT Cloud supported boards list: https://github.com/arduino-libraries/ArduinoIoTCloud/#what" +#endif + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +#if defined(BOARD_HAS_LORA) + #define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +void initProperties() { +#if defined(HAS_TCP) +#if !defined(BOARD_HAS_SECURE_ELEMENT) + ArduinoCloud.setBoardId(BOARD_ID); + ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); +#endif +#elif defined(HAS_LORA) + ArduinoCloud.setThingId(THING_ID); +#endif +} + +#if defined(BOARD_HAS_WIFI) + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_WIFI_SSID, SECRET_WIFI_PASS); +#elif defined(BOARD_HAS_GSM) + GSMConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_LORA) + LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, _lora_band::EU868, NULL, _lora_class::CLASS_A); +#elif defined(BOARD_HAS_NB) + NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CATM1_NBIOT) + CatM1ConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_ETHERNET) + /* DHCP mode */ + //EthernetConnectionHandler ArduinoIoTPreferredConnection; + /* Manual mode. It will fallback in DHCP mode if SECRET_OPTIONAL_IP is invalid or equal to "0.0.0.0" */ + EthernetConnectionHandler ArduinoIoTPreferredConnection(SECRET_OPTIONAL_IP, SECRET_OPTIONAL_DNS, SECRET_OPTIONAL_GATEWAY, SECRET_OPTIONAL_NETMASK); +#elif defined(BOARD_HAS_CELLULAR) + CellularConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#endif diff --git a/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino b/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino new file mode 100644 index 000000000..84eed2436 --- /dev/null +++ b/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino @@ -0,0 +1,96 @@ +/* + This sketch demonstrates how to handle deferred OTA from Arduino IoT Cloud. + + Deferred OTA can be triggered using the arduino-cloud-cli with the following command: + ./arduino-cloud-cli ota upload --device-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --file filename.ino.bin --deferred + The update file and the download link will be available to be used within one week. + + * always_deny callback will always postpone the OTA update + * always_allow callback will immediately apply the OTA update + * ask_user_via_serial callback will read user input from serial to apply or postpone OTA update + + IMPORTANT: + This sketch works with WiFi and Ethernet enabled boards supported by Arduino IoT Cloud. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud/#ota +*/ + +#include "thingProperties.h" + +#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32) +static int const LED_BUILTIN = 2; +#endif + +bool always_deny() { + return false; +} + +bool always_allow() { + return true; +} + +static bool ask_user_via_serial_first_run = true; +bool ask_user_via_serial() { + if (ask_user_via_serial_first_run) { + Serial.println("Apply OTA? y / [n]"); + ask_user_via_serial_first_run = false; + } + if (Serial.available()) { + char c = Serial.read(); + if (c == 'y' || c == 'Y') { + return true; + } + } + return false; +} + +bool onOTARequestCallback() +{ + /* Select the preferred behaviour changing the called function */ + //return always_deny(); + //return always_allow(); + return ask_user_via_serial(); +} + +void setup() { + /* Initialize serial and wait up to 5 seconds for port to open */ + Serial.begin(9600); + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); + + /* Configure LED pin as an output */ + pinMode(LED_BUILTIN, OUTPUT); + + /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ + initProperties(); + + /* Initialize Arduino IoT Cloud library */ + ArduinoCloud.begin(ArduinoIoTPreferredConnection); + + /* Setup OTA callback */ + ArduinoCloud.onOTARequestCb(onOTARequestCallback); + + ArduinoCloud.printDebugInfo(); +} + +void loop() { + ArduinoCloud.update(); +} + +/* + * 'onLedChange' is called when the "led" property of your Thing changes + */ +void onLedChange() { + Serial.print("LED set to "); + Serial.println(led); + digitalWrite(LED_BUILTIN, led); +} diff --git a/examples/ArduinoIoTCloud-DeferredOTA/arduino_secrets.h b/examples/ArduinoIoTCloud-DeferredOTA/arduino_secrets.h new file mode 100644 index 000000000..d0762b4c8 --- /dev/null +++ b/examples/ArduinoIoTCloud-DeferredOTA/arduino_secrets.h @@ -0,0 +1,22 @@ +#include + +/* A complete list of supported boards with WiFi is available here: + * https://github.com/arduino-libraries/ArduinoIoTCloud/#what + */ +#if defined(BOARD_HAS_WIFI) + #define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" +#endif + +/* ESP8266 ESP32 */ +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define SECRET_DEVICE_KEY "my-device-password" +#endif + +/* Portenta H7 + Ethernet shield */ +#if defined(BOARD_HAS_ETHERNET) + #define SECRET_OPTIONAL_IP "" + #define SECRET_OPTIONAL_DNS "" + #define SECRET_OPTIONAL_GATEWAY "" + #define SECRET_OPTIONAL_NETMASK "" +#endif diff --git a/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h b/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h new file mode 100644 index 000000000..1feeee877 --- /dev/null +++ b/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h @@ -0,0 +1,34 @@ +#include +#include +#include "arduino_secrets.h" + +#if !(defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_ETHERNET)) + #error "Please check Arduino IoT Cloud supported boards list: https://github.com/arduino-libraries/ArduinoIoTCloud/#what" +#endif + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +void onLedChange(); + +bool led; + +void initProperties() { +#if defined(HAS_TCP) +#if !defined(BOARD_HAS_SECURE_ELEMENT) + ArduinoCloud.setBoardId(BOARD_ID); + ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); +#endif +#endif + ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange); +} + +#if defined(BOARD_HAS_WIFI) + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_WIFI_SSID, SECRET_WIFI_PASS); +#elif defined(BOARD_HAS_ETHERNET) + /* DHCP mode */ + //EthernetConnectionHandler ArduinoIoTPreferredConnection; + /* Manual mode. It will fallback in DHCP mode if SECRET_OPTIONAL_IP is invalid or equal to "0.0.0.0" */ + EthernetConnectionHandler ArduinoIoTPreferredConnection(SECRET_OPTIONAL_IP, SECRET_OPTIONAL_DNS, SECRET_OPTIONAL_GATEWAY, SECRET_OPTIONAL_NETMASK); +#endif diff --git a/examples/ArduinoIoTCloud-Notecard/ArduinoIoTCloud-Notecard.ino b/examples/ArduinoIoTCloud-Notecard/ArduinoIoTCloud-Notecard.ino new file mode 100644 index 000000000..a31c404b7 --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/ArduinoIoTCloud-Notecard.ino @@ -0,0 +1,73 @@ +/* + This sketch demonstrates how to exchange data between your board and the + Arduino IoT Cloud, while using the Notecard for wireless communication. + + * Connect a potentiometer (or other analog sensor) to A0. + * When the potentiometer (or sensor) value changes the data is sent to the Cloud. + * When you flip the switch in the Cloud dashboard the onboard LED lights gets turned ON or OFF. + + IMPORTANT: + This sketch works with any Wi-Fi, Cellular, LoRa or Satellite enabled Notecard. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what +*/ + +#include +#include "thingProperties.h" + +#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32) +static int const LED_BUILTIN = 2; +#endif + +/* + * Choose an interrupt capable pin to reduce polling and improve + * the overall responsiveness of the ArduinoIoTCloud library + */ +// #define ATTN_PIN 9 + +void setup() { + /* Initialize serial and wait up to 5 seconds for port to open */ + Serial.begin(9600); + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); + + /* Configure LED pin as an output */ + pinMode(LED_BUILTIN, OUTPUT); + + /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ + initProperties(); + + /* Initialize Arduino IoT Cloud library */ +#ifndef ATTN_PIN + ArduinoCloud.begin(ArduinoIoTPreferredConnection); + ArduinoCloud.setNotecardPollingInterval(3000); // default: 1000ms, min: 250ms +#else + ArduinoCloud.begin(ArduinoIoTPreferredConnection, ATTN_PIN); +#endif + + ArduinoCloud.printDebugInfo(); +} + +void loop() { + ArduinoCloud.update(); + potentiometer = analogRead(A0); + seconds = millis() / 1000; +} + +/* + * 'onLedChange' is called when the "led" property of your Thing changes + */ +void onLedChange() { + Serial.print("LED set to "); + Serial.println(led); + digitalWrite(LED_BUILTIN, led); +} diff --git a/examples/ArduinoIoTCloud-Notecard/README.md b/examples/ArduinoIoTCloud-Notecard/README.md new file mode 100644 index 000000000..a17d954c1 --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/README.md @@ -0,0 +1,78 @@ +Notecard Connectivity +===================== + +The Notecard is a wireless, secure abstraction for device connectivity, that can +be used to enable _ANY*_ device with I2C, or UART, to connect to the Arduino IoT +Cloud via cellular, LoRa, satellite or Wi-Fi! + +As a result, your existing device architecture can now have first class support +in the Arduino IoT Cloud, by using a Notecard as a secure communication channel. + +> \*_While any device with I2C/UART may use the Notecard, the Arduino IoT Cloud +> library is not supported by the AVR toolchain. Therefore, devices based on the +> AVR architecture cannot access the Arduino IoT Cloud via the Notecard._ +> +> _However, any device (including AVR), may use the Notecard library to send data +> to Notehub, then that data may be routed to any endpoint of your choosing. See the +> [Notecard Routing Guide](https://dev.blues.io/guides-and-tutorials/routing-data-to-cloud) +> for more information..._ + +Wireless Connectivity Options +----------------------------- + +- [Cellular](https://shop.blues.com/collections/notecard/products/notecard-cellular) +- [Cellular + Wi-Fi](https://shop.blues.com/collections/notecard/products/notecard-cell-wifi) +- [Wi-Fi](https://shop.blues.com/collections/notecard/products/wifi-notecard) +- [LoRa](https://shop.blues.com/collections/notecard/products/notecard-lora) +- [Satellite](https://shop.blues.com/products/starnote) + +How it Works +------------ + +**Architecture Diagram:** + +``` +-------- ------------ ----------- ----------- +| | | | | | | | +| Host | | | Secure | | | Arduino | +| MCU |------| Notecard | ( ( Wireless ) ) | Notehub |------| IoT | +| | | | Protocol | | | Cloud | +|______| |__________| |_________| |_________| +``` + +Getting Started +--------------- + +### Setup a Notehub Account + +Using the Notecard only requires a couple of easy steps: + +1. [Purchase a Notecard](https://shop.blues.com/collections/notecard) (and +[Notecarrier](https://shop.blues.com/collections/notecarrier)) that fits the +needs of your device. + > _**NOTE:** We recommend starting with our [Dev Kit](https://shop.blues.com/products/blues-global-starter-kit) + > if you are unsure._ +1. [Setup a Notehub account](https://dev.blues.io/quickstart/notecard-quickstart/notecard-and-notecarrier-f/#set-up-notehub). + > _**NOTE:** Notehub accounts are free (no credit card required)._ +1. [Create a project on your Notehub account](https://dev.blues.io/quickstart/notecard-quickstart/notecard-and-notecarrier-f/#create-a-notehub-project). +1. In `thingProperties.h`, replace "com.domain.you:product" (from +`NOTECARD_PRODUCT_UID`) with the ProductUID of your new Notehub project. + +### Power-up the Device + +1. [Connect the Notecard to your Host MCU](https://dev.blues.io/quickstart/notecard-quickstart/notecard-and-notecarrier-f/#connect-your-notecard-and-notecarrier) +1. Flash the `ArduinoIoTCloud-Notecard` example sketch to your device. You +should see the device reporting itself as online in your [Notehub Project](https://notehub.io). + +### Associate Notecard to Arduino IoT Cloud + +1. Create a "MANUAL Device" in the Arduino IoT Cloud, then [add environment +variables for the "Device ID" and "Secret Key" to your Notecard in Notehub](https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-environment-variables/#setting-a-notehub-device-variable). + - `_sn`: \ + - `_secret_key`: \ + +### More Information + +For more information about the Notecard and Notehub in general, please see our +[Quickstart Guide](https://dev.blues.io/quickstart/) for a general overview of +how the Notecard and Notehub are designed to work. diff --git a/examples/ArduinoIoTCloud-Notecard/arduino_secrets.h b/examples/ArduinoIoTCloud-Notecard/arduino_secrets.h new file mode 100644 index 000000000..43e3b76e1 --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/arduino_secrets.h @@ -0,0 +1,7 @@ +#include + +/* If provided, the Wi-Fi Credentials will be passed along to the Notecard. If + * the Notecard supports Wi-Fi, it will attempt to connect to the network using + * these credentials, if not, the Notecard will safely ignore these values. */ +#define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" +#define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" diff --git a/examples/ArduinoIoTCloud-Notecard/thingProperties.h b/examples/ArduinoIoTCloud-Notecard/thingProperties.h new file mode 100644 index 000000000..cbcaf4da3 --- /dev/null +++ b/examples/ArduinoIoTCloud-Notecard/thingProperties.h @@ -0,0 +1,36 @@ +#include + +#include +#include +#include "arduino_secrets.h" + +/* The Notecard can provide connectivity to almost any board via ESLOV (I2C) + * or UART. An empty string (or the default value provided below) will not + * override the Notecard's existing configuration. + * Learn more at: https://dev.blues.io */ +#define NOTECARD_PRODUCT_UID "com.domain.you:product" + +/* Uncomment the following line to use the Notecard over UART */ +// #define UART_INTERFACE Serial1 + +void onLedChange(); + +bool led; +int potentiometer; +int seconds; + +#ifndef UART_INTERFACE +NotecardConnectionHandler ArduinoIoTPreferredConnection(NOTECARD_PRODUCT_UID); +#else +NotecardConnectionHandler ArduinoIoTPreferredConnection(NOTECARD_PRODUCT_UID, UART_INTERFACE); +#endif + +void initProperties() { + ArduinoCloud.addProperty(led, Permission::ReadWrite).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, Permission::Read).publishEvery(5 * MINUTES); + + if (::strncmp(SECRET_WIFI_SSID, "YOUR_WIFI_NETWORK_NAME", sizeof(SECRET_WIFI_SSID))) { + ArduinoIoTPreferredConnection.setWiFiCredentials(SECRET_WIFI_SSID, SECRET_WIFI_PASS); + } +} diff --git a/examples/ArduinoIoTCloud-Schedule/ArduinoIoTCloud-Schedule.ino b/examples/ArduinoIoTCloud-Schedule/ArduinoIoTCloud-Schedule.ino new file mode 100644 index 000000000..8529e9ec9 --- /dev/null +++ b/examples/ArduinoIoTCloud-Schedule/ArduinoIoTCloud-Schedule.ino @@ -0,0 +1,230 @@ +/* + This sketch demonstrates how to use the cloud schedule variable type. + + IMPORTANT: + This sketch works with WiFi, GSM, NB and Ethernet enabled boards supported by Arduino IoT Cloud. + +*/ + +#include "thingProperties.h" + +#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32) +static int const LED_BUILTIN = 2; +#endif + +void setup() { + /* Initialize the serial port and wait up to 5 seconds for a connection */ + Serial.begin(9600); + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); + + /* Configure LED pin as an output */ + pinMode(LED_BUILTIN, OUTPUT); + + /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */ + initProperties(); + + /* Initialize Arduino IoT Cloud library */ + ArduinoCloud.begin(ArduinoIoTPreferredConnection); + + ArduinoCloud.printDebugInfo(); + + /* Setup one shot schedule example */ + setupOneShotSchedule(); + + /* Setup per minute schedule example */ + setupMinuteSchedule(); + + /* Setup hourly schedule example */ + setupHourlySchedule(); + + /* Setup daily schedule example */ + setupDailySchedule(); + + /* Setup weekly schedule example */ + setupWeeklySchedule(); + + /* Setup monthly schedule example */ + setupMonthlySchedule(); + + /* Setup yearly schedule example */ + setupYearlySchedule(); +} + + /* Setup a schedule with an active period of 5 minutes that doesn't repeat + * Starting from 2021 11 01 17:00:00 + * Until 2021 11 02 17:00:00 + */ +void setupOneShotSchedule() { + + ScheduleTimeType startingFrom = TimeServiceClass::getTimeFromString("2021 Nov 01 17:00:00"); + ScheduleTimeType until = startingFrom + ( DAYS * 1 ); + ScheduleTimeType activePeriod = MINUTES * 5; + + /* Warning: there is no cross check between until and activePeriod */ + ScheduleConfigurationType scheduleConfiguration = Schedule::createOneShotScheduleConfiguration(); + + oneShot = Schedule(startingFrom, until, activePeriod, scheduleConfiguration); +} + + /* Setup a schedule with an active period of 15 seconds that repeats each minute + * Starting from 2021 11 01 17:00:00 + * Until 2021 11 02 17:00:00 + */ +void setupMinuteSchedule() { + + ScheduleTimeType startingFrom = TimeServiceClass::getTimeFromString("2021 Nov 01 17:00:00"); + ScheduleTimeType until = startingFrom + ( DAYS * 1 ); + ScheduleTimeType activePeriod = SECONDS * 15; + unsigned int repetitionPeriod = 1; + + /* Warning: there is no cross check between repetitionPeriod and activePeriod */ + ScheduleConfigurationType scheduleConfiguration = Schedule::createFixedDeltaScheduleConfiguration(ScheduleUnit::Minutes, repetitionPeriod); + + minute = Schedule(startingFrom, until, activePeriod, scheduleConfiguration); +} + +/* Setup a schedule with an active period of 20 minutes that repeats each hour + * Starting from 2021 11 01 17:00:00 + * Until 2021 11 15 13:00:00 + */ +void setupHourlySchedule() { + + ScheduleTimeType startingFrom = TimeServiceClass::getTimeFromString("2021 Nov 01 17:00:00"); + ScheduleTimeType until = TimeServiceClass::getTimeFromString("2021 Nov 15 13:00:00"); + ScheduleTimeType activePeriod = MINUTES * 20; + unsigned int repetitionPeriod = 1; + + /* Warning: there is no cross check between repetitionPeriod and activePeriod */ + ScheduleConfigurationType scheduleConfiguration = Schedule::createFixedDeltaScheduleConfiguration(ScheduleUnit::Hours, repetitionPeriod); + + hourly = Schedule(startingFrom, until, activePeriod, scheduleConfiguration); +} + +/* Setup a schedule with an active period of 2 hours that repeats each day + * Starting from 2021 11 01 17:00:00 + * Until 2021 11 15 13:00:00 + */ +void setupDailySchedule() { + + ScheduleTimeType startingFrom = TimeServiceClass::getTimeFromString("2021 Nov 01 17:00:00"); + ScheduleTimeType until = TimeServiceClass::getTimeFromString("2021 Nov 15 13:00:00"); + ScheduleTimeType activePeriod = HOURS * 2; + unsigned int repetitionPeriod = 1; + + /* Warning: there is no cross check between repetitionPeriod and activePeriod */ + ScheduleConfigurationType scheduleConfiguration = Schedule::createFixedDeltaScheduleConfiguration(ScheduleUnit::Days, repetitionPeriod); + + daily = Schedule(startingFrom, until, activePeriod, scheduleConfiguration); +} + +/* Setup a schedule with an active period of 3 minutes with a weekly configuration + * Starting from 2021 11 01 17:00:00 + * Until 2021 11 31 17:00:00 + * Weekly configuration + * Sunday -> Inactive + * Monday -> Active + * Tuesday -> Inactive + * Wednesday -> Active + * Thursday -> Inactive + * Friday -> Active + * Saturday -> Inactive + */ +void setupWeeklySchedule() { + + unsigned int startingFrom = TimeServiceClass::getTimeFromString("2021 Nov 01 17:00:00"); + unsigned int until = startingFrom + ( DAYS * 30 ); + unsigned int executionPeriod = MINUTES * 3; + + ScheduleWeeklyMask WeeklyMask = { + ScheduleState::Inactive, /* Sunday */ + ScheduleState::Active, /* Monday */ + ScheduleState::Inactive, /* Tuesday */ + ScheduleState::Active, /* Wednesday */ + ScheduleState::Inactive, /* Thursday */ + ScheduleState::Active, /* Friday */ + ScheduleState::Inactive, /* Saturday */ + }; + + ScheduleConfigurationType scheduleConfiguration = Schedule::createWeeklyScheduleConfiguration(WeeklyMask); + + weekly = Schedule(startingFrom, until, executionPeriod, scheduleConfiguration); +} + +/* Setup a schedule with an active period of 1 day that repeats each third day of the month + * Starting from 2021 11 01 17:00:00 + * Until 2022 11 15 13:00:00 + */ +void setupMonthlySchedule() { + + ScheduleTimeType startingFrom = TimeServiceClass::getTimeFromString("2021 Nov 01 17:00:00"); + ScheduleTimeType until = TimeServiceClass::getTimeFromString("2021 Nov 15 13:00:00"); + ScheduleTimeType activePeriod = DAYS * 1; + int dayOfMonth = 3; + + ScheduleConfigurationType scheduleConfiguration = Schedule::createMonthlyScheduleConfiguration(dayOfMonth); + + monthly = Schedule(startingFrom, until, activePeriod, scheduleConfiguration); +} + + +/* Setup a schedule with an active period of 2 days that repeats each year on November 6th + * Starting from 2021 11 06 17:00:00 + * Until 2041 11 15 13:00:00 + */ +void setupYearlySchedule() { + + ScheduleTimeType startingFrom = TimeServiceClass::getTimeFromString("2021 Nov 06 17:00:00"); + ScheduleTimeType until = TimeServiceClass::getTimeFromString("2041 Nov 06 13:00:00"); + ScheduleTimeType activePeriod = DAYS * 2; + int dayOfMonth = 6; + + ScheduleConfigurationType scheduleConfiguration = Schedule::createYearlyScheduleConfiguration(ScheduleMonth::Nov, dayOfMonth); + + yearly = Schedule(startingFrom, until, activePeriod, scheduleConfiguration); +} + +void loop() { + ArduinoCloud.update(); + + /* Print a message when the oneShot schedule is active */ + if(oneShot.isActive()) { + Serial.println("One shot schedule is active"); + } + + /* Print a message when the per minute schedule is active */ + if(minute.isActive()) { + Serial.println("Per minute schedule is active"); + } + + /* Print a message when the hourly schedule is active */ + if(hourly.isActive()) { + Serial.println("Hourly schedule is active"); + } + + /* Print a message when the daily schedule is active */ + if(daily.isActive()) { + Serial.println("Daily schedule is active"); + } + + /* Activate LED when the weekly schedule is active */ + digitalWrite(LED_BUILTIN, weekly.isActive()); + + /* Print a message when the monthly schedule is active */ + if(monthly.isActive()) { + Serial.println("Monthly schedule is active"); + } + + /* Print a message when the yearly schedule is active */ + if(yearly.isActive()) { + Serial.println("Yearly schedule is active"); + } +} diff --git a/examples/ArduinoIoTCloud-Schedule/arduino_secrets.h b/examples/ArduinoIoTCloud-Schedule/arduino_secrets.h new file mode 100644 index 000000000..7a9190b5d --- /dev/null +++ b/examples/ArduinoIoTCloud-Schedule/arduino_secrets.h @@ -0,0 +1,32 @@ +#include + +/* A complete list of supported boards with WiFi is available here: + * https://github.com/arduino-libraries/ArduinoIoTCloud/#what + */ +#if defined(BOARD_HAS_WIFI) + #define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" +#endif + +/* ESP8266 ESP32 */ +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define SECRET_DEVICE_KEY "my-device-password" +#endif + +/* MKR GSM 1400 */ /* MKR NB 1500 */ /* Portenta CAT.M1/NB IoT GNSS Shield */ +/* Portenta H7 and C33 + Portenta Mid Carrier + 4G Module */ +#if defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) || \ + defined(BOARD_HAS_CATM1_NBIOT) || defined(BOARD_HAS_CELLULAR) + #define SECRET_PIN "" + #define SECRET_APN "" + #define SECRET_LOGIN "" + #define SECRET_PASS "" +#endif + +/* Portenta H7 + Ethernet shield */ +#if defined(BOARD_HAS_ETHERNET) + #define SECRET_OPTIONAL_IP "" + #define SECRET_OPTIONAL_DNS "" + #define SECRET_OPTIONAL_GATEWAY "" + #define SECRET_OPTIONAL_NETMASK "" +#endif diff --git a/examples/ArduinoIoTCloud-Schedule/thingProperties.h b/examples/ArduinoIoTCloud-Schedule/thingProperties.h new file mode 100644 index 000000000..290feb8bd --- /dev/null +++ b/examples/ArduinoIoTCloud-Schedule/thingProperties.h @@ -0,0 +1,57 @@ +#include +#include +#include "arduino_secrets.h" + +#if !defined(HAS_TCP) + #error "Please check Arduino IoT Cloud supported boards list: https://github.com/arduino-libraries/ArduinoIoTCloud/#what" +#endif + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +void onSwitchButtonChange(); + +bool switchButton; +CloudSchedule oneShot; +CloudSchedule minute; +CloudSchedule hourly; +CloudSchedule daily; +CloudSchedule weekly; +CloudSchedule monthly; +CloudSchedule yearly; + +void initProperties() { +#if defined(HAS_TCP) + ArduinoCloud.addProperty(switchButton, Permission::Write); + ArduinoCloud.addProperty(oneShot, Permission::ReadWrite); + ArduinoCloud.addProperty(minute, Permission::ReadWrite); + ArduinoCloud.addProperty(hourly, Permission::ReadWrite); + ArduinoCloud.addProperty(daily, Permission::ReadWrite); + ArduinoCloud.addProperty(weekly, Permission::ReadWrite); + ArduinoCloud.addProperty(monthly, Permission::ReadWrite); + ArduinoCloud.addProperty(yearly, Permission::ReadWrite); + +#if !defined(BOARD_HAS_SECURE_ELEMENT) + ArduinoCloud.setBoardId(BOARD_ID); + ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); +#endif +#endif +} + +#if defined(BOARD_HAS_WIFI) + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_WIFI_SSID, SECRET_WIFI_PASS); +#elif defined(BOARD_HAS_GSM) + GSMConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_NB) + NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CATM1_NBIOT) + CatM1ConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_ETHERNET) + /* DHCP mode */ + //EthernetConnectionHandler ArduinoIoTPreferredConnection; + /* Manual mode. It will fallback in DHCP mode if SECRET_OPTIONAL_IP is invalid or equal to "0.0.0.0" */ + EthernetConnectionHandler ArduinoIoTPreferredConnection(SECRET_OPTIONAL_IP, SECRET_OPTIONAL_DNS, SECRET_OPTIONAL_GATEWAY, SECRET_OPTIONAL_NETMASK); +#elif defined(BOARD_HAS_CELLULAR) + CellularConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#endif diff --git a/examples/utility/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino b/examples/utility/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino index bcb46b680..4d478e065 100644 --- a/examples/utility/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino +++ b/examples/utility/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino @@ -1,23 +1,24 @@ /* This sketch is used in combination with Travis CI to check if - unintentional breaking changes are made to the used facing + unintentional breaking changes are made to the user facing Arduino IoT Cloud API. - This sketch is compatible with: - - MKR 1000 - - MKR WIFI 1010 - - MKR GSM 1400 - - MKR WAN 1300/1310 + IMPORTANT: + This sketch works with WiFi, GSM, NB, Ethernet and Lora enabled boards supported by Arduino IoT Cloud. + On a LoRa board, if it is configured as a class A device (default and preferred option), + values from Cloud dashboard are received only after a value is sent to Cloud. + + The full list of compatible boards can be found here: + - https://github.com/arduino-libraries/ArduinoIoTCloud#what */ -#include "arduino_secrets.h" #include "thingProperties.h" void setup() { Serial.begin(9600); unsigned long serialBeginTime = millis(); - while (!Serial && (millis() - serialBeginTime > 5000)); + while (!Serial && (millis() - serialBeginTime <= 5000)); Serial.println("Starting Arduino IoT Cloud Example"); diff --git a/examples/utility/ArduinoIoTCloud_Travis_CI/arduino_secrets.h b/examples/utility/ArduinoIoTCloud_Travis_CI/arduino_secrets.h index 020482cfc..f4bceabb6 100644 --- a/examples/utility/ArduinoIoTCloud_Travis_CI/arduino_secrets.h +++ b/examples/utility/ArduinoIoTCloud_Travis_CI/arduino_secrets.h @@ -1,13 +1,22 @@ #include -/* MKR1000, MKR WiFi 1010 */ +/* A complete list of supported boards with WiFi is available here: + * https://github.com/arduino-libraries/ArduinoIoTCloud/#what + */ #if defined(BOARD_HAS_WIFI) - #define SECRET_SSID "YOUR_WIFI_NETWORK_NAME" - #define SECRET_PASS "YOUR_WIFI_PASSWORD" + #define SECRET_WIFI_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_WIFI_PASS "YOUR_WIFI_PASSWORD" #endif -/* MKR GSM 1400 */ -#if defined(BOARD_HAS_GSM) +/* ESP8266 ESP32*/ +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define SECRET_DEVICE_KEY "my-device-password" +#endif + +/* MKR GSM 1400 */ /* MKR NB 1500 */ /* Portenta CAT.M1/NB IoT GNSS Shield */ +/* Portenta H7 and C33 + Portenta Mid Carrier + 4G Module */ +#if defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) || \ + defined(BOARD_HAS_CATM1_NBIOT) || defined(BOARD_HAS_CELLULAR) #define SECRET_PIN "" #define SECRET_APN "" #define SECRET_LOGIN "" @@ -20,10 +29,10 @@ #define SECRET_APP_KEY "" #endif -/* MKR NB 1500 */ -#if defined(BOARD_HAS_NB) - #define SECRET_PIN "" - #define SECRET_APN "" - #define SECRET_LOGIN "" - #define SECRET_PASS "" +/* Portenta H7 + Ethernet shield */ +#if defined(BOARD_HAS_ETHERNET) + #define SECRET_OPTIONAL_IP "" + #define SECRET_OPTIONAL_DNS "" + #define SECRET_OPTIONAL_GATEWAY "" + #define SECRET_OPTIONAL_NETMASK "" #endif diff --git a/examples/utility/ArduinoIoTCloud_Travis_CI/thingProperties.h b/examples/utility/ArduinoIoTCloud_Travis_CI/thingProperties.h index c0dded7a5..eb3c29258 100644 --- a/examples/utility/ArduinoIoTCloud_Travis_CI/thingProperties.h +++ b/examples/utility/ArduinoIoTCloud_Travis_CI/thingProperties.h @@ -1,23 +1,22 @@ -/****************************************************************************** - INCLUDE - ******************************************************************************/ - #include #include +#include "arduino_secrets.h" -#if defined(BOARD_HAS_WIFI) -#elif defined(BOARD_HAS_GSM) -#elif defined(BOARD_HAS_LORA) -#elif defined(BOARD_HAS_NB) -#else - #error "Arduino IoT Cloud currently only supports MKR1000, MKR WiFi 1010, MKR WAN 1300/1310, MKR NB 1500 and MKR GSM 1400" +#if !(defined(HAS_TCP) || defined(HAS_LORA)) + #error "Please check Arduino IoT Cloud supported boards list: https://github.com/arduino-libraries/ArduinoIoTCloud/#what" #endif /****************************************************************************** DEFINES ******************************************************************************/ -#define THING_ID "ARDUINO_IOT_CLOUD_THING_ID" +#if !defined(BOARD_HAS_SECURE_ELEMENT) + #define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif + +#if defined(HAS_LORA) + #define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#endif /****************************************************************************** GLOBAL CONSTANTS @@ -55,13 +54,22 @@ String str_property_7; String str_property_8; #if defined(BOARD_HAS_WIFI) - WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_SSID, SECRET_PASS); + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_WIFI_SSID, SECRET_WIFI_PASS); #elif defined(BOARD_HAS_GSM) GSMConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); #elif defined(BOARD_HAS_LORA) LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, EU868); #elif defined(BOARD_HAS_NB) NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_CATM1_NBIOT) + CatM1ConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_ETHERNET) + /* DHCP mode */ + //EthernetConnectionHandler ArduinoIoTPreferredConnection; + /* Manual mode. It will fallback in DHCP mode if SECRET_OPTIONAL_IP is invalid or equal to "0.0.0.0" */ + EthernetConnectionHandler ArduinoIoTPreferredConnection(SECRET_OPTIONAL_IP, SECRET_OPTIONAL_DNS, SECRET_OPTIONAL_GATEWAY, SECRET_OPTIONAL_NETMASK); +#elif defined(BOARD_HAS_CELLULAR) + CellularConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); #endif /****************************************************************************** @@ -76,10 +84,12 @@ void onStringPropertyChange(); /****************************************************************************** FUNCTIONS ******************************************************************************/ -#if defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) || defined (BOARD_HAS_NB) +#if defined(HAS_TCP) void initProperties() { - ArduinoCloud.setThingId(THING_ID); - +#if !defined(BOARD_HAS_SECURE_ELEMENT) + ArduinoCloud.setBoardId(BOARD_ID); + ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); +#endif ArduinoCloud.addProperty(bool_property_1, READWRITE, 1 * SECONDS); ArduinoCloud.addProperty(int_property_1, READ, 2 * MINUTES); ArduinoCloud.addProperty(float_property_1, WRITE, 3 * HOURS); @@ -107,7 +117,7 @@ void initProperties() { ArduinoCloud.addProperty(str_property_8, Permission::ReadWrite).publishEvery(1 * SECONDS).onSync(DEVICE_WINS); } -#elif defined(BOARD_HAS_LORA) +#elif defined(HAS_LORA) void initProperties() { ArduinoCloud.setThingId(THING_ID); diff --git a/examples/utility/Provisioning/ECCX08TLSConfig.h b/examples/utility/Provisioning/ECCX08TLSConfig.h deleted file mode 100644 index 9126b3666..000000000 --- a/examples/utility/Provisioning/ECCX08TLSConfig.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - This file is part of ArduinoIoTCloud. - - Copyright 2019 ARDUINO SA (http://www.arduino.cc/) - - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - 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. -*/ - -#ifndef _ECCX08_TLS_CONFIG_H_ -#define _ECCX08_TLS_CONFIG_H_ - -const byte DEFAULT_ECCX08_TLS_CONFIG[128] = { - // Read only - start - // SN[0:3] - 0x01, 0x23, 0x00, 0x00, - // RevNum - 0x00, 0x00, 0x50, 0x00, - // SN[4:8] - 0x00, 0x00, 0x00, 0x00, 0x00, - // Reserved - 0xC0, - // I2C_Enable - 0x71, - // Reserved - 0x00, - // Read only - end - // I2C_Address - 0xC0, - // Reserved - 0x00, - // OTPmode - 0x55, - // ChipMode - 0x00, - // SlotConfig - 0x83, 0x20, // External Signatures | Internal Signatures | IsSecret | Write Configure Never, Default: 0x83, 0x20, - 0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x87, 0x20, - 0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x8F, 0x20, - 0x87, 0x2F, // External Signatures | Internal Signatures | ECDH | IsSecret | WriteKey all slots | Write Configure Never, Default: 0xC4, 0x8F, - 0x87, 0x2F, // External Signatures | Internal Signatures | ECDH | IsSecret | WriteKey all slots | Write Configure Never, Default: 0x8F, 0x8F, - 0x8F, 0x8F, - 0x9F, 0x8F, - 0xAF, 0x8F, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0xAF, 0x8F, - // Counter[0] - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - // Counter[1] - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - // LastKeyUse - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - // Write via commands only - start - // UserExtra - 0x00, - // Selector - 0x00, - // LockValue - 0x55, - // LockConfig - 0x55, - // SlotLocked - 0xFF, 0xFF, - // Write via commands only - end - // RFU - 0x00, 0x00, - // X509format - 0x00, 0x00, 0x00, 0x00, - // KeyConfig - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x1C, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x1C, 0x00, - 0x1C, 0x00, - 0x1C, 0x00, - 0x1C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x1C, 0x00 -}; - -#endif /* _ECCX08_TLS_CONFIG_H_ */ diff --git a/examples/utility/Provisioning/Provisioning.ino b/examples/utility/Provisioning/Provisioning.ino index e4ea8ce1b..ebde2218b 100644 --- a/examples/utility/Provisioning/Provisioning.ino +++ b/examples/utility/Provisioning/Provisioning.ino @@ -1,27 +1,38 @@ -#include -#include "ECCX08TLSConfig.h" - -#include - -const bool DEBUG = true; -const int keySlot = 0; -const int compressedCertSlot = 10; -const int serialNumberAndAuthorityKeyIdentifierSlot = 11; -const int deviceIdSlot = 12; - -ECCX08CertClass ECCX08Cert; +#include +#include +#include +#include +#include + +#ifdef ARDUINO_SAMD_MKR1000 +#include +#define LATEST_WIFI_FIRMWARE_VERSION WIFI_FIRMWARE_LATEST_MODEL_B +#endif +#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_NANO_RP2040_CONNECT) +#include +#define LATEST_WIFI_FIRMWARE_VERSION WIFI_FIRMWARE_LATEST_VERSION +#endif +#if defined(ARDUINO_UNOR4_WIFI) +#include +#define LATEST_WIFI_FIRMWARE_VERSION WIFI_FIRMWARE_LATEST_VERSION +#endif + +String promptAndReadLine(const char* prompt, const unsigned int timeout = 0); void setup() { Serial.begin(9600); while (!Serial); - if (!ECCX08.begin()) { - Serial.println("No ECCX08 present!"); + SecureElement secureElement; + + if (!secureElement.begin()) { + Serial.println("No crypto present!"); while (1); } - if (!ECCX08.locked()) { - String lockConfirm = promptAndReadLine("Your ECCX08 is unlocked, would you like to lock it (y/N): "); + if (!secureElement.locked()) { + /* WARNING: This string is parsed from IoTCloud frontend */ + String lockConfirm = promptAndReadLine("Your crypto is unlocked, would you like to lock it (y/N): "); lockConfirm.toLowerCase(); if (lockConfirm != "y") { @@ -29,21 +40,24 @@ void setup() { while (1); } - if (!ECCX08.writeConfiguration(DEFAULT_ECCX08_TLS_CONFIG)) { - Serial.println("Writing ECCX08 configuration failed!"); + if (!secureElement.writeConfiguration()) { + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.println("Writing crypto configuration failed!"); while (1); } - if (!ECCX08.lock()) { - Serial.println("Locking ECCX08 configuration failed!"); + if (!secureElement.lock()) { + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.println("Locking crypto configuration failed!"); while (1); } - Serial.println("ECCX08 locked successfully"); + Serial.println("crypto locked successfully"); Serial.println(); } - String csrConfirm = promptAndReadLine("Would you like to generate a new private key and CSR (y/N): "); + /* WARNING: This string is parsed from IoTCloud frontend */ + String csrConfirm = promptAndReadLine("Would you like to generate a new private key and CSR (y/N): ", 5000); csrConfirm.toLowerCase(); if (csrConfirm != "y") { @@ -51,23 +65,34 @@ void setup() { while (1); } - if (!ECCX08Cert.beginCSR(keySlot, true)) { + ECP256Certificate Certificate; + + if (!Certificate.begin()) { Serial.println("Error starting CSR generation!"); while (1); } + /* WARNING: This string is parsed from IoTCloud frontend */ String deviceId = promptAndReadLine("Please enter the device id: "); - ECCX08Cert.setSubjectCommonName(deviceId); + Certificate.setSubjectCommonName(deviceId); + + if (!SElementCSR::build(secureElement, Certificate, (int)SElementArduinoCloudSlot::Key, true)) { + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.println("Error generating CSR!"); + while (1); + } - String csr = ECCX08Cert.endCSR(); + String csr = Certificate.getCSRPEM(); if (!csr) { + /* WARNING: This string is parsed from IoTCloud frontend */ Serial.println("Error generating CSR!"); while (1); } Serial.println("Generated CSR is:"); Serial.println(); + /* WARNING: This string is parsed from IoTCloud frontend */ Serial.println(csr); String issueYear = promptAndReadLine("Please enter the issue year of the certificate (2000 - 2031): "); @@ -79,63 +104,54 @@ void setup() { String authorityKeyIdentifier = promptAndReadLine("Please enter the certificates authority key identifier: "); String signature = promptAndReadLine("Please enter the certificates signature: "); - byte deviceIdBytes[72]; - byte serialNumberBytes[16]; - byte authorityKeyIdentifierBytes[20]; - byte signatureBytes[64]; + byte serialNumberBytes[ECP256_CERT_SERIAL_NUMBER_LENGTH]; + byte authorityKeyIdentifierBytes[ECP256_CERT_AUTHORITY_KEY_ID_LENGTH]; + byte signatureBytes[ECP256_CERT_SIGNATURE_LENGTH]; - deviceId.getBytes(deviceIdBytes, sizeof(deviceIdBytes)); hexStringToBytes(serialNumber, serialNumberBytes, sizeof(serialNumberBytes)); hexStringToBytes(authorityKeyIdentifier, authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); hexStringToBytes(signature, signatureBytes, sizeof(signatureBytes)); - if (!ECCX08.writeSlot(deviceIdSlot, deviceIdBytes, sizeof(deviceIdBytes))) { - Serial.println("Error storing device id!"); - while (1); - } - - if (!ECCX08Cert.beginStorage(compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { - Serial.println("Error starting ECCX08 storage!"); + if (!SElementArduinoCloudDeviceId::write(secureElement, deviceId, SElementArduinoCloudSlot::DeviceId)) { + Serial.println("Error storing device ID!"); while (1); } - ECCX08Cert.setSignature(signatureBytes); - ECCX08Cert.setAuthorityKeyIdentifier(authorityKeyIdentifierBytes); - ECCX08Cert.setSerialNumber(serialNumberBytes); - ECCX08Cert.setIssueYear(issueYear.toInt()); - ECCX08Cert.setIssueMonth(issueMonth.toInt()); - ECCX08Cert.setIssueDay(issueDay.toInt()); - ECCX08Cert.setIssueHour(issueHour.toInt()); - ECCX08Cert.setExpireYears(expireYears.toInt()); - - if (!ECCX08Cert.endStorage()) { - Serial.println("Error storing ECCX08 compressed cert!"); + if (!Certificate.begin()) { + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.println("Error starting crypto storage!"); while (1); } - if (!ECCX08Cert.beginReconstruction(keySlot, compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { - Serial.println("Error starting ECCX08 cert reconstruction!"); + Certificate.setSubjectCommonName(deviceId); + Certificate.setIssuerCountryName("US"); + Certificate.setIssuerOrganizationName("Arduino LLC US"); + Certificate.setIssuerOrganizationalUnitName("IT"); + Certificate.setIssuerCommonName("Arduino"); + Certificate.setSignature(signatureBytes, sizeof(signatureBytes)); + Certificate.setAuthorityKeyId(authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); + Certificate.setSerialNumber(serialNumberBytes, sizeof(serialNumberBytes)); + Certificate.setIssueYear(issueYear.toInt()); + Certificate.setIssueMonth(issueMonth.toInt()); + Certificate.setIssueDay(issueDay.toInt()); + Certificate.setIssueHour(issueHour.toInt()); + Certificate.setExpireYears(expireYears.toInt()); + + if (!SElementArduinoCloudCertificate::build(secureElement, Certificate, static_cast(SElementArduinoCloudSlot::Key))) { + Serial.println("Error building cert!"); while (1); } - ECCX08Cert.setIssuerCountryName("US"); - ECCX08Cert.setIssuerOrganizationName("Arduino LLC US"); - ECCX08Cert.setIssuerOrganizationalUnitName("IT"); - ECCX08Cert.setIssuerCommonName("Arduino"); - - if (!ECCX08Cert.endReconstruction()) { - Serial.println("Error reconstructing ECCX08 compressed cert!"); + if (!SElementArduinoCloudCertificate::write(secureElement, Certificate, SElementArduinoCloudSlot::CompressedCertificate)) { + Serial.println("Error storing cert!"); while (1); } - if (!DEBUG) { - return; - } - + /* WARNING: This string is parsed from IoTCloud frontend */ Serial.println("Compressed cert = "); - const byte* certData = ECCX08Cert.bytes(); - int certLength = ECCX08Cert.length(); + const byte* certData = Certificate.bytes(); + int certLength = Certificate.length(); for (int i = 0; i < certLength; i++) { byte b = certData[i]; @@ -146,23 +162,72 @@ void setup() { Serial.print(b, HEX); } Serial.println(); + + + String cert = Certificate.getCertPEM(); + if (!cert) { + Serial.println("Error generating cert!"); + while (1); + } + Serial.println("Cert PEM = "); + Serial.println(); + Serial.println(cert); + + +#ifdef LATEST_WIFI_FIRMWARE_VERSION + Serial.println("Checking firmware of WiFi module..."); + Serial.println(); + String fv = WiFi.firmwareVersion(); + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.print("Current firmware version: "); + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.println(fv); + + String latestFv = LATEST_WIFI_FIRMWARE_VERSION; + if (fv >= latestFv) { + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.println("Latest firmware version correctly installed."); + } else { + /* WARNING: This string is parsed from IoTCloud frontend */ + String latestFvStr = "The firmware is not up to date. Latest version available: " + latestFv; + Serial.println(latestFvStr); + } +#else + Serial.println(); + /* WARNING: This string is parsed from IoTCloud frontend */ + Serial.println("Program finished."); +#endif } void loop() { } -String promptAndReadLine(const char* prompt) { - Serial.print(prompt); - String s = readLine(); +String promptAndReadLine(const char* prompt, const unsigned int timeout) { + String s = ""; + while(1) { + Serial.print(prompt); + s = readLine(timeout); + if (s.length() > 0) { + break; + } + } Serial.println(s); return s; } -String readLine() { - String line; +bool isExpired(const unsigned int start, const unsigned int timeout) { + if (timeout) { + return (millis() - start) > timeout; + } else { + return false; + } +} - while (1) { +String readLine(const unsigned int timeout) { + String line; + const unsigned int start = millis(); + while (!isExpired(start, timeout)) { if (Serial.available()) { char c = Serial.read(); diff --git a/examples/utility/SelfProvisioning/ECCX08Cert.cpp b/examples/utility/SelfProvisioning/ECCX08Cert.cpp deleted file mode 100644 index a2e199e62..000000000 --- a/examples/utility/SelfProvisioning/ECCX08Cert.cpp +++ /dev/null @@ -1,941 +0,0 @@ -/* - This file is part of ArduinoIoTCloud. - - Copyright 2019 ARDUINO SA (http://www.arduino.cc/) - - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - 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. -*/ - -/****************************************************************************** - * INCLUDE - ******************************************************************************/ - -#include - -#include "bearssl/bearssl_hash.h" -#include - -#include "ECCX08Cert.h" - -/****************************************************************************** - * DEFINE - ******************************************************************************/ - -#define ASN1_INTEGER 0x02 -#define ASN1_BIT_STRING 0x03 -#define ASN1_NULL 0x05 -#define ASN1_OBJECT_IDENTIFIER 0x06 -#define ASN1_PRINTABLE_STRING 0x13 -#define ASN1_SEQUENCE 0x30 -#define ASN1_SET 0x31 - -struct __attribute__((__packed__)) CompressedCert { - byte signature[64]; - byte dates[3]; - byte unused[5]; -}; - -#define SERIAL_NUMBER_LENGTH 16 -#define AUTHORITY_KEY_IDENTIFIER_LENGTH 20 - -struct __attribute__((__packed__)) SerialNumberAndAuthorityKeyIdentifier { - byte serialNumber[SERIAL_NUMBER_LENGTH]; - byte authorityKeyIdentifier[AUTHORITY_KEY_IDENTIFIER_LENGTH]; -}; - -/****************************************************************************** - * LOCAL MODULE FUNCTIONS - ******************************************************************************/ - -static String base64Encode(const byte in[], unsigned int length, const char* prefix, const char* suffix) { - static const char* CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - - int b; - String out; - - int reserveLength = 4 * ((length + 2) / 3) + ((length / 3 * 4) / 76) + strlen(prefix) + strlen(suffix); - out.reserve(reserveLength); - - if (prefix) { - out += prefix; - } - - for (unsigned int i = 0; i < length; i += 3) { - if (i > 0 && (i / 3 * 4) % 76 == 0) { - out += '\n'; - } - - b = (in[i] & 0xFC) >> 2; - out += CODES[b]; - - b = (in[i] & 0x03) << 4; - if (i + 1 < length) { - b |= (in[i + 1] & 0xF0) >> 4; - out += CODES[b]; - b = (in[i + 1] & 0x0F) << 2; - if (i + 2 < length) { - b |= (in[i + 2] & 0xC0) >> 6; - out += CODES[b]; - b = in[i + 2] & 0x3F; - out += CODES[b]; - } else { - out += CODES[b]; - out += '='; - } - } else { - out += CODES[b]; - out += "=="; - } - } - - if (suffix) { - out += suffix; - } - - return out; -} - -/****************************************************************************** - * CTOR/DTOR - ******************************************************************************/ - -ECCX08CertClass::ECCX08CertClass() : - _keySlot(-1), - _compressedCertSlot(-1), - _serialNumberAndAuthorityKeyIdentifierSlot(-1), - _bytes(NULL), - _length(0) { -} - -ECCX08CertClass::~ECCX08CertClass() { - if (_bytes) { - free(_bytes); - _bytes = NULL; - } -} - -/****************************************************************************** - * PUBLIC MEMBER FUNCTIONS - ******************************************************************************/ - -int ECCX08CertClass::beginCSR(int keySlot, bool newPrivateKey) { - if (keySlot < 0 || keySlot > 8) { - return 0; - } - - _keySlot = keySlot; - - if (newPrivateKey) { - if (!ECCX08.generatePrivateKey(_keySlot, _temp)) { - return 0; - } - } else { - if (!ECCX08.generatePublicKey(_keySlot, _temp)) { - return 0; - } - } - - return 1; -} - -String ECCX08CertClass::endCSR() { - int versionLen = versionLength(); - int subjectLen = issuerOrSubjectLength(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName); - int subjectHeaderLen = sequenceHeaderLength(subjectLen); - int publicKeyLen = publicKeyLength(); - - int csrInfoLen = versionLen + subjectHeaderLen + subjectLen + publicKeyLen + 2; - int csrInfoHeaderLen = sequenceHeaderLength(csrInfoLen); - - byte csrInfo[csrInfoHeaderLen + csrInfoLen]; - byte* out = csrInfo; - - appendSequenceHeader(csrInfoLen, out); - out += csrInfoHeaderLen; - - // version - appendVersion(0x00, out); - out += versionLen; - - // subject - appendSequenceHeader(subjectLen, out); - out += subjectHeaderLen; - appendIssuerOrSubject(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName, out); - out += subjectLen; - - // public key - appendPublicKey(_temp, out); - out += publicKeyLen; - - // terminator - *out++ = 0xa0; - *out++ = 0x00; - - br_sha256_context sha256Context; - byte csrInfoSha256[64]; - byte signature[64]; - - br_sha256_init(&sha256Context); - br_sha256_update(&sha256Context, csrInfo, csrInfoHeaderLen + csrInfoLen); - br_sha256_out(&sha256Context, csrInfoSha256); - - if (!ECCX08.ecSign(_keySlot, csrInfoSha256, signature)) { - return ""; - } - - int signatureLen = signatureLength(signature); - int csrLen = csrInfoHeaderLen + csrInfoLen + signatureLen; - int csrHeaderLen = sequenceHeaderLength(csrLen); - - byte csr[csrLen + csrHeaderLen]; - out = csr; - - appendSequenceHeader(csrLen, out); - out += csrHeaderLen; - - // info - memcpy(out, csrInfo, csrInfoHeaderLen + csrInfoLen); - out += (csrInfoHeaderLen + csrInfoLen); - - // signature - appendSignature(signature, out); - out += signatureLen; - - return base64Encode(csr, csrLen + csrHeaderLen, "-----BEGIN CERTIFICATE REQUEST-----\n", "\n-----END CERTIFICATE REQUEST-----\n"); -} - -int ECCX08CertClass::beginStorage(int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot) { - if (compressedCertSlot < 8 || compressedCertSlot > 15) { - return 0; - } - - if (serialNumberAndAuthorityKeyIdentifierSlot < 8 || serialNumberAndAuthorityKeyIdentifierSlot > 15) { - return 0; - } - - _compressedCertSlot = compressedCertSlot; - _serialNumberAndAuthorityKeyIdentifierSlot = serialNumberAndAuthorityKeyIdentifierSlot; - - memset(_temp, 0x00, sizeof(_temp)); - - return 1; -} - -void ECCX08CertClass::setSignature(byte signature[]) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - memcpy(compressedCert->signature, signature, 64); -} - -void ECCX08CertClass::setIssueYear(int issueYear) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[0] &= 0x07; - compressedCert->dates[0] |= (issueYear - 2000) << 3; -} - -void ECCX08CertClass::setIssueMonth(int issueMonth) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[0] &= 0xf8; - compressedCert->dates[0] |= issueMonth >> 1; - - compressedCert->dates[1] &= 0x7f; - compressedCert->dates[1] |= issueMonth << 7; -} - -void ECCX08CertClass::setIssueDay(int issueDay) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[1] &= 0x83; - compressedCert->dates[1] |= issueDay << 2; -} - -void ECCX08CertClass::setIssueHour(int issueHour) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[2] &= 0x1f; - compressedCert->dates[2] |= issueHour << 5; - - compressedCert->dates[1] &= 0xfc; - compressedCert->dates[1] |= issueHour >> 3; -} - -void ECCX08CertClass::setExpireYears(int expireYears) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[2] &= 0xe0; - compressedCert->dates[2] |= expireYears; -} - -void ECCX08CertClass::setSerialNumber(const byte serialNumber[]) { - memcpy(&_temp[72], serialNumber, SERIAL_NUMBER_LENGTH); -} - -void ECCX08CertClass::setAuthorityKeyIdentifier(const byte authorityKeyIdentifier[]) { - memcpy(&_temp[88], authorityKeyIdentifier, AUTHORITY_KEY_IDENTIFIER_LENGTH); -} - -int ECCX08CertClass::endStorage() { - if (!ECCX08.writeSlot(_compressedCertSlot, &_temp[0], 72)) { - return 0; - } - - if (!ECCX08.writeSlot(_serialNumberAndAuthorityKeyIdentifierSlot, &_temp[72], SERIAL_NUMBER_LENGTH + AUTHORITY_KEY_IDENTIFIER_LENGTH)) { - return 0; - } - - return 1; -} - -int ECCX08CertClass::beginReconstruction(int keySlot, int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot) { - if (keySlot < 0 || keySlot > 8) { - return 0; - } - - if (compressedCertSlot < 8 || compressedCertSlot > 15) { - return 0; - } - - if (serialNumberAndAuthorityKeyIdentifierSlot < 8 || serialNumberAndAuthorityKeyIdentifierSlot > 15) { - return 0; - } - - _keySlot = keySlot; - _compressedCertSlot = compressedCertSlot; - _serialNumberAndAuthorityKeyIdentifierSlot = serialNumberAndAuthorityKeyIdentifierSlot; - - return 1; -} - -int ECCX08CertClass::endReconstruction() { - byte publicKey[64]; - struct CompressedCert compressedCert; - struct SerialNumberAndAuthorityKeyIdentifier serialNumberAndAuthorityKeyIdentifier; - - if (!ECCX08.generatePublicKey(_keySlot, publicKey)) { - return 0; - } - - if (!ECCX08.readSlot(_compressedCertSlot, (byte*)&compressedCert, sizeof(compressedCert))) { - return 0; - } - - if (!ECCX08.readSlot(_serialNumberAndAuthorityKeyIdentifierSlot, (byte*)&serialNumberAndAuthorityKeyIdentifier, sizeof(serialNumberAndAuthorityKeyIdentifier))) { - return 0; - } - - // dates - int year = (compressedCert.dates[0] >> 3) + 2000; - int month = ((compressedCert.dates[0] & 0x07) << 1) | (compressedCert.dates[1] >> 7); - int day = (compressedCert.dates[1] & 0x7c) >> 2; - int hour = ((compressedCert.dates[1] & 0x03) << 3) | (compressedCert.dates[2] >> 5); - int expireYears = (compressedCert.dates[2] & 0x1f); - - int datesSize = 30; - - if (year > 2049) { - // two more bytes for GeneralizedTime - datesSize += 2; - } - - if ((year + expireYears) > 2049) { - // two more bytes for GeneralizedTime - datesSize += 2; - } - - int serialNumberLen = serialNumberLength(serialNumberAndAuthorityKeyIdentifier.serialNumber); - - int issuerLen = issuerOrSubjectLength(_issuerCountryName, - _issuerStateProvinceName, - _issuerLocalityName, - _issuerOrganizationName, - _issuerOrganizationalUnitName, - _issuerCommonName); - - int issuerHeaderLen = sequenceHeaderLength(issuerLen); - - int subjectLen = issuerOrSubjectLength(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName); - - int subjectHeaderLen = sequenceHeaderLength(subjectLen); - - int publicKeyLen = publicKeyLength(); - - int authorityKeyIdentifierLen = authorityKeyIdentifierLength(serialNumberAndAuthorityKeyIdentifier.authorityKeyIdentifier); - - int signatureLen = signatureLength(compressedCert.signature); - - int certInfoLen = 5 + serialNumberLen + 12 + issuerHeaderLen + issuerLen + (datesSize + 2) + - subjectHeaderLen + subjectLen + publicKeyLen; - - if (authorityKeyIdentifierLen) { - certInfoLen += authorityKeyIdentifierLen; - } else { - certInfoLen += 4; - } - - int certInfoHeaderLen = sequenceHeaderLength(certInfoLen); - - int certDataLen = certInfoLen + certInfoHeaderLen + signatureLen; - int certDataHeaderLen = sequenceHeaderLength(certDataLen); - - _length = certDataLen + certDataHeaderLen; - _bytes = (byte*)realloc(_bytes, _length); - - if (!_bytes) { - _length = 0; - return 0; - } - - byte* out = _bytes; - - appendSequenceHeader(certDataLen, out); - out += certDataHeaderLen; - - appendSequenceHeader(certInfoLen, out); - out += certInfoHeaderLen; - - // version - *out++ = 0xA0; - *out++ = 0x03; - *out++ = 0x02; - *out++ = 0x01; - *out++ = 0x02; - - // serial number - appendSerialNumber(serialNumberAndAuthorityKeyIdentifier.serialNumber, out); - out += serialNumberLen; - - // ecdsaWithSHA256 - out += appendEcdsaWithSHA256(out); - - // issuer - appendSequenceHeader(issuerLen, out); - out += issuerHeaderLen; - appendIssuerOrSubject(_issuerCountryName, - _issuerStateProvinceName, - _issuerLocalityName, - _issuerOrganizationName, - _issuerOrganizationalUnitName, - _issuerCommonName, out); - out += issuerLen; - - *out++ = ASN1_SEQUENCE; - *out++ = datesSize; - out += appendDate(year, month, day, hour, 0, 0, out); - out += appendDate(year + expireYears, month, day, hour, 0, 0, out); - - // subject - appendSequenceHeader(subjectLen, out); - out += subjectHeaderLen; - appendIssuerOrSubject(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName, out); - out += subjectLen; - - // public key - appendPublicKey(publicKey, out); - out += publicKeyLen; - - if (authorityKeyIdentifierLen) { - appendAuthorityKeyIdentifier(serialNumberAndAuthorityKeyIdentifier.authorityKeyIdentifier, out); - out += authorityKeyIdentifierLen; - } else { - // null sequence - *out++ = 0xA3; - *out++ = 0x02; - *out++ = 0x30; - *out++ = 0x00; - } - - // signature - appendSignature(compressedCert.signature, out); - out += signatureLen; - - return 1; -} - -byte* ECCX08CertClass::bytes() { - return _bytes; -} - -int ECCX08CertClass::length() { - return _length; -} - -void ECCX08CertClass::setIssuerCountryName(const String& countryName) { - _issuerCountryName = countryName; -} - -void ECCX08CertClass::setIssuerStateProvinceName(const String& stateProvinceName) { - _issuerStateProvinceName = stateProvinceName; -} - -void ECCX08CertClass::setIssuerLocalityName(const String& localityName) { - _issuerLocalityName = localityName; -} - -void ECCX08CertClass::setIssuerOrganizationName(const String& organizationName) { - _issuerOrganizationName = organizationName; -} - -void ECCX08CertClass::setIssuerOrganizationalUnitName(const String& organizationalUnitName) { - _issuerOrganizationalUnitName = organizationalUnitName; -} - -void ECCX08CertClass::setIssuerCommonName(const String& commonName) { - _issuerCommonName = commonName; -} - -void ECCX08CertClass::setSubjectCountryName(const String& countryName) { - _subjectCountryName = countryName; -} - -void ECCX08CertClass::setSubjectStateProvinceName(const String& stateProvinceName) { - _subjectStateProvinceName = stateProvinceName; -} - -void ECCX08CertClass::setSubjectLocalityName(const String& localityName) { - _subjectLocalityName = localityName; -} - -void ECCX08CertClass::setSubjectOrganizationName(const String& organizationName) { - _subjectOrganizationName = organizationName; -} - -void ECCX08CertClass::setSubjectOrganizationalUnitName(const String& organizationalUnitName) { - _subjectOrganizationName = organizationalUnitName; -} - -void ECCX08CertClass::setSubjectCommonName(const String& commonName) { - _subjectCommonName = commonName; -} - -int ECCX08CertClass::versionLength() { - return 3; -} - -int ECCX08CertClass::issuerOrSubjectLength(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName) { - int length = 0; - int countryNameLength = countryName.length(); - int stateProvinceNameLength = stateProvinceName.length(); - int localityNameLength = localityName.length(); - int organizationNameLength = organizationName.length(); - int organizationalUnitNameLength = organizationalUnitName.length(); - int commonNameLength = commonName.length(); - - if (countryNameLength) { - length += (11 + countryNameLength); - } - - if (stateProvinceNameLength) { - length += (11 + stateProvinceNameLength); - } - - if (localityNameLength) { - length += (11 + localityNameLength); - } - - if (organizationNameLength) { - length += (11 + organizationNameLength); - } - - if (organizationalUnitNameLength) { - length += (11 + organizationalUnitNameLength); - } - - if (commonNameLength) { - length += (11 + commonNameLength); - } - - return length; -} - -int ECCX08CertClass::publicKeyLength() { - return (2 + 2 + 9 + 10 + 4 + 64); -} - -int ECCX08CertClass::authorityKeyIdentifierLength(const byte authorityKeyIdentifier[]) { - bool set = false; - - // check if the authority key identifier is non-zero - for (int i = 0; i < AUTHORITY_KEY_IDENTIFIER_LENGTH; i++) { - if (authorityKeyIdentifier[i] != 0) { - set = true; - break; - } - } - - return (set ? 37 : 0); -} - -int ECCX08CertClass::signatureLength(const byte signature[]) { - const byte* r = &signature[0]; - const byte* s = &signature[32]; - - int rLength = 32; - int sLength = 32; - - while (*r == 0x00 && rLength) { - r++; - rLength--; - } - - if (*r & 0x80) { - rLength++; - } - - while (*s == 0x00 && sLength) { - s++; - sLength--; - } - - if (*s & 0x80) { - sLength++; - } - - return (21 + rLength + sLength); -} - -int ECCX08CertClass::serialNumberLength(const byte serialNumber[]) { - int length = SERIAL_NUMBER_LENGTH; - - while (*serialNumber == 0 && length) { - serialNumber++; - length--; - } - - if (*serialNumber & 0x80) { - length++; - } - - return (2 + length); -} - -int ECCX08CertClass::sequenceHeaderLength(int length) { - if (length > 255) { - return 4; - } else if (length > 127) { - return 3; - } else { - return 2; - } -} - -void ECCX08CertClass::appendVersion(int version, byte out[]) { - out[0] = ASN1_INTEGER; - out[1] = 0x01; - out[2] = version; -} - -void ECCX08CertClass::appendIssuerOrSubject(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName, - byte out[]) { - if (countryName.length() > 0) { - out += appendName(countryName, 0x06, out); - } - - if (stateProvinceName.length() > 0) { - out += appendName(stateProvinceName, 0x08, out); - } - - if (localityName.length() > 0) { - out += appendName(localityName, 0x07, out); - } - - if (organizationName.length() > 0) { - out += appendName(organizationName, 0x0a, out); - } - - if (organizationalUnitName.length() > 0) { - out += appendName(organizationalUnitName, 0x0b, out); - } - - if (commonName.length() > 0) { - out += appendName(commonName, 0x03, out); - } -} - -void ECCX08CertClass::appendPublicKey(const byte publicKey[], byte out[]) { - int subjectPublicKeyDataLength = 2 + 9 + 10 + 4 + 64; - - // subject public key - *out++ = ASN1_SEQUENCE; - *out++ = (subjectPublicKeyDataLength) & 0xff; - - *out++ = ASN1_SEQUENCE; - *out++ = 0x13; - - // EC public key - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x07; - *out++ = 0x2a; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xce; - *out++ = 0x3d; - *out++ = 0x02; - *out++ = 0x01; - - // PRIME 256 v1 - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x08; - *out++ = 0x2a; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xce; - *out++ = 0x3d; - *out++ = 0x03; - *out++ = 0x01; - *out++ = 0x07; - - *out++ = 0x03; - *out++ = 0x42; - *out++ = 0x00; - *out++ = 0x04; - - memcpy(out, publicKey, 64); -} - -void ECCX08CertClass::appendAuthorityKeyIdentifier(const byte authorityKeyIdentifier[], byte out[]) { - // [3] - *out++ = 0xa3; - *out++ = 0x23; - - // sequence - *out++ = ASN1_SEQUENCE; - *out++ = 0x21; - - // sequence - *out++ = ASN1_SEQUENCE; - *out++ = 0x1f; - - // 2.5.29.35 authorityKeyIdentifier(X.509 extension) - *out++ = 0x06; - *out++ = 0x03; - *out++ = 0x55; - *out++ = 0x1d; - *out++ = 0x23; - - // octet string - *out++ = 0x04; - *out++ = 0x18; - - // sequence - *out++ = ASN1_SEQUENCE; - *out++ = 0x16; - - *out++ = 0x80; - *out++ = 0x14; - - memcpy(out, authorityKeyIdentifier, 20); -} - -void ECCX08CertClass::appendSignature(const byte signature[], byte out[]) { - // signature algorithm - *out++ = ASN1_SEQUENCE; - *out++ = 0x0a; - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x08; - - // ECDSA with SHA256 - *out++ = 0x2a; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xce; - *out++ = 0x3d; - *out++ = 0x04; - *out++ = 0x03; - *out++ = 0x02; - - const byte* r = &signature[0]; - const byte* s = &signature[32]; - - int rLength = 32; - int sLength = 32; - - while (*r == 0 && rLength) { - r++; - rLength--; - } - - while (*s == 0 && sLength) { - s++; - sLength--; - } - - if (*r & 0x80) { - rLength++; - } - - if (*s & 0x80) { - sLength++; - } - - *out++ = ASN1_BIT_STRING; - *out++ = (rLength + sLength + 7); - *out++ = 0; - - *out++ = ASN1_SEQUENCE; - *out++ = (rLength + sLength + 4); - - *out++ = ASN1_INTEGER; - *out++ = rLength; - if ((*r & 0x80) && rLength) { - *out++ = 0; - rLength--; - } - memcpy(out, r, rLength); - out += rLength; - - *out++ = ASN1_INTEGER; - *out++ = sLength; - if ((*s & 0x80) && sLength) { - *out++ = 0; - sLength--; - } - memcpy(out, s, sLength); - out += rLength; -} - -void ECCX08CertClass::appendSerialNumber(const byte serialNumber[], byte out[]) { - int length = SERIAL_NUMBER_LENGTH; - - while (*serialNumber == 0 && length) { - serialNumber++; - length--; - } - - if (*serialNumber & 0x80) { - length++; - } - - *out++ = ASN1_INTEGER; - *out++ = length; - - if (*serialNumber & 0x80) { - *out++ = 0x00; - length--; - } - - memcpy(out, serialNumber, length); -} - -int ECCX08CertClass::appendName(const String& name, int type, byte out[]) { - int nameLength = name.length(); - - *out++ = ASN1_SET; - *out++ = nameLength + 9; - - *out++ = ASN1_SEQUENCE; - *out++ = nameLength + 7; - - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x03; - *out++ = 0x55; - *out++ = 0x04; - *out++ = type; - - *out++ = ASN1_PRINTABLE_STRING; - *out++ = nameLength; - memcpy(out, name.c_str(), nameLength); - - return (nameLength + 11); -} - -void ECCX08CertClass::appendSequenceHeader(int length, byte out[]) { - *out++ = ASN1_SEQUENCE; - if (length > 255) { - *out++ = 0x82; - *out++ = (length >> 8) & 0xff; - } else if (length > 127) { - *out++ = 0x81; - } - *out++ = (length) & 0xff; -} - -int ECCX08CertClass::appendDate(int year, int month, int day, int hour, int minute, int second, byte out[]) { - bool useGeneralizedTime = (year > 2049); - - if (useGeneralizedTime) { - *out++ = 0x18; - *out++ = 0x0f; - *out++ = '0' + (year / 1000); - *out++ = '0' + ((year % 1000) / 100); - *out++ = '0' + ((year % 100) / 10); - *out++ = '0' + (year % 10); - } else { - year -= 2000; - - *out++ = 0x17; - *out++ = 0x0d; - *out++ = '0' + (year / 10); - *out++ = '0' + (year % 10); - } - *out++ = '0' + (month / 10); - *out++ = '0' + (month % 10); - *out++ = '0' + (day / 10); - *out++ = '0' + (day % 10); - *out++ = '0' + (hour / 10); - *out++ = '0' + (hour % 10); - *out++ = '0' + (minute / 10); - *out++ = '0' + (minute % 10); - *out++ = '0' + (second / 10); - *out++ = '0' + (second % 10); - *out++ = 0x5a; // UTC - - return (useGeneralizedTime ? 17 : 15); -} - -int ECCX08CertClass::appendEcdsaWithSHA256(byte out[]) { - *out++ = ASN1_SEQUENCE; - *out++ = 0x0A; - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x08; - *out++ = 0x2A; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xCE; - *out++ = 0x3D; - *out++ = 0x04; - *out++ = 0x03; - *out++ = 0x02; - - return 12; -} diff --git a/examples/utility/SelfProvisioning/ECCX08Cert.h b/examples/utility/SelfProvisioning/ECCX08Cert.h deleted file mode 100644 index d40d4cd02..000000000 --- a/examples/utility/SelfProvisioning/ECCX08Cert.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - This file is part of ArduinoIoTCloud. - - Copyright 2019 ARDUINO SA (http://www.arduino.cc/) - - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - 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. -*/ - -#ifndef _ECCX08_CERT_H_ -#define _ECCX08_CERT_H_ - -/****************************************************************************** - * INCLUDE - ******************************************************************************/ - -#include - -/****************************************************************************** - * CLASS DECLARATION - ******************************************************************************/ - -class ECCX08CertClass { - - public: - ECCX08CertClass(); - virtual ~ECCX08CertClass(); - - int beginCSR(int keySlot, bool newPrivateKey = true); - String endCSR(); - - int beginStorage(int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot); - void setSignature(byte signature[]); - void setIssueYear(int issueYear); - void setIssueMonth(int issueMonth); - void setIssueDay(int issueDay); - void setIssueHour(int issueHour); - void setExpireYears(int expireYears); - void setSerialNumber(const byte serialNumber[]); - void setAuthorityKeyIdentifier(const byte authorityKeyIdentifier[]); - int endStorage(); - - int beginReconstruction(int keySlot, int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot); - int endReconstruction(); - - byte* bytes(); - int length(); - - void setIssuerCountryName(const String& countryName); - void setIssuerStateProvinceName(const String& stateProvinceName); - void setIssuerLocalityName(const String& localityName); - void setIssuerOrganizationName(const String& organizationName); - void setIssuerOrganizationalUnitName(const String& organizationalUnitName); - void setIssuerCommonName(const String& commonName); - - void setSubjectCountryName(const String& countryName); - void setSubjectStateProvinceName(const String& stateProvinceName); - void setSubjectLocalityName(const String& localityName); - void setSubjectOrganizationName(const String& organizationName); - void setSubjectOrganizationalUnitName(const String& organizationalUnitName); - void setSubjectCommonName(const String& commonName); - - private: - int versionLength(); - - int issuerOrSubjectLength(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName); - - int publicKeyLength(); - - int authorityKeyIdentifierLength(const byte authorityKeyIdentifier[]); - - int signatureLength(const byte signature[]); - - int serialNumberLength(const byte serialNumber[]); - - int sequenceHeaderLength(int length); - - void appendVersion(int version, byte out[]); - - void appendIssuerOrSubject(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName, - byte out[]); - - void appendPublicKey(const byte publicKey[], byte out[]); - - void appendAuthorityKeyIdentifier(const byte authorityKeyIdentifier[], byte out[]); - - void appendSignature(const byte signature[], byte out[]); - - void appendSerialNumber(const byte serialNumber[], byte out[]); - - int appendName(const String& name, int type, byte out[]); - - void appendSequenceHeader(int length, byte out[]); - - int appendDate(int year, int month, int day, int hour, int minute, int second, byte out[]); - - int appendEcdsaWithSHA256(byte out[]); - - private: - int _keySlot; - int _compressedCertSlot; - int _serialNumberAndAuthorityKeyIdentifierSlot; - - String _issuerCountryName; - String _issuerStateProvinceName; - String _issuerLocalityName; - String _issuerOrganizationName; - String _issuerOrganizationalUnitName; - String _issuerCommonName; - - String _subjectCountryName; - String _subjectStateProvinceName; - String _subjectLocalityName; - String _subjectOrganizationName; - String _subjectOrganizationalUnitName; - String _subjectCommonName; - - byte _temp[108]; - byte* _bytes; - int _length; -}; - -#endif /* _ECCX08_CERT_H_ */ diff --git a/examples/utility/SelfProvisioning/ECCX08TLSConfig.h b/examples/utility/SelfProvisioning/ECCX08TLSConfig.h deleted file mode 100644 index 9126b3666..000000000 --- a/examples/utility/SelfProvisioning/ECCX08TLSConfig.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - This file is part of ArduinoIoTCloud. - - Copyright 2019 ARDUINO SA (http://www.arduino.cc/) - - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - 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. -*/ - -#ifndef _ECCX08_TLS_CONFIG_H_ -#define _ECCX08_TLS_CONFIG_H_ - -const byte DEFAULT_ECCX08_TLS_CONFIG[128] = { - // Read only - start - // SN[0:3] - 0x01, 0x23, 0x00, 0x00, - // RevNum - 0x00, 0x00, 0x50, 0x00, - // SN[4:8] - 0x00, 0x00, 0x00, 0x00, 0x00, - // Reserved - 0xC0, - // I2C_Enable - 0x71, - // Reserved - 0x00, - // Read only - end - // I2C_Address - 0xC0, - // Reserved - 0x00, - // OTPmode - 0x55, - // ChipMode - 0x00, - // SlotConfig - 0x83, 0x20, // External Signatures | Internal Signatures | IsSecret | Write Configure Never, Default: 0x83, 0x20, - 0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x87, 0x20, - 0x87, 0x20, // External Signatures | Internal Signatures | ECDH | IsSecret | Write Configure Never, Default: 0x8F, 0x20, - 0x87, 0x2F, // External Signatures | Internal Signatures | ECDH | IsSecret | WriteKey all slots | Write Configure Never, Default: 0xC4, 0x8F, - 0x87, 0x2F, // External Signatures | Internal Signatures | ECDH | IsSecret | WriteKey all slots | Write Configure Never, Default: 0x8F, 0x8F, - 0x8F, 0x8F, - 0x9F, 0x8F, - 0xAF, 0x8F, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0xAF, 0x8F, - // Counter[0] - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - // Counter[1] - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - // LastKeyUse - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, - // Write via commands only - start - // UserExtra - 0x00, - // Selector - 0x00, - // LockValue - 0x55, - // LockConfig - 0x55, - // SlotLocked - 0xFF, 0xFF, - // Write via commands only - end - // RFU - 0x00, 0x00, - // X509format - 0x00, 0x00, 0x00, 0x00, - // KeyConfig - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x33, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x1C, 0x00, - 0x33, 0x00, // Private | Public | P256 NIST ECC key, Default: 0x1C, 0x00, - 0x1C, 0x00, - 0x1C, 0x00, - 0x1C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x3C, 0x00, - 0x1C, 0x00 -}; - -#endif /* _ECCX08_TLS_CONFIG_H_ */ diff --git a/examples/utility/SelfProvisioning/SelfProvisioning.ino b/examples/utility/SelfProvisioning/SelfProvisioning.ino index a0c13724b..46bfcafef 100644 --- a/examples/utility/SelfProvisioning/SelfProvisioning.ino +++ b/examples/utility/SelfProvisioning/SelfProvisioning.ino @@ -16,23 +16,13 @@ */ #include "arduino_secrets.h" -#include "ECCX08TLSConfig.h" -#include "ECCX08Cert.h" -#include +#include +#include +#include +#include #include -#include - -// from section 10.3.3 of the SAMD datasheet -#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) -#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) -#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044) -#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) const bool DEBUG = true; -const int keySlot = 0; -const int compressedCertSlot = 10; -const int serialNumberAndAuthorityKeyIdentifierSlot = 11; -const int deviceIdSlot = 12; char ssid[] = SECRET_SSID; char pass[] = SECRET_PASS; @@ -40,13 +30,44 @@ char client_id[] = SECRET_CLIENT_ID; char secret_id[] = SECRET_SECRET_ID; #if defined(ARDUINO_SAMD_NANO_33_IOT) + #include char board_type[] = "nano_33_iot"; // Nano 33 IoT char board_fqbn[] = "arduino:samd:nano_33_iot"; // Nano 33 IoT #elif defined(ARDUINO_SAMD_MKRWIFI1010) + #include char board_type[] = "mkrwifi1010"; // MKR WiFi 1010 char board_fqbn[] = "arduino:samd:mkrwifi1010"; // MKR WiFi 1010 +#elif defined(ARDUINO_NANO_RP2040_CONNECT) + #include + char board_type[] = "nanorp2040connect"; // Nano RP2040 Connect + char board_fqbn[] = "arduino:mbed_nano:nanorp2040connect"; // Nano RP2040 Connect +#elif defined(ARDUINO_PORTENTA_H7_M7) + #include + char board_type[] = "envie_m7"; // Portenta H7 + char board_fqbn[] = "arduino:mbed_portenta:envie_m7"; // Portenta H7 +#elif defined(ARDUINO_NICLA_VISION) + #include + char board_type[] = "nicla_vision"; // Nicla Vision + char board_fqbn[] = "arduino:mbed_nicla:nicla_vision"; // Nicla Vision +#elif defined(ARDUINO_GIGA) + #include + char board_type[] = "giga"; // Giga R1 WiFi + char board_fqbn[] = "arduino:mbed_giga:giga"; // Giga R1 WiFi +#elif defined(ARDUINO_OPTA) + #include + char board_type[] = "opta"; // Opta + char board_fqbn[] = "arduino:mbed_opta:opta"; // Opta +#elif defined(ARDUINO_PORTENTA_C33) + #include + #include + char board_type[] = "portenta_c33"; // Portenta C33 + char board_fqbn[] = "arduino:renesas_portenta:portenta_c33"; // Portenta C33 +#elif defined(ARDUINO_UNOR4_WIFI) + #include + char board_type[] = "unor4wifi"; // UNO R4 WiFi + char board_fqbn[] = "arduino:renesas_uno:unor4wifi"; // UNO R4 WiFI #else - char board_type[] = "nonina"; // Not supported boards + char board_type[] = "unsupported"; // Not supported boards char board_fqbn[] = ""; #endif @@ -67,13 +88,13 @@ char server[] = "api2.arduino.cc"; // server address WiFiSSLClient client; int status = WL_IDLE_STATUS; -ECCX08CertClass ECCX08Cert; +SecureElement secureElement; void setup() { Serial.begin(9600); while (!Serial); - if (board_type == "nonina") { + if (board_type == "unsupported") { Serial.println("Sorry, this sketch only works on Nano 33 IoT and MKR 1010 WiFi"); while (1) { ; } } @@ -84,7 +105,7 @@ void setup() { // Connect to WPA/WPA2 network: status = WiFi.begin(ssid, pass); - delay(10000); + delay(3000); } Serial.print("SSID: "); @@ -94,56 +115,37 @@ void setup() { Serial.print("IP Address: "); Serial.println(ip); - while (!ECCX08.begin()) { - Serial.println("No ECCX08 present!"); + while (!secureElement.begin()) { + Serial.println("No secureElement present!"); delay(100); } - if (!ECCX08.locked()) { + if (!secureElement.locked()) { - if (!ECCX08.writeConfiguration(DEFAULT_ECCX08_TLS_CONFIG)) { - Serial.println("Writing ECCX08 configuration failed!"); + if (!secureElement.writeConfiguration()) { + Serial.println("Writing secureElement configuration failed!"); Serial.println("Stopping Provisioning"); while (1); } - if (!ECCX08.lock()) { - Serial.println("Locking ECCX08 configuration failed!"); + if (!secureElement.lock()) { + Serial.println("Locking secureElement configuration failed!"); Serial.println("Stopping Provisioning"); while (1); } - Serial.println("ECCX08 locked successfully"); + Serial.println("secureElement locked successfully"); Serial.println(); } //Random number for device name - board_name += String(ECCX08.random(65535)); - - uint32_t BoardUniqueID[4]; - BoardUniqueID[0] = SERIAL_NUMBER_WORD_0; - BoardUniqueID[1] = SERIAL_NUMBER_WORD_1; - BoardUniqueID[2] = SERIAL_NUMBER_WORD_2; - BoardUniqueID[3] = SERIAL_NUMBER_WORD_3; - uint8_t bid[32]; - for (int i = 0; i < 4; i++) - { - bid[i*4+0] = (uint8_t)(BoardUniqueID[i] >> 24); - bid[i*4+1] = (uint8_t)(BoardUniqueID[i] >> 16); - bid[i*4+2] = (uint8_t)(BoardUniqueID[i] >> 8); - bid[i*4+3] = (uint8_t)(BoardUniqueID[i] >> 0); - } - - for (size_t i = 0; i < 16; i++) { - if (bid[i] < 16) { - ArduinoID += String(0, HEX); - } - ArduinoID += String(bid[i], HEX); - } - ArduinoID.toUpperCase(); - + board_name += String(secureElement.random(65535)); Serial.print("Device Name: "); Serial.println(board_name); + //Board Serial Number + ArduinoID = ArduinoSerialNumber(); + Serial.print("SN: "); + Serial.println(ArduinoID); // Create Arduino Token ArduinoToken(client_id, secret_id); Serial.print("Bearer Token: "); @@ -155,18 +157,32 @@ void setup() { delay(2000); - while (!ECCX08Cert.beginCSR(keySlot, true)) { + // Configure WiFi firmware version + String fv = WiFi.firmwareVersion(); + WiFiFirmwareVersion(fv, deviceId, Arduino_Token); + Serial.print("WiFi Firmware Version: "); + Serial.println(fv); + + ECP256Certificate Certificate; + + while (!Certificate.begin()) { Serial.println("Error starting CSR generation!"); - delay(2000); + while (1); } - ECCX08Cert.setSubjectCommonName(deviceId); + Certificate.setSubjectCommonName(deviceId); - String csr = ECCX08Cert.endCSR(); + if (!SElementCSR::build(secureElement, Certificate, static_cast(SElementArduinoCloudSlot::Key), true)) { + Serial.println("Error generating CSR!"); + while (1); + } + + String csr = Certificate.getCSRPEM(); - while (!csr) { + if (!csr) { + /* WARNING: This string is parsed from IoTCloud frontend */ Serial.println("Error generating CSR!"); - delay(2000); + while (1); } Serial.println("Generated CSR is:"); @@ -206,42 +222,37 @@ void setup() { hexStringToBytes(authorityKeyIdentifier, authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); hexStringToBytes(signature, signatureBytes, sizeof(signatureBytes)); - if (!ECCX08.writeSlot(deviceIdSlot, deviceIdBytes, sizeof(deviceIdBytes))) { + if (!secureElement.writeSlot(static_cast(SElementArduinoCloudSlot::DeviceId), deviceIdBytes, sizeof(deviceIdBytes))) { Serial.println("Error storing device id!"); while (1); } - if (!ECCX08Cert.beginStorage(compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { - Serial.println("Error starting ECCX08 storage!"); - while (1); - } - - ECCX08Cert.setSignature(signatureBytes); - ECCX08Cert.setAuthorityKeyIdentifier(authorityKeyIdentifierBytes); - ECCX08Cert.setSerialNumber(serialNumberBytes); - ECCX08Cert.setIssueYear(issueYear.toInt()); - ECCX08Cert.setIssueMonth(issueMonth.toInt()); - ECCX08Cert.setIssueDay(issueDay.toInt()); - ECCX08Cert.setIssueHour(issueHour.toInt()); - ECCX08Cert.setExpireYears(expireYears.toInt()); - - if (!ECCX08Cert.endStorage()) { - Serial.println("Error storing ECCX08 compressed cert!"); + if (!Certificate.begin()) { + Serial.println("Error starting secureElement storage!"); while (1); } - if (!ECCX08Cert.beginReconstruction(keySlot, compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { - Serial.println("Error starting ECCX08 cert reconstruction!"); + Certificate.setSubjectCommonName(deviceId); + Certificate.setIssuerCountryName("US"); + Certificate.setIssuerOrganizationName("Arduino LLC US"); + Certificate.setIssuerOrganizationalUnitName("IT"); + Certificate.setIssuerCommonName("Arduino"); + Certificate.setSignature(signatureBytes, sizeof(signatureBytes)); + Certificate.setAuthorityKeyId(authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); + Certificate.setSerialNumber(serialNumberBytes, sizeof(serialNumberBytes)); + Certificate.setIssueYear(issueYear.toInt()); + Certificate.setIssueMonth(issueMonth.toInt()); + Certificate.setIssueDay(issueDay.toInt()); + Certificate.setIssueHour(issueHour.toInt()); + Certificate.setExpireYears(expireYears.toInt()); + + if (!SElementArduinoCloudCertificate::build(secureElement, Certificate, static_cast(SElementArduinoCloudSlot::Key))) { + Serial.println("Error building secureElement compressed cert!"); while (1); } - ECCX08Cert.setIssuerCountryName("US"); - ECCX08Cert.setIssuerOrganizationName("Arduino LLC US"); - ECCX08Cert.setIssuerOrganizationalUnitName("IT"); - ECCX08Cert.setIssuerCommonName("Arduino"); - - if (!ECCX08Cert.endReconstruction()) { - Serial.println("Error reconstructing ECCX08 compressed cert!"); + if (!SElementArduinoCloudCertificate::write(secureElement, Certificate, SElementArduinoCloudSlot::CompressedCertificate)) { + Serial.println("Error storing cert!"); while (1); } @@ -251,8 +262,8 @@ void setup() { Serial.println("Compressed cert = "); - const byte* certData = ECCX08Cert.bytes(); - int certLength = ECCX08Cert.length(); + const byte* certData = Certificate.bytes(); + int certLength = Certificate.length(); for (int i = 0; i < certLength; i++) { byte b = certData[i]; @@ -314,6 +325,81 @@ void hexStringToBytes(String& in, byte out[], int length) { } } +#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RENESAS) + +static void utox8(uint32_t val, uint8_t* s) { + for (int i = 0; i < 16; i=i+2) { + int d = val & 0XF; + val = (val >> 4); + + s[15 - i -1] = d > 9 ? 'A' + d - 10 : '0' + d; + s[15 - i] = '\0'; + } +} +#endif + +#ifdef ARDUINO_ARCH_SAMD + +uint8_t getUniqueSerialNumber(uint8_t* name) { + utox8(*(volatile uint32_t*)(0x0080A00C), &name[0]); + utox8(*(volatile uint32_t*)(0x0080A040), &name[16]); + utox8(*(volatile uint32_t*)(0x0080A044), &name[32]); + utox8(*(volatile uint32_t*)(0x0080A048), &name[48]); + return 64; +} + +#endif + +#ifdef ARDUINO_ARCH_RENESAS +uint8_t getUniqueSerialNumber(uint8_t* name) { + const bsp_unique_id_t* t = R_BSP_UniqueIdGet(); + utox8(t->unique_id_words[0], &name[0]); + utox8(t->unique_id_words[1], &name[16]); + utox8(t->unique_id_words[2], &name[32]); + utox8(t->unique_id_words[3], &name[48]); + return 64; +} +#endif + +String ArduinoSerialNumber() { + + uint8_t uniqueSerialNumber[64 + 1] = {0}; + char BoardUniqueID[32 + 1] = {0}; + + int uniqueSerialNumberLength = getUniqueSerialNumber(uniqueSerialNumber); + for(int i = 0, k = 0; i < uniqueSerialNumberLength; i = i+2, k++) { + BoardUniqueID[k] = uniqueSerialNumber[i]; + } + + String serialNumber = String(BoardUniqueID); + serialNumber.toUpperCase(); + return serialNumber; + +} + +void WiFiFirmwareVersion(String fv, String deviceId, String token) { + Serial.println("Configuring WiFi firmware version..."); + String PostData = "{\"wifi_fw_version\":\""; + PostData += fv; + PostData += "\"}"; + + if (client.connect(server, 443)) { + client.print("POST /iot/v2/devices/"); + client.print(deviceId); + client.println(" HTTP/1.1"); + client.println("Host: api2.arduino.cc"); + client.println("Connection: close"); + client.println("Content-Type: application/json;charset=UTF-8"); + client.print("Authorization: Bearer "); + client.println(token); + client.print("Content-Length: "); + client.println(PostData.length()); + client.println(); + client.println(PostData); + } + client.stop(); +} + void ArduinoToken(String client_id, String client_secret) { Serial.println("Creating Bearer Token..."); String PostData = "grant_type=client_credentials&client_id="; @@ -354,12 +440,14 @@ void ArduinoToken(String client_id, String client_secret) { if (tokenResponse[intIndex] == -1) { break; } + delay(1); intIndex++; } JSONVar myObject = JSON.parse(tokenResponse); if (myObject.hasOwnProperty("access_token")) { Arduino_Token += (const char*) myObject["access_token"]; } + client.stop(); } void BoardUuid(String board_name, String board_type, String board_fqbn, String board_serial, String user_token) { @@ -388,7 +476,8 @@ void BoardUuid(String board_name, String board_type, String board_fqbn, String b } while (!client.available()) { - ; + Serial.println("No client"); + delay(2000); } char endOfHeaders[] = "\r\n\r\n"; @@ -407,12 +496,14 @@ void BoardUuid(String board_name, String board_type, String board_fqbn, String b if (deviceResponse[intIndex] == -1) { break; } + delay(1); intIndex++; } JSONVar myObject = JSON.parse(deviceResponse); if (myObject.hasOwnProperty("id")) { deviceId += (const char*) myObject["id"]; } + client.stop(); } void ArduinoCertificate(String user_token, String DeviceUuid, String csr) { @@ -461,24 +552,27 @@ void ArduinoCertificate(String user_token, String DeviceUuid, String csr) { if (certResponse[intIndex] == -1) { break; } + delay(1); intIndex++; } - JSONVar myObject = JSON.parse(certResponse); + char* p = strstr(certResponse, "{"); + JSONVar myObject = JSON.parse(p); String certZip = JSON.stringify(myObject["compressed"]); JSONVar myCert = JSON.parse(certZip); - if (myCert.hasOwnProperty("not_before")) { + if (myCert.hasOwnProperty("not_before") && + myCert.hasOwnProperty("serial") && + myCert.hasOwnProperty("authority_key_identifier") && + myCert.hasOwnProperty("signature_asn1_x") && + myCert.hasOwnProperty("signature_asn1_x")) { not_before += (const char*) myCert["not_before"]; - } - if (myCert.hasOwnProperty("serial")) { serialNumber += (const char*) myCert["serial"]; - } - if (myCert.hasOwnProperty("authority_key_identifier")) { authorityKeyIdentifier += (const char*) myCert["authority_key_identifier"]; - } - if (myCert.hasOwnProperty("signature_asn1_x")) { signature += (const char*) myCert["signature_asn1_x"]; - } - if (myCert.hasOwnProperty("signature_asn1_y")) { signature += (const char*) myCert["signature_asn1_y"]; + } else { + Serial.println("Error parsing cloud certificate"); + while (1) { + ; + } } -} \ No newline at end of file +} diff --git a/extras/codespell-ignore-words-list.txt b/extras/codespell-ignore-words-list.txt deleted file mode 100644 index 513a50e47..000000000 --- a/extras/codespell-ignore-words-list.txt +++ /dev/null @@ -1,2 +0,0 @@ -wan -alocation diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index dd7b3e990..5435dfcf1 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -1,19 +1,54 @@ ########################################################################## -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.5) ########################################################################## project(testArduinoIoTCloud) +Include(FetchContent) + +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.4.0 +) + +FetchContent_Declare( + cloudutils + GIT_REPOSITORY https://github.com/arduino-libraries/Arduino_CloudUtils.git + GIT_TAG main +) + +FetchContent_MakeAvailable(Catch2) + +FetchContent_MakeAvailable(cloudutils) ########################################################################## include_directories(include) include_directories(../../src) +include_directories(../../src/message) include_directories(../../src/cbor) include_directories(../../src/property) -include_directories(external/catch/v2.12.1/include) -include_directories(external/fakeit/v2.0.5/include) +include_directories(../../src/utility/time) + +# add_library(cloudutils STATIC IMPORTED GLOBAL) +add_library(cloudutils INTERFACE) + +target_include_directories( + cloudutils INTERFACE + ${cloudutils_SOURCE_DIR}/src/ +) + +target_include_directories( + cloudutils INTERFACE + ${cloudutils_SOURCE_DIR}/src/cbor +) + +target_include_directories( + cloudutils INTERFACE + ${cloudutils_SOURCE_DIR}/src/interfaces +) ########################################################################## @@ -31,14 +66,21 @@ set(TEST_SRCS src/test_addPropertyReal.cpp src/test_callback.cpp src/test_CloudColor.cpp + src/test_CloudFloat.cpp + src/test_CloudWrapperFloat.cpp src/test_CloudLocation.cpp + src/test_CloudSchedule.cpp src/test_decode.cpp src/test_encode.cpp + src/test_command_decode.cpp + src/test_command_encode.cpp src/test_publishEvery.cpp src/test_publishOnChange.cpp src/test_publishOnChangeRateLimit.cpp src/test_readOnly.cpp src/test_writeOnly.cpp + src/test_writeOnDemand.cpp + src/test_writeOnChange.cpp ) set(TEST_UTIL_SRCS @@ -51,18 +93,22 @@ set(TEST_DUT_SRCS ../../src/property/PropertyContainer.cpp ../../src/cbor/CBORDecoder.cpp ../../src/cbor/CBOREncoder.cpp - ../../src/cbor/lib/tinycbor/src/cborencoder.c - ../../src/cbor/lib/tinycbor/src/cborencoder_close_container_checked.c - ../../src/cbor/lib/tinycbor/src/cborerrorstrings.c - ../../src/cbor/lib/tinycbor/src/cborparser.c - ../../src/cbor/lib/tinycbor/src/cborparser_dup_string.c - ../../src/cbor/lib/tinycbor/src/cborpretty.c - ../../src/cbor/lib/tinycbor/src/cborpretty_stdio.c - ../../src/cbor/lib/tinycbor/src/cbortojson.c - ../../src/cbor/lib/tinycbor/src/cborvalidation.c - ../../src/cbor/lib/tinycbor/src/open_memstream.c + ../../src/cbor/IoTCloudMessageDecoder.cpp + ../../src/cbor/IoTCloudMessageEncoder.cpp + + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborencoder.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborencoder_close_container_checked.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborerrorstrings.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborparser.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborparser_dup_string.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborpretty.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborpretty_stdio.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cbortojson.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/cborvalidation.c + ${cloudutils_SOURCE_DIR}/src/cbor/tinycbor/src/open_memstream.c + ${cloudutils_SOURCE_DIR}/src/cbor/MessageDecoder.cpp + ${cloudutils_SOURCE_DIR}/src/cbor/MessageEncoder.cpp ) - ########################################################################## set(TEST_TARGET_SRCS @@ -75,7 +121,7 @@ set(TEST_TARGET_SRCS ########################################################################## -add_compile_definitions(HOST) +add_compile_definitions(HOST HAS_TCP) add_compile_options(-Wall -Wextra -Wpedantic -Werror) add_compile_options(-Wno-cast-function-type) @@ -89,5 +135,8 @@ add_executable( ${TEST_TARGET_SRCS} ) +target_link_libraries( ${TEST_TARGET} cloudutils) +target_link_libraries( ${TEST_TARGET} Catch2WithMain ) + ########################################################################## diff --git a/extras/test/external/catch/v2.12.1/include/catch.hpp b/extras/test/external/catch/v2.12.1/include/catch.hpp deleted file mode 100644 index 1d2c9bb64..000000000 --- a/extras/test/external/catch/v2.12.1/include/catch.hpp +++ /dev/null @@ -1,17698 +0,0 @@ -/* - * Catch v2.12.1 - * Generated: 2020-04-21 19:29:20.964532 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 12 -#define CATCH_VERSION_PATCH 1 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -#ifdef __APPLE__ -# include -# if TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -#if defined(__cpp_lib_uncaught_exceptions) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - -// We have to avoid both ICC and Clang, because they try to mask themselves -// as gcc, and we want only GCC in this block -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) - -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) - -#endif - -#if defined(__clang__) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) - -// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug -// which results in calls to destructors being emitted for each temporary, -// without a matching initialization. In practice, this can result in something -// like `std::string::~string` being called on an uninitialized value. -// -// For example, this code will likely segfault under IBM XL: -// ``` -// REQUIRE(std::string("12") + "34" == "1234") -// ``` -// -// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg) */ -# endif - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#if defined(_MSC_VER) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) - -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(__clang__) // Handle Clang masquerading for msvc -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL -# endif // __clang__ - -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// RTX is a special version of Windows that is real time. -// This means that it is detected as Windows, but does not provide -// the same set of capabilities as real Windows does. -#if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE -#endif - -#if !defined(_GLIBCXX_USE_C99_MATH_TR1) -#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Various stdlib support checks that require __has_include -#if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Even if we do not think the compiler has that warning, we still have -// to provide a macro that can be used by the code. -#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -// The goal of this macro is to avoid evaluation of the arguments, but -// still have the compiler warn on problems inside... -#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) -#endif - -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template