Skip to content

Commit 5352b80

Browse files
committed
Implementation of the shadow Thing feature on the device
1 parent 25c5b71 commit 5352b80

File tree

2 files changed

+109
-10
lines changed

2 files changed

+109
-10
lines changed

src/ArduinoIoTCloud.cpp

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
#include "CloudSerial.h"
2121
#include "ArduinoIoTCloud.h"
2222

23+
#ifdef ARDUINO_ARCH_SAMD
24+
#include <RTCZero.h>
25+
RTCZero rtc;
26+
#endif
27+
2328
const static int keySlot = 0;
2429
const static int compressedCertSlot = 10;
2530
const 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+
4053
ArduinoIoTCloudClass::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

191214
void 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

208254
int 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+
257320
void 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

281357
void 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
}

src/ArduinoIoTCloud.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class ArduinoIoTCloudClass {
6363
static const int MQTT_TRANSMIT_BUFFER_SIZE = 256;
6464
static const int MAX_RETRIES = 5;
6565
static const int RECONNECTION_TIMEOUT = 2000;
66+
static const int MAX_CHECK_LASTVALUES_SYNC = 20;
6667

6768

6869
void onGetTime(unsigned long(*callback)(void));
@@ -71,10 +72,12 @@ class ArduinoIoTCloudClass {
7172
bool disconnect();
7273

7374
void poll() __attribute__((deprecated)); /* Attention: Function is deprecated - use 'update' instead */
74-
void update();
75+
void update(int mode = PROPERTIES_SYNC_FORCE_DEVICE, void (*callback)(void) = NULL);
7576

7677
// defined for users who want to specify max reconnections reties and timeout between them
77-
void update(int const reconnectionMaxRetries, int const reconnectionTimeoutMs);
78+
void update(int const reconnectionMaxRetries, int const reconnectionTimeoutMs, int mode = PROPERTIES_SYNC_FORCE_DEVICE, void (*callback)(void) = NULL);
79+
// get the status of synchronization after getLastValues request
80+
bool getLastValuesSyncStatus();
7881

7982
int connected();
8083
// Clean up existing Mqtt connection, create a new one and initialize it
@@ -120,10 +123,14 @@ class ArduinoIoTCloudClass {
120123
friend class CloudSerialClass;
121124
int writeStdout(const byte data[], int length);
122125
int writeProperties(const byte data[], int length);
126+
int writeShadowOut(const byte data[], int length);
127+
123128
// Used to initialize MQTTClient
124129
void mqttClientBegin();
125130
// Function in charge of perform MQTT reconnection, basing on class parameters(retries,and timeout)
126131
bool mqttReconnect(int const maxRetries, int const timeout);
132+
// Used to retrieve ast values from _shadowTopicIn
133+
void getLastValues();
127134

128135
ArduinoIoTConnectionStatus getIoTStatus() { return iotStatus; }
129136
void setIoTConnectionState(ArduinoIoTConnectionStatus _newState);
@@ -140,14 +147,25 @@ class ArduinoIoTCloudClass {
140147
ArduinoCloudThing Thing;
141148
BearSSLClient* _bearSslClient;
142149
MqttClient* _mqttClient;
150+
int _mode,
151+
_check_lastValues_sync;
152+
bool _callGetLastValueCallback;
153+
143154

144155
// Class attribute to define MTTQ topics 2 for stdIn/out and 2 for data, in order to avoid getting previous pupblished payload
145156
String _stdinTopic;
146157
String _stdoutTopic;
158+
String _shadowTopicOut;
159+
String _shadowTopicIn;
147160
String _dataTopicOut;
148161
String _dataTopicIn;
149162
String _otaTopic;
150163
Client *_net;
164+
165+
// Used to synchronize properties
166+
bool _firstReconnectionUpdate;
167+
bool _propertiesSynchronized;
168+
void (*_syncCallback)(void);
151169
};
152170

153171
extern ArduinoIoTCloudClass ArduinoCloud;

0 commit comments

Comments
 (0)