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

Commit fc6c29f

Browse files
committed
Adding a function to register a callback function for retrieving a global timestamp.
This is necessary because right now we are relying on the RTC within the SAMD MCU which is instantiated (RTCZero) within the ArduinoIoTCloud library and reference within ArduinoCloudThing via extern devlaration. Due to the extern binding this is a very brittle dependency which can be easily destroyed, it is therefore better to explicitly register a function which provides the time (this can be serviced by the TimeService class available in ArduinoIoTCloud
1 parent f5feaf4 commit fc6c29f

5 files changed

+309
-4
lines changed

src/ArduinoCloudProperty.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ ArduinoCloudProperty::ArduinoCloudProperty()
3939
_min_delta_property(0.0f),
4040
_min_time_between_updates_millis(0),
4141
_permission(Permission::Read),
42+
_get_time_func{nullptr},
4243
_update_callback_func(nullptr),
4344
_sync_callback_func(nullptr),
4445
_has_been_updated_once(false),
@@ -56,9 +57,10 @@ ArduinoCloudProperty::ArduinoCloudProperty()
5657
/******************************************************************************
5758
PUBLIC MEMBER FUNCTIONS
5859
******************************************************************************/
59-
void ArduinoCloudProperty::init(String const name, Permission const permission) {
60+
void ArduinoCloudProperty::init(String const name, Permission const permission, GetTimeCallbackFunc func) {
6061
_name = name;
6162
_permission = permission;
63+
_get_time_func = func;
6264
}
6365

6466
ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) {
@@ -259,7 +261,11 @@ String ArduinoCloudProperty::getAttributeName(String propertyName, char separato
259261

260262
void ArduinoCloudProperty::updateLocalTimestamp() {
261263
if (isReadableByCloud()) {
262-
_last_local_change_timestamp = getTimestamp();
264+
if (_get_time_func) {
265+
_last_local_change_timestamp = _get_time_func();
266+
} else {
267+
_last_local_change_timestamp = getTimestamp();
268+
}
263269
}
264270
}
265271

src/ArduinoCloudProperty.cpp.orig

+290
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
//
2+
// This file is part of ArduinoCloudThing
3+
//
4+
// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
5+
//
6+
// This software is released under the GNU General Public License version 3,
7+
// which covers the main part of ArduinoCloudThing.
8+
// The terms of this license can be found at:
9+
// https://www.gnu.org/licenses/gpl-3.0.en.html
10+
//
11+
// You can be released from the requirements of the above licenses by purchasing
12+
// a commercial license. Buying such a license is mandatory if you want to modify or
13+
// otherwise use the software for commercial activities involving the Arduino
14+
// software without disclosing the source code of your own applications. To purchase
15+
// a commercial license, send an email to [email protected].
16+
//
17+
18+
#include "ArduinoCloudProperty.h"
19+
20+
#ifdef ARDUINO_ARCH_SAMD
21+
#include <RTCZero.h>
22+
extern RTCZero rtc;
23+
#endif
24+
25+
static unsigned long getTimestamp() {
26+
#ifdef ARDUINO_ARCH_SAMD
27+
return rtc.getEpoch();
28+
#else
29+
#pragma message "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ."
30+
return 0;
31+
#endif
32+
}
33+
34+
/******************************************************************************
35+
CTOR/DTOR
36+
******************************************************************************/
37+
ArduinoCloudProperty::ArduinoCloudProperty()
38+
: _name(""),
39+
_get_time_func{nullptr},
40+
_min_delta_property(0.0f),
41+
_min_time_between_updates_millis(0),
42+
_permission(Permission::Read),
43+
_update_callback_func(nullptr),
44+
_sync_callback_func(nullptr),
45+
_has_been_updated_once(false),
46+
_has_been_modified_in_callback(false),
47+
_last_updated_millis(0),
48+
_update_interval_millis(0),
49+
_last_local_change_timestamp(0),
50+
_last_cloud_change_timestamp(0),
51+
_map_data_list(nullptr),
52+
_identifier(0),
53+
_attributeIdentifier(0),
54+
_lightPayload(false) {
55+
}
56+
57+
/******************************************************************************
58+
PUBLIC MEMBER FUNCTIONS
59+
******************************************************************************/
60+
void ArduinoCloudProperty::init(String const name, Permission const permission, GetTimeCallbackFunc func) {
61+
_name = name;
62+
_permission = permission;
63+
_get_time_func = func;
64+
}
65+
66+
ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) {
67+
_update_callback_func = func;
68+
return (*this);
69+
}
70+
71+
ArduinoCloudProperty & ArduinoCloudProperty::onSync(SyncCallbackFunc func) {
72+
_sync_callback_func = func;
73+
return (*this);
74+
}
75+
76+
ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(float const min_delta_property, unsigned long const min_time_between_updates_millis) {
77+
_update_policy = UpdatePolicy::OnChange;
78+
_min_delta_property = min_delta_property;
79+
_min_time_between_updates_millis = min_time_between_updates_millis;
80+
return (*this);
81+
}
82+
83+
ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) {
84+
_update_policy = UpdatePolicy::TimeInterval;
85+
_update_interval_millis = (seconds * 1000);
86+
return (*this);
87+
}
88+
89+
bool ArduinoCloudProperty::shouldBeUpdated() {
90+
if (!_has_been_updated_once) {
91+
return true;
92+
}
93+
94+
if (_has_been_modified_in_callback) {
95+
_has_been_modified_in_callback = false;
96+
return true;
97+
}
98+
99+
if (_update_policy == UpdatePolicy::OnChange) {
100+
return (isDifferentFromCloud() && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis)));
101+
} else if (_update_policy == UpdatePolicy::TimeInterval) {
102+
return ((millis() - _last_updated_millis) >= _update_interval_millis);
103+
} else {
104+
return false;
105+
}
106+
}
107+
108+
void ArduinoCloudProperty::execCallbackOnChange() {
109+
if (_update_callback_func != NULL) {
110+
_update_callback_func();
111+
}
112+
if (!isDifferentFromCloud()) {
113+
_has_been_modified_in_callback = true;
114+
}
115+
}
116+
117+
void ArduinoCloudProperty::execCallbackOnSync() {
118+
if (_sync_callback_func != NULL) {
119+
_sync_callback_func(*this);
120+
}
121+
}
122+
123+
void ArduinoCloudProperty::append(CborEncoder *encoder, bool lightPayload) {
124+
_lightPayload = lightPayload;
125+
_attributeIdentifier = 0;
126+
appendAttributesToCloudReal(encoder);
127+
fromLocalToCloud();
128+
_has_been_updated_once = true;
129+
_last_updated_millis = millis();
130+
}
131+
132+
void ArduinoCloudProperty::appendAttributeReal(bool value, String attributeName, CborEncoder *encoder) {
133+
appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) {
134+
cbor_encode_int(&mapEncoder, static_cast<int>(CborIntegerMapKey::BooleanValue));
135+
cbor_encode_boolean(&mapEncoder, value);
136+
}, encoder);
137+
}
138+
139+
void ArduinoCloudProperty::appendAttributeReal(int value, String attributeName, CborEncoder *encoder) {
140+
appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) {
141+
cbor_encode_int(&mapEncoder, static_cast<int>(CborIntegerMapKey::Value));
142+
cbor_encode_int(&mapEncoder, value);
143+
}, encoder);
144+
}
145+
146+
void ArduinoCloudProperty::appendAttributeReal(float value, String attributeName, CborEncoder *encoder) {
147+
appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) {
148+
cbor_encode_int(&mapEncoder, static_cast<int>(CborIntegerMapKey::Value));
149+
cbor_encode_float(&mapEncoder, value);
150+
}, encoder);
151+
}
152+
153+
void ArduinoCloudProperty::appendAttributeReal(String value, String attributeName, CborEncoder *encoder) {
154+
appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) {
155+
cbor_encode_int(&mapEncoder, static_cast<int>(CborIntegerMapKey::StringValue));
156+
cbor_encode_text_stringz(&mapEncoder, value.c_str());
157+
}, encoder);
158+
}
159+
160+
void ArduinoCloudProperty::appendAttributeName(String attributeName, std::function<void (CborEncoder& mapEncoder)>appendValue, CborEncoder *encoder) {
161+
if (attributeName != "") {
162+
// 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
163+
_attributeIdentifier++;
164+
}
165+
CborEncoder mapEncoder;
166+
cbor_encoder_create_map(encoder, &mapEncoder, 2);
167+
cbor_encode_int(&mapEncoder, static_cast<int>(CborIntegerMapKey::Name));
168+
169+
// if _lightPayload is true, the property and attribute identifiers will be encoded instead of the property name
170+
if (_lightPayload) {
171+
// the most significant byte of the identifier to be encoded represent the property identifier
172+
int completeIdentifier = _attributeIdentifier * 256;
173+
// the least significant byte of the identifier to be encoded represent the attribute identifier
174+
completeIdentifier += _identifier;
175+
cbor_encode_int(&mapEncoder, completeIdentifier);
176+
} else {
177+
String completeName = _name;
178+
if (attributeName != "") {
179+
completeName += ":" + attributeName;
180+
}
181+
cbor_encode_text_stringz(&mapEncoder, completeName.c_str());
182+
}
183+
appendValue(mapEncoder);
184+
cbor_encoder_close_container(encoder, &mapEncoder);
185+
}
186+
187+
void ArduinoCloudProperty::setAttributesFromCloud(LinkedList<CborMapData *> *map_data_list) {
188+
_map_data_list = map_data_list;
189+
_attributeIdentifier = 0;
190+
setAttributesFromCloud();
191+
}
192+
193+
void ArduinoCloudProperty::setAttributeReal(bool& value, String attributeName) {
194+
setAttributeReal(attributeName, [&value](CborMapData * md) {
195+
// Manage the case to have boolean values received as integers 0/1
196+
if (md->bool_val.isSet()) {
197+
value = md->bool_val.get();
198+
} else if (md->val.isSet()) {
199+
if (md->val.get() == 0) {
200+
value = false;
201+
} else if (md->val.get() == 1) {
202+
value = true;
203+
} else {
204+
/* This should not happen. Leave the previous value */
205+
}
206+
}
207+
});
208+
}
209+
210+
void ArduinoCloudProperty::setAttributeReal(int& value, String attributeName) {
211+
setAttributeReal(attributeName, [&value](CborMapData * md) {
212+
value = md->val.get();
213+
});
214+
}
215+
216+
void ArduinoCloudProperty::setAttributeReal(float& value, String attributeName) {
217+
setAttributeReal(attributeName, [&value](CborMapData * md) {
218+
value = md->val.get();
219+
});
220+
}
221+
222+
void ArduinoCloudProperty::setAttributeReal(String& value, String attributeName) {
223+
setAttributeReal(attributeName, [&value](CborMapData * md) {
224+
value = md->str_val.get();
225+
});
226+
}
227+
228+
void ArduinoCloudProperty::setAttributeReal(String attributeName, std::function<void (CborMapData *md)>setValue) {
229+
if (attributeName != "") {
230+
_attributeIdentifier++;
231+
}
232+
for (int i = 0; i < _map_data_list->size(); i++) {
233+
CborMapData *map = _map_data_list->get(i);
234+
if (map != nullptr) {
235+
if (map->light_payload.isSet() && map->light_payload.get()) {
236+
// if a light payload is detected, the attribute identifier is retrieved from the cbor map and the corresponding attribute is updated
237+
int attid = map->attribute_identifier.get();
238+
if (attid == _attributeIdentifier) {
239+
setValue(map);
240+
break;
241+
}
242+
} else {
243+
// if a normal payload is detected, the name of the attribute to be updated is extracted directly from the cbor map
244+
String an = map->attribute_name.get();
245+
if (an == attributeName) {
246+
setValue(map);
247+
break;
248+
}
249+
}
250+
}
251+
}
252+
253+
}
254+
255+
String ArduinoCloudProperty::getAttributeName(String propertyName, char separator) {
256+
int colonPos;
257+
String attributeName = "";
258+
(colonPos = propertyName.indexOf(separator)) != -1 ? attributeName = propertyName.substring(colonPos + 1) : "";
259+
return attributeName;
260+
}
261+
262+
void ArduinoCloudProperty::updateLocalTimestamp() {
263+
if (isReadableByCloud()) {
264+
if(_get_time_func) {
265+
_last_local_change_timestamp = _get_time_func();
266+
} else {
267+
_last_local_change_timestamp = getTimestamp();
268+
}
269+
}
270+
}
271+
272+
void ArduinoCloudProperty::setLastCloudChangeTimestamp(unsigned long cloudChangeEventTime) {
273+
_last_cloud_change_timestamp = cloudChangeEventTime;
274+
}
275+
276+
void ArduinoCloudProperty::setLastLocalChangeTimestamp(unsigned long localChangeTime) {
277+
_last_local_change_timestamp = localChangeTime;
278+
}
279+
280+
unsigned long ArduinoCloudProperty::getLastCloudChangeTimestamp() {
281+
return _last_cloud_change_timestamp;
282+
}
283+
284+
unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() {
285+
return _last_local_change_timestamp;
286+
}
287+
288+
void ArduinoCloudProperty::setIdentifier(int identifier) {
289+
_identifier = identifier;
290+
}

