From b9bf84d2b6ee449fa5366b295bc2c9531b2e62d6 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 2 Jul 2021 17:04:57 +0200 Subject: [PATCH 01/10] Add hook for OTA confirmation --- src/ArduinoIoTCloudTCP.cpp | 18 ++++++++++-------- src/ArduinoIoTCloudTCP.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 4cac10c6d..2289c00fe 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -511,14 +511,16 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() if (_ota_req) { - /* Clear the error flag. */ - _ota_error = static_cast(OTAError::None); - /* Transmit the cleared error flag to the cloud. */ - sendPropertiesToCloud(); - /* Clear the request flag. */ - _ota_req = false; - /* Call member function to handle OTA request. */ - onOTARequest(); + if (_automatic_ota || (_get_ota_confirmation != nullptr && _get_ota_confirmation())) { + /* Clear the error flag. */ + _ota_error = static_cast(OTAError::None); + /* Transmit the cleared error flag to the cloud. */ + sendPropertiesToCloud(); + /* Clear the request flag. */ + _ota_req = false; + /* Call member function to handle OTA request. */ + onOTARequest(); + } } #endif /* OTA_ENABLED */ diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index f29aafde7..38dee3ab2 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -49,6 +49,8 @@ static uint16_t const DEFAULT_BROKER_PORT_SECURE_AUTH = 8883; static char const DEFAULT_BROKER_ADDRESS_USER_PASS_AUTH[] = "mqtts-up.iot.arduino.cc"; static uint16_t const DEFAULT_BROKER_PORT_USER_PASS_AUTH = 8884; +typedef bool (*otaConfirmationStatus)(void); + /****************************************************************************** * CLASS DECLARATION ******************************************************************************/ @@ -80,6 +82,15 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass inline String getBrokerAddress() const { return _brokerAddress; } inline uint16_t getBrokerPort () const { return _brokerPort; } +#if OTA_ENABLED + /* The callback is triggered when the OTA is initiated and it gets executed until _ota_req flag is cleared. + * It should return true when the OTA can be applied or false otherwise. + */ + void onOTARequestCb(otaConfirmationStatus cb) { + _get_ota_confirmation = cb; + _automatic_ota = false; + } +#endif private: static const int MQTT_TRANSMIT_BUFFER_SIZE = 256; @@ -130,6 +141,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass String _ota_img_sha256; String _ota_url; bool _ota_req; + bool _automatic_ota = true; #endif /* OTA_ENABLED */ inline String getTopic_shadowout() { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/shadow/o"); } @@ -153,6 +165,8 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass #if OTA_ENABLED void onOTARequest(); #endif + + otaConfirmationStatus _get_ota_confirmation = {nullptr}; }; /****************************************************************************** From 9938936977b5c622e671ad6fc59a2d0672662f3c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 2 Jul 2021 17:08:05 +0200 Subject: [PATCH 02/10] Add example for Deferred OTA --- .../ArduinoIoTCloud-DeferredOTA.ino | 81 +++++++++++++++++++ .../arduino_secrets.h | 34 ++++++++ .../thingProperties.h | 40 +++++++++ 3 files changed, 155 insertions(+) create mode 100644 examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino create mode 100644 examples/ArduinoIoTCloud-DeferredOTA/arduino_secrets.h create mode 100644 examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h diff --git a/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino b/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino new file mode 100644 index 000000000..dba9927fb --- /dev/null +++ b/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino @@ -0,0 +1,81 @@ +/* + 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 + + This sketch is compatible with: + - MKR WIFI 1010 + - Nano 33 IoT + - Portenta + - Nano RP2040 +*/ + +#include "arduino_secrets.h" +#include "thingProperties.h" + +#if defined(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; +} + +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); ) { } + + /* 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(always_deny); + + setDebugMessageLevel(DBG_INFO); + 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..fc0b0661e --- /dev/null +++ b/examples/ArduinoIoTCloud-DeferredOTA/arduino_secrets.h @@ -0,0 +1,34 @@ +#include + +/* MKR1000, MKR WiFi 1010 */ +#if defined(BOARD_HAS_WIFI) + #define SECRET_SSID "YOUR_WIFI_NETWORK_NAME" + #define SECRET_PASS "YOUR_WIFI_PASSWORD" +#endif + +/* ESP8266 */ +#if defined(BOARD_ESP8266) + #define SECRET_DEVICE_KEY "my-device-password" +#endif + +/* MKR GSM 1400 */ +#if defined(BOARD_HAS_GSM) + #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 + +/* MKR NB 1500 */ +#if defined(BOARD_HAS_NB) + #define SECRET_PIN "" + #define SECRET_APN "" + #define SECRET_LOGIN "" + #define SECRET_PASS "" +#endif diff --git a/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h b/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h new file mode 100644 index 000000000..f5628033a --- /dev/null +++ b/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h @@ -0,0 +1,40 @@ +#include +#include + +#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" +#endif + +#define THING_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +#define BOARD_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + +void onLedChange(); + +bool led; + +void initProperties() { +#if defined(BOARD_ESP8266) + ArduinoCloud.setBoardId(BOARD_ID); + ArduinoCloud.setSecretDeviceKey(SECRET_DEVICE_KEY); +#endif + ArduinoCloud.setThingId(THING_ID); +#if defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB) + ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange); +#elif defined(BOARD_HAS_LORA) + ArduinoCloud.addProperty(led, 1, READWRITE, ON_CHANGE, onLedChange); +#endif +} + +#if defined(BOARD_HAS_WIFI) + WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_SSID, SECRET_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); +#elif defined(BOARD_HAS_NB) + NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#endif From 29417af9adb242de578a0ace547bc9201fd0c2f8 Mon Sep 17 00:00:00 2001 From: pennam Date: Fri, 15 Oct 2021 10:10:41 +0200 Subject: [PATCH 03/10] Change sync strategy to CLOUD_WINS --- src/ArduinoIoTCloudTCP.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 2289c00fe..3c0685ac6 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -238,8 +238,8 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, addPropertyReal(_ota_cap, "OTA_CAP", Permission::Read); addPropertyReal(_ota_error, "OTA_ERROR", Permission::Read); addPropertyReal(_ota_img_sha256, "OTA_SHA256", Permission::Read); - addPropertyReal(_ota_url, "OTA_URL", Permission::ReadWrite).onSync(DEVICE_WINS); - addPropertyReal(_ota_req, "OTA_REQ", Permission::ReadWrite).onSync(DEVICE_WINS); + addPropertyReal(_ota_url, "OTA_URL", Permission::ReadWrite).onSync(CLOUD_WINS); + addPropertyReal(_ota_req, "OTA_REQ", Permission::ReadWrite).onSync(CLOUD_WINS); #endif /* OTA_ENABLED */ #if OTA_STORAGE_PORTENTA_QSPI From 1be94146e063815b178efe121105d126c0da60c3 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 20 Oct 2021 08:44:04 +0200 Subject: [PATCH 04/10] Send cleared _ota_req to cloud to avoid recurring requests --- src/ArduinoIoTCloudTCP.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 3c0685ac6..55a82153b 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -514,10 +514,10 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() if (_automatic_ota || (_get_ota_confirmation != nullptr && _get_ota_confirmation())) { /* Clear the error flag. */ _ota_error = static_cast(OTAError::None); - /* Transmit the cleared error flag to the cloud. */ - sendPropertiesToCloud(); /* Clear the request flag. */ _ota_req = false; + /* Transmit the cleared error and request flags to the cloud. */ + sendPropertiesToCloud(); /* Call member function to handle OTA request. */ onOTARequest(); } From f650739f30bbc4d3e6c5ac5a0febe54cf6c96537 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 20 Oct 2021 15:45:21 +0200 Subject: [PATCH 05/10] Add example information --- src/ArduinoIoTCloudTCP.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index 38dee3ab2..2eaccb27a 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -85,6 +85,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass #if OTA_ENABLED /* The callback is triggered when the OTA is initiated and it gets executed until _ota_req flag is cleared. * It should return true when the OTA can be applied or false otherwise. + * See example ArduinoIoTCloud-DeferredOTA.ino */ void onOTARequestCb(otaConfirmationStatus cb) { _get_ota_confirmation = cb; From 18f84ecd942385bfe93ca99a583341dd7a7480a3 Mon Sep 17 00:00:00 2001 From: pennam Date: Mon, 25 Oct 2021 17:08:47 +0200 Subject: [PATCH 06/10] Handle _ota_req flag first to avoid running into minimum time between updates check and fail to update the cloud --- src/ArduinoIoTCloudTCP.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 55a82153b..6eabc60c9 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -499,11 +499,6 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() _mqtt_data_request_retransmit = false; } - /* Check if any properties need encoding and send them to - * the cloud if necessary. - */ - sendPropertiesToCloud(); - #if OTA_ENABLED /* Request a OTA download if the hidden property * OTA request has been set. @@ -524,6 +519,11 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() } #endif /* OTA_ENABLED */ + /* Check if any properties need encoding and send them to + * the cloud if necessary. + */ + sendPropertiesToCloud(); + return State::Connected; } } From 6e9113ce97059d7b57064f4d20b9124185d9c011 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 27 Oct 2021 13:14:48 +0200 Subject: [PATCH 07/10] Use more meaningful name for _automatic_ota and clarify conditions to run OTA update --- src/ArduinoIoTCloudTCP.cpp | 5 ++++- src/ArduinoIoTCloudTCP.h | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 6eabc60c9..b166c25f9 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -95,6 +95,7 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() , _ota_img_sha256{"Inv."} , _ota_url{""} , _ota_req{false} +, _ask_user_before_executing_ota{false} #endif /* OTA_ENABLED */ { @@ -506,7 +507,9 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() if (_ota_req) { - if (_automatic_ota || (_get_ota_confirmation != nullptr && _get_ota_confirmation())) { + bool const ota_execution_allowed_by_user = (_get_ota_confirmation != nullptr && _get_ota_confirmation()); + bool const perform_ota_now = ota_execution_allowed_by_user || !_ask_user_before_executing_ota; + if (perform_ota_now) { /* Clear the error flag. */ _ota_error = static_cast(OTAError::None); /* Clear the request flag. */ diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index 2eaccb27a..bca27e304 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -89,7 +89,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass */ void onOTARequestCb(otaConfirmationStatus cb) { _get_ota_confirmation = cb; - _automatic_ota = false; + _ask_user_before_executing_ota = true; } #endif @@ -142,7 +142,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass String _ota_img_sha256; String _ota_url; bool _ota_req; - bool _automatic_ota = true; + bool _ask_user_before_executing_ota; #endif /* OTA_ENABLED */ inline String getTopic_shadowout() { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/shadow/o"); } From 00a2f6bdfc7c68a293d10b800818074f3b8f93ca Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 27 Oct 2021 13:24:46 +0200 Subject: [PATCH 08/10] Initialize _get_ota_confirmation within constructor and chage type name --- src/ArduinoIoTCloudTCP.cpp | 1 + src/ArduinoIoTCloudTCP.h | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index b166c25f9..6ac511cba 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -96,6 +96,7 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() , _ota_url{""} , _ota_req{false} , _ask_user_before_executing_ota{false} +, _get_ota_confirmation{nullptr} #endif /* OTA_ENABLED */ { diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index bca27e304..fa04a6e33 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -49,7 +49,11 @@ static uint16_t const DEFAULT_BROKER_PORT_SECURE_AUTH = 8883; static char const DEFAULT_BROKER_ADDRESS_USER_PASS_AUTH[] = "mqtts-up.iot.arduino.cc"; static uint16_t const DEFAULT_BROKER_PORT_USER_PASS_AUTH = 8884; -typedef bool (*otaConfirmationStatus)(void); +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +typedef bool (*onOTARequestCallbackFunc)(void); /****************************************************************************** * CLASS DECLARATION @@ -87,7 +91,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass * It should return true when the OTA can be applied or false otherwise. * See example ArduinoIoTCloud-DeferredOTA.ino */ - void onOTARequestCb(otaConfirmationStatus cb) { + void onOTARequestCb(onOTARequestCallbackFunc cb) { _get_ota_confirmation = cb; _ask_user_before_executing_ota = true; } @@ -143,6 +147,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass String _ota_url; bool _ota_req; bool _ask_user_before_executing_ota; + onOTARequestCallbackFunc _get_ota_confirmation; #endif /* OTA_ENABLED */ inline String getTopic_shadowout() { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/shadow/o"); } @@ -167,7 +172,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass void onOTARequest(); #endif - otaConfirmationStatus _get_ota_confirmation = {nullptr}; }; /****************************************************************************** From 6f51148822a73a6688e34b6f726b90d8c3a72af4 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 27 Oct 2021 13:40:30 +0200 Subject: [PATCH 09/10] Clarify onOTARequestCb usage in the example sketch --- .../ArduinoIoTCloud-DeferredOTA.ino | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino b/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino index dba9927fb..34abcdc38 100644 --- a/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino +++ b/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino @@ -46,6 +46,14 @@ bool ask_user_via_serial() { 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); @@ -61,7 +69,7 @@ void setup() { ArduinoCloud.begin(ArduinoIoTPreferredConnection); /* Setup OTA callback */ - ArduinoCloud.onOTARequestCb(always_deny); + ArduinoCloud.onOTARequestCb(onOTARequestCallback); setDebugMessageLevel(DBG_INFO); ArduinoCloud.printDebugInfo(); From 93bb40aaeb6e69d3c9858947ae60dba6b4bbd17d Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 4 Nov 2021 13:05:03 +0100 Subject: [PATCH 10/10] Add missing channelMask parameter for LoraConnectionHandler --- examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h b/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h index f5628033a..549f80f7b 100644 --- a/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h +++ b/examples/ArduinoIoTCloud-DeferredOTA/thingProperties.h @@ -34,7 +34,7 @@ void initProperties() { #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); #endif