diff --git a/DeviceBuilder.h b/DeviceBuilder.h new file mode 100644 index 0000000..8fe7958 --- /dev/null +++ b/DeviceBuilder.h @@ -0,0 +1,34 @@ +#ifndef _DEVICE_BUILDER_H_ +#define _DEVICE_BUILDER_H_ + +//#define _PROTOCOL_ONLY_DEFINITIONS_ +#include "UbiDevice.h" + + +//TODO: refactor such that there is no need to define "getDeviceInstance" method for each device +// The only possible way to avoid having to define "getDeviceInstance" method for each device +// is to use c++20 Concepts which allow to define a template based upon structural typing +// however it is not yet supported by Arduino IDE compiler natively + + +#if defined(MKR_1400_GSM) + #include "Devices/UbiArduino_MKR_GSM.h" + UbiDevice *device = UbiArduino_MKR_GSM::getDeviceInstance(); +#elif defined(ESP32_CAM) + #include "Devices/UbiESP32.h" + UbiDevice *device = UbiESP32::getDeviceInstance(); +#elif defined(_UBI_ESP8266_) + #include "Devices/UbiESP8266.h" + UbiDevice* device = UbiESP8266::getDeviceInstance(); +#else + +#define _ONLY_DECLARATIONS_ + + +//ifdefined endif +#endif + + + +//Guard header endif +#endif \ No newline at end of file diff --git a/Devices/UbiArduino_MKR_GSM.h b/Devices/UbiArduino_MKR_GSM.h new file mode 100644 index 0000000..913a789 --- /dev/null +++ b/Devices/UbiArduino_MKR_GSM.h @@ -0,0 +1,422 @@ +#ifndef _UBI_ARDUINO_MKR_GSM_H_ +#define _UBI_ARDUINO_MKR_GSM_H_ + +#include "MKRGSM.h" +#include "../UbiDevice.h" + +class UbiArduino_MKR_GSM : public UbiDevice +{ +public: + static UbiDevice *getDeviceInstance(); + ~UbiArduino_MKR_GSM(); + + bool connect(); + bool connected(); + bool reconnect(); + bool sendHttp(const char *device_label, const char *deviceName, const char *payload); + bool sendTcp(const char *device_label, const char *deviceName, const char *payload); + bool sendUdp(const char *device_label, const char *deviceName, const char *payload); + bool serverConnected(); + double getHttp(const char *device_label, const char *variable_label); + double getTcp(const char *device_label, const char *variable_label); + void getUniqueID(char *ID); + void init(); + + friend class UbiUtils; + +protected: + + char *_APN; + char *_login; + char *_password; + char *_pin; + uint32_t _maxConnectionRetries{10}; + + GPRS _gprs; + GSM _gsm; + GSMClient _client; + GSMModem _modem; + GSMUDP _udp; + +private: + UbiArduino_MKR_GSM(); +}; + +#ifndef _MKR_GSM_ONLY_DECLARATIONS_ + +void UbiArduino_MKR_GSM::init() +{ + +} + +UbiArduino_MKR_GSM::UbiArduino_MKR_GSM() +{ + _userAgent = const_cast(MKRGSM_USER_AGENT); +} + +UbiArduino_MKR_GSM::~UbiArduino_MKR_GSM() +{ + delete UbiDevice::_device; +} + +UbiDevice *UbiArduino_MKR_GSM::getDeviceInstance() +{ + if (UbiDevice::_device == nullptr) + { + UbiDevice::_device = static_cast(new UbiArduino_MKR_GSM); + } + return UbiDevice::_device; +} + +bool UbiArduino_MKR_GSM::connect() +{ + bool Connected{false}; + uint32_t reconnectionAttempt = 0; + _APN = _credentials[0]; + _login = _credentials[1]; + _password = _credentials[2]; + _pin = _credentials[3]; + + while (!Connected && reconnectionAttempt < _maxConnectionRetries) + { + if (_debug) + { + Serial.print("Connecting to URL: "); + Serial.println(_host); + Serial.print("On port: "); + Serial.println(_port); + Serial.print("Attempt #: "); + Serial.print(reconnectionAttempt + 1); + Serial.println(""); + } + + if (_gsm.begin(_pin) == GSM_READY) + { + if (_debug) + { + Serial.println("[1/3] SIM card ready!"); + Serial.println("Attaching to GPRS network"); + } + if (_gprs.attachGPRS(_APN, _login, _password) == GPRS_READY) + { + if (_debug) + Serial.println("[2/3] GPRS ready!"); + if (_client.connect(_host, _port) == true) + { + if (_debug) + Serial.println("[3/3] Ubidots connection ready!"); + Connected = true; + } + } + } + reconnectionAttempt++; + } + //strcmp is equal to 0 when both strings are identical + //if module is connected and uniqueID is "undefined" call the getUniqueID method + //this is to avoid calling the method again the the uniqueID has already been set + if (Connected && !strcmp(_uniqueID, "undefined")) + this->getUniqueID(this->_uniqueID); + + return Connected; +} +bool UbiArduino_MKR_GSM::reconnect() +{ + return connect(); +} +bool UbiArduino_MKR_GSM::serverConnected() +{ + return false; +} + +bool UbiArduino_MKR_GSM::connected() +{ + return _client.connected(); +} + +bool UbiArduino_MKR_GSM::sendHttp(const char *deviceLabel, const char *deviceName, const char *payload) +{ + bool retVal{false}; + if (_client.connected()) + { + if (_debug) + { + Serial.print("sending through http to device with label: "); + Serial.println(deviceLabel); + } + + _client.print(F("POST /api/v1.6/devices/")); + _client.print(deviceLabel); + _client.println(F(" HTTP/1.1")); + _client.print(F("Host: ")); + _client.println(_host); + _client.println(F("User-Agent: Soracom/1.0")); + _client.println(F("Content-type: application/json")); + _client.print(F("X-Auth-Token: ")); + _client.println(F(_token)); + + _client.print(F("Content-Length: ")); + _client.println(strlen(payload)); + + _client.println(); + _client.println(payload); + + while (!_client.available()) + ; + + if (_client.ready() == 0) + { + Serial.print(F(".")); + delay(50); + } + if (_debug) + { + while (_client.available()) + { + char c = _client.read(); + Serial.print(c); + } + } + Serial.println("Data sent succesfully to: "); + Serial.println(_host); + retVal = true; + } + else + { + if (_debug) + Serial.println("Not connected, trying to reconnect..."); + if (_client.connect(_host, _port)) + { + if (_debug) + { + Serial.println("reconnect succesful"); + Serial.println("Sending data: "); + } + retVal = sendHttp(deviceLabel, deviceName, payload); + } + else + { + if (_debug) + Serial.println("reconnect failed"); + retVal = false; + } + } + return retVal; +} +bool UbiArduino_MKR_GSM::sendTcp(const char *deviceLabel, const char *deviceName, const char *payload) +{ + if (!_client.connected()) + { + if (_debug) + { + Serial.println("Server not connected!!!"); + Serial.print("Trying to reconnect to host: "); + Serial.println(_host); + Serial.print(F("On Port: ")); + Serial.println(_port); + } + if (!reconnect()) + { + if (_debug) + Serial.println("Reconnection failed"); + return false; + } + } + _client.print(""); + _client.print(payload); + + if (!UbiUtils::waitServerAnswer(_client, _debug)) + { + if (_debug) + { + Serial.println("[ERROR] Could not read server's response"); + } + _client.flush(); + _client.stop(); + return false; + } + + /* Parses the host answer, returns true if it is 'Ok' */ + char *response = (char *)malloc(sizeof(char) * 100); + float value = UbiUtils::parseTCPAnswer("POST", response, _client, _debug); + free(response); + if (value != ERROR_VALUE) + { + _client.flush(); + _client.stop(); + return true; + } + _client.flush(); + _client.stop(); + return false; +} +bool UbiArduino_MKR_GSM::sendUdp(const char *deviceLabel, const char *deviceName, const char *payload) +{ + //unsigned int localPort{2390}; + unsigned int localPort{UBIDOTS_TCP_PORT}; + + if (!connected()) + { + if (_debug) + { + Serial.println("Server not connected!!!"); + Serial.print("Trying to reconnect to host: "); + Serial.println(_host); + Serial.print(F("On Port: ")); + Serial.println(_port); + } + if (!reconnect()) + { + if (_debug) + Serial.println("Reconnection failed"); + return false; + } + } + + if (_debug) + Serial.println("\nStarting connection to server..."); + + _udp.begin(localPort); + if (!(_udp.beginPacket(_host, _port) && _udp.write(payload, strlen(payload)) && _udp.endPacket())) + { + if (_debug) + Serial.println("ERROR sending values with UDP"); + _udp.stop(); + return false; + } + _udp.stop(); + return true; +} + +double UbiArduino_MKR_GSM::getTcp(const char *deviceLabel, const char *variableLabel) +{ + if (!connected()) + { + if (_debug) + { + Serial.println("Server not connected!!!"); + Serial.print("Trying to reconnect to host: "); + Serial.println(_host); + Serial.print(F("On Port: ")); + Serial.println(_port); + } + if (!reconnect()) + { + if (_debug) + Serial.println("Reconnection failed"); + return ERROR_VALUE; + } + } + _client.print(_userAgent); + _client.print("|LV|"); + _client.print(_token); + _client.print("|"); + _client.print(deviceLabel); + _client.print(":"); + _client.print(variableLabel); + _client.print("|end"); + + if (_debug) + { + Serial.println("----"); + Serial.println("Payload for request:"); + Serial.print(_userAgent); + Serial.print("|LV|"); + Serial.print(_token); + Serial.print("|"); + Serial.print(deviceLabel); + Serial.print(":"); + Serial.print(variableLabel); + Serial.print("|end"); + Serial.println("\n----"); + } + if (!UbiUtils::waitServerAnswer(_client, _debug)) + { + return ERROR_VALUE; + } + + char *response = (char *)malloc(sizeof(char) * 100); + float value = UbiUtils::parseTCPAnswer("LV", response, _client, _debug); + _client.flush(); + _client.stop(); + return value; +} + +void UbiArduino_MKR_GSM::getUniqueID(char *ID) +{ + if (_debug) + Serial.println("getting IMEI"); + if (strcmp(_uniqueID, "undefined") == 0) + { + _modem.getIMEI().toCharArray(_uniqueID, 16); + } + ID = _uniqueID; + if (_debug) + { + Serial.println("Done! IMEI is: "); + Serial.println(ID); + } +} + +double UbiArduino_MKR_GSM::getHttp(const char *device_label, const char *variable_label) +{ + bool connected = connected(); + if (!connected) + { + Serial.println("not connected, returning an error :"); + return ERROR_VALUE; + } + if (_debug) + { + Serial.println("\nGetting data from: "); + Serial.print(_host); + Serial.print("\n"); + } + uint16_t pathLength = UbiUtils::getPathLength(device_label, variable_label); + char *path = (char *)malloc(sizeof(char) * pathLength + 1); + sprintf(path, "/api/v1.6/devices/%s/%s/lv", device_label, variable_label); + if (_debug) + { + Serial.print(F("\nRequesting to URL: ")); + Serial.println(path); + } + + uint16_t requestLineLength = UbiUtils::getRequestLength(path, _host, _token, _userAgent); + char *message = (char *)malloc(sizeof(char) * requestLineLength + 1); + sprintf(message, + "GET %s HTTP/1.1\r\nHost: %s\r\nX-Auth-Token: " + "%s\r\nUser-Agent: %s\r\nContent-Type: " + "application/json\r\nConnection: close\r\n\r\n", + path, _host, _token, _userAgent); + + if (_debug) + { + Serial.println(F("Request sent")); + Serial.println(message); + } + _client.print(message); + Serial.println(message); + + while (_client.connected()) + { + if (_client.available()) + { + String line = _client.readStringUntil('\n'); + if (line == "\r") + { + break; + } + } + } + + free(message); + free(path); + double value = UbiUtils::parseServerAnswer(_client, _debug); + + _client.flush(); + _client.stop(); + return value; +} + +#endif + +#endif \ No newline at end of file diff --git a/Devices/UbiESP32.h b/Devices/UbiESP32.h new file mode 100644 index 0000000..e5ea892 --- /dev/null +++ b/Devices/UbiESP32.h @@ -0,0 +1,135 @@ +#ifndef _UBI_ESP32_H_ +#define _UBI_ESP32_H_ + +#include "../UbiDevice.h" +#include "WiFi.h" +//#include + +class UbiESP32 : public UbiDevice +{ + + +public: + + static UbiDevice *getDeviceInstance(); + ~UbiESP32(); + + + bool connect(); + bool connected(); + bool reconnect(); + bool sendHttp(const char *device_label, const char *deviceName, const char *payload); + bool sendTcp(const char *device_label, const char *deviceName, const char *payload); + bool sendUdp(const char *device_label, const char *deviceName, const char *payload); + bool serverConnected(); + double getHttp(const char *device_label, const char *variable_label); + double getTcp(const char *device_label, const char *variable_label); + void getUniqueID(char *ID); + void init(); + + friend class UbiUtils; + +protected: + + char* _ssid; + char* _password; + uint32_t _maxConnectionRetries{10}; + WiFiClient _client; + +private: + UbiESP32(); + +}; + +#ifndef _ONLY_DECLARATIONS_ + +void UbiESP32::init() +{ + +} + +UbiESP32::UbiESP32(/* args */) +{ + _userAgent = const_cast(ESP32_USER_AGENT); +} + +UbiESP32::~UbiESP32() +{ + delete UbiDevice::_device; +} + + +UbiDevice *UbiESP32::getDeviceInstance() +{ + if(UbiDevice::_device == nullptr) + { + UbiDevice::_device = static_cast(new UbiESP32); + } + return UbiDevice::_device; +} + +bool UbiESP32::connect() +{ + bool connected{false}; + uint32_t reconnectionAttempt{0}; + _ssid = _credentials[0]; + _password = _credentials[1]; + + if(_debug) + { + Serial.print("\nconnecting esp32 cam to local WiFi: "); + Serial.println(_ssid); + } + WiFi.begin(_ssid, _password); + + return connected; +} + +bool UbiESP32::serverConnected() +{ + return false; +} + +bool UbiESP32::connected() +{ + return _client->connected(); +} + +bool UbiESP32::reconnect() +{ + return connect(); +} + + +bool UbiESP32::sendHttp(const char *device_label, const char *deviceName, const char *payload) +{ + + return 0; +} +bool UbiESP32::sendTcp(const char *device_label, const char *deviceName, const char *payload) +{ + + return 0; +} +bool UbiESP32::sendUdp(const char *device_label, const char *deviceName, const char *payload) +{ + + return 0; +} +double UbiESP32::getHttp(const char *device_label, const char *variable_label) +{ + + return 0; +} +double UbiESP32::getTcp(const char *device_label, const char *variable_label) +{ + return 0; +} +void UbiESP32::getUniqueID(char *ID) +{ + +} + +#endif + +#endif diff --git a/Devices/UbiESP8266.h b/Devices/UbiESP8266.h new file mode 100644 index 0000000..48148a0 --- /dev/null +++ b/Devices/UbiESP8266.h @@ -0,0 +1,499 @@ +#ifndef __UBIESP8266_H__ +#define __UBIESP8266_H__ + +#include +#include +#include "../UbiDevice.h" + +class UbiESP8266 : public UbiDevice +{ + +public: + static UbiDevice *getDeviceInstance(); + ~UbiESP8266(); + + bool connect(); + bool reconnect(); + bool connected(); + bool sendHttp(const char *device_label, const char *deviceName, const char *payload); + bool sendTcp(const char *device_label, const char *deviceName, const char *payload); + bool sendUdp(const char *device_label, const char *deviceName, const char *payload); + bool serverConnected(); + double getHttp(const char *device_label, const char *variable_label); + double getTcp(const char *device_label, const char *variable_label); + void getUniqueID(char *ID); + void init(); + + friend class UbiUtils; + +protected: + char *_ssid; + char *_password; + uint32_t _maxConnectionRetries{10}; + WiFiClientSecure _secureClient; + WiFiClient _nonSecureClient; + WiFiClient* _client; + WiFiUDP _udpClient; + Session _session; + X509List _certs; + int _timeout{5000}; + unsigned long _timerToSync{millis()}; + +private: + UbiESP8266(); + static bool syncronizeTime(const bool& debug); +}; + +#ifndef _ONLY_DECLARATIONS_ + +void UbiESP8266::init() +{ + if(_protocol == UBI_HTTPS || _protocol == UBI_TCPS) + { + syncronizeTime(_debug); + _secureClient.setSession(&_session); + UbiUtils::loadCerts(_certs, UBI_CA_CERT_1, UBI_CA_CERT_LEN_1, UBI_CA_CERT_2, UBI_CA_CERT_LEN_2, _debug); + _secureClient.setTrustAnchors(&_certs); + _client = &_secureClient; + } + else + { + _client = &_nonSecureClient; + } +} + +UbiESP8266::UbiESP8266(/* args */) +{ + _userAgent = const_cast(ESP8266_USER_AGENT); +} + +UbiESP8266::~UbiESP8266() +{ + delete UbiDevice::_device; +} + +UbiDevice *UbiESP8266::getDeviceInstance() +{ + if (UbiDevice::_device == nullptr) + { + UbiDevice::_device = static_cast(new UbiESP8266); + } + return UbiDevice::_device; +} + +bool UbiESP8266::connect() +{ + bool connected{false}; + uint32_t reconnectionAttempt{0}; + _ssid = _credentials[0]; + _password = _credentials[1]; + + if (_debug) + { + Serial.print("\nconnecting esp8266 to local WiFi: "); + Serial.println(_ssid); + } + WiFi.begin(_ssid, _password); + while (WiFi.status() != WL_CONNECTED && reconnectionAttempt < _maxConnectionRetries) + { + delay(500); + Serial.print("."); + reconnectionAttempt++; + } + if (WiFi.status() == WL_NO_SSID_AVAIL) + { + Serial.println("Your network SSID cannot be reached"); + return false; + } + if (WiFi.status() == WL_CONNECT_FAILED) + { + Serial.println("Network password incorrect"); + return false; + } + WiFi.setAutoReconnect(true); + Serial.println(F("WiFi connected")); + Serial.println(F("IP address: ")); + Serial.println(WiFi.localIP()); + return true; +} + +bool UbiESP8266::serverConnected() +{ + return false; +} +bool UbiESP8266::connected() +{ + return _client->connected(); +} + +bool UbiESP8266::reconnect() +{ + uint8_t attempts = 0; + while (!_client->connected() && attempts < _maxConnectionRetries) + { + if (_debug) + { + Serial.print(F("Trying to connect to ")); + Serial.print(_host); + Serial.print(F(" , attempt number: ")); + Serial.println(attempts); + } + if(_client->connect(_host, _port)) + { + if (_debug) + Serial.println(F("Reconnect successfull")); + return true; + } + attempts += 1; + delay(1000); + } + return false; +} + +bool UbiESP8266::sendHttp(const char *device_label, const char *deviceName, const char *payload) +{ + if(_protocol == UBI_HTTPS) + if (!UbiUtils::preConnectionChecks(_certs, _timerToSync, syncronizeTime, _debug)) + return false; + + if (!_client->connected()) + { + if (_debug) + { + Serial.println("Server not connected!!!"); + Serial.print("Trying to reconnect to host: "); + Serial.println(_host); + Serial.print(F("On Port: ")); + Serial.println(_port); + } + if (!reconnect()) + { + if (_debug) + Serial.println("Reconnection failed"); + return false; + } + } + + bool result{false}; + int contentLength{strlen(payload)}; + + _client->print(F("POST /api/v1.6/devices/")); + _client->print(device_label); + _client->print(F(" HTTP/1.1\r\n")); + _client->print(F("Host: ")); + _client->print(_host); + _client->print(F("\r\n")); + _client->print(F("User-Agent: ")); + _client->print(_userAgent); + _client->print(F("\r\n")); + _client->print(F("X-Auth-Token: ")); + _client->print(_token); + _client->print(F("\r\n")); + _client->print(F("Connection: close\r\n")); + _client->print(F("Content-Type: application/json\r\n")); + _client->print(F("Content-Length: ")); + _client->print(contentLength); + _client->print(F("\r\n\r\n")); + _client->print(payload); + _client->print(F("\r\n")); + _client->flush(); + + if (UbiUtils::waitServerAnswer(*_client, _debug)) + { + if (_debug) + { + Serial.println(F("\nUbidots' Server response:\n")); + while (_client->available()) + { + char c = _client->read(); + Serial.print(c); + } + } + result = true; + } + else + { + if (_debug) + { + Serial.println(F("Could not read server's response")); + } + } + _client->stop(); + return result; +} + +bool UbiESP8266::sendTcp(const char *device_label, const char *deviceName, const char *payload) +{ + if(_protocol == UBI_TCPS) + if (!UbiUtils::preConnectionChecks(_certs, _timerToSync, syncronizeTime, _debug)) + return false; + + if (!_client->connected()) + { + if (_debug) + { + Serial.println("Server not connected!!!"); + Serial.print("Trying to reconnect to host: "); + Serial.println(_host); + Serial.print(F("On Port: ")); + Serial.println(_port); + } + if (!reconnect()) + { + if (_debug) + Serial.println("Reconnection failed"); + return false; + } + } + _client->print(payload); + + if (!UbiUtils::waitServerAnswer(*_client, _debug)) + { + if (_debug) + { + Serial.println("[ERROR] Could not read server's response"); + } + _client->flush(); + _client->stop(); + return false; + } + + /* Parses the host answer, returns true if it is 'Ok' */ + char *response = (char *)malloc(sizeof(char) * 100); + float value = UbiUtils::parseTCPAnswer("POST", response, *_client, _debug); + free(response); + if (value != ERROR_VALUE) + { + _client->flush(); + _client->stop(); + return true; + } + _client->flush(); + _client->stop(); + return false; +} + +bool UbiESP8266::sendUdp(const char *device_label, const char *deviceName, const char *payload) +{ + if (!connected()) + { + if (_debug) + { + Serial.println("Server not connected!!!"); + Serial.print("Trying to reconnect to host: "); + Serial.println(_host); + Serial.print(F("On Port: ")); + Serial.println(_port); + } + if (!reconnect()) + { + if (_debug) + Serial.println("Reconnection failed"); + return false; + } + } + + if (_debug) + Serial.println("\nStarting connection to server..."); + + _udpClient.begin(UBIDOTS_TCP_PORT); + if (!(_udpClient.beginPacket(_host, _port) && _udpClient.write(payload, strlen(payload)) && _udpClient.endPacket())) + { + if (_debug) + Serial.println("ERROR sending values with UDP"); + _udpClient.stop(); + return false; + } + _udpClient.stop(); + return true; +} +double UbiESP8266::getHttp(const char *device_label, const char *variable_label) +{ + if(_protocol == UBI_HTTPS) + if (!UbiUtils::preConnectionChecks(_certs, _timerToSync, syncronizeTime, _debug)) + return ERROR_VALUE; + + if(!connected()) + { + if(!reconnect()) + { + if(_debug) + Serial.println("Failed to reconnect. Returning ERROR_VALUE"); + return ERROR_VALUE; + } + } + + if (_debug) + { + Serial.println("\nGetting data from: "); + Serial.print(_host); + Serial.print("\n"); + } + + uint16_t pathLength = UbiUtils::getPathLength(device_label, variable_label); + char *path = (char *)malloc(sizeof(char) * pathLength + 1); + sprintf(path, "/api/v1.6/devices/%s/%s/lv", device_label, variable_label); + if (_debug) + { + Serial.print(F("\nRequesting to URL: ")); + Serial.println(path); + } + + uint16_t requestLineLength = UbiUtils::getRequestLength(path, _host, _token, _userAgent); + char *message = (char *)malloc(sizeof(char) * requestLineLength + 1); + sprintf(message, + "GET %s HTTP/1.1\r\nHost: %s\r\nX-Auth-Token: " + "%s\r\nUser-Agent: %s\r\nContent-Type: " + "application/json\r\nConnection: close\r\n\r\n", + path, _host, _token, _userAgent); + + if (_debug) + { + Serial.println(F("Request sent")); + Serial.println(message); + } + + _client->print(message); + Serial.println(message); + + while (_client->connected()) + { + if (_client->available()) + { + String line = _client->readStringUntil('\n'); + if (line == "\r") + { + break; + } + } + } + + free(message); + free(path); + double value = UbiUtils::parseServerAnswer(*_client, _debug); + + _client->flush(); + _client->stop(); + return value; +} +double UbiESP8266::getTcp(const char *deviceLabel, const char *variableLabel) +{ + if(_protocol == UBI_TCPS) + if (!UbiUtils::preConnectionChecks(_certs, _timerToSync, syncronizeTime, _debug)) + return ERROR_VALUE; + + if (!connected()) + { + if (_debug) + { + Serial.println("Server not connected!!!"); + Serial.print("Trying to reconnect to host: "); + Serial.println(_host); + Serial.print(F("On Port: ")); + Serial.println(_port); + } + if (!reconnect()) + { + if (_debug) + Serial.println("Reconnection failed"); + return ERROR_VALUE; + } + } + + _client->print(_userAgent); + _client->print("|LV|"); + _client->print(_token); + _client->print("|"); + _client->print(deviceLabel); + _client->print(":"); + _client->print(variableLabel); + _client->print("|end"); + + if (_debug) + { + Serial.println("----"); + Serial.println("Payload for request:"); + Serial.print(_userAgent); + Serial.print("|LV|"); + Serial.print(_token); + Serial.print("|"); + Serial.print(deviceLabel); + Serial.print(":"); + Serial.print(variableLabel); + Serial.print("|end"); + Serial.println("\n----"); + } + if (!UbiUtils::waitServerAnswer(*_client, _debug)) + { + return ERROR_VALUE; + } + + char *response = (char *)malloc(sizeof(char) * 100); + float value = UbiUtils::parseTCPAnswer("LV", response, *_client, _debug); + _client->flush(); + _client->stop(); + free(response); + return value; +} + +void UbiESP8266::getUniqueID(char *ID) +{ + if (_debug) + Serial.println("getting MAC"); + if (strcmp(_uniqueID, "undefined") == 0) + { + uint8_t mac[6]; + WiFi.macAddress(mac); + sprintf(_uniqueID, "%.2X%.2X%.2X%.2X%.2X%.2X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } + ID = _uniqueID; + if (_debug) + { + Serial.println("Done! MAC is: "); + Serial.println(ID); + } +} + + +bool UbiESP8266::syncronizeTime(const bool& debug) +{ + if(debug) + { + Serial.print(F("Setting time using SNTP")); + } + configTime(8 * 3600, 0, NTP_SERVER, NIST_SERVER); + time_t now = time(nullptr); + uint8_t attempts = 0; + while (now < 8 * 3600 * 2 && attempts <= 5) + { + if (debug) + { + Serial.print("."); + } + now = time(nullptr); + attempts += 1; + delay(500); + } + + if (attempts > 5) + { + if (debug) + { + Serial.println(F("[ERROR] Could not set time using remote SNTP to verify Cert")); + } + return false; + } + + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + if (debug) + { + Serial.print(F("Current time: ")); + Serial.print(asctime(&timeinfo)); + } + return true; +} + +#endif + +#endif // __UBIESP8266_H__ \ No newline at end of file diff --git a/README.md b/README.md index 4dc3a1e..b7cfa92 100644 --- a/README.md +++ b/README.md @@ -1 +1,120 @@ -# ubidots-global-devices \ No newline at end of file +# Ubidots global library + +This library provides an easy way to connect several different devices to Ubidots by creating a common interface despite all the hardware differences. + +Currently the library is implemented for: + - [Arduino](https://store-usa.arduino.cc/products/arduino-mkr-gsm-1400). + - [ESP8266](https://www.espressif.com/en/products/socs/esp8266) + + +## Requirements + + - [Arduino IDE](https://wiki-content.arduino.cc/en/software), [PlatformIO](https://platformio.org/) or any IDE of your preference + - [Ubidots Global Devices Library](https://github.com/ubidots/ubidots-global-devices) + + ## Setup + +1.Depending if you are using PlatformIO or Arduino IDE, you might need to install the specific library dependencies according to your Development board. For example: + +PlatformIO: if you are using the Arduino MKR GSM 1400 you need to edit the "platformio.ini" file in your project to add the MKRGSM library in the following way: + + lib_deps = arduino-libraries/MKRGSM@^1.5.0 + +Arduino: if you are using the Arduino MKR GSM 1400 you need to install the corresponding Arduino SAMD Boards dependencies from the Boards Manager menu.lib_deps = arduino-libraries/MKRGSM@^ + +2.Download the **Ubidots Global Devices Library** [here](https://github.com/ubidots/ubidots-global-devices). +Add the library to your development environment. This may vary depending on which are you using. For example: + +PlatformIO: unzip the library and add it to your project's **lib** directory + +Arduino IDE: click on **Sketch -> Include Library -> Add .Zip Lirary** then select the Ubidots .ZIP file and click **Accept**. +Close the Arduino IDE and open it again. + +# Documentation + +## Constructor + +### Ubidots + +`Ubidots(const char *token, const IotProtocol& iotProtocol, const char* host)` + +Creates an Ubidots instance with which the user interacts with. + - @token,[Required]. Your Ubidots unique account [TOKEN](http://help.ubidots.com/user-guides/find-your-token-from-your-ubidots-account). + - @iotProtocol, [Optional]. [Options] = [`UBI_HTTP`, `UBI_TCP`, `UBI_UDP`, `UBI_HTTPS`], [Default] = `UBI_HTTP`. The IoT protocol that you will use to send or retrieve data. + - @host, [Optional]. [Options] = [`UBI_INDUSTRIAL`, ``], [Default] = `UBI_INDUSTRIAL`. The server to send data, set `` if you wish to point to another private server given by Ubidots. + + **NOTE:** If you use HTTPS for the devices which support it, the client will implement TLS 2.0 based on the [example for ESP32 secure client](https://github.com/espressif/arduino-esp32/blob/master/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino) by Espressif, to secure your data. Keep in mind that due to the security checks needed, the packet may take a little more time to be sent than without TLS. If you wish to send data insecurely, use UDP or any of the available examples at our [docs](https://ubidots.com/docs/hw/). + +As Ubidots makes its best to secure your data, we do not guarantee any issue, data miss or external sniff coming from the native secure client or bugs in the library. + +## Methods + +`void add(const char *variable_label, float value, char *context, unsigned long dot_timestamp_seconds, unsigned int dot_timestamp_millis)` + +Adds a dot with its related value, context and timestamp to be sent to a certain data source. + +**Important:** The max payload length is 700 bytes, if your payload is greater it won't be properly sent. You can see on your serial console the payload to send if you call the `setDebug(bool debug)` method and pass a true value to it. + +- @variable_label, [Required]. The label of the variable where the dot will be stored. +- @value, [Required]. The value of the dot. +- @context, [Optional]. The dot's context. +- @dot_timestamp_seconds, [Optional]. The dot's timestamp in seconds. +- @dot_timestamp_millis, [Optional]. The dot's timestamp number of milliseconds. If the timestamp's milliseconds values is not set, the seconds will be multplied by 1000. + +`float get(const char* device_label, const char* variable_label)` + +Returns as float the last value of the dot from the variable. + +- @device_label, [Required]. The device label which contains the variable to retrieve values from. +- @variable_label, [Required]. The variable label to retrieve values from. + +`void addContext(char *key_label, char *key_value)` + +Adds to local memory a new key-value context key. The method inputs must be char pointers. The method allows to store up to 10 key-value pairs. + +- @key_label, [Required]. The key context label to store values. +- @key_value, [Required]. The key pair value. + +`void getContext(char *context)` + +Builds the context according to the chosen protocol and stores it in the context char pointer. + +- @context, [Required]. A char pointer where the context will be stored. + +`void setDebug(bool debug)` + +Makes available debug messages through the serial port. + +- @debug, [Required]. Boolean type to turn off/on debug messages. + +`bool send(const char* device_label, const char* device_name);` + +Sends all the data added using the add() method. Returns true if the data was sent. + +- @device_label, [Optional], [Default] = Device's MAC Address. The device label to send data. If not set, the device's MAC address will be used. +- @device_name, [Optional], [Default] = @device_label. The device name otherwise assigned if the device doesn't already exist in your Ubidots account. If not set, the device label parameter will be used. **NOTE**: Device name is only supported through TCP/UDP, if you use another protocol, the device name will be the same as device label. + +`bool connect(const char* ssid, const char* password)` + +Attempts to connect to the cloud using WiFi with the specified credentials. + +- @ssid, [Required]. WiFi SSID to connect to name. +- @password, [Required]. WiFi password credential. + +`bool connect(const char* apn, const char* username, const char* password, const char* pin)` + +Attempts to connect to the cloud using GSM with the specified credentials. + +- @apn, [Required]. Your cellular carrier APN. +- @username, [Required]. Your cellular carrier username. +- @password, [Required]. Your cellular carrier password. +- @pin, [optional]. Your sim Card pin. + +`void setDeviceType(const char* deviceType)` + +Sets a [device type](https://help.ubidots.com/en/articles/2129204-device-types) to be added in your request. This method works only if you set HTTP as iot protocol in your instance constructor. + + +# Examples + +Refer to the [examples](/examples) folder diff --git a/UbiConstants.h b/UbiConstants.h new file mode 100644 index 0000000..f064bac --- /dev/null +++ b/UbiConstants.h @@ -0,0 +1,174 @@ + +/* +Copyright (c) 2013-2018 Ubidots. +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Developed and maintained by Jose Garcia and Cristian Arrieta for IoT Services +Inc +@jotathebest at github: https://github.com/jotathebest +@crisap94 at github: https://github.com/crisap94 +*/ + +#ifndef _UbiConstants_H_ +#define _UbiConstants_H_ + +#include "UbiTypes.h" +#include "stdint.h" + +const char UBIDOTS_INDUSTRIAL_IP[] = "169.55.61.243"; +const char *const MKRGSM_USER_AGENT = "UbidotsArduinoMKR1000/1.0.0"; +const char *const ESP32_USER_AGENT = "UbidotsESP32/1.0.0"; +const char* const ESP8266_USER_AGENT{"UbidotsESP8266/1.0.0"}; +const int UBIDOTS_HTTPS_PORT = 443; +const unsigned int UBIDOTS_HTTP_PORT = 80; +const int UBIDOTS_TCP_PORT = 9012; +const int UBIDOTS_TCPS_PORT = 9812; +const uint8_t MAX_VALUES = 10; +const float ERROR_VALUE = -3.4028235E+8; +const int MAX_BUFFER_SIZE = 700; +static hostUrl UBI_INDUSTRIAL = "industrial.api.ubidots.com"; +const int NUMBER_OF_SUPPORTED_PROTOCOLS = 3; +const int NUMBER_OF_SUPPORTED_DEVICES = 2; +const char* const NTP_SERVER = "pool.ntp.org"; +const char* const NIST_SERVER = "time.nist.gov"; + +const unsigned char UBI_CA_CERT_1[] PROGMEM = { + 0x30, 0x82, 0x05, 0x6b, 0x30, 0x82, 0x03, 0x53, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x11, 0x00, 0x82, 0x10, 0xcf, + 0xb0, 0xd2, 0x40, 0xe3, 0x59, 0x44, 0x63, 0xe0, 0xbb, 0x63, 0x82, 0x8b, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x4f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, 0x65, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x0c, 0x49, 0x53, 0x52, 0x47, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x58, 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x35, 0x30, 0x36, 0x30, 0x34, 0x31, 0x31, 0x30, 0x34, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x33, 0x35, 0x30, 0x36, 0x30, + 0x34, 0x31, 0x31, 0x30, 0x34, 0x33, 0x38, 0x5a, 0x30, 0x4f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, 0x65, 0x73, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0c, 0x49, 0x53, 0x52, 0x47, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x58, 0x31, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, + 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xad, 0xe8, 0x24, 0x73, 0xf4, 0x14, 0x37, 0xf3, 0x9b, 0x9e, 0x2b, + 0x57, 0x28, 0x1c, 0x87, 0xbe, 0xdc, 0xb7, 0xdf, 0x38, 0x90, 0x8c, 0x6e, 0x3c, 0xe6, 0x57, 0xa0, 0x78, 0xf7, 0x75, + 0xc2, 0xa2, 0xfe, 0xf5, 0x6a, 0x6e, 0xf6, 0x00, 0x4f, 0x28, 0xdb, 0xde, 0x68, 0x86, 0x6c, 0x44, 0x93, 0xb6, 0xb1, + 0x63, 0xfd, 0x14, 0x12, 0x6b, 0xbf, 0x1f, 0xd2, 0xea, 0x31, 0x9b, 0x21, 0x7e, 0xd1, 0x33, 0x3c, 0xba, 0x48, 0xf5, + 0xdd, 0x79, 0xdf, 0xb3, 0xb8, 0xff, 0x12, 0xf1, 0x21, 0x9a, 0x4b, 0xc1, 0x8a, 0x86, 0x71, 0x69, 0x4a, 0x66, 0x66, + 0x6c, 0x8f, 0x7e, 0x3c, 0x70, 0xbf, 0xad, 0x29, 0x22, 0x06, 0xf3, 0xe4, 0xc0, 0xe6, 0x80, 0xae, 0xe2, 0x4b, 0x8f, + 0xb7, 0x99, 0x7e, 0x94, 0x03, 0x9f, 0xd3, 0x47, 0x97, 0x7c, 0x99, 0x48, 0x23, 0x53, 0xe8, 0x38, 0xae, 0x4f, 0x0a, + 0x6f, 0x83, 0x2e, 0xd1, 0x49, 0x57, 0x8c, 0x80, 0x74, 0xb6, 0xda, 0x2f, 0xd0, 0x38, 0x8d, 0x7b, 0x03, 0x70, 0x21, + 0x1b, 0x75, 0xf2, 0x30, 0x3c, 0xfa, 0x8f, 0xae, 0xdd, 0xda, 0x63, 0xab, 0xeb, 0x16, 0x4f, 0xc2, 0x8e, 0x11, 0x4b, + 0x7e, 0xcf, 0x0b, 0xe8, 0xff, 0xb5, 0x77, 0x2e, 0xf4, 0xb2, 0x7b, 0x4a, 0xe0, 0x4c, 0x12, 0x25, 0x0c, 0x70, 0x8d, + 0x03, 0x29, 0xa0, 0xe1, 0x53, 0x24, 0xec, 0x13, 0xd9, 0xee, 0x19, 0xbf, 0x10, 0xb3, 0x4a, 0x8c, 0x3f, 0x89, 0xa3, + 0x61, 0x51, 0xde, 0xac, 0x87, 0x07, 0x94, 0xf4, 0x63, 0x71, 0xec, 0x2e, 0xe2, 0x6f, 0x5b, 0x98, 0x81, 0xe1, 0x89, + 0x5c, 0x34, 0x79, 0x6c, 0x76, 0xef, 0x3b, 0x90, 0x62, 0x79, 0xe6, 0xdb, 0xa4, 0x9a, 0x2f, 0x26, 0xc5, 0xd0, 0x10, + 0xe1, 0x0e, 0xde, 0xd9, 0x10, 0x8e, 0x16, 0xfb, 0xb7, 0xf7, 0xa8, 0xf7, 0xc7, 0xe5, 0x02, 0x07, 0x98, 0x8f, 0x36, + 0x08, 0x95, 0xe7, 0xe2, 0x37, 0x96, 0x0d, 0x36, 0x75, 0x9e, 0xfb, 0x0e, 0x72, 0xb1, 0x1d, 0x9b, 0xbc, 0x03, 0xf9, + 0x49, 0x05, 0xd8, 0x81, 0xdd, 0x05, 0xb4, 0x2a, 0xd6, 0x41, 0xe9, 0xac, 0x01, 0x76, 0x95, 0x0a, 0x0f, 0xd8, 0xdf, + 0xd5, 0xbd, 0x12, 0x1f, 0x35, 0x2f, 0x28, 0x17, 0x6c, 0xd2, 0x98, 0xc1, 0xa8, 0x09, 0x64, 0x77, 0x6e, 0x47, 0x37, + 0xba, 0xce, 0xac, 0x59, 0x5e, 0x68, 0x9d, 0x7f, 0x72, 0xd6, 0x89, 0xc5, 0x06, 0x41, 0x29, 0x3e, 0x59, 0x3e, 0xdd, + 0x26, 0xf5, 0x24, 0xc9, 0x11, 0xa7, 0x5a, 0xa3, 0x4c, 0x40, 0x1f, 0x46, 0xa1, 0x99, 0xb5, 0xa7, 0x3a, 0x51, 0x6e, + 0x86, 0x3b, 0x9e, 0x7d, 0x72, 0xa7, 0x12, 0x05, 0x78, 0x59, 0xed, 0x3e, 0x51, 0x78, 0x15, 0x0b, 0x03, 0x8f, 0x8d, + 0xd0, 0x2f, 0x05, 0xb2, 0x3e, 0x7b, 0x4a, 0x1c, 0x4b, 0x73, 0x05, 0x12, 0xfc, 0xc6, 0xea, 0xe0, 0x50, 0x13, 0x7c, + 0x43, 0x93, 0x74, 0xb3, 0xca, 0x74, 0xe7, 0x8e, 0x1f, 0x01, 0x08, 0xd0, 0x30, 0xd4, 0x5b, 0x71, 0x36, 0xb4, 0x07, + 0xba, 0xc1, 0x30, 0x30, 0x5c, 0x48, 0xb7, 0x82, 0x3b, 0x98, 0xa6, 0x7d, 0x60, 0x8a, 0xa2, 0xa3, 0x29, 0x82, 0xcc, + 0xba, 0xbd, 0x83, 0x04, 0x1b, 0xa2, 0x83, 0x03, 0x41, 0xa1, 0xd6, 0x05, 0xf1, 0x1b, 0xc2, 0xb6, 0xf0, 0xa8, 0x7c, + 0x86, 0x3b, 0x46, 0xa8, 0x48, 0x2a, 0x88, 0xdc, 0x76, 0x9a, 0x76, 0xbf, 0x1f, 0x6a, 0xa5, 0x3d, 0x19, 0x8f, 0xeb, + 0x38, 0xf3, 0x64, 0xde, 0xc8, 0x2b, 0x0d, 0x0a, 0x28, 0xff, 0xf7, 0xdb, 0xe2, 0x15, 0x42, 0xd4, 0x22, 0xd0, 0x27, + 0x5d, 0xe1, 0x79, 0xfe, 0x18, 0xe7, 0x70, 0x88, 0xad, 0x4e, 0xe6, 0xd9, 0x8b, 0x3a, 0xc6, 0xdd, 0x27, 0x51, 0x6e, + 0xff, 0xbc, 0x64, 0xf5, 0x33, 0x43, 0x4f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x42, 0x30, 0x40, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x79, 0xb4, 0x59, 0xe6, 0x7b, 0xb6, 0xe5, 0xe4, 0x01, 0x73, 0x80, 0x08, 0x88, 0xc8, 0x1a, 0x58, + 0xf6, 0xe9, 0x9b, 0x6e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x02, 0x01, 0x00, 0x55, 0x1f, 0x58, 0xa9, 0xbc, 0xb2, 0xa8, 0x50, 0xd0, 0x0c, 0xb1, 0xd8, 0x1a, 0x69, + 0x20, 0x27, 0x29, 0x08, 0xac, 0x61, 0x75, 0x5c, 0x8a, 0x6e, 0xf8, 0x82, 0xe5, 0x69, 0x2f, 0xd5, 0xf6, 0x56, 0x4b, + 0xb9, 0xb8, 0x73, 0x10, 0x59, 0xd3, 0x21, 0x97, 0x7e, 0xe7, 0x4c, 0x71, 0xfb, 0xb2, 0xd2, 0x60, 0xad, 0x39, 0xa8, + 0x0b, 0xea, 0x17, 0x21, 0x56, 0x85, 0xf1, 0x50, 0x0e, 0x59, 0xeb, 0xce, 0xe0, 0x59, 0xe9, 0xba, 0xc9, 0x15, 0xef, + 0x86, 0x9d, 0x8f, 0x84, 0x80, 0xf6, 0xe4, 0xe9, 0x91, 0x90, 0xdc, 0x17, 0x9b, 0x62, 0x1b, 0x45, 0xf0, 0x66, 0x95, + 0xd2, 0x7c, 0x6f, 0xc2, 0xea, 0x3b, 0xef, 0x1f, 0xcf, 0xcb, 0xd6, 0xae, 0x27, 0xf1, 0xa9, 0xb0, 0xc8, 0xae, 0xfd, + 0x7d, 0x7e, 0x9a, 0xfa, 0x22, 0x04, 0xeb, 0xff, 0xd9, 0x7f, 0xea, 0x91, 0x2b, 0x22, 0xb1, 0x17, 0x0e, 0x8f, 0xf2, + 0x8a, 0x34, 0x5b, 0x58, 0xd8, 0xfc, 0x01, 0xc9, 0x54, 0xb9, 0xb8, 0x26, 0xcc, 0x8a, 0x88, 0x33, 0x89, 0x4c, 0x2d, + 0x84, 0x3c, 0x82, 0xdf, 0xee, 0x96, 0x57, 0x05, 0xba, 0x2c, 0xbb, 0xf7, 0xc4, 0xb7, 0xc7, 0x4e, 0x3b, 0x82, 0xbe, + 0x31, 0xc8, 0x22, 0x73, 0x73, 0x92, 0xd1, 0xc2, 0x80, 0xa4, 0x39, 0x39, 0x10, 0x33, 0x23, 0x82, 0x4c, 0x3c, 0x9f, + 0x86, 0xb2, 0x55, 0x98, 0x1d, 0xbe, 0x29, 0x86, 0x8c, 0x22, 0x9b, 0x9e, 0xe2, 0x6b, 0x3b, 0x57, 0x3a, 0x82, 0x70, + 0x4d, 0xdc, 0x09, 0xc7, 0x89, 0xcb, 0x0a, 0x07, 0x4d, 0x6c, 0xe8, 0x5d, 0x8e, 0xc9, 0xef, 0xce, 0xab, 0xc7, 0xbb, + 0xb5, 0x2b, 0x4e, 0x45, 0xd6, 0x4a, 0xd0, 0x26, 0xcc, 0xe5, 0x72, 0xca, 0x08, 0x6a, 0xa5, 0x95, 0xe3, 0x15, 0xa1, + 0xf7, 0xa4, 0xed, 0xc9, 0x2c, 0x5f, 0xa5, 0xfb, 0xff, 0xac, 0x28, 0x02, 0x2e, 0xbe, 0xd7, 0x7b, 0xbb, 0xe3, 0x71, + 0x7b, 0x90, 0x16, 0xd3, 0x07, 0x5e, 0x46, 0x53, 0x7c, 0x37, 0x07, 0x42, 0x8c, 0xd3, 0xc4, 0x96, 0x9c, 0xd5, 0x99, + 0xb5, 0x2a, 0xe0, 0x95, 0x1a, 0x80, 0x48, 0xae, 0x4c, 0x39, 0x07, 0xce, 0xcc, 0x47, 0xa4, 0x52, 0x95, 0x2b, 0xba, + 0xb8, 0xfb, 0xad, 0xd2, 0x33, 0x53, 0x7d, 0xe5, 0x1d, 0x4d, 0x6d, 0xd5, 0xa1, 0xb1, 0xc7, 0x42, 0x6f, 0xe6, 0x40, + 0x27, 0x35, 0x5c, 0xa3, 0x28, 0xb7, 0x07, 0x8d, 0xe7, 0x8d, 0x33, 0x90, 0xe7, 0x23, 0x9f, 0xfb, 0x50, 0x9c, 0x79, + 0x6c, 0x46, 0xd5, 0xb4, 0x15, 0xb3, 0x96, 0x6e, 0x7e, 0x9b, 0x0c, 0x96, 0x3a, 0xb8, 0x52, 0x2d, 0x3f, 0xd6, 0x5b, + 0xe1, 0xfb, 0x08, 0xc2, 0x84, 0xfe, 0x24, 0xa8, 0xa3, 0x89, 0xda, 0xac, 0x6a, 0xe1, 0x18, 0x2a, 0xb1, 0xa8, 0x43, + 0x61, 0x5b, 0xd3, 0x1f, 0xdc, 0x3b, 0x8d, 0x76, 0xf2, 0x2d, 0xe8, 0x8d, 0x75, 0xdf, 0x17, 0x33, 0x6c, 0x3d, 0x53, + 0xfb, 0x7b, 0xcb, 0x41, 0x5f, 0xff, 0xdc, 0xa2, 0xd0, 0x61, 0x38, 0xe1, 0x96, 0xb8, 0xac, 0x5d, 0x8b, 0x37, 0xd7, + 0x75, 0xd5, 0x33, 0xc0, 0x99, 0x11, 0xae, 0x9d, 0x41, 0xc1, 0x72, 0x75, 0x84, 0xbe, 0x02, 0x41, 0x42, 0x5f, 0x67, + 0x24, 0x48, 0x94, 0xd1, 0x9b, 0x27, 0xbe, 0x07, 0x3f, 0xb9, 0xb8, 0x4f, 0x81, 0x74, 0x51, 0xe1, 0x7a, 0xb7, 0xed, + 0x9d, 0x23, 0xe2, 0xbe, 0xe0, 0xd5, 0x28, 0x04, 0x13, 0x3c, 0x31, 0x03, 0x9e, 0xdd, 0x7a, 0x6c, 0x8f, 0xc6, 0x07, + 0x18, 0xc6, 0x7f, 0xde, 0x47, 0x8e, 0x3f, 0x28, 0x9e, 0x04, 0x06, 0xcf, 0xa5, 0x54, 0x34, 0x77, 0xbd, 0xec, 0x89, + 0x9b, 0xe9, 0x17, 0x43, 0xdf, 0x5b, 0xdb, 0x5f, 0xfe, 0x8e, 0x1e, 0x57, 0xa2, 0xcd, 0x40, 0x9d, 0x7e, 0x62, 0x22, + 0xda, 0xde, 0x18, 0x27}; +const unsigned int UBI_CA_CERT_LEN_1 = 1391; + +const unsigned char UBI_CA_CERT_2[] PROGMEM = { + 0x30, 0x82, 0x03, 0x4a, 0x30, 0x82, 0x02, 0x32, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x44, 0xaf, 0xb0, 0x80, + 0xd6, 0xa3, 0x27, 0xba, 0x89, 0x30, 0x39, 0x86, 0x2e, 0xf8, 0x40, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x3f, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x1b, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x58, 0x33, 0x30, 0x1e, 0x17, + 0x0d, 0x30, 0x30, 0x30, 0x39, 0x33, 0x30, 0x32, 0x31, 0x31, 0x32, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x30, + 0x39, 0x33, 0x30, 0x31, 0x34, 0x30, 0x31, 0x31, 0x35, 0x5a, 0x30, 0x3f, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x1b, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x58, 0x33, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdf, 0xaf, 0xe9, 0x97, 0x50, 0x08, + 0x83, 0x57, 0xb4, 0xcc, 0x62, 0x65, 0xf6, 0x90, 0x82, 0xec, 0xc7, 0xd3, 0x2c, 0x6b, 0x30, 0xca, 0x5b, 0xec, 0xd9, + 0xc3, 0x7d, 0xc7, 0x40, 0xc1, 0x18, 0x14, 0x8b, 0xe0, 0xe8, 0x33, 0x76, 0x49, 0x2a, 0xe3, 0x3f, 0x21, 0x49, 0x93, + 0xac, 0x4e, 0x0e, 0xaf, 0x3e, 0x48, 0xcb, 0x65, 0xee, 0xfc, 0xd3, 0x21, 0x0f, 0x65, 0xd2, 0x2a, 0xd9, 0x32, 0x8f, + 0x8c, 0xe5, 0xf7, 0x77, 0xb0, 0x12, 0x7b, 0xb5, 0x95, 0xc0, 0x89, 0xa3, 0xa9, 0xba, 0xed, 0x73, 0x2e, 0x7a, 0x0c, + 0x06, 0x32, 0x83, 0xa2, 0x7e, 0x8a, 0x14, 0x30, 0xcd, 0x11, 0xa0, 0xe1, 0x2a, 0x38, 0xb9, 0x79, 0x0a, 0x31, 0xfd, + 0x50, 0xbd, 0x80, 0x65, 0xdf, 0xb7, 0x51, 0x63, 0x83, 0xc8, 0xe2, 0x88, 0x61, 0xea, 0x4b, 0x61, 0x81, 0xec, 0x52, + 0x6b, 0xb9, 0xa2, 0xe2, 0x4b, 0x1a, 0x28, 0x9f, 0x48, 0xa3, 0x9e, 0x0c, 0xda, 0x09, 0x8e, 0x3e, 0x17, 0x2e, 0x1e, + 0xdd, 0x20, 0xdf, 0x5b, 0xc6, 0x2a, 0x8a, 0xab, 0x2e, 0xbd, 0x70, 0xad, 0xc5, 0x0b, 0x1a, 0x25, 0x90, 0x74, 0x72, + 0xc5, 0x7b, 0x6a, 0xab, 0x34, 0xd6, 0x30, 0x89, 0xff, 0xe5, 0x68, 0x13, 0x7b, 0x54, 0x0b, 0xc8, 0xd6, 0xae, 0xec, + 0x5a, 0x9c, 0x92, 0x1e, 0x3d, 0x64, 0xb3, 0x8c, 0xc6, 0xdf, 0xbf, 0xc9, 0x41, 0x70, 0xec, 0x16, 0x72, 0xd5, 0x26, + 0xec, 0x38, 0x55, 0x39, 0x43, 0xd0, 0xfc, 0xfd, 0x18, 0x5c, 0x40, 0xf1, 0x97, 0xeb, 0xd5, 0x9a, 0x9b, 0x8d, 0x1d, + 0xba, 0xda, 0x25, 0xb9, 0xc6, 0xd8, 0xdf, 0xc1, 0x15, 0x02, 0x3a, 0xab, 0xda, 0x6e, 0xf1, 0x3e, 0x2e, 0xf5, 0x5c, + 0x08, 0x9c, 0x3c, 0xd6, 0x83, 0x69, 0xe4, 0x10, 0x9b, 0x19, 0x2a, 0xb6, 0x29, 0x57, 0xe3, 0xe5, 0x3d, 0x9b, 0x9f, + 0xf0, 0x02, 0x5d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x42, 0x30, 0x40, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc4, + 0xa7, 0xb1, 0xa4, 0x7b, 0x2c, 0x71, 0xfa, 0xdb, 0xe1, 0x4b, 0x90, 0x75, 0xff, 0xc4, 0x15, 0x60, 0x85, 0x89, 0x10, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0xa3, 0x1a, 0x2c, 0x9b, 0x17, 0x00, 0x5c, 0xa9, 0x1e, 0xee, 0x28, 0x66, 0x37, 0x3a, 0xbf, 0x83, 0xc7, 0x3f, + 0x4b, 0xc3, 0x09, 0xa0, 0x95, 0x20, 0x5d, 0xe3, 0xd9, 0x59, 0x44, 0xd2, 0x3e, 0x0d, 0x3e, 0xbd, 0x8a, 0x4b, 0xa0, + 0x74, 0x1f, 0xce, 0x10, 0x82, 0x9c, 0x74, 0x1a, 0x1d, 0x7e, 0x98, 0x1a, 0xdd, 0xcb, 0x13, 0x4b, 0xb3, 0x20, 0x44, + 0xe4, 0x91, 0xe9, 0xcc, 0xfc, 0x7d, 0xa5, 0xdb, 0x6a, 0xe5, 0xfe, 0xe6, 0xfd, 0xe0, 0x4e, 0xdd, 0xb7, 0x00, 0x3a, + 0xb5, 0x70, 0x49, 0xaf, 0xf2, 0xe5, 0xeb, 0x02, 0xf1, 0xd1, 0x02, 0x8b, 0x19, 0xcb, 0x94, 0x3a, 0x5e, 0x48, 0xc4, + 0x18, 0x1e, 0x58, 0x19, 0x5f, 0x1e, 0x02, 0x5a, 0xf0, 0x0c, 0xf1, 0xb1, 0xad, 0xa9, 0xdc, 0x59, 0x86, 0x8b, 0x6e, + 0xe9, 0x91, 0xf5, 0x86, 0xca, 0xfa, 0xb9, 0x66, 0x33, 0xaa, 0x59, 0x5b, 0xce, 0xe2, 0xa7, 0x16, 0x73, 0x47, 0xcb, + 0x2b, 0xcc, 0x99, 0xb0, 0x37, 0x48, 0xcf, 0xe3, 0x56, 0x4b, 0xf5, 0xcf, 0x0f, 0x0c, 0x72, 0x32, 0x87, 0xc6, 0xf0, + 0x44, 0xbb, 0x53, 0x72, 0x6d, 0x43, 0xf5, 0x26, 0x48, 0x9a, 0x52, 0x67, 0xb7, 0x58, 0xab, 0xfe, 0x67, 0x76, 0x71, + 0x78, 0xdb, 0x0d, 0xa2, 0x56, 0x14, 0x13, 0x39, 0x24, 0x31, 0x85, 0xa2, 0xa8, 0x02, 0x5a, 0x30, 0x47, 0xe1, 0xdd, + 0x50, 0x07, 0xbc, 0x02, 0x09, 0x90, 0x00, 0xeb, 0x64, 0x63, 0x60, 0x9b, 0x16, 0xbc, 0x88, 0xc9, 0x12, 0xe6, 0xd2, + 0x7d, 0x91, 0x8b, 0xf9, 0x3d, 0x32, 0x8d, 0x65, 0xb4, 0xe9, 0x7c, 0xb1, 0x57, 0x76, 0xea, 0xc5, 0xb6, 0x28, 0x39, + 0xbf, 0x15, 0x65, 0x1c, 0xc8, 0xf6, 0x77, 0x96, 0x6a, 0x0a, 0x8d, 0x77, 0x0b, 0xd8, 0x91, 0x0b, 0x04, 0x8e, 0x07, + 0xdb, 0x29, 0xb6, 0x0a, 0xee, 0x9d, 0x82, 0x35, 0x35, 0x10}; +const unsigned int UBI_CA_CERT_LEN_2 = 846; + +#endif diff --git a/UbiDevice.cpp b/UbiDevice.cpp new file mode 100644 index 0000000..d85fbe6 --- /dev/null +++ b/UbiDevice.cpp @@ -0,0 +1,92 @@ +#include "UbiDevice.h" + + + + +UbiDevice::UbiDevice() +{ + +} + +UbiDevice::~UbiDevice() +{ + delete _protocolHandler; + delete _device; +} + + +void UbiDevice::setSettings(const UbiDeviceSettings &arg) +{ + this->_credentials = arg._credentials; + this->_host = arg._host; + this->_token = arg._token; + this->_port = arg._port; + this->_protocol = arg._protocol; +} + +void UbiDevice::buildProtocolHandlerInstance(const IotProtocol& protocol) +{ + if(protocol == UBI_HTTP || protocol == UBI_HTTPS) + this->_protocolHandler = reinterpret_cast(new ProtocolHandler(protocol)); + else if(protocol == UBI_TCP || protocol == UBI_TCPS) + this->_protocolHandler = reinterpret_cast(new ProtocolHandler(protocol)); + else if(protocol == UBI_UDP) + this->_protocolHandler = reinterpret_cast(new ProtocolHandler(protocol)); + +} + +void UbiDevice::setDebug(const bool &debug) { _debug = debug; } + + + + + + + + + + + +void UbiProtocolHandler::setDebug(const bool& debug) +{ + this->_debug = debug; + device->setDebug(debug); +} + +bool UbiProtocolHandler::connect() +{ + return device->connect(); +} + +bool UbiProtocolHandler::connected() +{ + return device->connected(); +} +bool UbiProtocolHandler::reconnect() +{ + return device->reconnect(); +} + +bool UbiProtocolHandler::serverConnected() +{ + return device->serverConnected(); +} + + +void UbiProtocolHandler::addContext(char* key_label, char* key_value) +{ + (_context + _currentContext)->key_label = key_label; + (_context + _currentContext)->key_value = key_value; + _currentContext++; + if (_currentContext >= MAX_VALUES) + { + Serial.println(F("You are adding more than the maximum of consecutive " + "key-values pairs")); + _currentContext = MAX_VALUES; + } +} + +void UbiProtocolHandler::init() +{ + device->init(); +} \ No newline at end of file diff --git a/UbiDevice.h b/UbiDevice.h new file mode 100644 index 0000000..bf88eea --- /dev/null +++ b/UbiDevice.h @@ -0,0 +1,182 @@ +#ifndef _UBI_DEVICE_H_ +#define _UBI_DEVICE_H_ + +#include "Arduino.h" +#include "UbiTypes.h" +#include "UbiConstants.h" +#include "UbiProtocolHandler.h" + +class UbiDevice; +extern UbiDevice *device; + +class UbiDevice +{ +public: + virtual ~UbiDevice(); + void setDebug(const bool &debug); + void setSettings(const UbiDeviceSettings &arg); + void buildProtocolHandlerInstance(const IotProtocol &protocol); + virtual void getUniqueID(char *ID) = 0; + + // Assign the user agent as undefined by default. This forces to instantiate + userAgent _userAgent{"undefined"}; + char _uniqueID[18]{"undefined"}; + + UbiProtocolHandler *_protocolHandler{nullptr}; + +protected: + // Constructor needs to be protected in order for the derived classs to be able to call the Base class constructor + explicit UbiDevice(); + /*static pointer to store this class's singleton instance*/ + /*needs to be protected in order for the derived class's "constructor" to be able to invoke this*/ + inline static UbiDevice *_device{nullptr}; + + bool _debug{false}; + + // All of the derived "devices" class need to use these members. So they need to be declared as protected + ubiToken _token; + hostUrl _host; + portNumber _port; + IotProtocol _protocol; + char **_credentials; + +private: + virtual bool connect() = 0; + virtual bool reconnect() = 0; + virtual bool serverConnected() = 0; + virtual bool connected() = 0; + virtual void init() = 0; + virtual bool sendHttp(const char *deviceLabel, const char *deviceName, const char *payload) = 0; + virtual bool sendTcp(const char *deviceLabel, const char *deviceName, const char *payload) = 0; + virtual bool sendUdp(const char *deviceLabel, const char *deviceName, const char *payload) = 0; + virtual double getHttp(const char *deviceLabel, const char *variableLabel) = 0; + virtual double getTcp(const char *deviceLabel, const char *variableLabel) = 0; + + friend class ProtocolHandler; + friend class ProtocolHandler; + friend class ProtocolHandler; + friend class UbiProtocolHandler; +}; + +#ifndef _PROTOCOL_ONLY_DEFINITIONS_ + +/* <-------------------------------------------------------------------------------------------> */ +/* <----------------------------------Add functions--------------------------------------------> */ +/* <-------------------------------------------------------------------------------------------> */ + +template +void ProtocolHandler::add(const char *variableLabel, double value, char *context, unsigned long dot_timestamp_seconds, unsigned int dot_timestamp_millis) +{ + _dirty = true; + (_dots + _currentValue)->variable_label = variableLabel; + (_dots + _currentValue)->dot_value = value; + (_dots + _currentValue)->dot_context = context; + (_dots + _currentValue)->dot_timestamp_seconds = dot_timestamp_seconds; + (_dots + _currentValue)->dot_timestamp_millis = dot_timestamp_millis; + _currentValue++; + if (_currentValue > MAX_VALUES) + { + if (_debug) + { + Serial.println(F("You are sending more than the maximum of consecutive variables")); + } + _currentValue = MAX_VALUES; + } +} +/* <-------------------------------------------------------------------------------------------> */ +/* <---------------------------------Send functions--------------------------------------------> */ +/* <-------------------------------------------------------------------------------------------> */ +template +bool ProtocolHandler::send(const char *deviceLabel, const char *deviceName) +{ + char* payload = (char*) malloc(sizeof(char) * MAX_BUFFER_SIZE); + + if constexpr(Protocol == UBI_HTTP || Protocol == UBI_HTTPS) + { + UbiUtils::buildHttpPayload(payload, _currentValue, _dots, _debug); + } + else if constexpr(Protocol == UBI_TCP || Protocol == UBI_TCPS || Protocol == UBI_UDP) + { + UbiUtils::buildTcpPayload(payload, device->_token, deviceLabel, deviceName, device->_userAgent, _currentValue, _dots,_debug); + } + + if (_debug) + Serial.println("Sending data..."); + + bool result{false}; + + if constexpr(Protocol == UBI_HTTP || Protocol == UBI_HTTPS) + { + result = device->sendHttp(deviceLabel, deviceName, payload); + } + else if constexpr(Protocol == UBI_TCP || Protocol == UBI_TCPS) + { + result = device->sendTcp(deviceLabel, deviceName, payload); + } + else if constexpr(Protocol == UBI_UDP) + { + result = device->sendUdp(deviceLabel, deviceName, payload); + } + + free(payload); + if(result) + { + _dirty = false; + _currentValue = 0; + } + _currentValue = 0; + return result; +} + + +/* <-------------------------------------------------------------------------------------------> */ +/* <---------------------------------Get functions--------------------------------------------> */ +/* <-------------------------------------------------------------------------------------------> */ +template +double ProtocolHandler::get(const char *device_label, const char *variable_label) +{ + double value{ERROR_VALUE}; + if constexpr(Protocol == UBI_HTTP || Protocol == UBI_HTTPS) + value = device->getHttp(device_label, variable_label); + if constexpr(Protocol == UBI_TCP || Protocol == UBI_TCPS) + value = device->getTcp(device_label, variable_label); + if constexpr(Protocol == UBI_UDP) + Serial.println("ERROR, data retrieval is only supported using TCP or HTTP protocols"); + return value; +} + +/* <-------------------------------------------------------------------------------------------> */ +/* <---------------------------------GetContext functions--------------------------------------------> */ +/* <-------------------------------------------------------------------------------------------> */ +template +void ProtocolHandler::getContext(char *context_result) +{ + sprintf(context_result, ""); + for (uint8_t i = 0; i < _currentContext; ) + { + if constexpr(Protocol == UBI_TCP || Protocol == UBI_UDP) + sprintf(context_result, "%s%s=%s", context_result, (_context + i)->key_label, (_context + i)->key_value); + else if constexpr(Protocol == UBI_HTTP) + sprintf(context_result, "%s\"%s\":\"%s\"", context_result, (_context + i)->key_label, (_context + i)->key_value); + + i++; + + if (i < _currentContext) + { + if constexpr(Protocol == UBI_TCP || Protocol == UBI_UDP) + sprintf(context_result, "%s$", context_result); + else if constexpr(Protocol == UBI_HTTP) + sprintf(context_result, "%s,", context_result); + } + else + { + sprintf(context_result, "%s", context_result); + _currentContext = 0; + } + } +} + + +#endif + +#endif \ No newline at end of file diff --git a/UbiProtocolHandler.h b/UbiProtocolHandler.h new file mode 100644 index 0000000..6cfc941 --- /dev/null +++ b/UbiProtocolHandler.h @@ -0,0 +1,67 @@ +#ifndef __UBIPROTOCOLHANDLER_H__ +#define __UBIPROTOCOLHANDLER_H__ + +#include "UbiTypes.h" +#include "UbiUtils.h" + +class UbiProtocolHandler +{ + public: + explicit UbiProtocolHandler(const IotProtocol& protocol) { _protocol = protocol; }; + virtual ~UbiProtocolHandler() { delete _dots; }; + + bool connect(); + bool connected(); + bool reconnect(); + bool serverConnected(); + void setDebug(const bool& debug); + void addContext(char* key, char* value); + void init(); + virtual void getContext(char* context_result) = 0; + virtual bool send(const char* deviceLabel, const char* deviceName) = 0; + virtual void add(const char *variable_label, double value, char *context, unsigned long dot_timestamp_seconds, unsigned int dot_timestamp_millis) = 0; + virtual double get(const char *device_label, const char *variable_label) = 0; + + + + protected: + + bool _debug{false}; + IotProtocol _protocol; + bool _dirty{false}; + Value* _dots{(Value *)malloc(MAX_VALUES * sizeof(Value))}; + int8_t _currentValue{0}; + int _connectionTimeout = 5000; + ContextUbi* _context; + int8_t _currentContext = 0; + + private: + +}; + + +template +class ProtocolHandler : public UbiProtocolHandler +{ + public: + explicit ProtocolHandler(const IotProtocol& protocol) : UbiProtocolHandler(protocol) { } + virtual ~ProtocolHandler() { } + + bool send(const char* deviceLabel, const char* deviceName); + void add(const char *variable_label, double value, char *context, unsigned long dot_timestamp_seconds, unsigned int dot_timestamp_millis); + double get(const char *device_label, const char *variable_label); + void getContext(char* context_result); + + protected: + + private: + +}; + + + + + + + +#endif // __UBIPROTOCOLHANDLER_H__ \ No newline at end of file diff --git a/UbiTypes.h b/UbiTypes.h new file mode 100644 index 0000000..f23b76b --- /dev/null +++ b/UbiTypes.h @@ -0,0 +1,61 @@ + +/* +Copyright (c) 2013-2018 Ubidots. +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Developed and maintained by Jose Garcia and Cristian Arrieta for IoT Services +Inc +@jotathebest at github: https://github.com/jotathebest +@crisap94 at github: https://github.com/crisap94 +*/ + +#ifndef _UbiTypes_H_ +#define _UbiTypes_H_ + +typedef struct Value { + const char *variable_label; + char *dot_context; + float dot_value; + unsigned long dot_timestamp_seconds; + unsigned int dot_timestamp_millis; +} Value; + +typedef struct ContextUbi { + char *key_label; + char *key_value; +} ContextUbi; + + +typedef char *ubiToken; +typedef char *hostUrl; +typedef char *userAgent; +typedef unsigned int portNumber; + +typedef enum { UBI_HTTP, UBI_TCP, UBI_TCPS, UBI_UDP, UBI_HTTPS } IotProtocol; +typedef enum {UBI_WIFI, UBI_ETHERNET, UBI_GSM} IotClient; +typedef enum {UBI_ESP32, UBI_ARDUINO_MKR_1400_GSM, UBI_ESP8266} supportedDevices; + +typedef struct UbiDeviceSettings +{ + char *_host; + char *_token; + char **_credentials; + unsigned int _port; + IotProtocol _protocol; +}; + +#endif diff --git a/UbiUtils.cpp b/UbiUtils.cpp new file mode 100644 index 0000000..b6c7d4e --- /dev/null +++ b/UbiUtils.cpp @@ -0,0 +1,195 @@ +#include "UbiUtils.h" + +int UbiUtils::hexadecimalToDecimal(char hexVal[]) +{ + int len = strlen(hexVal); + + // Initializing base value to 1, i.e 16^0 + int base = 1; + + int dec_val = 0; + + // Extracting characters as digits from last character + for (int i = len - 1; i >= 0; i--) + { + // if character lies in '0'-'9', converting + // it to integral 0-9 by subtracting 48 from + // ASCII value. + if (hexVal[i] >= '0' && hexVal[i] <= '9') + { + dec_val += (hexVal[i] - 48) * base; + // incrementing base by power + base = base * 16; + } + + // if character lies in 'A'-'F' , converting + // it to integral 10 - 15 by subtracting 55 + // from ASCII value + else if (hexVal[i] >= 'A' && hexVal[i] <= 'F') + { + dec_val += (hexVal[i] - 55) * base; + // incrementing base by power + base = base * 16; + } + } + return dec_val; +} + +void UbiUtils::buildTcpPayload(char *payload, const char *token, const char *deviceLabel, const char *deviceName, const char *userAgent, int8_t ¤tValue, Value *dots, const bool &debug) +{ + sprintf(payload, ""); + sprintf(payload, "%s|POST|%s|", userAgent, token); + sprintf(payload, "%s%s:%s", payload, deviceLabel, deviceName); + sprintf(payload, "%s=>", payload); + for (uint8_t i = 0; i < currentValue;) + { + char str_value[20]; + floatToChar(str_value, (dots + i)->dot_value); + sprintf(payload, "%s%s:%s", payload, (dots + i)->variable_label, str_value); + + // Adds dot context + if ((dots + i)->dot_context != NULL) + { + sprintf(payload, "%s$%s", payload, (dots + i)->dot_context); + } + + // Adds timestamp seconds + if ((dots + i)->dot_timestamp_seconds != NULL) + { + sprintf(payload, "%s@%lu", payload, (dots + i)->dot_timestamp_seconds); + // Adds timestamp milliseconds + if ((dots + i)->dot_timestamp_millis != NULL) + { + char milliseconds[3]; + int timestamp_millis = (dots + i)->dot_timestamp_millis; + uint8_t units = timestamp_millis % 10; + uint8_t dec = (timestamp_millis / 10) % 10; + uint8_t hund = (timestamp_millis / 100) % 10; + sprintf(milliseconds, "%d%d%d", hund, dec, units); + sprintf(payload, "%s%s", payload, milliseconds); + } + else + { + sprintf(payload, "%s000", payload); + } + } + + i++; + + if (i < currentValue) + { + sprintf(payload, "%s,", payload); + } + else + { + sprintf(payload, "%s|end", payload); + currentValue = 0; + } + } + + if (debug) + { + Serial.println("----------"); + Serial.println("payload:"); + Serial.println(payload); + Serial.println("----------"); + Serial.println(""); + } +} + +void UbiUtils::buildHttpPayload(char *payload, int8_t ¤tValue, Value *dots, const bool &debug) +{ + /* Builds the payload */ + sprintf(payload, "{"); + + for (uint8_t i = 0; i < currentValue;) + { + char str_value[25]; + floatToChar(str_value, (dots + i)->dot_value); + sprintf(payload, "%s\"%s\":{\"value\":%s", payload, (dots + i)->variable_label, str_value); + + // Adds timestamp seconds + if ((dots + i)->dot_timestamp_seconds != NULL) + { + sprintf(payload, "%s,\"timestamp\":%lu", payload, (dots + i)->dot_timestamp_seconds); + // Adds timestamp milliseconds + if ((dots + i)->dot_timestamp_millis != NULL) + { + char milliseconds[3]; + int timestamp_millis = (dots + i)->dot_timestamp_millis; + uint8_t units = timestamp_millis % 10; + uint8_t dec = (timestamp_millis / 10) % 10; + uint8_t hund = (timestamp_millis / 100) % 10; + sprintf(milliseconds, "%d%d%d", hund, dec, units); + sprintf(payload, "%s%s", payload, milliseconds); + } + else + { + sprintf(payload, "%s000", payload); + } + } + + // Adds dot context + if ((dots + i)->dot_context != NULL) + { + sprintf(payload, "%s,\"context\": {%s}", payload, (dots + i)->dot_context); + } + + sprintf(payload, "%s}", payload); + i++; + + if (i < currentValue) + { + sprintf(payload, "%s,", payload); + } + else + { + sprintf(payload, "%s}", payload); + currentValue = 0; + } + } + + if (debug) + { + Serial.println("----------"); + Serial.println("payload:"); + Serial.println(payload); + Serial.println("----------"); + Serial.println(""); + } +} + +void UbiUtils::floatToChar(char *strValue, float value) +{ + char temp_arr[25]; + sprintf(temp_arr, "%17g", value); + uint8_t j = 0; + uint8_t k = 0; + while (j < 25) + { + if (temp_arr[j] != ' ') + { + strValue[k] = temp_arr[j]; + k++; + } + if (temp_arr[j] == '\0') + { + strValue[k] = temp_arr[j]; + break; + } + j++; + } +} + +uint16_t UbiUtils::getPathLength(const char* deviceLabel, const char* variableLabel) +{ + return strlen("/api/v1.6/devices///lv") + strlen(deviceLabel) + strlen(variableLabel); +} + +uint16_t UbiUtils::getRequestLength(const char* path, const char* host, const char* token, const char* userAgent) +{ + return strlen("GET HTTP/1.1\r\nHost: \r\nX-Auth-Token: " + "\r\nUser-Agent: \r\nContent-Type: " + "application/json\r\nConnection: close\r\n\r\n") + + strlen(path) + strlen(host) + strlen(token) + strlen(userAgent); +} diff --git a/UbiUtils.h b/UbiUtils.h new file mode 100644 index 0000000..7ce6fcf --- /dev/null +++ b/UbiUtils.h @@ -0,0 +1,249 @@ +#ifndef __UBIUTILS_H__ +#define __UBIUTILS_H__ + +#include +#include "Arduino.h" +#include "UbiConstants.h" +#include +#include + +class UbiUtils +{ +public: + template + static double parseServerAnswer(Callable &client, const bool &debug); + template + static bool waitServerAnswer(Callable &client, const bool &debug); + template + static float parseTCPAnswer(const char *requestType, char *response, Callable client, const bool &debug); + template + static void parsePartialServerAnswer(char *serverResponse, Callable &client, const bool &debug); + template + static bool preConnectionChecks(Callable &client, unsigned long &timerToSync, SyncronizeTime& syncronizeTime, const bool &debug = false); + template + static void loadCerts(Callable &client, const unsigned char cert1[], const unsigned int& size1, const unsigned char cert2[], const unsigned int& size2, const bool &debug = false); + + static int hexadecimalToDecimal(char hexVal[]); + static void buildTcpPayload(char *payload, const char *token, const char *deviceLabel, const char *deviceName, const char *userAgent, int8_t ¤tValue, Value *dots, const bool &debug); + static void buildHttpPayload(char *payload, int8_t ¤tValue, Value *dots, const bool &debug); + + static void floatToChar(char *strValue, float value); + static uint16_t getPathLength(const char *deviceLabel, const char *variableLabel); + static uint16_t getRequestLength(const char *path, const char *host, const char *token, const char *userAgent); +}; + +template +double UbiUtils::parseServerAnswer(Callable &client, const bool &debug) +{ + + /** + * @param _charResponseLength 3 is the maximun amount of digits for the length + * to have + */ + char *_charLength = (char *)malloc(sizeof(char) * 3); + + UbiUtils::parsePartialServerAnswer(_charLength, client, debug); + /** + * The server respond the length of the value in HEX so it has to be converted + * to DEC + * */ + uint8_t length = UbiUtils::hexadecimalToDecimal(_charLength); + char *_charValue = (char *)malloc(sizeof(char) * length + 1); + + parsePartialServerAnswer(_charValue, client, debug); + + double value = strtof(_charValue, NULL); + + free(_charLength); + free(_charValue); + + Serial.println(value); + return value; +} + +template +bool UbiUtils::waitServerAnswer(Callable &client, const bool &debug) +{ + int timeout = 0; + while (!client.available() && timeout < 50000) + { + timeout++; + delay(1); + if (timeout > 50000 - 1) + { + if (debug) + Serial.println("timeout, could not read any response from the host"); + + client.flush(); + client.stop(); + return false; + } + } + return true; +} + +template +float UbiUtils::parseTCPAnswer(const char *requestType, char *response, Callable client, const bool &debug) +{ + uint32_t retries{10}; + int j = 0; + + if (debug) + { + Serial.println(F("----------")); + Serial.println(F("Server's response:")); + } + + while(!client.available() && retries) + { + delay(1000); + retries--; + } + while (client.available()) + { + char c = client.read(); + if (debug) + { + Serial.write(c); + } + response[j] = c; + j++; + if (j >= MAX_BUFFER_SIZE - 1) + { + break; + } + } + + if (debug) + { + Serial.println(F("\n----------")); + } + + response[j] = '\0'; + float result = ERROR_VALUE; + + // POST + if (requestType == "POST") + { + char *pch = strstr(response, "OK"); + if (pch != NULL) + { + result = 1; + } + return result; + } + + // LV + char *pch = strchr(response, '|'); + if (pch != NULL) + { + result = atof(pch + 1); + } + + return result; +} + +template +void UbiUtils::parsePartialServerAnswer(char *serverResponse, Callable &client, const bool &debug) +{ + /** + * Server Response Ascii code -> Character from the server + First extract the following value + 52 -> 4 ->Length of the value in HEX + 13 -> \r + 10 -> \n + + At the next time it will read the whole value with the allocated memory set by the previus value extracted + 51 -> 3 ->Value in char + 57 -> 9 ->Value in char + 46 -> . ->Decimal point + 48 -> 0 ->Value in char + 13 -> \r + 10 -> \n + + 48 -> 0 ->Value in char + 13 -> \r + 10 -> \n + + 13 -> \r + 10 -> \n + */ + sprintf(serverResponse, "%c", client.read()); + + while (client.available()) + { + char charRead = (char)client.read(); + char c[2]; + sprintf(c, "%c\0", charRead); + if (charRead == '\r') + { // If the character is \r means we have ended the line then we request + // Get the last character \n to enable the function to run again + client.read(); // clean the buffer asking for the next character + break; + } + else if (charRead == 'e') + { + /** + * After 18 digits it will show the response in scientific notation, and + * there is no space to store such a huge number + * */ + sprintf(serverResponse, "%f", ERROR_VALUE); + if (debug) + { + Serial.println(F("[ERROR]The value from the server exceeded memory capacity")); + } + } + else + { + strcat(serverResponse, c); // Add the value to the expected response. + } + } +} + +template +bool UbiUtils::preConnectionChecks(Callable &certs, unsigned long &timerToSync, SyncronizeTime& syncronizeTime, const bool &debug) +{ + bool syncronized = true; + if (millis() - timerToSync > 3600000) + { + syncronized = syncronizeTime(debug); + timerToSync = millis(); + } + + if (!syncronized) + { + if (debug) + { + Serial.println( + F("[ERROR] Could not syncronize device time with external " + "source, make sure that you are not behind a " + "firewall")); + } + return false; + } + + if (!certs.getCount()) + { + if (debug) + { + Serial.println(F("[ERROR] Please load a valid certificate")); + } + return false; + } + + return true; +} + +template +void UbiUtils::loadCerts(Callable &client, const unsigned char cert1[], const unsigned int& size1, const unsigned char cert2[], const unsigned int& size2, const bool &debug) +{ + client.append(cert1, size1); + client.append(cert2, size2); + if (!client.getCount() && debug) + { + Serial.println("Failed to load root CA certificates!"); + } +} + + +#endif // __UBIUTILS_H__ \ No newline at end of file diff --git a/Ubidots.cpp b/Ubidots.cpp new file mode 100644 index 0000000..4a7d855 --- /dev/null +++ b/Ubidots.cpp @@ -0,0 +1,148 @@ +#include "Ubidots.h" + +Ubidots::Ubidots(const char *token, const IotProtocol &iotProtocol, const char *host) +{ + _deviceSettings._token = const_cast(token); + _deviceSettings._port = getPort(iotProtocol); + _deviceSettings._host = const_cast(host); + _deviceSettings._protocol = iotProtocol; + _deviceSettings._credentials = nullptr; + device->setSettings(_deviceSettings); + device->buildProtocolHandlerInstance(iotProtocol); + device->_protocolHandler->init(); + //device->getUniqueID(_defaultDeviceLabel); + // Given the fact that the "device" object is instantiated before calling "Ubidots" constructor, it is not possible to assing + // to the protocol pointer at the "device" constructor, because at that point, host, token and protocol are not known +} + +Ubidots::~Ubidots() +{ +} + +void Ubidots::add(const char *variableLabel, double value) +{ + add(variableLabel, value, NULL, NULL, NULL); +} + +void Ubidots::add(const char *variableLabel, double value, char *context) +{ + add(variableLabel, value, context, NULL, NULL); +} + +void Ubidots::add(const char *variableLabel, double value, char *context, long unsigned dotTimestampSeconds) +{ + add(variableLabel, value, context, dotTimestampSeconds, NULL); +} + +void Ubidots::add(const char *variableLabel, double value, char *context, long unsigned dotTimestampSeconds, unsigned int dotTimestampMillis) +{ + device->_protocolHandler->add(variableLabel, value, context, dotTimestampSeconds, dotTimestampMillis); +} + +bool Ubidots::send() { return send(_defaultDeviceLabel, _defaultDeviceLabel); } + +bool Ubidots::send(const char *device_label) { return send(device_label, device_label); } + +bool Ubidots::send(const char *device_label, const char *device_name) +{ + if (strlen(_deviceType) > 0 && _deviceSettings._protocol == UBI_HTTP) + { + char builtDeviceLabel[50]; + sprintf(builtDeviceLabel, "%s/?type=%s", device_label, _deviceType); + return device->_protocolHandler->send(builtDeviceLabel, device_name); + } + return device->_protocolHandler->send(device_label, device_name); +} + +bool Ubidots::serverConnected() +{ + return device->_protocolHandler->serverConnected(); +} + +bool Ubidots::connected() +{ + return device->_protocolHandler->connected(); +} +unsigned int Ubidots::getPort(const IotProtocol &protocol) +{ + uint32_t port = 0; + switch (protocol) + { + case UBI_HTTP: + port = UBIDOTS_HTTP_PORT; + break; + case UBI_TCP: + port = UBIDOTS_TCP_PORT; + break; + case UBI_UDP: + port = UBIDOTS_TCP_PORT; + break; + case UBI_HTTPS: + port = UBIDOTS_HTTPS_PORT; + break; + case UBI_TCPS: + port = UBIDOTS_TCPS_PORT; + break; + } + return port; +} + +bool Ubidots::connect(const char *ssid, const char *password) +{ + const char *credentials[2]{ssid, password}; + _deviceSettings._credentials = const_cast(credentials); + device->setSettings(_deviceSettings); + return device->_protocolHandler->connect(); +} + +// TODO implement as a functor class +bool Ubidots::connect(const char *APN, const char *username, const char *password, const char *pin) +{ + const char *credentials[4]{APN, username, password, pin}; + _deviceSettings._credentials = const_cast(credentials); + device->setSettings(_deviceSettings); + return device->_protocolHandler->connect(); +} + +double Ubidots::get(const char *device_label, const char *variable_label) +{ + return device->_protocolHandler->get(device_label, variable_label); +} + +void Ubidots::addContext(char *key_label, char *key_value) +{ + device->_protocolHandler->addContext(key_label, key_value); +} + +void Ubidots::getContext(char *context_result) +{ + device->_protocolHandler->getContext(context_result); +} + +void Ubidots::setDebug(const bool &debug) +{ + this->_debug = debug; + device->_protocolHandler->setDebug(debug); +} + +void Ubidots::setDeviceType(const char *deviceType) +{ + if (strlen(deviceType) > 0 && _deviceSettings._protocol == UBI_HTTP) + { + sprintf(_deviceType, "%s", deviceType); + } + else + { + Serial.println("Device Type is only available using HTTP"); + } +} + +void Ubidots::getDeviceID(char *ID) +{ + device->getUniqueID(ID); +} + +bool Ubidots::reconnect() +{ + return device->_protocolHandler->reconnect(); +} \ No newline at end of file diff --git a/Ubidots.h b/Ubidots.h new file mode 100644 index 0000000..993c1a1 --- /dev/null +++ b/Ubidots.h @@ -0,0 +1,47 @@ +#ifndef __UBIDOTS_H_59QTAT1NY8HK__ +#define __UBIDOTS_H_59QTAT1NY8HK__ + +/////////////////////////////////////////////////////////// +/// @brief Ubidots +/// +#include "DeviceBuilder.h" + + +class Ubidots +{ +public: + + explicit Ubidots(const char *token, const IotProtocol& iotProtocol = UBI_HTTP, const char* host = UBI_INDUSTRIAL); + ~Ubidots(); + bool connect(const char *APN, const char *username, const char *password, const char *pin); + bool connect(const char *ssid, const char *password); + bool reconnect(); + bool send(); + bool send(const char* deviceLabel); + bool send(const char* deviceLabel, const char* deviceName); + bool serverConnected(); + bool connected(); + double get(const char *deviceLabel, const char *variableLabel); + void add(const char *variableLabel, double value, char *context, unsigned long dotTimestampSeconds, unsigned int dotTimestampSecondsMillis); + void add(const char *variableLabel, double value, char *context, unsigned long dotTimestampSeconds); + void add(const char *variableLabel, double value, char *context); + void add(const char *variableLabel, double value); + void addContext(char *keyLabel, char *keyValue); + void getContext(char *context_result); + void setDebug(const bool& debug); + void setDeviceType(const char *deviceType); + void getDeviceID(char *ID); + + + +private: + // Members + bool _debug{false}; + char _defaultDeviceLabel[18]{"undefined"}; + char _deviceType[25]{"undefined"}; + UbiDeviceSettings _deviceSettings; + + unsigned int getPort(const IotProtocol& protocol); +}; + +#endif // __UBIDOTS_H_59QTAT1NY8HK__