Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

LoRa support #48

Merged
merged 9 commits into from
Dec 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 48 additions & 10 deletions src/ArduinoCloudProperty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ ArduinoCloudProperty::ArduinoCloudProperty()
_update_interval_millis(0),
_last_local_change_timestamp(0),
_last_cloud_change_timestamp(0),
_map_data_list(nullptr) {
_map_data_list(nullptr),
_identifier(0),
_attributeIdentifier(0),
_lightPayload(false) {
}

/******************************************************************************
Expand Down Expand Up @@ -115,7 +118,9 @@ void ArduinoCloudProperty::execCallbackOnSync() {
}
}

void ArduinoCloudProperty::append(CborEncoder *encoder) {
void ArduinoCloudProperty::append(CborEncoder *encoder, bool lightPayload) {
_lightPayload = lightPayload;
_attributeIdentifier = 0;
appendAttributesToCloudReal(encoder);
fromLocalToCloud();
_has_been_updated_once = true;
Expand Down Expand Up @@ -151,20 +156,35 @@ void ArduinoCloudProperty::appendAttributeReal(String value, String attributeNam
}

void ArduinoCloudProperty::appendAttributeName(String attributeName, std::function<void (CborEncoder& mapEncoder)>appendValue, CborEncoder *encoder) {
if (attributeName != "") {
// when the attribute name string is not empty, the attribute identifier is incremented in order to be encoded in the message if the _lightPayload flag is set
_attributeIdentifier++;
}
CborEncoder mapEncoder;
cbor_encoder_create_map(encoder, &mapEncoder, 2);
cbor_encode_int(&mapEncoder, static_cast<int>(CborIntegerMapKey::Name));
String completeName = _name;
if (attributeName != "") {
completeName += ":" + attributeName;

// if _lightPayload is true, the property and attribute identifiers will be encoded instead of the property name
if (_lightPayload) {
// the most significant byte of the identifier to be encoded represent the property identifier
int completeIdentifier = _attributeIdentifier * 256;
// the least significant byte of the identifier to be encoded represent the attribute identifier
completeIdentifier += _identifier;
cbor_encode_int(&mapEncoder, completeIdentifier);
} else {
String completeName = _name;
if (attributeName != "") {
completeName += ":" + attributeName;
}
cbor_encode_text_stringz(&mapEncoder, completeName.c_str());
}
cbor_encode_text_stringz(&mapEncoder, completeName.c_str());
appendValue(mapEncoder);
cbor_encoder_close_container(encoder, &mapEncoder);
}

void ArduinoCloudProperty::setAttributesFromCloud(LinkedList<CborMapData *> *map_data_list) {
_map_data_list = map_data_list;
_attributeIdentifier = 0;
setAttributesFromCloud();
}

Expand Down Expand Up @@ -193,16 +213,30 @@ void ArduinoCloudProperty::setAttributeReal(String& value, String attributeName)
}

void ArduinoCloudProperty::setAttributeReal(String attributeName, std::function<void (CborMapData *md)>setValue) {
if (attributeName != "") {
_attributeIdentifier++;
}
for (int i = 0; i < _map_data_list->size(); i++) {
CborMapData *map = _map_data_list->get(i);
if (map != nullptr) {
String an = map->attribute_name.get();
if (an == attributeName) {
setValue(map);
break;
if (map->light_payload.isSet() && map->light_payload.get()) {
// if a light payload is detected, the attribute identifier is retrieved from the cbor map and the corresponding attribute is updated
int attid = map->attribute_identifier.get();
if (attid == _attributeIdentifier) {
setValue(map);
break;
}
} else {
// if a normal payload is detected, the name of the attribute to be updated is extracted directly from the cbor map
String an = map->attribute_name.get();
if (an == attributeName) {
setValue(map);
break;
}
}
}
}

}

String ArduinoCloudProperty::getAttributeName(String propertyName, char separator) {
Expand Down Expand Up @@ -233,3 +267,7 @@ unsigned long ArduinoCloudProperty::getLastCloudChangeTimestamp() {
unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() {
return _last_local_change_timestamp;
}

void ArduinoCloudProperty::setIdentifier(int identifier) {
_identifier = identifier;
}
15 changes: 14 additions & 1 deletion src/ArduinoCloudProperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ class CborMapData {
MapEntry<String> base_name;
MapEntry<double> base_time;
MapEntry<String> name;
MapEntry<int> name_identifier;
MapEntry<bool> light_payload;
MapEntry<String> attribute_name;
MapEntry<int> attribute_identifier;
MapEntry<int> property_identifier;
MapEntry<float> val;
MapEntry<String> str_val;
MapEntry<bool> bool_val;
Expand Down Expand Up @@ -138,6 +142,9 @@ class ArduinoCloudProperty {
inline String name() const {
return _name;
}
inline int identifier() const {
return _identifier;
}
inline bool isReadableByCloud() const {
return (_permission == Permission::Read) || (_permission == Permission::ReadWrite);
}
Expand All @@ -152,9 +159,10 @@ class ArduinoCloudProperty {
void setLastLocalChangeTimestamp(unsigned long localChangeTime);
unsigned long getLastCloudChangeTimestamp();
unsigned long getLastLocalChangeTimestamp();
void setIdentifier(int identifier);

void updateLocalTimestamp();
void append(CborEncoder * encoder);
void append(CborEncoder * encoder, bool lightPayload);
void appendAttributeReal(bool value, String attributeName = "", CborEncoder *encoder = nullptr);
void appendAttributeReal(int value, String attributeName = "", CborEncoder *encoder = nullptr);
void appendAttributeReal(float value, String attributeName = "", CborEncoder *encoder = nullptr);
Expand Down Expand Up @@ -197,6 +205,11 @@ class ArduinoCloudProperty {
unsigned long _last_local_change_timestamp;
unsigned long _last_cloud_change_timestamp;
LinkedList<CborMapData *> * _map_data_list;
/* Store the identifier of the property in the array list */
int _identifier;
int _attributeIdentifier;
/* Indicates if the property shall be encoded using the identifier instead of the name */
bool _lightPayload;
};

