diff --git a/examples/ArduinoIoTCloud-Basic-dynamic/ArduinoIoTCloud-Basic-dynamic.ino b/examples/ArduinoIoTCloud-Basic-dynamic/ArduinoIoTCloud-Basic-dynamic.ino new file mode 100644 index 000000000..2c79c62bf --- /dev/null +++ b/examples/ArduinoIoTCloud-Basic-dynamic/ArduinoIoTCloud-Basic-dynamic.ino @@ -0,0 +1,98 @@ +/* + 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 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 + - Nano 33 IoT + - ESP 8266 +*/ + +#include "arduino_secrets.h" +#include "thingProperties.h" + +#if defined(ESP32) +static int const LED_BUILTIN = 2; +#endif + +/* +// Can use it as soon as https://github.com/arduino-libraries/Arduino_ConnectionHandler/pull/63 gets merged + +ConnectionHandler& get_default_connection_handler() { + char ssid[MAX_LEN]; + char password[MAX_LEN]; + retrieveCredentialsFromFile("/fs/credential.txt", ssid, password); + static WiFiConnectionHandlerDynamic ArduinoIoTPreferredConnection(ssid, password); + return ArduinoIoTPreferredConnection; +} +*/ + +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(get_default_connection_handler()); + + ArduinoCloud.onOTARequestCb(always_deny); + + setDebugMessageLevel(DBG_INFO); + 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-Basic-dynamic/arduino_secrets.h b/examples/ArduinoIoTCloud-Basic-dynamic/arduino_secrets.h new file mode 100644 index 000000000..fc0b0661e --- /dev/null +++ b/examples/ArduinoIoTCloud-Basic-dynamic/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-Basic-dynamic/default_connection.cpp b/examples/ArduinoIoTCloud-Basic-dynamic/default_connection.cpp new file mode 100644 index 000000000..f87f732e3 --- /dev/null +++ b/examples/ArduinoIoTCloud-Basic-dynamic/default_connection.cpp @@ -0,0 +1,16 @@ +#include +#include +#include "arduino_secrets.h" + +ConnectionHandler& __attribute__((weak)) get_default_connection_handler() { +#if defined(BOARD_HAS_WIFI) + static WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_SSID, SECRET_PASS); +#elif defined(BOARD_HAS_GSM) + static GSMConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#elif defined(BOARD_HAS_LORA) + static LoRaConnectionHandler ArduinoIoTPreferredConnection(SECRET_APP_EUI, SECRET_APP_KEY, _lora_band::EU868, _lora_class::CLASS_A); +#elif defined(BOARD_HAS_NB) + static NBConnectionHandler ArduinoIoTPreferredConnection(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); +#endif + return ArduinoIoTPreferredConnection; +} diff --git a/examples/ArduinoIoTCloud-Basic-dynamic/thingProperties.h b/examples/ArduinoIoTCloud-Basic-dynamic/thingProperties.h new file mode 100644 index 000000000..f689dc4fa --- /dev/null +++ b/examples/ArduinoIoTCloud-Basic-dynamic/thingProperties.h @@ -0,0 +1,38 @@ +#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; +int potentiometer; +int seconds; + +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); + 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 +} + +ConnectionHandler& __attribute__((weak)) get_default_connection_handler(); 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..f2c2a80dc 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,27 @@ 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 + // Should return true when the OTA can be applied, false otherwise + +/* + static first_run = true; + bool sample_ota_confirmation() { + if (first_run) { + HMI.show(confirmationModal) + first_run = false; + } + return HMI.getConfirmation(); + } +*/ + + void onOTARequestCb(otaConfirmationStatus cb) { + _get_ota_confirmation = cb; + _automatic_ota = false; + } +#endif private: static const int MQTT_TRANSMIT_BUFFER_SIZE = 256; @@ -130,6 +153,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 +177,8 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass #if OTA_ENABLED void onOTARequest(); #endif + + otaConfirmationStatus _get_ota_confirmation = {nullptr}; }; /******************************************************************************