2020#include " CloudSerial.h"
2121#include " ArduinoIoTCloud.h"
2222
23+ #ifdef ARDUINO_ARCH_SAMD
24+ #include < RTCZero.h>
25+ RTCZero rtc;
26+ #endif
27+
2328const static int keySlot = 0 ;
2429const static int compressedCertSlot = 10 ;
2530const static int serialNumberAndAuthorityKeyIdentifierSlot = 11 ;
@@ -37,11 +42,21 @@ static unsigned long getTime() {
3742 return time;
3843}
3944
45+ static unsigned long getTimestamp () {
46+ #ifdef ARDUINO_ARCH_SAMD
47+ return rtc.getEpoch ();
48+ #else
49+ #warning "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ."
50+ return 0 ;
51+ #endif
52+ }
53+
4054ArduinoIoTCloudClass::ArduinoIoTCloudClass () :
4155 _thing_id (" " ),
4256 _bearSslClient(NULL ),
4357 _mqttClient (NULL ),
44- connection (NULL )
58+ connection (NULL ),
59+ _lastSyncRequestTickTime(0 )
4560{
4661}
4762
@@ -64,6 +79,9 @@ int ArduinoIoTCloudClass::begin(ConnectionManager *c, String brokerAddress, uint
6479 Client &connectionClient = c->getClient ();
6580 _brokerAddress = brokerAddress;
6681 _brokerPort = brokerPort;
82+ #ifdef ARDUINO_ARCH_SAMD
83+ rtc.begin ();
84+ #endif
6785 return begin (connectionClient, _brokerAddress, _brokerPort);
6886}
6987
@@ -148,6 +166,8 @@ void ArduinoIoTCloudClass::mqttClientBegin()
148166 else {
149167 _dataTopicIn = " /a/t/" + _thing_id + " /e/i" ;
150168 _dataTopicOut = " /a/t/" + _thing_id + " /e/o" ;
169+ _shadowTopicIn = " /a/t/" + _thing_id + " /shadow/i" ;
170+ _shadowTopicOut = " /a/t/" + _thing_id + " /shadow/o" ;
151171 }
152172
153173 // use onMessage as callback for received mqtt messages
@@ -166,6 +186,10 @@ int ArduinoIoTCloudClass::connect()
166186 }
167187 _mqttClient->subscribe (_stdinTopic);
168188 _mqttClient->subscribe (_dataTopicIn);
189+ _mqttClient->subscribe (_shadowTopicIn);
190+
191+ _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES;
192+ _lastSyncRequestTickTime = 0 ;
169193
170194 return 1 ;
171195}
@@ -182,14 +206,18 @@ void ArduinoIoTCloudClass::poll()
182206 update ();
183207}
184208
185- void ArduinoIoTCloudClass::update ()
209+ void ArduinoIoTCloudClass::update (CallbackFunc onSyncCompleteCallback )
186210{
187211 // If user call update() without parameters use the default ones
188- update (MAX_RETRIES, RECONNECTION_TIMEOUT);
212+ update (MAX_RETRIES, RECONNECTION_TIMEOUT, onSyncCompleteCallback );
189213}
190214
191- void ArduinoIoTCloudClass::update (int const reconnectionMaxRetries, int const reconnectionTimeoutMs)
215+ void ArduinoIoTCloudClass::update (int const reconnectionMaxRetries, int const reconnectionTimeoutMs, CallbackFunc onSyncCompleteCallback )
192216{
217+ unsigned long const timestamp = getTimestamp ();
218+ // check if a property is changed
219+ if (timestamp != 0 ) Thing.updateTimestampOnChangedProperties (timestamp);
220+
193221 connectionCheck ();
194222 if (iotStatus != IOT_STATUS_CLOUD_CONNECTED){
195223 return ;
@@ -198,6 +226,26 @@ void ArduinoIoTCloudClass::update(int const reconnectionMaxRetries, int const re
198226 // MTTQClient connected!, poll() used to retrieve data from MQTT broker
199227 _mqttClient->poll ();
200228
229+ switch (_syncStatus) {
230+ case ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED:
231+ sendPropertiesToCloud ();
232+ break ;
233+ case ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES:
234+ if (millis () - _lastSyncRequestTickTime > TIMEOUT_FOR_LASTVALUES_SYNC) {
235+ requestLastValue ();
236+ _lastSyncRequestTickTime = millis ();
237+ }
238+ break ;
239+ case ArduinoIoTSynchronizationStatus::SYNC_STATUS_VALUES_PROCESSED:
240+ if (onSyncCompleteCallback != NULL )
241+ (*onSyncCompleteCallback)();
242+ _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_SYNCHRONIZED;
243+ break ;
244+ }
245+ }
246+
247+ void ArduinoIoTCloudClass::sendPropertiesToCloud ()
248+ {
201249 uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE];
202250 int const length = Thing.encode (data, sizeof (data));
203251 if (length > 0 ) {
@@ -254,6 +302,23 @@ int ArduinoIoTCloudClass::writeStdout(const byte data[], int length)
254302 return 1 ;
255303}
256304
305+ int ArduinoIoTCloudClass::writeShadowOut (const byte data[], int length)
306+ {
307+ if (!_mqttClient->beginMessage (_shadowTopicOut, length, false , 0 )) {
308+ return 0 ;
309+ }
310+
311+ if (!_mqttClient->write (data, length)) {
312+ return 0 ;
313+ }
314+
315+ if (!_mqttClient->endMessage ()) {
316+ return 0 ;
317+ }
318+
319+ return 1 ;
320+ }
321+
257322void ArduinoIoTCloudClass::onMessage (int length)
258323{
259324 ArduinoCloud.handleMessage (length);
@@ -276,6 +341,20 @@ void ArduinoIoTCloudClass::handleMessage(int length)
276341 if (_dataTopicIn == topic) {
277342 Thing.decode ((uint8_t *)bytes, length);
278343 }
344+ if ((_shadowTopicIn == topic) && _syncStatus == ArduinoIoTSynchronizationStatus::SYNC_STATUS_WAIT_FOR_CLOUD_VALUES) {
345+ Thing.decode ((uint8_t *)bytes, length, true );
346+ sendPropertiesToCloud ();
347+ _syncStatus = ArduinoIoTSynchronizationStatus::SYNC_STATUS_VALUES_PROCESSED;
348+ }
349+ }
350+
351+ void ArduinoIoTCloudClass::requestLastValue ()
352+ {
353+ // Send the getLastValues CBOR message to the cloud
354+ // [{0: "r:m", 3: "getLastValues"}] = 81 A2 00 63 72 3A 6D 03 6D 67 65 74 4C 61 73 74 56 61 6C 75 65 73
355+ // Use http://cbor.me to easily generate CBOR encoding
356+ const uint8_t CBOR_REQUEST_LAST_VALUE_MSG[] = { 0x81 , 0xA2 , 0x00 , 0x63 , 0x72 , 0x3A , 0x6D , 0x03 , 0x6D , 0x67 , 0x65 , 0x74 , 0x4C , 0x61 , 0x73 , 0x74 , 0x56 , 0x61 , 0x6C , 0x75 , 0x65 , 0x73 };
357+ writeShadowOut (CBOR_REQUEST_LAST_VALUE_MSG, sizeof (CBOR_REQUEST_LAST_VALUE_MSG));
279358}
280359
281360void ArduinoIoTCloudClass::connectionCheck ()
@@ -332,6 +411,11 @@ void ArduinoIoTCloudClass::connectionCheck()
332411 CloudSerial.begin (9600 );
333412 CloudSerial.println (" Hello from Cloud Serial!" );
334413 }
414+ #ifdef ARDUINO_ARCH_SAMD
415+ unsigned long const epoch = getTime ();
416+ if (epoch!=0 )
417+ rtc.setEpoch (epoch);
418+ #endif
335419 break ;
336420 }
337421}
0 commit comments