src/ArduinoCloudProperty.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ enum class UpdatePolicy {
122122
};
123123

124124
typedef void(*UpdateCallbackFunc)(void);
125+
typedef unsigned long(*GetTimeCallbackFunc)();
125126

126127
/******************************************************************************
127128
CLASS DECLARATION
@@ -131,7 +132,7 @@ class ArduinoCloudProperty {
131132
typedef void(*SyncCallbackFunc)(ArduinoCloudProperty &property);
132133
public:
133134
ArduinoCloudProperty();
134-
void init(String const name, Permission const permission);
135+
void init(String const name, Permission const permission, GetTimeCallbackFunc func);
135136

136137
/* Composable configuration of the ArduinoCloudProperty class */
137138
ArduinoCloudProperty & onUpdate(UpdateCallbackFunc func);
@@ -192,6 +193,7 @@ class ArduinoCloudProperty {
192193

193194
private:
194195
Permission _permission;
196+
GetTimeCallbackFunc _get_time_func;
195197
UpdateCallbackFunc _update_callback_func;
196198
void (*_sync_callback_func)(ArduinoCloudProperty &property);
197199

src/ArduinoCloudThing.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ void PrintFreeRam(void) {
4444
******************************************************************************/
4545

4646
ArduinoCloudThing::ArduinoCloudThing() :
47+
_get_time_func{nullptr},
4748
_numPrimitivesProperties(0),
4849
_numProperties(0),
4950
_isSyncMessage(false),
@@ -59,6 +60,10 @@ ArduinoCloudThing::ArduinoCloudThing() :
5960
void ArduinoCloudThing::begin() {
6061
}
6162

63+
void ArduinoCloudThing::registerGetTimeCallbackFunc(GetTimeCallbackFunc func) {
64+
_get_time_func = func;
65+
}
66+
6267
int ArduinoCloudThing::encode(uint8_t * data, size_t const size, bool lightPayload) {
6368

6469
// check if backing storage and cloud has diverged
@@ -87,7 +92,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size, bool lightPaylo
8792
}
8893

8994
ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier) {
90-
property.init(name, permission);
95+
property.init(name, permission, _get_time_func);
9196
if (isPropertyInContainer(name)) {
9297
return (*getProperty(name));
9398
} else {

src/ArduinoCloudThing.h

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class ArduinoCloudThing {
7777
ArduinoCloudThing();
7878

7979
void begin();
80+
void registerGetTimeCallbackFunc(GetTimeCallbackFunc func);
8081
//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
8182
ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier = -1);
8283

@@ -93,6 +94,7 @@ class ArduinoCloudThing {
9394
String getPropertyNameByIdentifier(int propertyIdentifier);
9495

9596
private:
97+
GetTimeCallbackFunc _get_time_func;
9698
LinkedList<ArduinoCloudProperty *> _property_list;
9799
/* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */
98100
int _numPrimitivesProperties;

0 commit comments

Comments
 (0)