diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 5eb4f66d9..79c30b5a6 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -21,12 +21,18 @@ jobs: UNIVERSAL_LIBRARIES: | # Install the ArduinoIoTCloud library from the repository - source-path: ./ - - name: Arduino_ConnectionHandler + - name: ArduinoBLE + - name: Arduino_KVStore - name: ArduinoHttpClient - name: Arduino_DebugUtils - name: ArduinoMqttClient - - name: Arduino_SecureElement - - name: Arduino_CloudUtils + - source-url: https://github.com/arduino-libraries/Arduino_ConnectionHandler.git + version: 0314cf54593029aea05bb8c179e40a26128f7d67 + - source-url: https://github.com/arduino-libraries/Arduino_SecureElement.git + version: 4900febf84435c1a06bb13451bced3bac6f16e76 + - source-url: https://github.com/arduino-libraries/Arduino_CloudUtils.git + version: 2e2facc2209b66905c180ec0058ef8b083912278 + - source-url: https://github.com/arduino-libraries/Arduino_NetworkConfigurator.git # sketch paths to compile (recursive) for all boards UNIVERSAL_SKETCH_PATHS: | - examples/ArduinoIoTCloud-Advanced @@ -105,12 +111,13 @@ jobs: # Install samd platform via Boards Manager - name: arduino:samd libraries: | - - name: ArduinoBearSSL + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c - name: ArduinoECCX08 - name: Blues Wireless Notecard - name: RTCZero - name: WiFi101 - - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git + - name: Adafruit SleepyDog Library sketch-paths: | - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -123,14 +130,16 @@ jobs: - name: arduino:samd - name: arduino:mbed_nano libraries: | - - name: ArduinoBearSSL + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c - name: ArduinoECCX08 - name: Blues Wireless Notecard - name: RTCZero - - name: WiFiNINA - - name: Arduino_JSON - - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git + - source-url: https://github.com/arduino-libraries/WiFiNINA.git + version: 69e786c5c73fe94b7f408853550f6f151cfc58b0 + - name: Adafruit SleepyDog Library sketch-paths: | + - examples/ArduinoIoTCloud-NetConfig - examples/ArduinoIoTCloud-DeferredOTA - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -146,7 +155,7 @@ jobs: - name: Blues Wireless Notecard - name: RTCZero - name: MKRWAN - - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git + - name: Adafruit SleepyDog Library sketch-paths: | - examples/ArduinoIoTCloud-Notecard # GSM boards @@ -156,12 +165,13 @@ jobs: # Install samd platform via Boards Manager - name: arduino:samd libraries: | - - name: ArduinoBearSSL + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c - name: ArduinoECCX08 - name: Blues Wireless Notecard - name: RTCZero - name: MKRGSM - - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git + - name: Adafruit SleepyDog Library sketch-paths: | - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -173,12 +183,13 @@ jobs: # Install samd platform via Boards Manager - name: arduino:samd libraries: | - - name: ArduinoBearSSL + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c - name: ArduinoECCX08 - name: Blues Wireless Notecard - name: RTCZero - name: MKRNB - - source-url: https://github.com/adafruit/Adafruit_SleepyDog.git + - name: Adafruit SleepyDog Library sketch-paths: | - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -190,11 +201,13 @@ jobs: # Install mbed_portenta platform via Boards Manager - name: arduino:mbed_portenta libraries: | - - name: ArduinoBearSSL + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c - name: ArduinoECCX08 - name: Arduino_Cellular - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-NetConfig - examples/ArduinoIoTCloud-DeferredOTA - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -208,6 +221,7 @@ jobs: libraries: | - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-NetConfig - examples/ArduinoIoTCloud-DeferredOTA - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -219,10 +233,12 @@ jobs: # Install mbed_opta platform via Boards Manager - name: arduino:mbed_opta libraries: | - - name: ArduinoBearSSL + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c - name: ArduinoECCX08 - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-NetConfig - examples/ArduinoIoTCloud-DeferredOTA - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -234,10 +250,12 @@ jobs: # Install mbed_giga platform via Boards Manager - name: arduino:mbed_giga libraries: | - - name: ArduinoBearSSL + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c - name: ArduinoECCX08 - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-NetConfig - examples/ArduinoIoTCloud-DeferredOTA - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule @@ -252,6 +270,7 @@ jobs: - name: Arduino_Cellular - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-NetConfig - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule - examples/utility/Provisioning @@ -264,6 +283,7 @@ jobs: libraries: | - name: Blues Wireless Notecard sketch-paths: | + - examples/ArduinoIoTCloud-NetConfig - examples/ArduinoIoTCloud-Notecard - examples/ArduinoIoTCloud-Schedule # Nano ESP32 diff --git a/.github/workflows/compile-provisioning.yml b/.github/workflows/compile-provisioning.yml new file mode 100644 index 000000000..e4aa0d923 --- /dev/null +++ b/.github/workflows/compile-provisioning.yml @@ -0,0 +1,239 @@ +name: Compile Provisioning + +on: + pull_request: + paths: + - ".github/workflows/compile-provisioning.yml" + - "examples/**" + - "src/**" + push: + paths: + - ".github/workflows/compile-provisioning.yml" + - "examples/**" + - "src/**" + +jobs: + build: + runs-on: ubuntu-latest + + env: + # libraries to install for all boards + UNIVERSAL_LIBRARIES: | + # Install the ArduinoIoTCloud library from the repository + - source-path: ./ + - name: ArduinoBLE + version: 1.4.0 + - name: ArduinoHttpClient + version: 0.6.1 + - name: Arduino_DebugUtils + version: 1.4.0 + - name: ArduinoMqttClient + version: 0.1.8 + - name: Arduino_KVStore + version: 1.0.0 + - source-url: https://github.com/arduino-libraries/Arduino_ConnectionHandler.git + version: 0314cf54593029aea05bb8c179e40a26128f7d67 + - source-url: https://github.com/arduino-libraries/Arduino_SecureElement.git + version: 4900febf84435c1a06bb13451bced3bac6f16e76 + - source-url: https://github.com/arduino-libraries/Arduino_CloudUtils.git + version: 2e2facc2209b66905c180ec0058ef8b083912278 + - source-url: https://github.com/arduino-libraries/Arduino_UniqueHWId.git + version: 7e1bfeb586cac00f043c39997a1e9937ed8152b0 + - source-url: https://github.com/arduino-libraries/Arduino_NetworkConfigurator.git + version: d887ec0fd15d3d6bd427fa3e2c4f95b582f964a0 + # sketch paths to compile (recursive) for all boards + UNIVERSAL_SKETCH_PATHS: | + - examples/utility/Provisioning_2.0 + SKETCHES_REPORTS_PATH: sketches-reports + + strategy: + fail-fast: false + + matrix: + board: + - 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:mbed_portenta:envie_m7:split=100_0 + type: mbed_portenta + artifact-name-suffix: arduino-mbed_portenta-envie_m7 + - 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 + + # make board type-specific customizations to the matrix jobs + include: + # MKR WiFi 1010, Nano 33 IoT, Nano RP2040 Connect + - board: + type: nina + platforms: | + # Install samd and mbed_nano platform via Boards Manager + - name: arduino:samd + version: 1.8.14 + - name: arduino:mbed_nano + version: 4.2.4 + libraries: | + - name: RTCZero + version: 1.6.0 + - name: ArduinoECCX08 + version: 1.3.8 + - name: Adafruit SleepyDog Library + version: 1.6.5 + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c + - source-url: https://github.com/arduino-libraries/WiFiNINA.git + version: 69e786c5c73fe94b7f408853550f6f151cfc58b0 + # Portenta + - board: + type: mbed_portenta + platforms: | + # Install mbed_portenta platform via Boards Manager + - name: arduino:mbed_portenta + version: 4.2.4 + libraries: | + - name: Arduino_Cellular + version: 1.2.1 + - name: ArduinoECCX08 + version: 1.3.8 + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c + # Nicla Vision + - board: + type: mbed_nicla + platforms: | + # Install mbed_nicla platform via Boards Manager + - name: arduino:mbed_nicla + version: 4.2.4 + # Opta + - board: + type: mbed_opta + platforms: | + # Install mbed_opta platform via Boards Manager + - name: arduino:mbed_opta + version: 4.2.4 + libraries: | + - name: ArduinoECCX08 + version: 1.3.8 + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c + # GIGA + - board: + type: mbed_giga + platforms: | + # Install mbed_giga platform via Boards Manager + - name: arduino:mbed_giga + version: 4.2.4 + libraries: | + - name: ArduinoECCX08 + version: 1.3.8 + - source-url: https://github.com/arduino-libraries/ArduinoBearSSL.git + version: a891ad3cb15bca3d080adc8188a63277f92fa81c + # Portenta C33 + - board: + type: renesas_portenta + platforms: | + # Install renesas_portenta platform via Boards Manager + - name: arduino:renesas_portenta + version: 1.4.1 + libraries: | + - name: Arduino_Cellular + version: 1.2.1 + # UNO R4 WiFi + - board: + type: renesas_uno + platforms: | + # Install renesas_uno platform via Boards Manager + - name: arduino:renesas_uno + version: 1.4.1 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Compile production provisioning sketch + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + platforms: ${{ matrix.platforms }} + fqbn: ${{ matrix.board.fqbn }} + libraries: | + ${{ env.UNIVERSAL_LIBRARIES }} + ${{ matrix.libraries }} + sketch-paths: | + ${{ env.UNIVERSAL_SKETCH_PATHS }} + ${{ matrix.sketch-paths }} + enable-deltas-report: "true" + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + cli-compile-flags: | + - --verbose + - --output-dir + - ${{ runner.temp }}/provisioning-prod + + - name: Compile staging provisioning sketch + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + platforms: ${{ matrix.platforms }} + fqbn: ${{ matrix.board.fqbn }} + libraries: | + ${{ env.UNIVERSAL_LIBRARIES }} + ${{ matrix.libraries }} + sketch-paths: | + ${{ env.UNIVERSAL_SKETCH_PATHS }} + ${{ matrix.sketch-paths }} + enable-deltas-report: "true" + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + cli-compile-flags: | + - --verbose + - --build-property + - "build.extra_flags=-DCOMPILE_TEST=1" + - --output-dir + - ${{ runner.temp }}/provisioning-staging + + - name: Write data to size trends report spreadsheet + # Update report on every push to the master branch + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + uses: arduino/report-size-trends@main + with: + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + google-key-file: ${{ secrets.GOOGLE_KEY_FILE }} + spreadsheet-id: 1I6NZkpZpf8KugBkE92adB1Z3_b7ZepOpCdYTOigJpN4 + + - name: Save memory usage change report as artifact + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: sketches-report-${{ matrix.board.artifact-name-suffix }} + path: ${{ env.SKETCHES_REPORTS_PATH }} + + - name: Save production artifact + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: provisioning-prod-${{ matrix.board.artifact-name-suffix }} + path: ${{ runner.temp }}/provisioning-prod/ + + - name: Save staging artifact + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: provisioning-staging-${{ matrix.board.artifact-name-suffix }} + path: ${{ runner.temp }}/provisioning-staging/ diff --git a/examples/ArduinoIoTCloud-NetConfig/ArduinoIoTCloud-NetConfig.ino b/examples/ArduinoIoTCloud-NetConfig/ArduinoIoTCloud-NetConfig.ino new file mode 100644 index 000000000..238b59f55 --- /dev/null +++ b/examples/ArduinoIoTCloud-NetConfig/ArduinoIoTCloud-NetConfig.ino @@ -0,0 +1,62 @@ +/* + This sketch demonstrates how to exchange data between your board and the Arduino IoT Cloud. + + * 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 + +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); + + 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-NetConfig/thingProperties.h b/examples/ArduinoIoTCloud-NetConfig/thingProperties.h new file mode 100644 index 000000000..ce3e17a39 --- /dev/null +++ b/examples/ArduinoIoTCloud-NetConfig/thingProperties.h @@ -0,0 +1,48 @@ +#if !defined(ARDUINO_SAMD_MKRWIFI1010) && !defined(ARDUINO_SAMD_NANO_33_IOT) && !defined(ARDUINO_NANO_RP2040_CONNECT) \ + && !defined(ARDUINO_PORTENTA_H7_M7) && !defined(ARDUINO_NICLA_VISION) && !defined(ARDUINO_OPTA) && !defined(ARDUINO_GIGA) \ + && !defined(ARDUINO_UNOR4_WIFI) && !defined(ARDUINO_PORTENTA_C33) +#error "This example is not compatible with this board." +#endif +#include +#include +#include +#include "ConfiguratorAgents/agents/BLE/BLEAgent.h" +#include "ConfiguratorAgents/agents/Serial/SerialAgent.h" + +void onLedChange(); + +bool led; +int potentiometer; +int seconds; + +GenericConnectionHandler ArduinoIoTPreferredConnection; +KVStore kvStore; +NetworkConfiguratorClass NetworkConfigurator(ArduinoIoTPreferredConnection); +BLEAgentClass BLEAgent; +SerialAgentClass SerialAgent; + +void initProperties() { + NetworkConfigurator.addAgent(BLEAgent); + NetworkConfigurator.addAgent(SerialAgent); + NetworkConfigurator.setStorage(kvStore); + + /* For changing the default reset pin uncomment and set your preferred pin. + * Use DISABLE_PIN for disabling the reset procedure. + * The pin must be in the list of digital pins usable for interrupts. + * Please refer to the Arduino documentation for more details: + * https://docs.arduino.cc/language-reference/en/functions/external-interrupts/attachInterrupt/ + */ + //NetworkConfigurator.setReconfigurePin(your_pin); + + /* Otherwise if you need to monitor the pin status changes + * you can set a custom callback function that is fired on every change + */ + //NetworkConfigurator.setPinChangedCallback(your_callback); + + ArduinoCloud.setConfigurator(NetworkConfigurator); + + ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange); + ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10); + ArduinoCloud.addProperty(seconds, Permission::Read).publishOnChange(1); + +} diff --git a/examples/utility/Provisioning_2.0/CSRHandler.cpp b/examples/utility/Provisioning_2.0/CSRHandler.cpp new file mode 100644 index 000000000..1a6101e4b --- /dev/null +++ b/examples/utility/Provisioning_2.0/CSRHandler.cpp @@ -0,0 +1,428 @@ +/* + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "CSRHandler.h" +#include +#include +#include +#include +#include +#include +#include +#include "Arduino_DebugUtils.h" +#include + +#define RESPONSE_TIMEOUT 5000 +#define CONNECTION_RETRY_TIMEOUT_MS 2000 +#define BACKEND_INTERVAL_s 12 +#define MAX_CSR_REQUEST_INTERVAL 180000 +#define MAX_CSR_REQUEST_INTERVAL_ATTEMPTS 15 + +#ifdef COMPILE_TEST +constexpr char *server = "boards-v2.oniudra.cc"; +#else +constexpr char *server = "boards-v2.arduino.cc"; +#endif + +CSRHandlerClass::CSRHandlerClass() : + _ledFeedback{LEDFeedbackClass::getInstance()}, + _state{CSRHandlerStates::END}, + _nextRequestAt{0}, + _requestAttempt{0}, + _startWaitingResponse{0}, + _uhwid{nullptr}, + _certForCSR{nullptr}, + _connectionHandler{nullptr}, + _secureElement{nullptr}, + _tlsClient{nullptr}, + _client{nullptr}, + _fw_version{""}, + _deviceId{""}, + _issueYear{0}, + _issueMonth{0}, + _issueDay{0}, + _issueHour{0} { + memset(_serialNumber, 0, sizeof(_serialNumber)); + memset(_authorityKeyIdentifier, 0, sizeof(_authorityKeyIdentifier)); + memset(_signature, 0, sizeof(_signature)); +} + +CSRHandlerClass::~CSRHandlerClass() { + if (_certForCSR) { + delete _certForCSR; + _certForCSR = nullptr; + } + if (_client) { + delete _client; + _client = nullptr; + } + + if(_tlsClient){ + delete _tlsClient; + _tlsClient = nullptr; + } +} + +bool CSRHandlerClass::begin(ConnectionHandler &connectionHandler, SecureElement &secureElement, String &uhwid) { + if(_state != CSRHandlerStates::END) { + return true; + } + + if(uhwid == "") { + return false; + } + + _connectionHandler = &connectionHandler; + _secureElement = &secureElement; + _uhwid = &uhwid; + +#ifdef BOARD_HAS_WIFI + _fw_version = WiFi.firmwareVersion(); +#endif + if(!_tlsClient){ + _tlsClient = new TLSClientMqtt(); + } + _tlsClient->begin(*_connectionHandler); + _tlsClient->setTimeout(RESPONSE_TIMEOUT); + _client = new HttpClient(*_tlsClient, server, 443); + TimeService.begin(_connectionHandler); + _requestAttempt = 0; + _nextRequestAt = 0; + _startWaitingResponse = 0; + _state = CSRHandlerStates::BUILD_CSR; +} + +void CSRHandlerClass::end() { + if (_client) { + _client->stop(); + delete _client; + _client = nullptr; + } + + if (_certForCSR) { + delete _certForCSR; + _certForCSR = nullptr; + } + + if(_tlsClient){ + delete _tlsClient; + _tlsClient = nullptr; + } + _fw_version = ""; + _deviceId = ""; + _state = CSRHandlerStates::END; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::poll() { + switch (_state) { + case CSRHandlerStates::BUILD_CSR: _state = handleBuildCSR (); break; + case CSRHandlerStates::REQUEST_SIGNATURE: _state = handleRequestSignature (); break; + case CSRHandlerStates::WAITING_RESPONSE: _state = handleWaitingResponse (); break; + case CSRHandlerStates::PARSE_RESPONSE: _state = handleParseResponse (); break; + case CSRHandlerStates::BUILD_CERTIFICATE: _state = handleBuildCertificate (); break; + case CSRHandlerStates::CERT_CREATED: _state = handleCertCreated (); break; + case CSRHandlerStates::WAITING_COMPLETE_RES: _state = handleWaitingCompleteRes(); break; + case CSRHandlerStates::COMPLETED: break; + case CSRHandlerStates::ERROR: handleError (); break; + case CSRHandlerStates::END: break; + } + + return _state; +} + +void CSRHandlerClass::updateNextRequestAt() { + uint32_t delay; + if(_requestAttempt <= MAX_CSR_REQUEST_INTERVAL_ATTEMPTS) { + delay = BACKEND_INTERVAL_s * _requestAttempt * 1000; // use linear backoff since backend has a rate limit + }else { + delay = MAX_CSR_REQUEST_INTERVAL; + } + + _nextRequestAt = millis() + delay + jitter(); +} + +uint32_t CSRHandlerClass::jitter(uint32_t base, uint32_t max) { + srand(millis()); + return base + rand() % (max - base); +} + +bool CSRHandlerClass::postRequest(const char *url, String &postData) { + if(!_client){ + _client = new HttpClient(*_tlsClient, server, 443); + } + + uint32_t ts = getTimestamp(); + if(ts == 0){ + DEBUG_WARNING("CSRH::%s Failed getting timestamp", __FUNCTION__); + return false; + } + + String token = getAIoTCloudJWT(*_secureElement, *_uhwid, ts, 1); + + _requestAttempt++; + _client->beginRequest(); + + if(_client->post(url) == 0){ + _client->sendHeader("Host", server); + _client->sendHeader("Connection", "close"); + _client->sendHeader("Content-Type", "application/json;charset=UTF-8"); + _client->sendHeader("Authorization", "Bearer " + token); + _client->sendHeader("Content-Length", postData.length()); + _client->beginBody(); + _client->print(postData); + _startWaitingResponse = millis(); + return true; + } + return false; +} + +uint32_t CSRHandlerClass::getTimestamp() { + uint8_t getTsAttempt = 0; + uint32_t ts = 0; + do{ + TimeService.sync(); + ts = TimeService.getTime(); + getTsAttempt++; + }while(ts == 0 && getTsAttempt < 3); + + return ts; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleBuildCSR() { + if (!_certForCSR) { + _certForCSR = new ECP256Certificate(); + } + _certForCSR->begin(); + + _certForCSR->setSubjectCommonName(*_uhwid); + + if (!SElementCSR::build(*_secureElement, *_certForCSR, static_cast(SElementArduinoCloudSlot::Key), true)) { + DEBUG_ERROR("CSRH::%s Error generating CSR!", __FUNCTION__); + _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + return CSRHandlerStates::ERROR; + } + return CSRHandlerStates::REQUEST_SIGNATURE; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleRequestSignature() { + CSRHandlerStates nextState = _state; + + if(millis() < _nextRequestAt) { + return nextState; + } + + NetworkConnectionState connectionRes = _connectionHandler->check(); + if (connectionRes != NetworkConnectionState::CONNECTED) { + nextNetworkRetry(); + return nextState; + } + + if(!_certForCSR){ + return CSRHandlerStates::BUILD_CSR; + } + + String csr = _certForCSR->getCSRPEM(); + csr.replace("\n", "\\n"); + + String PostData = "{\"csr\":\""; + PostData += csr; + PostData += "\"}"; + DEBUG_INFO("CSRH Downloading certificate..."); + + if(postRequest("/provisioning/v1/onboarding/provision/csr", PostData)){ + nextState = CSRHandlerStates::WAITING_RESPONSE; + } else { + updateNextRequestAt(); + DEBUG_WARNING("CSRH::%s Failed sending request, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis()); + } + + return nextState; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleWaitingResponse() { + CSRHandlerStates nextState = _state; + NetworkConnectionState connectionRes = _connectionHandler->check(); + if (connectionRes != NetworkConnectionState::CONNECTED) { + nextNetworkRetry(); + _client->stop(); + return CSRHandlerStates::REQUEST_SIGNATURE; + } + + if (millis() - _startWaitingResponse > RESPONSE_TIMEOUT) { + _client->stop(); + updateNextRequestAt(); + DEBUG_WARNING("CSRH::%s CSR request timeout, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis()); + nextState = CSRHandlerStates::REQUEST_SIGNATURE; + } + + + int statusCode = _client->responseStatusCode(); + if(statusCode == 200){ + nextState = CSRHandlerStates::PARSE_RESPONSE; + } else { + _client->stop(); + updateNextRequestAt(); + DEBUG_WARNING("CSRH::%s CSR request error code %d, retrying in %d ms", __FUNCTION__, statusCode ,_nextRequestAt - millis()); + nextState = CSRHandlerStates::REQUEST_SIGNATURE; + } + + return nextState; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleParseResponse() { + String certResponse = _client->responseBody(); + _client->stop(); + + /* Parse the response in format: + * device_id|authority_key_identifier|not_before|serial|signature_asn1_x|signature_asn1_y + */ + char *response = (char *)certResponse.c_str(); + char *token[6]; + int i = 1; + token[0] = strtok(response, "|"); + for (; i < 6; i++) { + char *tok = strtok(NULL, "|"); + if(tok == NULL){ + break; + } + token[i] = tok; + } + + if(i < 6 || strlen(token[0]) != 36 || strlen(token[1]) != 40 + || strlen(token[2]) < 10 || strlen(token[3]) != 32 + || strlen(token[4]) != 64 || strlen(token[5]) != 64 + || sscanf(token[2], "%4d-%2d-%2dT%2d", &_issueYear, &_issueMonth, &_issueDay, &_issueHour) != 4){ + updateNextRequestAt(); + DEBUG_ERROR("CSRH::%s Error parsing response, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis()); + return CSRHandlerStates::REQUEST_SIGNATURE; + } + + _deviceId = token[0]; + hex::decode(token[1], _authorityKeyIdentifier, sizeof(_authorityKeyIdentifier)); + hex::decode(token[3], _serialNumber, sizeof(_serialNumber)); + hex::decode(token[4], _signature, sizeof(_signature)); + hex::decode(token[5], &_signature[32], sizeof(_signature) - 32); + + return CSRHandlerStates::BUILD_CERTIFICATE; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleBuildCertificate() { + int expireYears = 31; + + if (!SElementArduinoCloudDeviceId::write(*_secureElement, _deviceId, SElementArduinoCloudSlot::DeviceId)) { + DEBUG_ERROR("CSRH::%s Error storing device id!", __FUNCTION__); + _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + return CSRHandlerStates::ERROR; + } + + ECP256Certificate cert; + cert.begin(); + + cert.setSubjectCommonName(_deviceId); + cert.setIssuerCountryName("US"); + cert.setIssuerOrganizationName("Arduino LLC US"); + cert.setIssuerOrganizationalUnitName("IT"); + cert.setIssuerCommonName("Arduino"); + cert.setSignature(_signature, sizeof(_signature)); + cert.setAuthorityKeyId(_authorityKeyIdentifier, sizeof(_authorityKeyIdentifier)); + cert.setSerialNumber(_serialNumber, sizeof(_serialNumber)); + cert.setIssueYear(_issueYear); + cert.setIssueMonth(_issueMonth); + cert.setIssueDay(_issueDay); + cert.setIssueHour(_issueHour); + cert.setExpireYears(expireYears); + + if (!SElementArduinoCloudCertificate::build(*_secureElement, cert, static_cast(SElementArduinoCloudSlot::Key))) { + DEBUG_ERROR("CSRH::%s Error building secureElement compressed cert!", __FUNCTION__); + _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + return CSRHandlerStates::ERROR; + } + + if (!SElementArduinoCloudCertificate::write(*_secureElement, cert, SElementArduinoCloudSlot::CompressedCertificate)) { + DEBUG_ERROR("CSRH::%s Error storing cert!" , __FUNCTION__); + _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + return CSRHandlerStates::ERROR; + } + + DEBUG_INFO("CSRH Certificate created!"); + _nextRequestAt = 0; + _requestAttempt = 0; + return CSRHandlerStates::CERT_CREATED; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleCertCreated() { + CSRHandlerStates nextState = _state; + + if(millis() < _nextRequestAt) { + return nextState; + } + + NetworkConnectionState connectionRes = _connectionHandler->check(); + if (connectionRes != NetworkConnectionState::CONNECTED) { + nextNetworkRetry(); + return nextState; + } + + String PostData = "{\"wifi_fw_version\":\""; + PostData += _fw_version; + PostData += "\"}"; + if(postRequest("/provisioning/v1/onboarding/provision/complete", PostData)){ + nextState = CSRHandlerStates::WAITING_COMPLETE_RES; + } else { + updateNextRequestAt(); + DEBUG_WARNING("CSRH::%s Error sending complete request, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis()); + } + + return nextState; +} + +CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleWaitingCompleteRes() { + CSRHandlerStates nextState = _state; + NetworkConnectionState connectionRes = _connectionHandler->check(); + if (connectionRes != NetworkConnectionState::CONNECTED) { + nextNetworkRetry(); + _client->stop(); + return CSRHandlerStates::CERT_CREATED; + } + + if (millis() - _startWaitingResponse > RESPONSE_TIMEOUT) { + _client->stop(); + updateNextRequestAt(); + DEBUG_WARNING("CSRH::%s Complete request timeout, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis()); + nextState = CSRHandlerStates::CERT_CREATED; + } + + int statusCode = _client->responseStatusCode(); + if(statusCode == 200){ + if (_certForCSR) { + delete _certForCSR; + _certForCSR = nullptr; + } + DEBUG_INFO("CSRH Provisioning completed!"); + nextState = CSRHandlerStates::COMPLETED; + } else if (statusCode == 429 || statusCode == 503) { + updateNextRequestAt(); + nextState = CSRHandlerStates::CERT_CREATED; + } else { + DEBUG_WARNING("CSRH::%s Complete request error code %d, retrying in %d ms", __FUNCTION__, statusCode ,_nextRequestAt - millis()); + _requestAttempt = 0; + _nextRequestAt = 0; + nextState = CSRHandlerStates::REQUEST_SIGNATURE; + } + _client->stop(); + + return nextState; +} + +void CSRHandlerClass::nextNetworkRetry() { + _nextRequestAt = millis() + CONNECTION_RETRY_TIMEOUT_MS; +} + +void CSRHandlerClass::handleError() { + _ledFeedback.update(); +} diff --git a/examples/utility/Provisioning_2.0/CSRHandler.h b/examples/utility/Provisioning_2.0/CSRHandler.h new file mode 100644 index 000000000..1165ac05b --- /dev/null +++ b/examples/utility/Provisioning_2.0/CSRHandler.h @@ -0,0 +1,74 @@ +/* + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once +#include +#include +#include +#include +#include +#include "Utility/LEDFeedback/LEDFeedback.h" +#define JITTER_BASE 0 +#define JITTER_MAX 1000 + +class CSRHandlerClass { +public: + CSRHandlerClass(); + ~CSRHandlerClass(); + enum class CSRHandlerStates { + BUILD_CSR, + REQUEST_SIGNATURE, + WAITING_RESPONSE, + PARSE_RESPONSE, + BUILD_CERTIFICATE, + CERT_CREATED, + WAITING_COMPLETE_RES, + COMPLETED, + ERROR, + END + }; + bool begin(ConnectionHandler &connectionHandler, SecureElement &secureElement, String &uhwid); + void end(); + CSRHandlerStates poll(); +private: + CSRHandlerStates _state; + unsigned long _nextRequestAt; + uint32_t _requestAttempt; + uint32_t _startWaitingResponse; + String *_uhwid; + String _fw_version; + + int _issueYear; + uint8_t _issueMonth; + uint8_t _issueDay; + uint8_t _issueHour; + byte _serialNumber[16]; + byte _authorityKeyIdentifier[20]; + byte _signature[64]; + String _deviceId; + + ECP256Certificate *_certForCSR; + ConnectionHandler *_connectionHandler; + SecureElement *_secureElement; + TLSClientMqtt *_tlsClient; + HttpClient *_client; + LEDFeedbackClass &_ledFeedback; + void updateNextRequestAt(); + void nextNetworkRetry(); + uint32_t jitter(uint32_t base = JITTER_BASE, uint32_t max = JITTER_MAX); + bool postRequest(const char *url, String &postData); + uint32_t getTimestamp(); + CSRHandlerStates handleBuildCSR(); + CSRHandlerStates handleRequestSignature(); + CSRHandlerStates handleWaitingResponse(); + CSRHandlerStates handleParseResponse(); + CSRHandlerStates handleBuildCertificate(); + CSRHandlerStates handleCertCreated(); + CSRHandlerStates handleWaitingCompleteRes(); + void handleError(); +}; diff --git a/examples/utility/Provisioning_2.0/ClaimingHandler.cpp b/examples/utility/Provisioning_2.0/ClaimingHandler.cpp new file mode 100644 index 000000000..7456f410b --- /dev/null +++ b/examples/utility/Provisioning_2.0/ClaimingHandler.cpp @@ -0,0 +1,192 @@ +/* + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include "ClaimingHandler.h" +#include +#include "Arduino_DebugUtils.h" +#include +#include "utility/HCI.h" +#include + +extern const char *SKETCH_VERSION; + +ClaimingHandlerClass::ClaimingHandlerClass(): + _uhwid {nullptr}, + _state {ClaimingHandlerStates::END}, + _secureElement {nullptr}, + _clearStoredCredentials {nullptr}, + _agentManager { AgentsManagerClass::getInstance()}, + _ledFeedback {LEDFeedbackClass::getInstance()} { + _receivedEvent = ClaimingReqEvents::NONE; + _ts = 0; +} + +bool ClaimingHandlerClass::begin(SecureElement &secureElement, String &uhwid, ClearStoredCredentialsHandler clearStoredCredentials) { + if(_state != ClaimingHandlerStates::END) { + return true; + } + + if(uhwid == "" || clearStoredCredentials == nullptr) { + return false; + } + + if (!_agentManager.addRequestHandler(RequestType::GET_ID, getIdRequestCb)) { + return false; + } + + if (!_agentManager.addRequestHandler(RequestType::RESET, resetStoredCredRequestCb)) { + return false; + } + + if(!_agentManager.addRequestHandler(RequestType::GET_BLE_MAC_ADDRESS, getBLEMacAddressRequestCb)) { + return false; + } + + if(!_agentManager.addRequestHandler(RequestType::GET_PROVISIONING_SKETCH_VERSION, getProvSketchVersionRequestCb)) { + return false; + } + + if (!_agentManager.addReturnTimestampCallback(setTimestamp)) { + return false; + } + + _agentManager.begin(); + _uhwid = &uhwid; + _secureElement = &secureElement; + _clearStoredCredentials = clearStoredCredentials; + _state = ClaimingHandlerStates::INIT; +} + +void ClaimingHandlerClass::end() { + if(_state == ClaimingHandlerStates::END) { + return; + } + + _agentManager.removeReturnTimestampCallback(); + _agentManager.removeRequestHandler(RequestType::GET_ID); + _agentManager.removeRequestHandler(RequestType::RESET); + _agentManager.end(); + _state = ClaimingHandlerStates::END; +} + +void ClaimingHandlerClass::poll() { + if(_state == ClaimingHandlerStates::END) { + return; + } + _ledFeedback.update(); + _agentManager.update(); + + switch (_receivedEvent) { + case ClaimingReqEvents::GET_ID: getIdReqHandler (); break; + case ClaimingReqEvents::RESET: resetStoredCredReqHandler (); break; + case ClaimingReqEvents::GET_BLE_MAC_ADDRESS: getBLEMacAddressReqHandler (); break; + case ClaimingReqEvents::GET_PROV_SKETCH_VERSION: getProvSketchVersionReqHandler(); break; + } + _receivedEvent = ClaimingReqEvents::NONE; + return; +} + +void ClaimingHandlerClass::getIdReqHandler() { + if (_ts != 0) { + byte _uhwidBytes[32]; + hex::decode(_uhwid->c_str(), _uhwidBytes, _uhwid->length()); + //Send UHWID + ProvisioningOutputMessage idMsg = {MessageOutputType::UHWID}; + idMsg.m.uhwid = _uhwidBytes; + _agentManager.sendMsg(idMsg); + + String token = getAIoTCloudJWT(*_secureElement, *_uhwid, _ts, 1); + if (token == "") { + DEBUG_ERROR("CH::%s Error: token not created", __FUNCTION__); + sendStatus(StatusMessage::ERROR); + return; + } + + //Send JWT + ProvisioningOutputMessage jwtMsg = {MessageOutputType::JWT}; + jwtMsg.m.jwt = token.c_str(); + _agentManager.sendMsg(jwtMsg); + _ts = 0; + } else { + DEBUG_ERROR("CH::%s Error: timestamp not provided" , __FUNCTION__); + sendStatus(StatusMessage::PARAMS_NOT_FOUND); + } +} + +void ClaimingHandlerClass::resetStoredCredReqHandler() { + if( !_clearStoredCredentials()){ + DEBUG_ERROR("CH::%s Error: reset stored credentials failed", __FUNCTION__); + sendStatus(StatusMessage::ERROR); + } else { + sendStatus(StatusMessage::RESET_COMPLETED); + } + +} + +void ClaimingHandlerClass::getBLEMacAddressReqHandler() { + uint8_t mac[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + bool activated = false; + ConfiguratorAgent * connectedAgent = _agentManager.getConnectedAgent(); + if(!_agentManager.isAgentEnabled(ConfiguratorAgent::AgentTypes::BLE) || (connectedAgent != nullptr && + connectedAgent->getAgentType() != ConfiguratorAgent::AgentTypes::BLE)) { + activated = true; + BLE.begin(); + } + + HCI.readBdAddr(mac); + + for(int i = 0; i < 3; i++){ + uint8_t byte = mac[i]; + mac[i] = mac[5-i]; + mac[5-i] = byte; + } + if (activated) { + BLE.end(); + } + + ProvisioningOutputMessage outputMsg; + outputMsg.type = MessageOutputType::BLE_MAC_ADDRESS; + outputMsg.m.BLEMacAddress = mac; + _agentManager.sendMsg(outputMsg); +} + +void ClaimingHandlerClass::getProvSketchVersionReqHandler() { + ProvisioningOutputMessage outputMsg; + outputMsg.type = MessageOutputType::PROV_SKETCH_VERSION; + outputMsg.m.provSketchVersion = SKETCH_VERSION; + _agentManager.sendMsg(outputMsg); +} + +void ClaimingHandlerClass::getIdRequestCb() { + DEBUG_VERBOSE("CH Get ID request received"); + _receivedEvent = ClaimingReqEvents::GET_ID; +} +void ClaimingHandlerClass::setTimestamp(uint64_t ts) { + _ts = ts; +} + +void ClaimingHandlerClass::resetStoredCredRequestCb() { + DEBUG_VERBOSE("CH Reset stored credentials request received"); + _receivedEvent = ClaimingReqEvents::RESET; +} + +void ClaimingHandlerClass::getBLEMacAddressRequestCb() { + DEBUG_VERBOSE("CH Get BLE MAC address request received"); + _receivedEvent = ClaimingReqEvents::GET_BLE_MAC_ADDRESS; +} + +void ClaimingHandlerClass::getProvSketchVersionRequestCb() { + DEBUG_VERBOSE("CH Get provisioning sketch version request received"); + _receivedEvent = ClaimingReqEvents::GET_PROV_SKETCH_VERSION; +} + +bool ClaimingHandlerClass::sendStatus(StatusMessage msg) { + ProvisioningOutputMessage statusMsg = { MessageOutputType::STATUS, { msg } }; + return _agentManager.sendMsg(statusMsg); +} diff --git a/examples/utility/Provisioning_2.0/ClaimingHandler.h b/examples/utility/Provisioning_2.0/ClaimingHandler.h new file mode 100644 index 000000000..fc6a2605e --- /dev/null +++ b/examples/utility/Provisioning_2.0/ClaimingHandler.h @@ -0,0 +1,53 @@ +/* + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once +#include "Arduino.h" +#include "ConfiguratorAgents/AgentsManager.h" +#include +#include "Utility/LEDFeedback/LEDFeedback.h" + +typedef bool (*ClearStoredCredentialsHandler)(); +class ClaimingHandlerClass { +public: + ClaimingHandlerClass(); + bool begin(SecureElement &secureElement, String &uhwid, ClearStoredCredentialsHandler clearStoredCredentials); + void end(); + void poll(); +private: + String *_uhwid; + enum class ClaimingHandlerStates { + INIT, + END + }; + enum class ClaimingReqEvents { NONE, + GET_ID, + RESET, + GET_BLE_MAC_ADDRESS, + GET_PROV_SKETCH_VERSION}; + static inline ClaimingReqEvents _receivedEvent; + ClaimingHandlerStates _state; + AgentsManagerClass &_agentManager; + LEDFeedbackClass &_ledFeedback; + static inline uint64_t _ts; + SecureElement *_secureElement; + + bool sendStatus(StatusMessage msg); + /* Commands handlers */ + void getIdReqHandler(); + void resetStoredCredReqHandler(); + void getBLEMacAddressReqHandler(); + void getProvSketchVersionReqHandler(); + ClearStoredCredentialsHandler _clearStoredCredentials; + /* Callbacks for receiving commands */ + static void getIdRequestCb(); + static void setTimestamp(uint64_t ts); + static void resetStoredCredRequestCb(); + static void getBLEMacAddressRequestCb(); + static void getProvSketchVersionRequestCb(); +}; diff --git a/examples/utility/Provisioning_2.0/Provisioning_2.0.ino b/examples/utility/Provisioning_2.0/Provisioning_2.0.ino new file mode 100644 index 000000000..5f9e2831c --- /dev/null +++ b/examples/utility/Provisioning_2.0/Provisioning_2.0.ino @@ -0,0 +1,228 @@ +/* + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +#include "thingProperties.h" +#include "CSRHandler.h" +#include "ClaimingHandler.h" +#include "SecretsHelper.h" +#include +#include +#include +#include +#include "Utility/LEDFeedback/LEDFeedback.h" + +const char *SKETCH_VERSION = "0.1.1"; + +enum class DeviceState { + HARDWARE_CHECK, + BEGIN, + NETWORK_CONFIG, + CSR, + BEGIN_CLOUD, + RUN, + ERROR + }; + +DeviceState _state = DeviceState::HARDWARE_CHECK; +SecureElement secureElement; + +String uhwid = ""; +bool resetEvent = false; + +CSRHandlerClass CSRHandler; +ClaimingHandlerClass ClaimingHandler; + +bool clearStoredCredentials() { + const uint8_t empty[4] = {0x00,0x00,0x00,0x00}; + if(!NetworkConfigurator.resetStoredConfiguration() || \ + !secureElement.writeSlot(static_cast(SElementArduinoCloudSlot::DeviceId), (byte*)empty, sizeof(empty)) || \ + !secureElement.writeSlot(static_cast(SElementArduinoCloudSlot::CompressedCertificate), (byte*)empty, sizeof(empty))) { + return false; + } + + ArduinoCloud.disconnect(); + resetEvent = true; + return true; + } + +void setup() { + Serial.begin(9600); + + delay(1500); + + setDebugMessageLevel(4); + + initProperties(); + AgentsManagerClass::getInstance().begin(); + LEDFeedbackClass::getInstance().begin(); + DEBUG_INFO("Starting Provisioning"); +} + +void sendStatus(StatusMessage msg) { + ProvisioningOutputMessage outMsg = { MessageOutputType::STATUS, { msg } }; + AgentsManagerClass::getInstance().sendMsg(outMsg); +} + +DeviceState handleHardwareCheck() { + // Init the secure element + if(!secureElement.begin()) { + DEBUG_ERROR("Sketch: Error during secureElement begin!"); + LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + sendStatus(StatusMessage::HW_ERROR_SE_BEGIN); + return DeviceState::ERROR; + } + + if (!secureElement.locked()) { + if (!secureElement.writeConfiguration()) { + DEBUG_ERROR("Sketch: Writing secureElement configuration failed!"); + LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + sendStatus(StatusMessage::HW_ERROR_SE_CONFIG); + return DeviceState::ERROR; + } + + if (!secureElement.lock()) { + DEBUG_ERROR("Sketch: Locking secureElement configuration failed!"); + LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + sendStatus(StatusMessage::HW_ERROR_SE_LOCK); + return DeviceState::ERROR; + } + DEBUG_INFO("secureElement locked successfully"); + } + + FlashFormatter flashFormatter; + // Check if the board storage is properly formatted + if(!flashFormatter.checkandFormatPartition()) { + DEBUG_ERROR("Sketch: Error partitioning storage"); + LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + sendStatus(StatusMessage::FAIL_TO_PARTITION_STORAGE); + return DeviceState::ERROR; + } + + return DeviceState::BEGIN; +} + +DeviceState handleBegin() { + uhwid = GetUHWID(); + if(uhwid == ""){ + DEBUG_ERROR("Sketch: Error getting UHWID"); + LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR); + sendStatus(StatusMessage::ERROR_GENERATING_UHWID); + return DeviceState::ERROR; + } + // Scan the network options + NetworkConfigurator.scanNetworkOptions(); + NetworkConfigurator.begin(); + ClaimingHandler.begin(secureElement, uhwid, clearStoredCredentials); + DEBUG_INFO("BLE Available"); + return DeviceState::NETWORK_CONFIG; +} + +DeviceState handleNetworkConfig() { + ClaimingHandler.poll(); + if(resetEvent){ + resetEvent = false; + } + NetworkConfiguratorStates s = NetworkConfigurator.update(); + + DeviceState nextState = _state; + if (s == NetworkConfiguratorStates::CONFIGURED) { + String deviceId = ""; + SElementArduinoCloudDeviceId::read(secureElement, deviceId, SElementArduinoCloudSlot::DeviceId); + + if (deviceId == "") { + CSRHandler.begin(ArduinoIoTPreferredConnection, secureElement, uhwid); + nextState = DeviceState::CSR; + } else { + nextState = DeviceState::BEGIN_CLOUD; + } + } + return nextState; +} + +DeviceState handleCSR() { + NetworkConfigurator.update(); + ClaimingHandler.poll(); + if(resetEvent) { + resetEvent = false; + CSRHandler.end(); + return DeviceState::NETWORK_CONFIG; + } + + DeviceState nextState = _state; + + CSRHandlerClass::CSRHandlerStates res = CSRHandler.poll(); + if (res == CSRHandlerClass::CSRHandlerStates::COMPLETED) { + CSRHandler.end(); + nextState = DeviceState::BEGIN_CLOUD; + } + + return nextState; +} + +DeviceState handleBeginCloud() { + // Close the connection to the peer (App mobile, FE, etc) + NetworkConfigurator.disconnectAgent(); + // Close the BLE connectivity + if (NetworkConfigurator.isAgentEnabled(ConfiguratorAgent::AgentTypes::BLE)) { + NetworkConfigurator.enableAgent(ConfiguratorAgent::AgentTypes::BLE, false); + } + // Connect to Arduino IoT Cloud +#ifdef COMPILE_TEST + ArduinoCloud.begin(ArduinoIoTPreferredConnection, false, "mqtts-sa.iot.oniudra.cc"); +#else + ArduinoCloud.begin(ArduinoIoTPreferredConnection); +#endif + ArduinoCloud.printDebugInfo(); + + return DeviceState::RUN; +} + +void cloudConnectedHandler(bool connected) { + static bool _status = false; + if(connected != _status){ + _status = connected; + if(connected){ + LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::CONNECTED_TO_CLOUD); + } else { + LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::NONE); + } + } +} + +DeviceState handleRun() { + ClaimingHandler.poll(); + if(resetEvent) { + resetEvent = false; + return DeviceState::NETWORK_CONFIG; + } + + DeviceState nextState = _state; + ArduinoCloud.update(); + + cloudConnectedHandler(ArduinoCloud.connected()); + + return nextState; +} + +DeviceState handleError() { + LEDFeedbackClass::getInstance().update(); + AgentsManagerClass::getInstance().update(); + return DeviceState::ERROR; +} + +void loop() { + switch (_state) { + case DeviceState::HARDWARE_CHECK: _state = handleHardwareCheck(); break; + case DeviceState::BEGIN: _state = handleBegin (); break; + case DeviceState::NETWORK_CONFIG : _state = handleNetworkConfig(); break; + case DeviceState::CSR: _state = handleCSR (); break; + case DeviceState::BEGIN_CLOUD: _state = handleBeginCloud (); break; + case DeviceState::RUN: _state = handleRun (); break; + case DeviceState::ERROR: _state = handleError (); break; + default: break; + } +} diff --git a/examples/utility/Provisioning_2.0/SecretsHelper.h b/examples/utility/Provisioning_2.0/SecretsHelper.h new file mode 100644 index 000000000..bdb6354d1 --- /dev/null +++ b/examples/utility/Provisioning_2.0/SecretsHelper.h @@ -0,0 +1,20 @@ +/* + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include +#include + +inline String GetUHWID() { + UniqueHWId Id; + if (Id.begin()) { + return Id.get(); + } + return ""; +} diff --git a/examples/utility/Provisioning_2.0/thingProperties.h b/examples/utility/Provisioning_2.0/thingProperties.h new file mode 100644 index 000000000..d2127c693 --- /dev/null +++ b/examples/utility/Provisioning_2.0/thingProperties.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +#if defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) +#error "Board not supported for Provisioning 2.0" +#endif + +#include +#include +#include +#include "ConfiguratorAgents/agents/BLE/BLEAgent.h" +#include "ConfiguratorAgents/agents/Serial/SerialAgent.h" + +GenericConnectionHandler ArduinoIoTPreferredConnection; +KVStore kvStore; +BLEAgentClass BLEAgent; +SerialAgentClass SerialAgent; +NetworkConfiguratorClass NetworkConfigurator(ArduinoIoTPreferredConnection); + +void initProperties() { + + NetworkConfigurator.addAgent(BLEAgent); + NetworkConfigurator.addAgent(SerialAgent); + NetworkConfigurator.setStorage(kvStore); + ArduinoCloud.setConfigurator(NetworkConfigurator); +} + + diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index 82e5093ef..cf3e259d5 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -148,6 +148,14 @@ #define BOARD_STM32H7 #endif +#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_NANO_RP2040_CONNECT) \ + || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA) \ + || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_PORTENTA_C33) + #define NETWORK_CONFIGURATOR_ENABLED (1) +#else + #define NETWORK_CONFIGURATOR_ENABLED (0) +#endif + /****************************************************************************** * CONSTANTS ******************************************************************************/ diff --git a/src/ArduinoIoTCloud.cpp b/src/ArduinoIoTCloud.cpp index a565cac44..74443ff5b 100644 --- a/src/ArduinoIoTCloud.cpp +++ b/src/ArduinoIoTCloud.cpp @@ -27,6 +27,9 @@ ArduinoIoTCloudClass::ArduinoIoTCloudClass() : _connection{nullptr} +#if NETWORK_CONFIGURATOR_ENABLED +, _configurator{nullptr} +#endif , _time_service(TimeService) , _thing_id{"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"} , _lib_version{AIOT_CONFIG_LIB_VERSION} diff --git a/src/ArduinoIoTCloud.h b/src/ArduinoIoTCloud.h index 7cea9382e..16e07d1c7 100644 --- a/src/ArduinoIoTCloud.h +++ b/src/ArduinoIoTCloud.h @@ -25,6 +25,9 @@ #include #include +#if NETWORK_CONFIGURATOR_ENABLED +#include +#endif #if defined(DEBUG_ERROR) || defined(DEBUG_WARNING) || defined(DEBUG_INFO) || defined(DEBUG_DEBUG) || defined(DEBUG_VERBOSE) # include @@ -87,7 +90,7 @@ class ArduinoIoTCloudClass virtual void update () = 0; virtual int connected () = 0; virtual void printDebugInfo() = 0; - + virtual void disconnect () { } void push(); bool setTimestamp(String const & prop_name, unsigned long const timestamp); @@ -101,6 +104,9 @@ class ArduinoIoTCloudClass inline unsigned long getInternalTime() { return _time_service.getTime(); } inline unsigned long getLocalTime() { return _time_service.getLocalTime(); } + #if NETWORK_CONFIGURATOR_ENABLED + inline void setConfigurator(NetworkConfiguratorClass & configurator) { _configurator = &configurator; } + #endif void addCallback(ArduinoIoTCloudEvent const event, OnCloudEventCallback callback); #define addProperty( v, ...) addPropertyReal(v, #v, __VA_ARGS__) @@ -146,6 +152,9 @@ class ArduinoIoTCloudClass protected: ConnectionHandler * _connection; + #if NETWORK_CONFIGURATOR_ENABLED + NetworkConfiguratorClass * _configurator = nullptr; + #endif TimeServiceClass & _time_service; String _thing_id; String _lib_version; diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index dc8bb86d0..2f2f67770 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -35,7 +35,7 @@ #include /****************************************************************************** - LOCAL MODULE FUNCTIONS + LOCAL MODULE FUNCTIONS ******************************************************************************/ unsigned long getTime() @@ -48,7 +48,7 @@ unsigned long getTime() ******************************************************************************/ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() -: _state{State::ConnectPhy} +: _state{State::ConfigPhy} , _connection_attempt(0,0) , _message_stream(std::bind(&ArduinoIoTCloudTCP::sendMessage, this, std::placeholders::_1)) , _thing(&_message_stream) @@ -79,29 +79,21 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() * PUBLIC MEMBER FUNCTIONS ******************************************************************************/ -int ArduinoIoTCloudTCP::begin(ConnectionHandler & connection, bool const enable_watchdog, String brokerAddress, uint16_t brokerPort) +int ArduinoIoTCloudTCP::begin(ConnectionHandler & connection, bool const enable_watchdog, String brokerAddress, uint16_t brokerPort, bool auto_reconnect) { _connection = &connection; _brokerAddress = brokerAddress; - ArduinoIoTAuthenticationMode authMode = ArduinoIoTAuthenticationMode::CERTIFICATE; + _authMode = ArduinoIoTAuthenticationMode::CERTIFICATE; #if defined (BOARD_HAS_SECRET_KEY) /* If board supports and sketch is configured for username and password login */ if(_password.length()) { - authMode = ArduinoIoTAuthenticationMode::PASSWORD; + _authMode = ArduinoIoTAuthenticationMode::PASSWORD; } #endif - /* Setup broker TLS client */ - _brokerClient.begin(connection, authMode); - -#if OTA_ENABLED - /* Setup OTA TLS client */ - _otaClient.begin(connection); -#endif - /* If board is configured for certificate authentication and mTLS */ - if(authMode == ArduinoIoTAuthenticationMode::CERTIFICATE) + if(_authMode == ArduinoIoTAuthenticationMode::CERTIFICATE) { #if defined(BOARD_HAS_SECURE_ELEMENT) if (!_selement.begin()) @@ -141,18 +133,19 @@ int ArduinoIoTCloudTCP::begin(ConnectionHandler & connection, bool const enable_ _brokerPort = (brokerPort == DEFAULT_BROKER_PORT_AUTO) ? DEFAULT_BROKER_PORT_USER_PASS_AUTH : brokerPort; } - /* Setup TimeService */ - _time_service.begin(_connection); - /* Setup retry timers */ _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); - return begin(enable_watchdog, _brokerAddress, _brokerPort); + return begin(enable_watchdog, _brokerAddress, _brokerPort, auto_reconnect); } -int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, uint16_t brokerPort) +int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, uint16_t brokerPort, bool auto_reconnect) { + _enable_watchdog = enable_watchdog; _brokerAddress = brokerAddress; _brokerPort = brokerPort; + _auto_reconnect = auto_reconnect; + + _state = State::ConfigPhy; _mqttClient.setClient(_brokerClient); @@ -195,20 +188,12 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, } #endif - /* Since we do not control what code the user inserts - * between ArduinoIoTCloudTCP::begin() and the first - * call to ArduinoIoTCloudTCP::update() it is wise to - * set a rather large timeout at first. - */ -#if defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_MBED) - if (enable_watchdog) { - /* Initialize watchdog hardware */ - watchdog_enable(); - /* Setup callbacks to feed the watchdog during offloaded network operations (connection/download)*/ - watchdog_enable_network_feed(_connection->getInterface()); +#if NETWORK_CONFIGURATOR_ENABLED + if(_configurator != nullptr){ + _configurator->enableAgent(ConfiguratorAgent::AgentTypes::BLE,false); + _configurator->begin(); } #endif - return 1; } @@ -225,12 +210,17 @@ void ArduinoIoTCloudTCP::update() State next_state = _state; switch (_state) { + case State::ConfigPhy: next_state = handle_ConfigPhy(); break; + case State::updatePhy: next_state = handle_updatePhy(); break; + case State::Init: next_state = handle_Init(); break; case State::ConnectPhy: next_state = handle_ConnectPhy(); break; case State::SyncTime: next_state = handle_SyncTime(); break; case State::ConnectMqttBroker: next_state = handle_ConnectMqttBroker(); break; case State::Connected: next_state = handle_Connected(); break; case State::Disconnect: next_state = handle_Disconnect(); break; + case State::Disconnected: break; } + _state = next_state; /* This watchdog feed is actually needed only by the RP2040 Connect because its @@ -241,11 +231,24 @@ void ArduinoIoTCloudTCP::update() watchdog_reset(); #endif + /* Poll the network configurator to check if it is updating the configuration. + * The polling must be performed only if the the first configuration is completed. + */ + #if NETWORK_CONFIGURATOR_ENABLED + if(_configurator != nullptr && _state > State::Init && _configurator->update() == NetworkConfiguratorStates::UPDATING_CONFIG){ + _state = State::updatePhy; + } + #endif + #if OTA_ENABLED /* OTA FSM needs to reach the Idle state before being able to run independently from * the mqttClient. The state can be reached only after the mqttClient is connected to * the broker. */ + if(_state <= State::Init){ + return; + } + if((_ota.getState() != OTACloudProcessInterface::Resume && _ota.getState() != OTACloudProcessInterface::OtaBegin) || _mqttClient.connected()) { @@ -262,6 +265,9 @@ void ArduinoIoTCloudTCP::update() int ArduinoIoTCloudTCP::connected() { + if (_state <= State::Init) { + return 0; + } return _mqttClient.connected(); } @@ -272,10 +278,84 @@ void ArduinoIoTCloudTCP::printDebugInfo() DEBUG_INFO("MQTT Broker: %s:%d", _brokerAddress.c_str(), _brokerPort); } +void ArduinoIoTCloudTCP::disconnect() { + if (_state == State::ConfigPhy || _state == State::Init) { + return; + } + + _mqttClient.stop(); + _auto_reconnect = false; + _state = State::Disconnect; +} + /****************************************************************************** * PRIVATE MEMBER FUNCTIONS ******************************************************************************/ +ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConfigPhy() +{ +#if NETWORK_CONFIGURATOR_ENABLED + if (_configurator == nullptr) { + return State::Init; + } + + if(_configurator->update() == NetworkConfiguratorStates::CONFIGURED) { + _configurator->disconnectAgent(); + return State::Init; + } + return State::ConfigPhy; +#else + return State::Init; +#endif + +} + +ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_updatePhy() +{ +#if NETWORK_CONFIGURATOR_ENABLED + if(_configurator != nullptr && _configurator->update() == NetworkConfiguratorStates::CONFIGURED){ + _configurator->disconnectAgent(); + // Go to Connected state since the connectionHandler obj doesn't change + return State::Connected; + } + + return State::updatePhy; +#else + return State::Connected; +#endif +} + +ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Init() +{ + /* Setup broker TLS client */ + /* Setup broker TLS client */ + _brokerClient.begin(*_connection, _authMode); + +#if OTA_ENABLED + /* Setup OTA TLS client */ + _otaClient.begin(*_connection); +#endif + + /* Setup TimeService */ + _time_service.begin(_connection); + + /* Since we do not control what code the user inserts + * between ArduinoIoTCloudTCP::begin() and the first + * call to ArduinoIoTCloudTCP::update() it is wise to + * set a rather large timeout at first. + */ +#if defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_MBED) + if (_enable_watchdog) { + /* Initialize watchdog hardware */ + watchdog_enable(); + /* Setup callbacks to feed the watchdog during offloaded network operations (connection/download)*/ + watchdog_enable_network_feed(_connection->getInterface()); + } +#endif + + return State::ConnectPhy; +} + ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectPhy() { if (_connection->check() == NetworkConnectionState::CONNECTED) @@ -381,9 +461,13 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Disconnect() DEBUG_INFO("Disconnected from Arduino IoT Cloud"); execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); - /* Setup timer for broker connection and restart */ - _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); - return State::ConnectPhy; + if(_auto_reconnect) { + /* Setup timer for broker connection and restart */ + _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); + return State::ConnectPhy; + } + + return State::Disconnected; } void ArduinoIoTCloudTCP::onMessage(int length) @@ -467,10 +551,10 @@ void ArduinoIoTCloudTCP::handleMessage(int length) execCloudEventCallback(ArduinoIoTCloudEvent::SYNC); /* - * NOTE: in this current version properties are not properly integrated with the new paradigm of - * modeling the messages with C structs. The current CBOR library allocates an array in the heap - * thus we need to delete it after decoding it with the old CBORDecoder - */ + * NOTE: in this current version properties are not properly integrated with the new paradigm of + * modeling the messages with C structs. The current CBOR library allocates an array in the heap + * thus we need to delete it after decoding it with the old CBORDecoder + */ free(command.lastValuesUpdateCmd.params.last_values); } break; @@ -525,8 +609,8 @@ void ArduinoIoTCloudTCP::sendPropertyContainerToCloud(String const topic, Proper if (bytes_encoded > 0) { /* If properties have been encoded store them in the back-up buffer - * in order to allow retransmission in case of failure. - */ + * in order to allow retransmission in case of failure. + */ _mqtt_data_len = bytes_encoded; memcpy(_mqtt_data_buf, data, _mqtt_data_len); /* Transmit the properties to the MQTT broker */ @@ -630,11 +714,12 @@ int ArduinoIoTCloudTCP::updateCertificate(String authorityKeyIdentifier, String } return 0; } + #endif /****************************************************************************** - * EXTERN DEFINITION - ******************************************************************************/ +* EXTERN DEFINITION +******************************************************************************/ ArduinoIoTCloudTCP ArduinoCloud; diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index 6560a5f70..aa075827c 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -72,9 +72,10 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass virtual void update () override; virtual int connected () override; virtual void printDebugInfo() override; + virtual void disconnect () override; - int begin(ConnectionHandler & connection, bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO); - int begin(bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO); + int begin(ConnectionHandler & connection, bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO, bool auto_reconnect = true); + int begin(bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO, bool auto_reconnect = true); #if defined(BOARD_HAS_SECURE_ELEMENT) int updateCertificate(String authorityKeyIdentifier, String serialNumber, String notBefore, String notAfter, String signature); @@ -120,11 +121,15 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass enum class State { + ConfigPhy, + updatePhy, + Init, ConnectPhy, SyncTime, ConnectMqttBroker, Connected, Disconnect, + Disconnected, }; State _state; @@ -133,11 +138,14 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass ArduinoCloudThing _thing; ArduinoCloudDevice _device; + ArduinoIoTAuthenticationMode _authMode; String _brokerAddress; uint16_t _brokerPort; uint8_t _mqtt_data_buf[MQTT_TRANSMIT_BUFFER_SIZE]; int _mqtt_data_len; bool _mqtt_data_request_retransmit; + bool _enable_watchdog; + bool _auto_reconnect; #if defined(BOARD_HAS_SECRET_KEY) String _password; @@ -170,6 +178,9 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass inline String getTopic_dataout () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/e/o"); } inline String getTopic_datain () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/e/i"); } + State handle_ConfigPhy(); + State handle_updatePhy(); + State handle_Init(); State handle_ConnectPhy(); State handle_SyncTime(); State handle_ConnectMqttBroker();