/******************************************************************************
Expand Down
60 changes: 53 additions & 7 deletions src/ArduinoCloudThing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ void PrintFreeRam(void) {

ArduinoCloudThing::ArduinoCloudThing() :
_numPrimitivesProperties(0),
_numProperties(0),
_isSyncMessage(false),
_currentPropertyName(""),
_currentPropertyBaseTime(0),
Expand All @@ -58,7 +59,8 @@ ArduinoCloudThing::ArduinoCloudThing() :
void ArduinoCloudThing::begin() {
}

int ArduinoCloudThing::encode(uint8_t * data, size_t const size) {
int ArduinoCloudThing::encode(uint8_t * data, size_t const size, bool lightPayload) {

// check if backing storage and cloud has diverged
// time interval may be elapsed or property may be changed
CborEncoder encoder, arrayEncoder;
Expand All @@ -69,7 +71,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) {
return -1;
}

if (appendChangedProperties(&arrayEncoder) < 1) {
if (appendChangedProperties(&arrayEncoder, lightPayload) < 1) {
return -1;
}

Expand All @@ -84,17 +86,19 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) {
return bytes_encoded;
}

ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission) {
ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier) {
property.init(name, permission);
if (isPropertyInContainer(name)) {
return (*getProperty(name));
} else {
if (property.isPrimitive()) {
_numPrimitivesProperties++;
}
addProperty(&property);
_numProperties++;
addProperty(&property, propertyIdentifier);
return (property);
}

}

void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length, bool isSyncMessage) {
Expand Down Expand Up @@ -158,18 +162,19 @@ bool ArduinoCloudThing::isPropertyInContainer(String const & name) {
return false;
}

int ArduinoCloudThing::appendChangedProperties(CborEncoder * arrayEncoder) {
int ArduinoCloudThing::appendChangedProperties(CborEncoder * arrayEncoder, bool lightPayload) {
int appendedProperties = 0;
for (int i = 0; i < _property_list.size(); i++) {
ArduinoCloudProperty * p = _property_list.get(i);
if (p->shouldBeUpdated() && p->isReadableByCloud()) {
p->append(arrayEncoder);
p->append(arrayEncoder, lightPayload);
appendedProperties++;
}
}
return appendedProperties;
}

//retrieve property by name
ArduinoCloudProperty * ArduinoCloudThing::getProperty(String const & name) {
for (int i = 0; i < _property_list.size(); i++) {
ArduinoCloudProperty * p = _property_list.get(i);
Expand All @@ -180,6 +185,17 @@ ArduinoCloudProperty * ArduinoCloudThing::getProperty(String const & name) {
return NULL;
}

//retrieve property by identifier
ArduinoCloudProperty * ArduinoCloudThing::getProperty(int const & pos) {
for (int i = 0; i < _property_list.size(); i++) {
ArduinoCloudProperty * p = _property_list.get(i);
if (p->identifier() == pos) {
return p;
}
}
return NULL;
}

// this function updates the timestamps on the primitive properties that have been modified locally since last cloud synchronization
void ArduinoCloudThing::updateTimestampOnLocallyChangedProperties() {
if (_numPrimitivesProperties == 0) {
Expand Down Expand Up @@ -312,6 +328,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * val
MapParserState next_state = MapParserState::Error;

if (cbor_value_is_text_string(value_iter)) {
// if the value in the cbor message is a string, it corresponds to the name of the property to be updated (int the form [property_name]:[attribute_name])
char * val = nullptr;
size_t val_size = 0;
if (cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) {
Expand All @@ -326,8 +343,26 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * val
map_data->attribute_name.set(attribute_name);
next_state = MapParserState::MapKey;
}
} else if (cbor_value_is_integer(value_iter)) {
// if the value in the cbor message is an integer, a light payload has been used and an integer identifier should be decode in order to retrieve the corresponding property and attribute name to be updated
int val = 0;
if (cbor_value_get_int(value_iter, &val) == CborNoError) {
map_data->light_payload.set(true);
map_data->name_identifier.set(val & 255);
map_data->attribute_identifier.set(val >> 8);
map_data->light_payload.set(true);
String name = getPropertyNameByIdentifier(val);
map_data->name.set(name);


if (cbor_value_advance(value_iter) == CborNoError) {
next_state = MapParserState::MapKey;
}
}
}



return next_state;
}

Expand Down Expand Up @@ -404,7 +439,6 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue *
}

if (_currentPropertyName != "" && propertyName != _currentPropertyName) {

/* Update the property containers depending on the parsed data */
updateProperty(_currentPropertyName, _currentPropertyBaseTime + _currentPropertyTime);
/* Reset current property data */
Expand Down Expand Up @@ -461,6 +495,18 @@ void ArduinoCloudThing::updateProperty(String propertyName, unsigned long cloudC
}
}
}

