diff --git a/.gitignore b/.gitignore index 6e260c5ef..65731e42a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *~ .vscode *.orig +.vs \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 8c2f3b895..68ff08503 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ matrix: - BOARD="arduino:samd:mkrwifi1010" - env: - BOARD="arduino:samd:mkrgsm1400" + - env: + - BOARD="arduino:samd:mkrwan1300" - env: - BOARD="esp8266:esp8266:huzzah" - env: @@ -69,6 +71,7 @@ before_install: - installLibrary arduino-libraries/ArduinoBearSSL - installLibrary arduino-libraries/ArduinoMqttClient - installLibrary arduino-libraries/MKRGSM + - installLibrary arduino-libraries/MKRWAN - installLibrary arduino-libraries/RTCZero - installLibrary arduino-libraries/WiFi101 - installLibrary arduino-libraries/WiFiNINA @@ -87,6 +90,11 @@ script: buildExampleSketch ArduinoIoTCloud_Travis_CI; buildExampleUtilitySketch Provisioning; fi + - | + if [ "$BOARD" == "arduino:samd:mkrwan1300" ]; then + buildExampleSketch ArduinoIoTCloud_LED_switch; + buildExampleSketch ArduinoIoTCloud_Travis_CI; + fi - | if [ "$BOARD" == "arduino:samd:mkr1000" ] || [ "$BOARD" == "arduino:samd:mkrwifi1010" ]; then buildExampleSketch WiFi_Cloud_Blink; diff --git a/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino b/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino index 090e8e045..b1b435382 100644 --- a/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino +++ b/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino @@ -6,14 +6,15 @@ When you flip the switch in the Cloud dashboard the onboard LED lights gets turned ON or OFF. IMPORTANT: - This sketch will work with both WiFi and GSM enabled boards supported by Arduino IoT Cloud. - By default, settings for WiFi are chosen. If you prefer to use a GSM board take a look at thingProperties.h arduino_secrets.h, - to make sure you uncomment what's needed and comment incompatible instructions. + This sketch will work with WiFi, GSM 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 WAN 1300/1310 */ #include "arduino_secrets.h" #include "thingProperties.h" @@ -30,7 +31,7 @@ void setup() { // initProperties takes care of connecting your sketch variables to the ArduinoIoTCloud object initProperties(); - // tell ArduinoIoTCloud to use our WiFi connection + // tell ArduinoIoTCloud to use right connection handler ArduinoCloud.begin(ArduinoIoTPreferredConnection); /* diff --git a/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h b/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h index 87593f2dd..9a5f514b9 100644 --- a/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h +++ b/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h @@ -13,3 +13,9 @@ #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 diff --git a/examples/ArduinoIoTCloud_LED_switch/thingProperties.h b/examples/ArduinoIoTCloud_LED_switch/thingProperties.h index a04fe7c00..10f13bd4f 100644 --- a/examples/ArduinoIoTCloud_LED_switch/thingProperties.h +++ b/examples/ArduinoIoTCloud_LED_switch/thingProperties.h @@ -3,6 +3,7 @@ #if defined(BOARD_HAS_WIFI) #elif defined(BOARD_HAS_GSM) +#elif defined(BOARD_HAS_LORA) #else #error "Arduino IoT Cloud currently only supports MKR1000, MKR WiFi 1010 and MKR GSM 1400" #endif @@ -18,12 +19,20 @@ int potentiometer; void initProperties() { ArduinoCloud.setThingId(THING_ID); + #if defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) ArduinoCloud.addProperty(led, READWRITE, ON_CHANGE, onLedChange); ArduinoCloud.addProperty(potentiometer, READ, ON_CHANGE); + #elif defined(BOARD_HAS_LORA) + ArduinoCloud.addProperty(led, 1, READWRITE, ON_CHANGE, onLedChange); + ArduinoCloud.addProperty(potentiometer, 2, READ, ON_CHANGE); + #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); #endif diff --git a/examples/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino b/examples/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino index 3385d8fb4..bcb46b680 100644 --- a/examples/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino +++ b/examples/ArduinoIoTCloud_Travis_CI/ArduinoIoTCloud_Travis_CI.ino @@ -7,6 +7,7 @@ - MKR 1000 - MKR WIFI 1010 - MKR GSM 1400 + - MKR WAN 1300/1310 */ #include "arduino_secrets.h" diff --git a/examples/ArduinoIoTCloud_Travis_CI/arduino_secrets.h b/examples/ArduinoIoTCloud_Travis_CI/arduino_secrets.h index 87593f2dd..fd08461a0 100644 --- a/examples/ArduinoIoTCloud_Travis_CI/arduino_secrets.h +++ b/examples/ArduinoIoTCloud_Travis_CI/arduino_secrets.h @@ -13,3 +13,10 @@ #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 + diff --git a/examples/ArduinoIoTCloud_Travis_CI/thingProperties.h b/examples/ArduinoIoTCloud_Travis_CI/thingProperties.h index 05f981cce..5f31fd386 100644 --- a/examples/ArduinoIoTCloud_Travis_CI/thingProperties.h +++ b/examples/ArduinoIoTCloud_Travis_CI/thingProperties.h @@ -7,8 +7,9 @@ #if defined(BOARD_HAS_WIFI) #elif defined(BOARD_HAS_GSM) +#elif defined(BOARD_HAS_LORA) #else - #error "Arduino IoT Cloud currently only supports MKR1000, MKR WiFi 1010 and MKR GSM 1400" + #error "Arduino IoT Cloud currently only supports MKR1000, MKR WiFi 1010, MKR GSM 1400 and MKR WAN 1300/1310" #endif /****************************************************************************** @@ -57,6 +58,8 @@ String str_property_8; 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, EU868); #endif /****************************************************************************** @@ -71,7 +74,7 @@ void onStringPropertyChange(); /****************************************************************************** FUNCTIONS ******************************************************************************/ - +#if defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) void initProperties() { ArduinoCloud.setThingId(THING_ID); @@ -101,3 +104,35 @@ void initProperties() { ArduinoCloud.addProperty(str_property_7, Permission::ReadWrite).publishEvery(1 * SECONDS).onSync(CLOUD_WINS); ArduinoCloud.addProperty(str_property_8, Permission::ReadWrite).publishEvery(1 * SECONDS).onSync(DEVICE_WINS); } + +#elif defined(BOARD_HAS_LORA) +void initProperties() { + ArduinoCloud.setThingId(THING_ID); + + ArduinoCloud.addProperty(bool_property_1, 1, READWRITE, 1 * SECONDS); + ArduinoCloud.addProperty(int_property_1, 2, READ, 2 * MINUTES); + ArduinoCloud.addProperty(float_property_1, 3, WRITE, 3 * HOURS); + ArduinoCloud.addProperty(str_property_1, 4, READWRITE, 4 * DAYS); + + ArduinoCloud.addProperty(bool_property_2, 5, Permission::ReadWrite).publishEvery(1 * SECONDS); + ArduinoCloud.addProperty(int_property_2, 6, Permission::Read).publishEvery(1 * MINUTES); + ArduinoCloud.addProperty(float_property_2, 7, Permission::Write).publishEvery(3 * HOURS); + ArduinoCloud.addProperty(str_property_2, 8, Permission::ReadWrite).publishEvery(4 * DAYS); + + ArduinoCloud.addProperty(int_property_3, 9, READWRITE, ON_CHANGE); /* Default 'minDelta' = 0 */ + ArduinoCloud.addProperty(int_property_4, 10, READWRITE, ON_CHANGE, onIntPropertyChange); /* Default 'minDelta' = 0 */ + ArduinoCloud.addProperty(int_property_5, 11, READWRITE, ON_CHANGE, 0 /* onIntPropertyChange */, MIN_DELTA_INT_PROPERTY); + ArduinoCloud.addProperty(int_property_6, 12, READWRITE, ON_CHANGE, onIntPropertyChange, MIN_DELTA_INT_PROPERTY); + + ArduinoCloud.addProperty(float_property_3, 13, Permission::ReadWrite).publishOnChange(MIN_DELTA_FLOAT_PROPERTY); + ArduinoCloud.addProperty(float_property_4, 14, Permission::ReadWrite).publishOnChange(MIN_DELTA_FLOAT_PROPERTY).onUpdate(onFloatPropertyChange); + + ArduinoCloud.addProperty(str_property_3, 15, READWRITE, 1 * SECONDS, 0 /* onStringPropertyChange */, 0.0 /* 'minDelta' */, MOST_RECENT_WINS); + ArduinoCloud.addProperty(str_property_4, 16, READWRITE, 1 * SECONDS, 0 /* onStringPropertyChange */, 0.0 /* 'minDelta' */, CLOUD_WINS); + ArduinoCloud.addProperty(str_property_5, 17, READWRITE, 1 * SECONDS, 0 /* onStringPropertyChange */, 0.0 /* 'minDelta' */, DEVICE_WINS); + + ArduinoCloud.addProperty(str_property_6, 18, Permission::ReadWrite).publishEvery(1 * SECONDS).onSync(MOST_RECENT_WINS); + ArduinoCloud.addProperty(str_property_7, 19, Permission::ReadWrite).publishEvery(1 * SECONDS).onSync(CLOUD_WINS); + ArduinoCloud.addProperty(str_property_8, 20, Permission::ReadWrite).publishEvery(1 * SECONDS).onSync(DEVICE_WINS); +} +#endif diff --git a/extras/codespell-ignore-words-list.txt b/extras/codespell-ignore-words-list.txt index e69de29bb..e49ad4874 100644 --- a/extras/codespell-ignore-words-list.txt +++ b/extras/codespell-ignore-words-list.txt @@ -0,0 +1 @@ +wan diff --git a/src/ArduinoIoTCloud.cpp b/src/ArduinoIoTCloud.cpp index 56653f6c4..1556c2a12 100644 --- a/src/ArduinoIoTCloud.cpp +++ b/src/ArduinoIoTCloud.cpp @@ -17,419 +17,95 @@ #include -#ifdef BOARD_HAS_ECCX08 - #include "utility/ECCX08Cert.h" - #include "utility/BearSSLTrustAnchor.h" - #include -#endif - -#ifdef ARDUINO_ARCH_SAMD - #include - RTCZero rtc; -#endif - -#ifdef BOARD_HAS_ECCX08 - const static int keySlot = 0; - const static int compressedCertSlot = 10; - const static int serialNumberAndAuthorityKeyIdentifierSlot = 11; - const static int deviceIdSlot = 12; -#endif - -const static int CONNECT_SUCCESS = 1; -const static int CONNECT_FAILURE = 0; -const static int CONNECT_FAILURE_SUBSCRIBE = -1; - -static unsigned long getTime() { - if (!ArduinoCloud.getConnection()) { - return 0; - } - ConnectionHandler * connection = ArduinoCloud.getConnection(); - unsigned long time = connection->getTime(); - Debug.print(DBG_DEBUG, "NTP time: %lu", time); - if (!NTPUtils::isTimeValid(time)) { - Debug.print(DBG_ERROR, "Bogus NTP time from API, fallback to UDP method"); - time = NTPUtils(connection->getUDP()).getTime(); - } - #ifdef ARDUINO_ARCH_SAMD - rtc.setEpoch(time); - #endif - return time; -} - -ArduinoIoTCloudClass::ArduinoIoTCloudClass() : - _connection(NULL), - _thing_id(""), - _sslClient(NULL), - #ifdef BOARD_ESP - _password(""), - #endif - _mqttClient(NULL), - _lastSyncRequestTickTime(0), - _stdinTopic(""), - _stdoutTopic(""), - _shadowTopicOut(""), - _shadowTopicIn(""), - _dataTopicOut(""), - _dataTopicIn(""), - _otaTopic(""), - _on_sync_event_callback(NULL), - _on_connect_event_callback(NULL), - _on_disconnect_event_callback(NULL), - _device_id("") {} - -ArduinoIoTCloudClass::~ArduinoIoTCloudClass() { - if (_mqttClient) { - delete _mqttClient; - _mqttClient = NULL; - } - - if (_sslClient) { - delete _sslClient; - _sslClient = NULL; - } -} - -int ArduinoIoTCloudClass::begin(ConnectionHandler & connection, String brokerAddress, uint16_t brokerPort) { - _connection = &connection; - _brokerAddress = brokerAddress; - _brokerPort = brokerPort; - #ifdef ARDUINO_ARCH_SAMD - rtc.begin(); - #endif - return begin(_connection->getClient(), _brokerAddress, _brokerPort); +void ArduinoIoTCloudClass::addPropertyReal(ArduinoCloudProperty& property, String name, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + addPropertyReal(property, name, -1, permission_type, seconds, fn, minDelta, synFn); } -int ArduinoIoTCloudClass::begin(Client& net, String brokerAddress, uint16_t brokerPort) { - - _net = &net; - // store the broker address as class member - _brokerAddress = brokerAddress; - _brokerPort = brokerPort; - - #ifdef BOARD_HAS_ECCX08 - byte deviceIdBytes[72]; - if (!ECCX08.begin()) { - Debug.print(DBG_ERROR, "Cryptography processor failure. Make sure you have a compatible board."); - return 0; - } - - if (!ECCX08.readSlot(deviceIdSlot, deviceIdBytes, sizeof(deviceIdBytes))) { - Debug.print(DBG_ERROR, "Cryptography processor read failure."); - return 0; - } - _device_id = (char *)deviceIdBytes; - - if (!ECCX08Cert.beginReconstruction(keySlot, compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { - Debug.print(DBG_ERROR, "Cryptography certificate reconstruction failure."); - return 0; - } - - ECCX08Cert.setSubjectCommonName(_device_id); - ECCX08Cert.setIssuerCountryName("US"); - ECCX08Cert.setIssuerOrganizationName("Arduino LLC US"); - ECCX08Cert.setIssuerOrganizationalUnitName("IT"); - ECCX08Cert.setIssuerCommonName("Arduino"); - - if (!ECCX08Cert.endReconstruction()) { - Debug.print(DBG_ERROR, "Cryptography certificate reconstruction failure."); - return 0; - } - - ArduinoBearSSL.onGetTime(getTime); - #endif /* BOARD_HAS_ECCX08 */ - - if (_sslClient) { - delete _sslClient; - _sslClient = NULL; - } - - #ifdef BOARD_HAS_ECCX08 - if (_connection != NULL) { - _sslClient = new BearSSLClient(_connection->getClient(), ArduinoIoTCloudTrustAnchor, ArduinoIoTCloudTrustAnchor_NUM); +void ArduinoIoTCloudClass::addPropertyReal(ArduinoCloudProperty& property, String name, int tag, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + Permission permission = Permission::ReadWrite; + if (permission_type == READ) { + permission = Permission::Read; + } else if (permission_type == WRITE) { + permission = Permission::Write; } else { - _sslClient = new BearSSLClient(*_net, ArduinoIoTCloudTrustAnchor, ArduinoIoTCloudTrustAnchor_NUM); + permission = Permission::ReadWrite; } - _sslClient->setEccSlot(keySlot, ECCX08Cert.bytes(), ECCX08Cert.length()); - #elif defined(BOARD_ESP) - _sslClient = new WiFiClientSecure(); - _sslClient->setInsecure(); - #endif - - _mqttClient = new MqttClient(*_sslClient); - - #ifdef BOARD_ESP - _mqttClient->setUsernamePassword(_device_id, _password); - #endif - - mqttClientBegin(); - - Thing.begin(); - return 1; -} -// private class method used to initialize mqttClient class member. (called in the begin class method) -void ArduinoIoTCloudClass::mqttClientBegin() { - // MQTT topics definition - _stdoutTopic = "/a/d/" + _device_id + "/s/o"; - _stdinTopic = "/a/d/" + _device_id + "/s/i"; - if (_thing_id == "") { - _dataTopicIn = "/a/d/" + _device_id + "/e/i"; - _dataTopicOut = "/a/d/" + _device_id + "/e/o"; + if (seconds == ON_CHANGE) { + Thing.addPropertyReal(property, name, permission, tag).publishOnChange(minDelta, DEFAULT_MIN_TIME_BETWEEN_UPDATES_MILLIS).onUpdate(fn).onSync(synFn); } else { - _dataTopicIn = "/a/t/" + _thing_id + "/e/i"; - _dataTopicOut = "/a/t/" + _thing_id + "/e/o"; - _shadowTopicIn = "/a/t/" + _thing_id + "/shadow/i"; - _shadowTopicOut = "/a/t/" + _thing_id + "/shadow/o"; + Thing.addPropertyReal(property, name, permission, tag).publishEvery(seconds).onUpdate(fn).onSync(synFn); } - - // use onMessage as callback for received mqtt messages - _mqttClient->onMessage(ArduinoIoTCloudClass::onMessage); - _mqttClient->setKeepAliveInterval(30 * 1000); - _mqttClient->setConnectionTimeout(1500); - _mqttClient->setId(_device_id.c_str()); } -int ArduinoIoTCloudClass::connect() { - - if (!_mqttClient->connect(_brokerAddress.c_str(), _brokerPort)) { - return CONNECT_FAILURE; - } - if (_mqttClient->subscribe(_stdinTopic) == 0) { - return CONNECT_FAILURE_SUBSCRIBE; - } - if (_mqttClient->subscribe(_dataTopicIn) == 0) { - return CONNECT_FAILURE_SUBSCRIBE; - } - if (_shadowTopicIn != "") { - if (_mqttClient->subscribe(_shadowTopicIn) == 0) { - return CONNECT_FAILURE_SUBSCRIBE; - } - - _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES; - _lastSyncRequestTickTime = 0; - } - - return CONNECT_SUCCESS; +void ArduinoIoTCloudClass::addPropertyReal(bool& property, String name, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + addPropertyReal(property, name, -1, permission_type, seconds, fn, minDelta, synFn); } -bool ArduinoIoTCloudClass::disconnect() { - _mqttClient->stop(); - - return true; +void ArduinoIoTCloudClass::addPropertyReal(bool& property, String name, int tag, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + ArduinoCloudProperty* p = new CloudWrapperBool(property); + addPropertyReal(*p, name, tag, permission_type, seconds, fn, minDelta, synFn); } - -void ArduinoIoTCloudClass::update(CallbackFunc onSyncCompleteCallback) { - // Check if a primitive property wrapper is locally changed - Thing.updateTimestampOnLocallyChangedProperties(); - - connectionCheck(); - - if (_iotStatus != ArduinoIoTConnectionStatus::CONNECTED) { - return; - } - - // MTTQClient connected!, poll() used to retrieve data from MQTT broker - _mqttClient->poll(); - - switch (_syncStatus) { - case ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED: { - sendPropertiesToCloud(); - } - break; - case ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES: { - if (millis() - _lastSyncRequestTickTime > TIMEOUT_FOR_LASTVALUES_SYNC) { - requestLastValue(); - _lastSyncRequestTickTime = millis(); - } - } - break; - case ArduinoIoTSynchronizationStatus::SYNC_STATUS_VALUES_PROCESSED: { - if (onSyncCompleteCallback != NULL) { - (*onSyncCompleteCallback)(); - } - execCloudEventCallback(_on_sync_event_callback, 0 /* callback_arg */); - _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED; - } - break; - } +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(bool& property, String name, Permission const permission) { + return addPropertyReal(property, name, -1, permission); } - -void ArduinoIoTCloudClass::sendPropertiesToCloud() { - uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE]; - int const length = Thing.encode(data, sizeof(data)); - if (length > 0) { - writeProperties(data, length); - } +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(bool& property, String name, int tag, Permission const permission) { + ArduinoCloudProperty* p = new CloudWrapperBool(property); + return Thing.addPropertyReal(*p, name, permission, tag); } -int ArduinoIoTCloudClass::reconnect(Client& /* net */) { - if (_mqttClient->connected()) { - _mqttClient->stop(); - } - - // Connect to the broker - return connect(); +void ArduinoIoTCloudClass::addPropertyReal(float& property, String name, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + addPropertyReal(property, name, -1, permission_type, seconds, fn, minDelta, synFn); } -int ArduinoIoTCloudClass::connected() { - return _mqttClient->connected(); +void ArduinoIoTCloudClass::addPropertyReal(float& property, String name, int tag, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + ArduinoCloudProperty* p = new CloudWrapperFloat(property); + addPropertyReal(*p, name, tag, permission_type, seconds, fn, minDelta, synFn); } -int ArduinoIoTCloudClass::writeProperties(const byte data[], int length) { - if (!_mqttClient->beginMessage(_dataTopicOut, length, false, 0)) { - return 0; - } - - if (!_mqttClient->write(data, length)) { - return 0; - } - - if (!_mqttClient->endMessage()) { - return 0; - } - - return 1; +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(float& property, String name, Permission const permission) { + return addPropertyReal(property, name, -1, permission); } -int ArduinoIoTCloudClass::writeStdout(const byte data[], int length) { - if (!_mqttClient->beginMessage(_stdoutTopic, length, false, 0)) { - return 0; - } - - if (!_mqttClient->write(data, length)) { - return 0; - } - - if (!_mqttClient->endMessage()) { - return 0; - } - - return 1; +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(float& property, String name, int tag, Permission const permission) { + ArduinoCloudProperty* p = new CloudWrapperFloat(property); + return Thing.addPropertyReal(*p, name, permission, tag); } -int ArduinoIoTCloudClass::writeShadowOut(const byte data[], int length) { - if (!_mqttClient->beginMessage(_shadowTopicOut, length, false, 0)) { - return 0; - } - - if (!_mqttClient->write(data, length)) { - return 0; - } - - if (!_mqttClient->endMessage()) { - return 0; - } - - return 1; +void ArduinoIoTCloudClass::addPropertyReal(int& property, String name, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + addPropertyReal(property, name, -1, permission_type, seconds, fn, minDelta, synFn); } -void ArduinoIoTCloudClass::onMessage(int length) { - ArduinoCloud.handleMessage(length); +void ArduinoIoTCloudClass::addPropertyReal(int& property, String name, int tag, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + ArduinoCloudProperty* p = new CloudWrapperInt(property); + addPropertyReal(*p, name, tag, permission_type, seconds, fn, minDelta, synFn); } -void ArduinoIoTCloudClass::handleMessage(int length) { - String topic = _mqttClient->messageTopic(); - - byte bytes[length]; - - for (int i = 0; i < length; i++) { - bytes[i] = _mqttClient->read(); - } - - if (_stdinTopic == topic) { - CloudSerial.appendStdin((uint8_t*)bytes, length); - } - if (_dataTopicIn == topic) { - Thing.decode((uint8_t*)bytes, length); - } - if ((_shadowTopicIn == topic) && _syncStatus == ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES) { - Thing.decode((uint8_t*)bytes, length, true); - sendPropertiesToCloud(); - _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_VALUES_PROCESSED; - } +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(int& property, String name, Permission const permission) { + return addPropertyReal(property, name, -1, permission); } -void ArduinoIoTCloudClass::requestLastValue() { - // Send the getLastValues CBOR message to the cloud - // [{0: "r:m", 3: "getLastValues"}] = 81 A2 00 63 72 3A 6D 03 6D 67 65 74 4C 61 73 74 56 61 6C 75 65 73 - // Use http://cbor.me to easily generate CBOR encoding - const uint8_t CBOR_REQUEST_LAST_VALUE_MSG[] = { 0x81, 0xA2, 0x00, 0x63, 0x72, 0x3A, 0x6D, 0x03, 0x6D, 0x67, 0x65, 0x74, 0x4C, 0x61, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x73 }; - writeShadowOut(CBOR_REQUEST_LAST_VALUE_MSG, sizeof(CBOR_REQUEST_LAST_VALUE_MSG)); +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(int& property, String name, int tag, Permission const permission) { + ArduinoCloudProperty* p = new CloudWrapperInt(property); + return Thing.addPropertyReal(*p, name, permission, tag); } -void ArduinoIoTCloudClass::connectionCheck() { - - if (_connection != NULL) { - - _connection->check(); +void ArduinoIoTCloudClass::addPropertyReal(String& property, String name, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + addPropertyReal(property, name, -1, permission_type, seconds, fn, minDelta, synFn); +} - if (_connection->getStatus() != NetworkConnectionState::CONNECTED) { - if (_iotStatus == ArduinoIoTConnectionStatus::CONNECTED) { - _iotStatus = ArduinoIoTConnectionStatus::DISCONNECTED; - printConnectionStatus(_iotStatus); - } - return; - } - } +void ArduinoIoTCloudClass::addPropertyReal(String& property, String name, int tag, permissionType permission_type, long seconds, void(*fn)(void), float minDelta, void(*synFn)(ArduinoCloudProperty & property)) { + ArduinoCloudProperty* p = new CloudWrapperString(property); + addPropertyReal(*p, name, tag, permission_type, seconds, fn, minDelta, synFn); +} - switch (_iotStatus) { - case ArduinoIoTConnectionStatus::IDLE: { - _iotStatus = ArduinoIoTConnectionStatus::CONNECTING; - printConnectionStatus(_iotStatus); - } - break; - case ArduinoIoTConnectionStatus::ERROR: { - _iotStatus = ArduinoIoTConnectionStatus::RECONNECTING; - printConnectionStatus(_iotStatus); - } - break; - case ArduinoIoTConnectionStatus::CONNECTED: { - if (!_mqttClient->connected()) { - _iotStatus = ArduinoIoTConnectionStatus::DISCONNECTED; - printConnectionStatus(_iotStatus); - execCloudEventCallback(_on_disconnect_event_callback, 0 /* callback_arg - e.g. could be error code casted to void * */); - } - } - break; - case ArduinoIoTConnectionStatus::DISCONNECTED: { - _iotStatus = ArduinoIoTConnectionStatus::RECONNECTING; - printConnectionStatus(_iotStatus); - } - break; - case ArduinoIoTConnectionStatus::RECONNECTING: { - int const ret_code_reconnect = reconnect(*_net); - Debug.print(DBG_INFO, "ArduinoCloud.reconnect(): %d", ret_code_reconnect); - if (ret_code_reconnect == CONNECT_SUCCESS) { - _iotStatus = ArduinoIoTConnectionStatus::CONNECTED; - printConnectionStatus(_iotStatus); - execCloudEventCallback(_on_connect_event_callback, 0 /* callback_arg */); - CloudSerial.begin(9600); - CloudSerial.println("Hello from Cloud Serial!"); - } - } - break; - case ArduinoIoTConnectionStatus::CONNECTING: { - int const ret_code_connect = connect(); - Debug.print(DBG_VERBOSE, "ArduinoCloud.connect(): %d", ret_code_connect); - if (ret_code_connect == CONNECT_SUCCESS) { - _iotStatus = ArduinoIoTConnectionStatus::CONNECTED; - printConnectionStatus(_iotStatus); - execCloudEventCallback(_on_connect_event_callback, 0 /* callback_arg */); - CloudSerial.begin(9600); - CloudSerial.println("Hello from Cloud Serial!"); - } else if (ret_code_connect == CONNECT_FAILURE_SUBSCRIBE) { - Debug.print(DBG_INFO, "ERROR - Please verify your THING ID"); - } - } - break; - } +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(String& property, String name, Permission const permission) { + return addPropertyReal(property, name, -1, permission); } -void ArduinoIoTCloudClass::printDebugInfo() { - Debug.print(DBG_INFO, "***** Arduino IoT Cloud - configuration info *****"); - Debug.print(DBG_INFO, "Device ID: %s", getDeviceId().c_str()); - Debug.print(DBG_INFO, "Thing ID: %s", getThingId().c_str()); - Debug.print(DBG_INFO, "MQTT Broker: %s:%d", _brokerAddress.c_str(), _brokerPort); +ArduinoCloudProperty& ArduinoIoTCloudClass::addPropertyReal(String& property, String name, int tag, Permission const permission) { + ArduinoCloudProperty* p = new CloudWrapperString(property); + return Thing.addPropertyReal(*p, name, permission, tag); } void ArduinoIoTCloudClass::addCallback(ArduinoIoTCloudEvent const event, OnCloudEventCallback callback) { @@ -438,14 +114,13 @@ void ArduinoIoTCloudClass::addCallback(ArduinoIoTCloudEvent const event, OnCloud case ArduinoIoTCloudEvent::CONNECT: _on_connect_event_callback = callback; break; case ArduinoIoTCloudEvent::DISCONNECT: _on_disconnect_event_callback = callback; break; } -} +}; -void ArduinoIoTCloudClass::execCloudEventCallback(OnCloudEventCallback & callback, void * callback_arg) { +void ArduinoIoTCloudClass::execCloudEventCallback(OnCloudEventCallback& callback, void* callback_arg) { if (callback) { (*callback)(callback_arg); } } - void ArduinoIoTCloudClass::printConnectionStatus(ArduinoIoTConnectionStatus status) { switch (status) { case ArduinoIoTConnectionStatus::IDLE: Debug.print(DBG_INFO, "Arduino IoT Cloud Connection status: IDLE"); break; @@ -455,6 +130,4 @@ void ArduinoIoTCloudClass::printConnectionStatus(ArduinoIoTConnectionStatus stat case ArduinoIoTConnectionStatus::CONNECTED: Debug.print(DBG_INFO, "Arduino IoT Cloud Connection status: CONNECTED"); break; case ArduinoIoTConnectionStatus::DISCONNECTED: Debug.print(DBG_ERROR, "Arduino IoT Cloud Connection status: DISCONNECTED"); break; } -} - -ArduinoIoTCloudClass ArduinoCloud; +} \ No newline at end of file diff --git a/src/ArduinoIoTCloud.h b/src/ArduinoIoTCloud.h index 98129ffe5..b5702aa98 100644 --- a/src/ArduinoIoTCloud.h +++ b/src/ArduinoIoTCloud.h @@ -20,28 +20,19 @@ #include -#ifdef BOARD_HAS_ECCX08 - #include -#elif defined(BOARD_ESP) - #include -#endif + #include -#include + #include -#include #include "types/CloudWrapperBool.h" #include "types/CloudWrapperFloat.h" #include "types/CloudWrapperInt.h" #include "types/CloudWrapperString.h" -#include "utility/NTPUtils.h" + #include "CloudSerial.h" -static char const DEFAULT_BROKER_ADDRESS_SECURE_AUTH[] = "mqtts-sa.iot.arduino.cc"; -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 enum { READ = 0x01, @@ -49,12 +40,6 @@ typedef enum { READWRITE = READ | WRITE } permissionType; -// Declaration of the struct for the mqtt connection options -typedef struct { - int keepAlive; - bool cleanSession; - int timeout; -} mqttConnectionOptions; enum class ArduinoIoTConnectionStatus { IDLE, @@ -78,181 +63,119 @@ enum class ArduinoIoTCloudEvent { typedef void (*CallbackFunc)(void); typedef void (*OnCloudEventCallback)(void * /* arg */); +/************************************************* + Pure Virtual Class Definition +**************************************************/ class ArduinoIoTCloudClass { public: - ArduinoIoTCloudClass(); - ~ArduinoIoTCloudClass(); - - #ifdef BOARD_HAS_ECCX08 - int begin(ConnectionHandler &connection, String brokerAddress = DEFAULT_BROKER_ADDRESS_SECURE_AUTH, uint16_t brokerPort = DEFAULT_BROKER_PORT_SECURE_AUTH); - #else - int begin(ConnectionHandler &connection, String brokerAddress = DEFAULT_BROKER_ADDRESS_USER_PASS_AUTH, uint16_t brokerPort = DEFAULT_BROKER_PORT_USER_PASS_AUTH); - #endif - int begin(Client &net, String brokerAddress = DEFAULT_BROKER_ADDRESS_SECURE_AUTH, uint16_t brokerPort = DEFAULT_BROKER_PORT_SECURE_AUTH); - // Class constant declaration - static const int MQTT_TRANSMIT_BUFFER_SIZE = 256; static const int TIMEOUT_FOR_LASTVALUES_SYNC = 10000; + /*Public Virtual Functions*/ + virtual int connect() = 0; + virtual bool disconnect() = 0; - int connect(); - bool disconnect(); + virtual void update() = 0; + virtual void update(int const reconnectionMaxRetries, int const reconnectionTimeoutMs) __attribute__((deprecated)) = 0; + virtual void update(CallbackFunc onSyncCompleteCallback) __attribute__((deprecated)) = 0; /* Attention: Function is deprecated - use 'addCallback(ArduinoIoTCloudConnectionEvent::SYNC, &onSync)' for adding a onSyncCallback instead */ - inline void update() { - update(NULL); - } - inline void update(int const reconnectionMaxRetries, int const reconnectionTimeoutMs) __attribute__((deprecated)) { - update(NULL); - } - void update(CallbackFunc onSyncCompleteCallback) __attribute__((deprecated)); /* Attention: Function is deprecated - use 'addCallback(ArduinoIoTCloudConnectionEvent::SYNC, &onSync)' for adding a onSyncCallback instead */ + virtual int connected() = 0; - int connected(); - // Clean up existing Mqtt connection, create a new one and initialize it - int reconnect(Client& /* net */); + virtual void connectionCheck() = 0; + + virtual void printDebugInfo() = 0; inline void setThingId(String const thing_id) { _thing_id = thing_id; }; - #ifdef BOARD_ESP - inline void setBoardId(String const device_id) { - _device_id = device_id; - } - inline void setSecretDeviceKey(String const password) { - _password = password; - } - #endif + inline String getThingId() const { return _thing_id; }; + inline String getDeviceId() const { return _device_id; }; - inline ConnectionHandler * getConnection() { - return _connection; - } #define addProperty( v, ...) addPropertyReal(v, #v, __VA_ARGS__) static unsigned long const DEFAULT_MIN_TIME_BETWEEN_UPDATES_MILLIS = 500; /* Data rate throttled to 2 Hz */ - void addPropertyReal(ArduinoCloudProperty& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty& property) = CLOUD_WINS) { - Permission permission = Permission::ReadWrite; - if (permission_type == READ) { - permission = Permission::Read; - } else if (permission_type == WRITE) { - permission = Permission::Write; - } else { - permission = Permission::ReadWrite; - } - - if (seconds == ON_CHANGE) { - Thing.addPropertyReal(property, name, permission).publishOnChange(minDelta, DEFAULT_MIN_TIME_BETWEEN_UPDATES_MILLIS).onUpdate(fn).onSync(synFn); - } else { - Thing.addPropertyReal(property, name, permission).publishEvery(seconds).onUpdate(fn).onSync(synFn); - } - } - void addPropertyReal(bool& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS) { - ArduinoCloudProperty *p = new CloudWrapperBool(property); - addPropertyReal(*p, name, permission_type, seconds, fn, minDelta, synFn); - } - ArduinoCloudProperty& addPropertyReal(bool& property, String name, Permission const permission) { - ArduinoCloudProperty *p = new CloudWrapperBool(property); - return Thing.addPropertyReal(*p, name, permission); - } - void addPropertyReal(float& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS) { - ArduinoCloudProperty *p = new CloudWrapperFloat(property); - addPropertyReal(*p, name, permission_type, seconds, fn, minDelta, synFn); - } - ArduinoCloudProperty& addPropertyReal(float& property, String name, Permission const permission) { - ArduinoCloudProperty *p = new CloudWrapperFloat(property); - return Thing.addPropertyReal(*p, name, permission); - } - void addPropertyReal(int& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS) { - ArduinoCloudProperty *p = new CloudWrapperInt(property); - addPropertyReal(*p, name, permission_type, seconds, fn, minDelta, synFn); - } - ArduinoCloudProperty& addPropertyReal(int& property, String name, Permission const permission) { - ArduinoCloudProperty *p = new CloudWrapperInt(property); - return Thing.addPropertyReal(*p, name, permission); - } - void addPropertyReal(String& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS) { - ArduinoCloudProperty *p = new CloudWrapperString(property); - addPropertyReal(*p, name, permission_type, seconds, fn, minDelta, synFn); - } - ArduinoCloudProperty& addPropertyReal(String& property, String name, Permission const permission) { - ArduinoCloudProperty *p = new CloudWrapperString(property); - return Thing.addPropertyReal(*p, name, permission); - } + void addPropertyReal(ArduinoCloudProperty& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + + void addPropertyReal(ArduinoCloudProperty& property, String name, int tag, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + + void addPropertyReal(bool& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + void addPropertyReal(bool& property, String name, int tag, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + ArduinoCloudProperty& addPropertyReal(bool& property, String name, Permission const permission); + ArduinoCloudProperty& addPropertyReal(bool& property, String name, int tag, Permission const permission); + + void addPropertyReal(float& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + + void addPropertyReal(float& property, String name, int tag, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + + ArduinoCloudProperty& addPropertyReal(float& property, String name, Permission const permission); + + ArduinoCloudProperty& addPropertyReal(float& property, String name, int tag, Permission const permission); + + void addPropertyReal(int& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + + void addPropertyReal(int& property, String name, int tag, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + + ArduinoCloudProperty& addPropertyReal(int& property, String name, Permission const permission); + + ArduinoCloudProperty& addPropertyReal(int& property, String name, int tag, Permission const permission); + + void addPropertyReal(String& property, String name, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + void addPropertyReal(String& property, String name, int tag, permissionType permission_type = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f, void(*synFn)(ArduinoCloudProperty & property) = CLOUD_WINS); + ArduinoCloudProperty& addPropertyReal(String& property, String name, Permission const permission); + + ArduinoCloudProperty& addPropertyReal(String& property, String name, int tag, Permission const permission); - void connectionCheck(); - String getBrokerAddress() { - return _brokerAddress; - } - uint16_t getBrokerPort() { - return _brokerPort; - } - void printDebugInfo(); void addCallback(ArduinoIoTCloudEvent const event, OnCloudEventCallback callback); + protected: - friend class CloudSerialClass; - int writeStdout(const byte data[], int length); - int writeProperties(const byte data[], int length); - int writeShadowOut(const byte data[], int length); - - // Used to initialize MQTTClient - void mqttClientBegin(); - // Function in charge of perform MQTT reconnection, basing on class parameters(retries,and timeout) - bool mqttReconnect(int const maxRetries, int const timeout); - // Used to retrieve last values from _shadowTopicIn - void requestLastValue(); + + virtual int writeStdout(const byte data[], int length) = 0; + virtual int writeProperties(const byte data[], int length) = 0; + virtual int writeShadowOut(const byte data[], int length) = 0; + + + ArduinoIoTConnectionStatus getIoTStatus() { return _iotStatus; } - private: ArduinoIoTConnectionStatus _iotStatus = ArduinoIoTConnectionStatus::IDLE; - ConnectionHandler * _connection; - static void onMessage(int length); - void handleMessage(int length); - ArduinoIoTSynchronizationStatus _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED; - void sendPropertiesToCloud(); - String _device_id, _thing_id, _brokerAddress; - uint16_t _brokerPort; - ArduinoCloudThing Thing; + ArduinoIoTSynchronizationStatus _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED; + - #ifdef BOARD_HAS_ECCX08 - BearSSLClient *_sslClient; - #elif defined(BOARD_ESP) - WiFiClientSecure *_sslClient; - String _password; - #endif + String _device_id = ""; + String _thing_id = ""; - MqttClient *_mqttClient; - int _lastSyncRequestTickTime; + ArduinoCloudThing Thing; - // Class attribute to define MTTQ topics 2 for stdIn/out and 2 for data, in order to avoid getting previous pupblished payload - String _stdinTopic; - String _stdoutTopic; - String _shadowTopicOut; - String _shadowTopicIn; - String _dataTopicOut; - String _dataTopicIn; - String _otaTopic; - Client *_net; + int _lastSyncRequestTickTime = 0; - OnCloudEventCallback _on_sync_event_callback, - _on_connect_event_callback, - _on_disconnect_event_callback; + OnCloudEventCallback _on_sync_event_callback = NULL; + OnCloudEventCallback _on_connect_event_callback = NULL; + OnCloudEventCallback _on_disconnect_event_callback = NULL; static void execCloudEventCallback(OnCloudEventCallback & callback, void * callback_arg); static void printConnectionStatus(ArduinoIoTConnectionStatus status); }; -extern ArduinoIoTCloudClass ArduinoCloud; +#ifdef HAS_TCP + #include "ArduinoIoTCloudTCP.h" + +#elif defined(HAS_LORA) + #include "ArduinoIoTCloudLPWAN.h" + +#endif #endif diff --git a/src/ArduinoIoTCloudLPWAN.cpp b/src/ArduinoIoTCloudLPWAN.cpp new file mode 100644 index 000000000..be01b27f6 --- /dev/null +++ b/src/ArduinoIoTCloudLPWAN.cpp @@ -0,0 +1,208 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ +#include "ArduinoIoTCloud_Defines.h" +#ifdef HAS_LORA + +#include + +#ifdef ARDUINO_ARCH_SAMD + #include + RTCZero rtc; +#endif + +ArduinoIoTCloudLPWAN::ArduinoIoTCloudLPWAN() : + _connection(NULL) {} + +ArduinoIoTCloudLPWAN::~ArduinoIoTCloudLPWAN() { +} + +int ArduinoIoTCloudLPWAN::connect() { + _connection->connect(); + const int state = _connection->getStatus() == NetworkConnectionState::INIT ? 1 : 0; + return state; +} + +bool ArduinoIoTCloudLPWAN::disconnect() { + _connection->disconnect(); + return true; +} + +int ArduinoIoTCloudLPWAN::connected() { + int state = _connection->getStatus() == NetworkConnectionState::INIT ? 1 : 0; + return state; +} + +int ArduinoIoTCloudLPWAN::begin(LPWANConnectionHandler& connection, bool retry) { + _connection = &connection; + _connection->init(); + _retryEnable = retry; + _maxNumRetry = 5; + _intervalRetry = 1000; + Thing.begin(); + return 1; +} + +void ArduinoIoTCloudLPWAN::update(CallbackFunc onSyncCompleteCallback) { + // Check if a primitive property wrapper is locally changed + Thing.updateTimestampOnLocallyChangedProperties(); + + connectionCheck(); + + if (_iotStatus != ArduinoIoTConnectionStatus::CONNECTED) { + return; + } + + if (_connection->available()) { + uint8_t msgBuf[DEFAULT_CBOR_LORA_MSG_SIZE]; + uint8_t i = 0; + while (_connection->available()) { + msgBuf[i++] = _connection->read(); + } + + Thing.decode(msgBuf, sizeof(msgBuf)); + } + + + + + sendPropertiesToCloud(); + + + if (onSyncCompleteCallback != NULL) { + (*onSyncCompleteCallback)(); + } + execCloudEventCallback(_on_sync_event_callback, 0 /* callback_arg */); + +} + +void ArduinoIoTCloudLPWAN::connectionCheck() { + if (_connection != NULL) { + + _connection->check(); + + if (_connection->getStatus() != NetworkConnectionState::CONNECTED) { + if (_iotStatus == ArduinoIoTConnectionStatus::CONNECTED) { + _iotStatus = ArduinoIoTConnectionStatus::DISCONNECTED; + printConnectionStatus(_iotStatus); + } + return; + } + } + + switch (_iotStatus) { + case ArduinoIoTConnectionStatus::IDLE: { + _iotStatus = ArduinoIoTConnectionStatus::CONNECTING; + printConnectionStatus(_iotStatus); + } + break; + case ArduinoIoTConnectionStatus::ERROR: { + _iotStatus = ArduinoIoTConnectionStatus::RECONNECTING; + printConnectionStatus(_iotStatus); + } + break; + case ArduinoIoTConnectionStatus::CONNECTED: { + if (_connection->getStatus() != NetworkConnectionState::CONNECTED) { + _iotStatus = ArduinoIoTConnectionStatus::DISCONNECTED; + printConnectionStatus(_iotStatus); + execCloudEventCallback(_on_disconnect_event_callback, 0 /* callback_arg - e.g. could be error code casted to void * */); + } + } + break; + case ArduinoIoTConnectionStatus::DISCONNECTED: { + _iotStatus = ArduinoIoTConnectionStatus::RECONNECTING; + printConnectionStatus(_iotStatus); + } + break; + case ArduinoIoTConnectionStatus::RECONNECTING: { + int const ret_code = connect(); + Debug.print(DBG_INFO, "ArduinoCloud.reconnect()"); + if (ret_code == 1) { + _iotStatus = ArduinoIoTConnectionStatus::IDLE; + } else { + _iotStatus = ArduinoIoTConnectionStatus::ERROR; + } + + } + break; + case ArduinoIoTConnectionStatus::CONNECTING: { + NetworkConnectionState net_status = _connection->getStatus(); + Debug.print(DBG_VERBOSE, "ArduinoCloud.connect(): %d", net_status); + if (net_status == NetworkConnectionState::CONNECTED) { + _iotStatus = ArduinoIoTConnectionStatus::CONNECTED; + printConnectionStatus(_iotStatus); + execCloudEventCallback(_on_connect_event_callback, 0 /* callback_arg */); + } + + } + break; + } +} + +void ArduinoIoTCloudLPWAN::printDebugInfo() { + Debug.print(DBG_INFO, "***** Arduino IoT Cloud LPWAN- configuration info *****"); + Debug.print(DBG_INFO, "Device ID: %s", getDeviceId().c_str()); + Debug.print(DBG_INFO, "Thing ID: %s", getThingId().c_str()); + +} + + +int ArduinoIoTCloudLPWAN::writeProperties(const byte data[], int length) { + int retcode = _connection->write(data, length); + int i = 0; + while (_retryEnable && retcode < 0 && i < _maxNumRetry) { + delay(_intervalRetry); + retcode = _connection->write(data, length); + i++; + } + + return 1; +} + +int ArduinoIoTCloudLPWAN::writeStdout(const byte data[], int length) { + int retcode = _connection->write(data, length); + int i = 0; + while (_retryEnable && retcode < 0 && i < _maxNumRetry) { + delay(_intervalRetry); + retcode = _connection->write(data, length); + i++; + } + + return 1; +} + +int ArduinoIoTCloudLPWAN::writeShadowOut(const byte data[], int length) { + int retcode = _connection->write(data, length); + int i = 0; + while (_retryEnable && retcode < 0 && i < _maxNumRetry) { + delay(_intervalRetry); + retcode = _connection->write(data, length); + i++; + } + return 1; +} + +void ArduinoIoTCloudLPWAN::sendPropertiesToCloud() { + uint8_t data[DEFAULT_CBOR_LORA_MSG_SIZE]; + int const length = Thing.encode(data, sizeof(data), true); + if (length > 0) { + writeProperties(data, length); + } +} + +ArduinoIoTCloudLPWAN ArduinoCloud; + +#endif \ No newline at end of file diff --git a/src/ArduinoIoTCloudLPWAN.h b/src/ArduinoIoTCloudLPWAN.h new file mode 100644 index 000000000..b44ade2f6 --- /dev/null +++ b/src/ArduinoIoTCloudLPWAN.h @@ -0,0 +1,87 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +#ifndef ARDUINO_IOT_CLOUD_LPWAN_H +#define ARDUINO_IOT_CLOUD_LPWAN_H + +#include +#include + +static uint8_t const DEFAULT_CBOR_LORA_MSG_SIZE = 255; + +class ArduinoIoTCloudLPWAN : public ArduinoIoTCloudClass { + public: + ArduinoIoTCloudLPWAN(); + ~ArduinoIoTCloudLPWAN(); + int connect(); + bool disconnect(); + int connected(); + inline void update() { + update(NULL); + } + inline void update(int const reconnectionMaxRetries, int const reconnectionTimeoutMs) __attribute__((deprecated)) { + update(NULL); + } + void update(CallbackFunc onSyncCompleteCallback) __attribute__((deprecated)); + void connectionCheck(); + void printDebugInfo(); + int begin(LPWANConnectionHandler& connection, bool retry = false); + inline LPWANConnectionHandler* getConnection() { + return _connection; + } + bool isRetryEnabled() { + return _retryEnable; + } + + void enableRetry(bool val) { + _retryEnable = val; + } + + int getMaxRetry() { + return _maxNumRetry; + } + + void setMaxRetry(int val) { + _maxNumRetry = val; + } + + long getIntervalRetry() { + return _intervalRetry; + } + + void setIntervalRetry(long val) { + _intervalRetry = val; + } + + protected: + int writeStdout(const byte data[], int length); + int writeProperties(const byte data[], int length); + int writeShadowOut(const byte data[], int length); + + private: + LPWANConnectionHandler* _connection; + void sendPropertiesToCloud(); + bool _retryEnable; + int _maxNumRetry; + long _intervalRetry; + +}; + +extern ArduinoIoTCloudLPWAN ArduinoCloud; + + +#endif \ No newline at end of file diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp new file mode 100644 index 000000000..3db419853 --- /dev/null +++ b/src/ArduinoIoTCloudTCP.cpp @@ -0,0 +1,442 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ +#include "ArduinoIoTCloud_Defines.h" + +#ifdef HAS_TCP +#include +#ifdef BOARD_HAS_ECCX08 + #include "utility/ECCX08Cert.h" + #include "utility/BearSSLTrustAnchor.h" + #include +#endif + +#ifdef ARDUINO_ARCH_SAMD + #include + RTCZero rtc; +#endif + +#ifdef BOARD_HAS_ECCX08 + const static int keySlot = 0; + const static int compressedCertSlot = 10; + const static int serialNumberAndAuthorityKeyIdentifierSlot = 11; + const static int deviceIdSlot = 12; +#endif + +const static int CONNECT_SUCCESS = 1; +const static int CONNECT_FAILURE = 0; +const static int CONNECT_FAILURE_SUBSCRIBE = -1; + +static unsigned long getTime() { + if (!ArduinoCloud.getConnection()) { + return 0; + } + TcpIpConnectionHandler * connection = ArduinoCloud.getConnection(); + unsigned long time = connection->getTime(); + Debug.print(DBG_DEBUG, "NTP time: %lu", time); + if (!NTPUtils::isTimeValid(time)) { + Debug.print(DBG_ERROR, "Bogus NTP time from API, fallback to UDP method"); + time = NTPUtils(connection->getUDP()).getTime(); + } + #ifdef ARDUINO_ARCH_SAMD + rtc.setEpoch(time); + #endif + return time; +} + +ArduinoIoTCloudTCP::ArduinoIoTCloudTCP(): + _connection(NULL), + + _sslClient(NULL), + #ifdef BOARD_ESP + _password(""), + #endif + _mqttClient(NULL), + + _stdinTopic(""), + _stdoutTopic(""), + _shadowTopicOut(""), + _shadowTopicIn(""), + _dataTopicOut(""), + _dataTopicIn(""), + _otaTopic("") +{} + + +ArduinoIoTCloudTCP::~ArduinoIoTCloudTCP() { + if (_mqttClient) { + delete _mqttClient; + _mqttClient = NULL; + } + + if (_sslClient) { + delete _sslClient; + _sslClient = NULL; + } +} + +int ArduinoIoTCloudTCP::begin(TcpIpConnectionHandler & connection, String brokerAddress, uint16_t brokerPort) { + _connection = &connection; + _brokerAddress = brokerAddress; + _brokerPort = brokerPort; + #ifdef ARDUINO_ARCH_SAMD + rtc.begin(); + #endif + return begin(_connection->getClient(), _brokerAddress, _brokerPort); +} + +int ArduinoIoTCloudTCP::begin(Client& net, String brokerAddress, uint16_t brokerPort) { + + _net = &net; + // store the broker address as class member + _brokerAddress = brokerAddress; + _brokerPort = brokerPort; + + #ifdef BOARD_HAS_ECCX08 + byte deviceIdBytes[72]; + if (!ECCX08.begin()) { + Debug.print(DBG_ERROR, "Cryptography processor failure. Make sure you have a compatible board."); + return 0; + } + + if (!ECCX08.readSlot(deviceIdSlot, deviceIdBytes, sizeof(deviceIdBytes))) { + Debug.print(DBG_ERROR, "Cryptography processor read failure."); + return 0; + } + _device_id = (char*)deviceIdBytes; + + if (!ECCX08Cert.beginReconstruction(keySlot, compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { + Debug.print(DBG_ERROR, "Cryptography certificate reconstruction failure."); + return 0; + } + + ECCX08Cert.setSubjectCommonName(_device_id); + ECCX08Cert.setIssuerCountryName("US"); + ECCX08Cert.setIssuerOrganizationName("Arduino LLC US"); + ECCX08Cert.setIssuerOrganizationalUnitName("IT"); + ECCX08Cert.setIssuerCommonName("Arduino"); + + if (!ECCX08Cert.endReconstruction()) { + Debug.print(DBG_ERROR, "Cryptography certificate reconstruction failure."); + return 0; + } + + ArduinoBearSSL.onGetTime(getTime); + #endif /* BOARD_HAS_ECCX08 */ + + if (_sslClient) { + delete _sslClient; + _sslClient = NULL; + } + + #ifdef BOARD_HAS_ECCX08 + if (_connection != NULL) { + _sslClient = new BearSSLClient(_connection->getClient(), ArduinoIoTCloudTrustAnchor, ArduinoIoTCloudTrustAnchor_NUM); + } else { + _sslClient = new BearSSLClient(*_net, ArduinoIoTCloudTrustAnchor, ArduinoIoTCloudTrustAnchor_NUM); + } + _sslClient->setEccSlot(keySlot, ECCX08Cert.bytes(), ECCX08Cert.length()); + #elif defined(BOARD_ESP) + _sslClient = new WiFiClientSecure(); + _sslClient->setInsecure(); + #endif + + _mqttClient = new MqttClient(*_sslClient); + + #ifdef BOARD_ESP + _mqttClient->setUsernamePassword(_device_id, _password); + #endif + + mqttClientBegin(); + + Thing.begin(); + return 1; +} + +// private class method used to initialize mqttClient class member. (called in the begin class method) +void ArduinoIoTCloudTCP::mqttClientBegin() { + // MQTT topics definition + _stdoutTopic = "/a/d/" + _device_id + "/s/o"; + _stdinTopic = "/a/d/" + _device_id + "/s/i"; + if (_thing_id == "") { + _dataTopicIn = "/a/d/" + _device_id + "/e/i"; + _dataTopicOut = "/a/d/" + _device_id + "/e/o"; + } else { + _dataTopicIn = "/a/t/" + _thing_id + "/e/i"; + _dataTopicOut = "/a/t/" + _thing_id + "/e/o"; + _shadowTopicIn = "/a/t/" + _thing_id + "/shadow/i"; + _shadowTopicOut = "/a/t/" + _thing_id + "/shadow/o"; + } + + // use onMessage as callback for received mqtt messages + _mqttClient->onMessage(ArduinoIoTCloudTCP::onMessage); + _mqttClient->setKeepAliveInterval(30 * 1000); + _mqttClient->setConnectionTimeout(1500); + _mqttClient->setId(_device_id.c_str()); +} + + +int ArduinoIoTCloudTCP::connect() { + + if (!_mqttClient->connect(_brokerAddress.c_str(), _brokerPort)) { + return CONNECT_FAILURE; + } + if (_mqttClient->subscribe(_stdinTopic) == 0) { + return CONNECT_FAILURE_SUBSCRIBE; + } + if (_mqttClient->subscribe(_dataTopicIn) == 0) { + return CONNECT_FAILURE_SUBSCRIBE; + } + if (_shadowTopicIn != "") { + if (_mqttClient->subscribe(_shadowTopicIn) == 0) { + return CONNECT_FAILURE_SUBSCRIBE; + } + + _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES; + _lastSyncRequestTickTime = 0; + } + + return CONNECT_SUCCESS; +} + + +bool ArduinoIoTCloudTCP::disconnect() { + _mqttClient->stop(); + + return true; +} + +void ArduinoIoTCloudTCP::update(CallbackFunc onSyncCompleteCallback) { + // Check if a primitive property wrapper is locally changed + Thing.updateTimestampOnLocallyChangedProperties(); + + connectionCheck(); + + if (_iotStatus != ArduinoIoTConnectionStatus::CONNECTED) { + return; + } + + // MTTQClient connected!, poll() used to retrieve data from MQTT broker + _mqttClient->poll(); + + switch (_syncStatus) { + case ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED: { + sendPropertiesToCloud(); + } + break; + case ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES: { + if (millis() - _lastSyncRequestTickTime > TIMEOUT_FOR_LASTVALUES_SYNC) { + requestLastValue(); + _lastSyncRequestTickTime = millis(); + } + } + break; + case ArduinoIoTSynchronizationStatus::SYNC_STATUS_VALUES_PROCESSED: { + if (onSyncCompleteCallback != NULL) { + (*onSyncCompleteCallback)(); + } + execCloudEventCallback(_on_sync_event_callback, 0 /* callback_arg */); + _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED; + } + break; + } +} + + + +void ArduinoIoTCloudTCP::sendPropertiesToCloud() { + uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE]; + int const length = Thing.encode(data, sizeof(data)); + if (length > 0) { + writeProperties(data, length); + } +} + + +int ArduinoIoTCloudTCP::reconnect(Client& /* net */) { + if (_mqttClient->connected()) { + _mqttClient->stop(); + } + + // Connect to the broker + return connect(); +} + + +int ArduinoIoTCloudTCP::connected() { + return _mqttClient->connected(); +} + +int ArduinoIoTCloudTCP::writeProperties(const byte data[], int length) { + if (!_mqttClient->beginMessage(_dataTopicOut, length, false, 0)) { + return 0; + } + + if (!_mqttClient->write(data, length)) { + return 0; + } + + if (!_mqttClient->endMessage()) { + return 0; + } + + return 1; +} + +int ArduinoIoTCloudTCP::writeStdout(const byte data[], int length) { + if (!_mqttClient->beginMessage(_stdoutTopic, length, false, 0)) { + return 0; + } + + if (!_mqttClient->write(data, length)) { + return 0; + } + + if (!_mqttClient->endMessage()) { + return 0; + } + + return 1; +} + +int ArduinoIoTCloudTCP::writeShadowOut(const byte data[], int length) { + if (!_mqttClient->beginMessage(_shadowTopicOut, length, false, 0)) { + return 0; + } + + if (!_mqttClient->write(data, length)) { + return 0; + } + + if (!_mqttClient->endMessage()) { + return 0; + } + + return 1; +} + +void ArduinoIoTCloudTCP::onMessage(int length) { + ArduinoCloud.handleMessage(length); +} + +void ArduinoIoTCloudTCP::handleMessage(int length) { + String topic = _mqttClient->messageTopic(); + + byte bytes[length]; + + for (int i = 0; i < length; i++) { + bytes[i] = _mqttClient->read(); + } + + if (_stdinTopic == topic) { + CloudSerial.appendStdin((uint8_t*)bytes, length); + } + if (_dataTopicIn == topic) { + Thing.decode((uint8_t*)bytes, length); + } + if ((_shadowTopicIn == topic) && _syncStatus == ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES) { + Thing.decode((uint8_t*)bytes, length, true); + sendPropertiesToCloud(); + _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_VALUES_PROCESSED; + } +} + +void ArduinoIoTCloudTCP::requestLastValue() { + // Send the getLastValues CBOR message to the cloud + // [{0: "r:m", 3: "getLastValues"}] = 81 A2 00 63 72 3A 6D 03 6D 67 65 74 4C 61 73 74 56 61 6C 75 65 73 + // Use http://cbor.me to easily generate CBOR encoding + const uint8_t CBOR_REQUEST_LAST_VALUE_MSG[] = { 0x81, 0xA2, 0x00, 0x63, 0x72, 0x3A, 0x6D, 0x03, 0x6D, 0x67, 0x65, 0x74, 0x4C, 0x61, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x73 }; + writeShadowOut(CBOR_REQUEST_LAST_VALUE_MSG, sizeof(CBOR_REQUEST_LAST_VALUE_MSG)); +} + +void ArduinoIoTCloudTCP::connectionCheck() { + + if (_connection != NULL) { + + _connection->check(); + + if (_connection->getStatus() != NetworkConnectionState::CONNECTED) { + if (_iotStatus == ArduinoIoTConnectionStatus::CONNECTED) { + _iotStatus = ArduinoIoTConnectionStatus::DISCONNECTED; + printConnectionStatus(_iotStatus); + } + return; + } + } + + switch (_iotStatus) { + case ArduinoIoTConnectionStatus::IDLE: { + _iotStatus = ArduinoIoTConnectionStatus::CONNECTING; + printConnectionStatus(_iotStatus); + } + break; + case ArduinoIoTConnectionStatus::ERROR: { + _iotStatus = ArduinoIoTConnectionStatus::RECONNECTING; + printConnectionStatus(_iotStatus); + } + break; + case ArduinoIoTConnectionStatus::CONNECTED: { + if (!_mqttClient->connected()) { + _iotStatus = ArduinoIoTConnectionStatus::DISCONNECTED; + printConnectionStatus(_iotStatus); + execCloudEventCallback(_on_disconnect_event_callback, 0 /* callback_arg - e.g. could be error code casted to void * */); + } + } + break; + case ArduinoIoTConnectionStatus::DISCONNECTED: { + _iotStatus = ArduinoIoTConnectionStatus::RECONNECTING; + printConnectionStatus(_iotStatus); + } + break; + case ArduinoIoTConnectionStatus::RECONNECTING: { + int const ret_code_reconnect = reconnect(*_net); + Debug.print(DBG_INFO, "ArduinoCloud.reconnect(): %d", ret_code_reconnect); + if (ret_code_reconnect == CONNECT_SUCCESS) { + _iotStatus = ArduinoIoTConnectionStatus::CONNECTED; + printConnectionStatus(_iotStatus); + execCloudEventCallback(_on_connect_event_callback, 0 /* callback_arg */); + CloudSerial.begin(9600); + CloudSerial.println("Hello from Cloud Serial!"); + } + } + break; + case ArduinoIoTConnectionStatus::CONNECTING: { + int const ret_code_connect = connect(); + Debug.print(DBG_VERBOSE, "ArduinoCloud.connect(): %d", ret_code_connect); + if (ret_code_connect == CONNECT_SUCCESS) { + _iotStatus = ArduinoIoTConnectionStatus::CONNECTED; + printConnectionStatus(_iotStatus); + execCloudEventCallback(_on_connect_event_callback, 0 /* callback_arg */); + CloudSerial.begin(9600); + CloudSerial.println("Hello from Cloud Serial!"); + } else if (ret_code_connect == CONNECT_FAILURE_SUBSCRIBE) { + Debug.print(DBG_INFO, "ERROR - Please verify your THING ID"); + } + } + break; + } +} + +void ArduinoIoTCloudTCP::printDebugInfo() { + Debug.print(DBG_INFO, "***** Arduino IoT Cloud - configuration info *****"); + Debug.print(DBG_INFO, "Device ID: %s", getDeviceId().c_str()); + Debug.print(DBG_INFO, "Thing ID: %s", getThingId().c_str()); + Debug.print(DBG_INFO, "MQTT Broker: %s:%d", _brokerAddress.c_str(), _brokerPort); +} + +ArduinoIoTCloudTCP ArduinoCloud; + +#endif \ No newline at end of file diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h new file mode 100644 index 000000000..84e9eaed0 --- /dev/null +++ b/src/ArduinoIoTCloudTCP.h @@ -0,0 +1,144 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +#ifndef ARDUINO_IOT_CLOUD_TCP_H +#define ARDUINO_IOT_CLOUD_TCP_H + + +#include +#include + +#ifdef BOARD_HAS_ECCX08 / + #include +#elif defined(BOARD_ESP) + #include +#endif + +#include + +#include "utility/NTPUtils.h" + + +static char const DEFAULT_BROKER_ADDRESS_SECURE_AUTH[] = "mqtts-sa.iot.arduino.cc"; +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; + +// Declaration of the struct for the mqtt connection options +typedef struct { + int keepAlive; + bool cleanSession; + int timeout; +} mqttConnectionOptions; + + +class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass { + public: + ArduinoIoTCloudTCP(); + ~ArduinoIoTCloudTCP(); + int connect(); + bool disconnect(); + int connected(); + inline void update() { + update(NULL); + } + inline void update(int const reconnectionMaxRetries, int const reconnectionTimeoutMs) __attribute__((deprecated)) { + update(NULL); + } + void update(CallbackFunc onSyncCompleteCallback) __attribute__((deprecated)); + void connectionCheck(); + void printDebugInfo(); + #ifdef BOARD_HAS_ECCX08 + int begin(TcpIpConnectionHandler & connection, String brokerAddress = DEFAULT_BROKER_ADDRESS_SECURE_AUTH, uint16_t brokerPort = DEFAULT_BROKER_PORT_SECURE_AUTH); + #else + int begin(TcpIpConnectionHandler & connection, String brokerAddress = DEFAULT_BROKER_ADDRESS_USER_PASS_AUTH, uint16_t brokerPort = DEFAULT_BROKER_PORT_USER_PASS_AUTH); + #endif + int begin(Client& net, String brokerAddress = DEFAULT_BROKER_ADDRESS_SECURE_AUTH, uint16_t brokerPort = DEFAULT_BROKER_PORT_SECURE_AUTH); + // Class constant declaration + static const int MQTT_TRANSMIT_BUFFER_SIZE = 256; + + #ifdef BOARD_ESP + inline void setBoardId(String const device_id) { + _device_id = device_id; + } + inline void setSecretDeviceKey(String const password) { + _password = password; + } + #endif + + inline TcpIpConnectionHandler * getConnection() { + return _connection; + } + + String getBrokerAddress() { + return _brokerAddress; + } + uint16_t getBrokerPort() { + return _brokerPort; + } + + // Clean up existing Mqtt connection, create a new one and initialize it + int reconnect(Client& /* net */); + + protected: + friend class CloudSerialClass; + // Used to initialize MQTTClient + void mqttClientBegin(); + // Function in charge of perform MQTT reconnection, basing on class parameters(retries,and timeout) + bool mqttReconnect(int const maxRetries, int const timeout); + // Used to retrieve last values from _shadowTopicIn + int writeStdout(const byte data[], int length); + int writeProperties(const byte data[], int length); + int writeShadowOut(const byte data[], int length); + + void requestLastValue(); + + private: + TcpIpConnectionHandler * _connection; + String _brokerAddress; + uint16_t _brokerPort; + + #ifdef BOARD_HAS_ECCX08 + BearSSLClient* _sslClient; + #elif defined(BOARD_ESP) + WiFiClientSecure* _sslClient; + String _password; + #endif + + MqttClient* _mqttClient; + + // Class attribute to define MTTQ topics 2 for stdIn/out and 2 for data, in order to avoid getting previous pupblished payload + String _stdinTopic; + String _stdoutTopic; + String _shadowTopicOut; + String _shadowTopicIn; + String _dataTopicOut; + String _dataTopicIn; + String _otaTopic; + Client* _net; + + static void onMessage(int length); + + void handleMessage(int length); + + void sendPropertiesToCloud(); +}; + +extern ArduinoIoTCloudTCP ArduinoCloud; + + +#endif \ No newline at end of file diff --git a/src/ArduinoIoTCloud_Defines.h b/src/ArduinoIoTCloud_Defines.h index 7b99f11b5..da6775cca 100644 --- a/src/ArduinoIoTCloud_Defines.h +++ b/src/ArduinoIoTCloud_Defines.h @@ -22,10 +22,16 @@ defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_NANO_33_IOT) || \ defined(ARDUINO_SAMD_MKRNB1500) #define BOARD_HAS_ECCX08 + #define HAS_TCP +#endif + +#if defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) + #define HAS_LORA #endif #if defined(ARDUINO_ESP8266_ESP12) || defined(ARDUINO_ARCH_ESP32) || defined(ESP8266) #define BOARD_ESP + #define HAS_TCP #endif #endif /* ARDUINO_IOT_CLOUD_DEFINES_H_ */ diff --git a/src/CloudSerial.cpp b/src/CloudSerial.cpp index 19f380a76..72622adfe 100644 --- a/src/CloudSerial.cpp +++ b/src/CloudSerial.cpp @@ -14,6 +14,8 @@ software without disclosing the source code of your own applications. To purchase a commercial license, send an email to license@arduino.cc. */ +#include "ArduinoIoTCloud_Defines.h" +#ifndef HAS_LORA #include "ArduinoIoTCloud.h" #include "CloudSerial.h" @@ -86,3 +88,5 @@ void CloudSerialClass::appendStdin(const uint8_t *buffer, size_t size) { } CloudSerialClass CloudSerial; + +#endif \ No newline at end of file diff --git a/src/CloudSerial.h b/src/CloudSerial.h index b27811e52..5d53465a9 100644 --- a/src/CloudSerial.h +++ b/src/CloudSerial.h @@ -28,7 +28,9 @@ #define CLOUD_SERIAL_TX_BUFFER_SIZE 64 #define CLOUD_SERIAL_RX_BUFFER_SIZE 512 -class ArduinoIoTCloudClass; + +class ArduinoIoTCloudTCP; + class CloudSerialClass : public Stream { public: @@ -48,7 +50,10 @@ class CloudSerialClass : public Stream { operator bool(); protected: - friend class ArduinoIoTCloudClass; + + friend class ArduinoIoTCloudTCP; + + void appendStdin(const uint8_t *buffer, size_t size);