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,20 @@ 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+ return 0 ;
50+ #endif
51+ }
52+
4053ArduinoIoTCloudClass::ArduinoIoTCloudClass () :
4154 _thing_id (" " ),
4255 _bearSslClient(NULL ),
4356 _mqttClient (NULL ),
44- connection (NULL )
57+ connection (NULL ),
58+ _lastSyncRequestTickTime(0 )
4559{
4660}
4761
@@ -64,6 +78,9 @@ int ArduinoIoTCloudClass::begin(ConnectionManager *c, String brokerAddress, uint
6478 Client &connectionClient = c->getClient ();
6579 _brokerAddress = brokerAddress;
6680 _brokerPort = brokerPort;
81+ #ifdef ARDUINO_ARCH_SAMD
82+ rtc.begin ();
83+ #endif
6784 return begin (connectionClient, _brokerAddress, _brokerPort);
6885}
6986
@@ -148,6 +165,8 @@ void ArduinoIoTCloudClass::mqttClientBegin()
148165 else {
149166 _dataTopicIn = " /a/t/" + _thing_id + " /e/i" ;
150167 _dataTopicOut = " /a/t/" + _thing_id + " /e/o" ;
168+ _shadowTopicIn = " /a/t/" + _thing_id + " /shadow/i" ;
169+ _shadowTopicOut = " /a/t/" + _thing_id + " /shadow/o" ;
151170 }
152171
153172 // use onMessage as callback for received mqtt messages
@@ -166,6 +185,10 @@ int ArduinoIoTCloudClass::connect()
166185 }
167186 _mqttClient->subscribe (_stdinTopic);
168187 _mqttClient->subscribe (_dataTopicIn);
188+ _mqttClient->subscribe (_shadowTopicIn);
189+
190+ syncStatus = SYNC_STATUS_WAIT_FOR_CLOUD_VALUES;
191+ _lastSyncRequestTickTime = 0 ;
169192
170193 return 1 ;
171194}
@@ -182,14 +205,18 @@ void ArduinoIoTCloudClass::poll()
182205 update ();
183206}
184207
185- void ArduinoIoTCloudClass::update ()
208+ void ArduinoIoTCloudClass::update (void (*callback)( void ) )
186209{
187210 // If user call update() without parameters use the default ones
188- update (MAX_RETRIES, RECONNECTION_TIMEOUT);
211+ update (MAX_RETRIES, RECONNECTION_TIMEOUT, callback );
189212}
190213
191214void ArduinoIoTCloudClass::update (int const reconnectionMaxRetries, int const reconnectionTimeoutMs)
192215{
216+ unsigned long timestamp = getTimestamp ();
217+ // check if a property is changed
218+ if (timestamp) Thing.updateTimestampOnChangedProperties (timestamp);
219+
193220 connectionCheck ();
194221 if (iotStatus != IOT_STATUS_CLOUD_CONNECTED){
195222 return ;
@@ -198,11 +225,30 @@ void ArduinoIoTCloudClass::update(int const reconnectionMaxRetries, int const re
198225 // MTTQClient connected!, poll() used to retrieve data from MQTT broker
199226 _mqttClient->poll ();
200227
201- uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE];
202- int const length = Thing.encode (data, sizeof (data));
203- if (length > 0 ) {
204- writeProperties (data, length);
205- }
228+ switch (syncStatus) {
229+ case SYNC_STATUS_SYNCHRONIZED:
230+ sendPropertiesToCloud ();
231+ break ;
232+ case SYNC_STATUS_WAIT_FOR_CLOUD_VALUES:
233+ if (millis () - _lastSyncRequestTickTime > TIMEOUT_FOR_LASTVALUES_SYNC) {
234+ requestLastValue ();
235+ _lastSyncRequestTickTime = millis ();
236+ }
237+ break ;
238+ case SYNC_STATUS_VALUES_PROCESSED:
239+ if (callback != NULL )
240+ (*callback)();
241+ syncStatus = SYNC_STATUS_SYNCHRONIZED;
242+ break ;
243+ }
244+ }
245+
246+ void ArduinoIoTCloudClass::sendPropertiesToCloud () {
247+ uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE];
248+ int const length = Thing.encode (data, sizeof (data));
249+ if (length > 0 ) {
250+ writeProperties (data, length);
251+ }
206252}
207253
208254int ArduinoIoTCloudClass::reconnect (Client& /* net */ )
@@ -254,6 +300,23 @@ int ArduinoIoTCloudClass::writeStdout(const byte data[], int length)
254300 return 1 ;
255301}
256302
303+ int ArduinoIoTCloudClass::writeShadowOut (const byte data[], int length)
304+ {
305+ if (!_mqttClient->beginMessage (_shadowTopicOut, length, false , 0 )) {
306+ return 0 ;
307+ }
308+
309+ if (!_mqttClient->write (data, length)) {
310+ return 0 ;
311+ }
312+
313+ if (!_mqttClient->endMessage ()) {
314+ return 0 ;
315+ }
316+
317+ return 1 ;
318+ }
319+
257320void ArduinoIoTCloudClass::onMessage (int length)
258321{
259322 ArduinoCloud.handleMessage (length);
@@ -276,6 +339,19 @@ void ArduinoIoTCloudClass::handleMessage(int length)
276339 if (_dataTopicIn == topic) {
277340 Thing.decode ((uint8_t *)bytes, length);
278341 }
342+ if ((_shadowTopicIn == topic) && syncStatus == SYNC_STATUS_WAIT_FOR_CLOUD_VALUES) {
343+ Thing.decode ((uint8_t *)bytes, length, true );
344+ sendPropertiesToCloud ();
345+ syncStatus = SYNC_STATUS_VALUES_PROCESSED;
346+ }
347+ }
348+
349+ void ArduinoIoTCloudClass::requestLastValue ()
350+ {
351+ // Send the getLastValues CBOR message to the cloud
352+ // [{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
353+ const uint8_t data[] = { 0x81 , 0xA2 , 0x00 , 0x63 , 0x72 , 0x3A , 0x6D , 0x03 , 0x6D , 0x67 , 0x65 , 0x74 , 0x4C , 0x61 , 0x73 , 0x74 , 0x56 , 0x61 , 0x6C , 0x75 , 0x65 , 0x73 };
354+ writeShadowOut (data, sizeof data);
279355}
280356
281357void ArduinoIoTCloudClass::connectionCheck ()
@@ -332,6 +408,11 @@ void ArduinoIoTCloudClass::connectionCheck()
332408 CloudSerial.begin (9600 );
333409 CloudSerial.println (" Hello from Cloud Serial!" );
334410 }
411+ #ifdef ARDUINO_ARCH_SAMD
412+ unsigned long epoch = getTime ();
413+ if (epoch!=0 )
414+ rtc.setEpoch (epoch);
415+ #endif
335416 break ;
336417 }
337418}
0 commit comments