// retrieve the property name by the identifier
String ArduinoCloudThing::getPropertyNameByIdentifier(int propertyIdentifier) {
ArduinoCloudProperty* property;
if (propertyIdentifier > 255) {
property = getProperty(propertyIdentifier & 255);
} else {
property = getProperty(propertyIdentifier);
}
return property->name();
}

bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val) {

if (cbor_value_is_integer(value_iter)) {
Expand Down
22 changes: 16 additions & 6 deletions src/ArduinoCloudThing.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,26 @@ class ArduinoCloudThing {
ArduinoCloudThing();

void begin();

ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission);
//if propertyIdentifier is different from -1, an integer identifier is associated to the added property to be use instead of the property name when the parameter lightPayload is true in the encode method
ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier = -1);

/* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */
int encode(uint8_t * data, size_t const size);
/* if lightPayload is true the integer identifier of the property will be encoded in the message instead of the property name in order to reduce the size of the message payload*/
int encode(uint8_t * data, size_t const size, bool lightPayload = false);
/* decode a CBOR payload received from the cloud */
void decode(uint8_t const * const payload, size_t const length, bool isSyncMessage = false);

bool isPropertyInContainer(String const & name);
int appendChangedProperties(CborEncoder * arrayEncoder);
int appendChangedProperties(CborEncoder * arrayEncoder, bool lightPayload);
void updateTimestampOnLocallyChangedProperties();
void updateProperty(String propertyName, unsigned long cloudChangeEventTime);
String getPropertyNameByIdentifier(int propertyIdentifier);

private:
LinkedList<ArduinoCloudProperty *> _property_list;
/* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */
int _numPrimitivesProperties;
int _numProperties;
/* Indicates the if the message received to be decoded is a response to the getLastValues inquiry */
bool _isSyncMessage;
/* List of map data that will hold all the attributes of a property */
Expand Down Expand Up @@ -135,11 +138,18 @@ class ArduinoCloudThing {

static bool ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val);
static double convertCborHalfFloatToDouble(uint16_t const half_val);
void freeMapDataList(LinkedList<CborMapData *> *map_data_list);
inline void addProperty(ArduinoCloudProperty * property_obj) {
void freeMapDataList(LinkedList<CborMapData *> * map_data_list);
inline void addProperty(ArduinoCloudProperty * property_obj, int propertyIdentifier) {
if (propertyIdentifier != -1) {
property_obj->setIdentifier(propertyIdentifier);
} else {
// if property identifier is -1, an incremental value will be assigned as identifier.
property_obj->setIdentifier(_numProperties);
}
_property_list.add(property_obj);
}
ArduinoCloudProperty * getProperty(String const & name);
ArduinoCloudProperty * getProperty(int const & identifier);

};

Expand Down
1 change: 0 additions & 1 deletion src/types/CloudLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ class CloudLocation : public ArduinoCloudProperty {
return _value;
}


virtual void fromCloudToLocal() {
_value = _cloud_value;
}
Expand Down
2 changes: 1 addition & 1 deletion test/include/TestUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
PROTOTYPES
**************************************************************************************/

std::vector<uint8_t> encode(ArduinoCloudThing & thing);
std::vector<uint8_t> encode(ArduinoCloudThing & thing, bool lightPayload = false);
void print(std::vector<uint8_t> const & vect);

#endif /* INCLUDE_TESTUTIL_H_ */
4 changes: 2 additions & 2 deletions test/src/TestUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
PUBLIC FUNCTIONS
**************************************************************************************/

std::vector<uint8_t> encode(ArduinoCloudThing & thing) {
std::vector<uint8_t> encode(ArduinoCloudThing & thing, bool lightPayload) {
uint8_t buf[200] = {0};
int const bytes_buf = thing.encode(buf, 200);
int const bytes_buf = thing.encode(buf, 200, lightPayload);
if (bytes_buf == -1) {
return std::vector<uint8_t>();
} else {
Expand Down
Loading