diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 2809dc0048f..103d1960935 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -47,22 +47,6 @@ void ArduinoCloudThing::begin() { addPropertyReal(status, "status").readOnly(); } -int ArduinoCloudThing::publish(CborArray& object, uint8_t* data, size_t size) { - - ssize_t len = object.encode(data, size); - -#ifdef TESTING_PROTOCOL - decode(data, len); -#endif - - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - p->updateShadow(); - } - - return len; -} - int ArduinoCloudThing::poll(uint8_t* data, size_t size) { // check if backing storage and cloud has diverged @@ -70,30 +54,52 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { diff = checkNewData(); if (diff > 0) { - CborBuffer buffer(1024); - CborArray object = CborArray(buffer); - compress(object, buffer); - diff = publish(object, data, size); + CborError err; + CborEncoder encoder, arrayEncoder; + cbor_encoder_init(&encoder, data, size, 0); + // create a cbor array containing the property that should be updated. + err = cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength); + if (err) { + Serial.println(cbor_error_string(err)); + return -1; + } + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + // If a property should be updated and has read permission from the Cloud point of view + if (p->shouldBeUpdated() && p->canRead()) { + // create a cbor object for the property and automatically add it into array + p->append(&arrayEncoder); + } + } + + err = cbor_encoder_close_container(&encoder, &arrayEncoder); + // update properties shadow values, in order to check if variable has changed since last publish + + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + p->updateShadow(); + } + // return the number of byte of the CBOR encoded array + return cbor_encoder_get_buffer_size(&encoder, data); } #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) PrintFreeRam(); #endif - + // If nothing has to be sent, return diff, that is 0 in this case return diff; } -void ArduinoCloudThing::compress(CborArray& object, CborBuffer& buffer) { - +// It return the index of the property, inside the local array, with the name passed as parameter. (-1 if it does not exist.) +int ArduinoCloudThing::findPropertyByName(String &name) { for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); - if (p->shouldBeUpdated() && p->canRead()) { - CborObject child = CborObject(buffer); - p->append(child); - CborVariant variant = CborVariant(buffer, child); - object.add(variant); + // Check the property existance just comparing its name with existent ones + if (p->getName() == name) { + return i; } } + return -1; } int ArduinoCloudThing::checkNewData() { @@ -144,88 +150,113 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, return *(reinterpret_cast(thing)); } -void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { - CborBuffer buffer(200); - CborVariant total = buffer.decode(payload, length); +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name) { + if (ArduinoCloudPropertyGeneric* p = exists(name)) { + return *p; + } + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); + list.add(thing); + return *(reinterpret_cast(thing)); +} - CborArray array = total.asArray(); +void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { + CborError err; + CborParser parser; + CborValue recursedMap, propValue, dataArray; + int propId; String propType, propName; + + err = cbor_parser_init(payload, length, 0, &parser, &dataArray); + if (err) { + return; + } - for (int i=0; ;i++) { - CborVariant variant = array.get(i); + // parse cbor data only if a cbor array is received. + if (dataArray.type != CborArrayType) + return; + + // main loop through the cbor array elements + while (!cbor_value_at_end(&dataArray)) { + // parse cbor object + cbor_value_enter_container(&dataArray, &recursedMap); + CborType type = cbor_value_get_type(&recursedMap); + if (type != CborMapType) { + // stop the decode when 1st item thai is not a cbor map is found. + cbor_value_advance(&dataArray); + continue; + } else { + + while (!cbor_value_at_end(&recursedMap)) { + // if the current element is not a cbor object as expected, skip it and go ahead. + if (cbor_value_get_type(&recursedMap) != CborMapType) { + cbor_value_advance(&recursedMap); + continue; + } - if (!variant.isValid()) { - break; - } + CborValue name; + // chechk for the if the a property has a name, if yes Cbor value name will properly updated + cbor_value_map_find_value(&recursedMap, "n", &name); + // check if a property has a name, of string type, if not do nothin and skip curtrent property + if (name.type != CborTextStringType) { + cbor_value_advance(&recursedMap); + continue; + } + // get the property name from cbor map as char* string + char *nameVal; size_t nameValSize; + err = cbor_value_dup_text_string(&name, &nameVal, &nameValSize, NULL); + if (err) { + break; // couldn't get the value of the field + } + // get the name of the received property as String object + propName = String(nameVal); + // used to avoid memory leaks (cbor_value_dup_text_string automatically perform a malloc) + free(nameVal); + // Search for the index of the device property with that name + propId = findPropertyByName(propName); + // If property does not exist, skip it and do nothing. + if (propId < 0) { + cbor_value_advance(&recursedMap); + continue; + } - CborObject object = variant.asObject(); + ArduinoCloudPropertyGeneric* property = list.get(propId); + // Check for the property type, write method internally check for the permission - String name = ""; - if (object.get("n").isValid()) { - name = object.get("n").asString(); - // search for the property with the same name - for (int idx = 0; idx < list.size(); idx++) { - ArduinoCloudPropertyGeneric *p = list.get(idx); - if (p->getName() == name) { - currentListIndex = idx; - break; + if (propValue.type == CborDoubleType) { + double val; + // get the value of the property as a double + cbor_value_get_double(&propValue, &val); + reinterpret_cast*>(property)->write((float)val); } - if (idx == list.size()) { - currentListIndex = -1; + // if no key proper key was found, do nothing + if (propValue.type == CborIntegerType) { + int val; + cbor_value_get_int(&propValue, &val); + reinterpret_cast*>(property)->write(val); } - } - } - - if (object.get("t").isValid()) { - int tag = object.get("t").asInteger(); - if (name != "") { - list.get(currentListIndex)->setTag(tag); - } else { - for (int idx = 0; idx < list.size(); idx++) { - ArduinoCloudPropertyGeneric *p = list.get(idx); - if (p->getTag() == tag) { - // if name == "" associate name and tag, otherwise set current list index - currentListIndex = idx; - break; - } - if (idx == list.size()) { - Serial.println("Property not found, skipping"); - currentListIndex = -1; + if (propValue.type == CborBooleanType) { + bool val; + cbor_value_get_boolean(&propValue, &val); + reinterpret_cast*>(property)->write(val); + } + if (propValue.type == CborTextStringType) { + char *val; size_t valSize; + err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); + // Char* string transformed into array + reinterpret_cast*>(property)->write(String((char*)val)); + free(val); + } + // If the property has been changed call its callback + if (property->newData()) { + if (property->callback != NULL) { + property->callback(); } } + // Continue to scan the cbor map + cbor_value_advance(&recursedMap); } } - - if (object.get("i").isValid()) { - int value_i = object.get("i").asInteger(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_i); - } - - if (object.get("b").isValid()) { - bool value_b = object.get("b").asInteger(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_b); - } -/* - if (object.get("f").isValid()) { - float value_f = object.get("f").asFloat(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_f); - } -*/ - if (object.get("s").isValid()) { - String value_s = object.get("s").asString(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_s); - } - - if (object.get("p").isValid()) { - permissionType value_p = (permissionType)object.get("p").asInteger(); - list.get(currentListIndex)->setPermission(value_p); - } - - if (list.get(currentListIndex)->newData()) { - // call onUpdate() - if (list.get(currentListIndex)->callback != NULL) { - list.get(currentListIndex)->callback(); - } - } + // Leave the current cbor object, and advance to the next one + err = cbor_value_leave_container(&dataArray, &recursedMap); } } \ No newline at end of file diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 0e36cb8c990..635a85d7fcd 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -4,11 +4,7 @@ #include #include #include "lib/LinkedList/LinkedList.h" -#include "lib/ArduinoCbor/src/ArduinoCbor.h" - -//#define TESTING_PROTOCOL -//#define DEBUG_MEMORY -#define USE_ARDUINO_CLOUD +#include "lib/tinycbor/cbor.h" enum permissionType { READ = 0b01, @@ -33,7 +29,7 @@ enum times { class ArduinoCloudPropertyGeneric { public: - virtual void append(CborObject& object) = 0; + virtual void append(CborEncoder* encoder) = 0; virtual String& getName() = 0; virtual void setName(String _name) = 0; virtual ArduinoCloudPropertyGeneric& setTag(int _tag) = 0; @@ -57,8 +53,10 @@ template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { public: - ArduinoCloudProperty(T& _property, String _name) : - property(_property), name(_name) {} + ArduinoCloudProperty(T& _property, String _name) : + property(_property), name(_name) + { + } bool write(T value) { /* permissions are intended as seen from cloud */ @@ -134,19 +132,23 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return *(reinterpret_cast(this)); } - void appendValue(CborObject &cbor); + void appendValue(CborEncoder* mapEncoder); - void append(CborObject &cbor) { + void append(CborEncoder* encoder) { if (!canRead()) { return; } + CborEncoder mapEncoder; + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); if (tag != -1) { - cbor.set("t", tag); + cbor_encode_text_stringz(&mapEncoder, "t"); + cbor_encode_int(&mapEncoder, tag); } else { - cbor.set("n", name.c_str()); + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, name.c_str()); } - appendValue(cbor); - cbor.set("p", permission); + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); lastUpdated = millis(); } @@ -156,7 +158,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric } bool newData() { - return (property != shadow_property && abs(property - shadow_property) > minDelta ); + return (property != shadow_property); } bool shouldBeUpdated() { @@ -167,9 +169,10 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric } inline bool operator==(const ArduinoCloudProperty& rhs){ - return (strcmp(getName(), rhs.getName) == 0); + return (strcmp(getName(), rhs.getName()) == 0); } + protected: T& property; T shadow_property; @@ -177,34 +180,49 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric int tag = -1; long lastUpdated = 0; long updatePolicy = ON_CHANGE; - T minDelta = 0; + T minDelta; permissionType permission = READWRITE; static int tagIndex; }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("i", property); +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) > minDelta ); +} + +template <> +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) > minDelta ); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_int(mapEncoder, property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("b", property); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_boolean(mapEncoder, property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("f", property); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_float(mapEncoder, property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("s", property.c_str()); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, property.c_str()); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("s", property); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, property->c_str()); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, property); }; class ArduinoCloudThing { @@ -215,17 +233,15 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name); // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); void decode(uint8_t * payload, size_t length); private: - int publish(CborArray& object, uint8_t* data, size_t size); - void update(); int checkNewData(); - void compress(CborArray& object, CborBuffer& buffer); + int findPropertyByName(String &name); ArduinoCloudPropertyGeneric* exists(String &name);