Skip to content

Commit a0916cd

Browse files
committed
ArduinoIoTCloudTCP state machine changes to handle thig_id discovery protocol
1 parent 7adfea5 commit a0916cd

File tree

4 files changed

+202
-29
lines changed

4 files changed

+202
-29
lines changed

src/ArduinoIoTCloud.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,12 @@ class ArduinoIoTCloudClass
9696
inline void setDeviceId(String const device_id) { _device_id = device_id; };
9797
inline String & getDeviceId() { return _device_id; };
9898

99-
inline bool deviceNotAttached() { return _thing_id == "null";}
100-
inline bool deviceNotConfigured() { return _thing_id == "";}
99+
inline void setThingIdOutdatedFlag() { _thing_id_outdated = true ; }
100+
inline void clrThingIdOutdatedFlag() { _thing_id_outdated = false ; }
101+
inline bool getThingIdOutdatedFlag() { return _thing_id_outdated; }
102+
103+
inline bool deviceNotAttached() { return _thing_id == "null"; }
104+
inline bool deviceNotConfigured() { return _thing_id == ""; }
101105

102106
inline ConnectionHandler * getConnection() { return _connection; }
103107

@@ -178,6 +182,7 @@ class ArduinoIoTCloudClass
178182

179183
String _device_id;
180184
OnCloudEventCallback _cloud_event_callback[3];
185+
bool _thing_id_outdated;
181186
};
182187

183188
#ifdef HAS_TCP

src/ArduinoIoTCloudLPWAN.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ ArduinoIoTCloudLPWAN::State ArduinoIoTCloudLPWAN::handle_Connected()
120120
}
121121

122122
/* Check if a primitive property wrapper is locally changed. */
123-
updateTimestampOnLocallyChangedProperties(_property_container);
123+
updateTimestampOnLocallyChangedProperties(_thing_property_container);
124124

125125
/* Decode available data. */
126126
if (_connection->available())

src/ArduinoIoTCloudTCP.cpp

+171-20
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ extern "C" void updateTimezoneInfo()
6969
ArduinoCloud.updateInternalTimezoneInfo();
7070
}
7171

72+
extern "C" void setThingIdOutdated()
73+
{
74+
ArduinoCloud.setThingIdOutdatedFlag();
75+
}
76+
7277
/******************************************************************************
7378
CTOR/DTOR
7479
******************************************************************************/
@@ -77,6 +82,8 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
7782
: _state{State::ConnectPhy}
7883
, _next_connection_attempt_tick{0}
7984
, _last_connection_attempt_cnt{0}
85+
, _next_device_subscribe_attempt_tick{0}
86+
, _last_device_subscribe_cnt{0}
8087
, _last_sync_request_tick{0}
8188
, _last_sync_request_cnt{0}
8289
, _last_subscribe_request_tick{0}
@@ -91,10 +98,13 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
9198
, _password("")
9299
#endif
93100
, _mqttClient{nullptr}
101+
, _deviceTopicOut("")
102+
, _deviceTopicIn("")
94103
, _shadowTopicOut("")
95104
, _shadowTopicIn("")
96105
, _dataTopicOut("")
97106
, _dataTopicIn("")
107+
, _deviceSubscribedToThing{false}
98108
#if OTA_ENABLED
99109
, _ota_cap{false}
100110
, _ota_error{static_cast<int>(OTAError::None)}
@@ -237,10 +247,8 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
237247
_mqttClient.setConnectionTimeout(1500);
238248
_mqttClient.setId(getDeviceId().c_str());
239249

240-
_shadowTopicOut = getTopic_shadowout();
241-
_shadowTopicIn = getTopic_shadowin();
242-
_dataTopicOut = getTopic_dataout();
243-
_dataTopicIn = getTopic_datain();
250+
_deviceTopicOut = getTopic_deviceout();
251+
_deviceTopicIn = getTopic_devicein();
244252

245253
#if OTA_ENABLED
246254
addPropertyReal(_ota_cap, _device_property_container, "OTA_CAP", Permission::Read);
@@ -253,7 +261,7 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
253261
addPropertyReal(_tz_offset, _thing_property_container, "tz_offset", Permission::ReadWrite).onSync(CLOUD_WINS).onUpdate(updateTimezoneInfo);
254262
addPropertyReal(_tz_dst_until, _thing_property_container, "tz_dst_until", Permission::ReadWrite).onSync(CLOUD_WINS).onUpdate(updateTimezoneInfo);
255263

256-
addPropertyReal(_thing_id, _device_property_container, "thing_id", Permission::ReadWrite);
264+
addPropertyReal(_thing_id, _device_property_container, "thing_id", Permission::ReadWrite).onUpdate(setThingIdOutdated);
257265

258266
#if OTA_STORAGE_PORTENTA_QSPI
259267
#define BOOTLOADER_ADDR (0x8000000)
@@ -322,12 +330,16 @@ void ArduinoIoTCloudTCP::update()
322330
State next_state = _state;
323331
switch (_state)
324332
{
325-
case State::ConnectPhy: next_state = handle_ConnectPhy(); break;
326-
case State::SyncTime: next_state = handle_SyncTime(); break;
327-
case State::ConnectMqttBroker: next_state = handle_ConnectMqttBroker(); break;
328-
case State::SubscribeMqttTopics: next_state = handle_SubscribeMqttTopics(); break;
329-
case State::RequestLastValues: next_state = handle_RequestLastValues(); break;
330-
case State::Connected: next_state = handle_Connected(); break;
333+
case State::ConnectPhy: next_state = handle_ConnectPhy(); break;
334+
case State::SyncTime: next_state = handle_SyncTime(); break;
335+
case State::ConnectMqttBroker: next_state = handle_ConnectMqttBroker(); break;
336+
case State::SendDeviceProperties: next_state = handle_SendDeviceProperties(); break;
337+
case State::SubscribeDeviceTopic: next_state = handle_SubscribeDeviceTopic(); break;
338+
case State::WaitDeviceConfig: next_state = handle_WaitDeviceConfig(); break;
339+
case State::CheckDeviceConfig: next_state = handle_CheckDeviceConfig(); break;
340+
case State::SubscribeThingTopics: next_state = handle_SubscribeThingTopics(); break;
341+
case State::RequestLastValues: next_state = handle_RequestLastValues(); break;
342+
case State::Connected: next_state = handle_Connected(); break;
331343
}
332344
_state = next_state;
333345

@@ -385,7 +397,7 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectMqttBroker()
385397
if (_mqttClient.connect(_brokerAddress.c_str(), _brokerPort))
386398
{
387399
_last_connection_attempt_cnt = 0;
388-
return State::SubscribeMqttTopics;
400+
return State::SendDeviceProperties;
389401
}
390402

391403
_last_connection_attempt_cnt++;
@@ -398,7 +410,127 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectMqttBroker()
398410
return State::ConnectPhy;
399411
}
400412

401-
ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeMqttTopics()
413+
ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SendDeviceProperties()
414+
{
415+
if (!_mqttClient.connected())
416+
{
417+
DEBUG_ERROR("ArduinoIoTCloudTCP::%s MQTT client connection lost", __FUNCTION__);
418+
_mqttClient.stop();
419+
execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);
420+
return State::ConnectPhy;
421+
}
422+
423+
#if OTA_ENABLED
424+
sendOTAPropertiesToCloud();
425+
#endif
426+
return State::SubscribeDeviceTopic;
427+
}
428+
429+
ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeDeviceTopic()
430+
{
431+
if (!_mqttClient.connected())
432+
{
433+
DEBUG_ERROR("ArduinoIoTCloudTCP::%s MQTT client connection lost", __FUNCTION__);
434+
_mqttClient.stop();
435+
execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);
436+
return State::ConnectPhy;
437+
}
438+
439+
if (!_mqttClient.subscribe(_deviceTopicIn))
440+
{
441+
return State::SubscribeDeviceTopic;
442+
}
443+
444+
if (_last_device_subscribe_cnt > AIOT_CONFIG_LASTVALUES_SYNC_MAX_RETRY_CNT)
445+
{
446+
_last_device_subscribe_cnt = 0;
447+
_next_device_subscribe_attempt_tick = 0;
448+
_mqttClient.stop();
449+
execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);
450+
return State::ConnectPhy;
451+
}
452+
453+
unsigned long reconnection_retry_delay = (1 << _last_device_subscribe_cnt) * AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms;
454+
reconnection_retry_delay = min(reconnection_retry_delay, static_cast<unsigned long>(AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms));
455+
_next_device_subscribe_attempt_tick = millis() + reconnection_retry_delay;
456+
_last_device_subscribe_cnt++;
457+
458+
return State::WaitDeviceConfig;
459+
}
460+
461+
ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_WaitDeviceConfig()
462+
{
463+
if (!_mqttClient.connected())
464+
{
465+
DEBUG_ERROR("ArduinoIoTCloudTCP::%s MQTT client connection lost", __FUNCTION__);
466+
_mqttClient.stop();
467+
execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);
468+
return State::ConnectPhy;
469+
}
470+
471+
if (millis() > _next_device_subscribe_attempt_tick)
472+
{
473+
/* Configuration not received try to resubscribe */
474+
if (_mqttClient.unsubscribe(_deviceTopicIn))
475+
{
476+
return State::SubscribeDeviceTopic;
477+
}
478+
}
479+
480+
return State::WaitDeviceConfig;
481+
}
482+
483+
ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_CheckDeviceConfig()
484+
{
485+
if (!_mqttClient.connected())
486+
{
487+
DEBUG_ERROR("ArduinoIoTCloudTCP::%s MQTT client connection lost", __FUNCTION__);
488+
_mqttClient.stop();
489+
execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);
490+
return State::ConnectPhy;
491+
}
492+
493+
if(getThingIdOutdatedFlag())
494+
{
495+
if(_deviceSubscribedToThing == true)
496+
{
497+
/* Unsubscribe from old things topics and go on with a new subsctiption */
498+
_mqttClient.unsubscribe(_shadowTopicIn);
499+
_mqttClient.unsubscribe(_dataTopicIn);
500+
501+
_deviceSubscribedToThing = false;
502+
}
503+
}
504+
505+
updateThingTopics();
506+
507+
if (deviceNotConfigured())
508+
{
509+
/* maybe we have only missed the thing_id property...
510+
* unsubsribe and resubscribe immediately to trigger a new configuration command
511+
*/
512+
_mqttClient.unsubscribe(_deviceTopicIn);
513+
return State::SubscribeDeviceTopic;
514+
}
515+
516+
if (deviceNotAttached())
517+
{
518+
/* start long timeout counter
519+
* return return State::SubscribeThingTopics
520+
* if long timeout expired unsubscribe and
521+
* return State::SubscribeDeviceTopic
522+
*/
523+
unsigned long reconnection_retry_delay = (1 << _last_device_subscribe_cnt) * AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms * 10000;
524+
reconnection_retry_delay = min(reconnection_retry_delay, static_cast<unsigned long>(AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms));
525+
_next_device_subscribe_attempt_tick = millis() + reconnection_retry_delay;
526+
_last_device_subscribe_cnt++;
527+
return State::WaitDeviceConfig;
528+
}
529+
530+
return State::SubscribeThingTopics;
531+
}
532+
533+
ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeThingTopics()
402534
{
403535
if (!_mqttClient.connected())
404536
{
@@ -414,7 +546,7 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeMqttTopics()
414546

415547
if (!is_first_subscribe_request && !is_subscribe_retry_delay_expired)
416548
{
417-
return State::SubscribeMqttTopics;
549+
return State::SubscribeThingTopics;
418550
}
419551

420552
if (_last_subscribe_request_cnt > AIOT_CONFIG_SUBSCRIBE_MAX_RETRY_CNT)
@@ -435,7 +567,7 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeMqttTopics()
435567
#if !defined(__AVR__)
436568
DEBUG_ERROR("Check your thing configuration, and press the reset button on your board.");
437569
#endif
438-
return State::SubscribeMqttTopics;
570+
return State::SubscribeThingTopics;
439571
}
440572

441573
if (_shadowTopicIn != "")
@@ -446,14 +578,13 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeMqttTopics()
446578
#if !defined(__AVR__)
447579
DEBUG_ERROR("Check your thing configuration, and press the reset button on your board.");
448580
#endif
449-
return State::SubscribeMqttTopics;
581+
return State::SubscribeThingTopics;
450582
}
451583
}
452584

453585
DEBUG_INFO("Connected to Arduino IoT Cloud");
454586
execCloudEventCallback(ArduinoIoTCloudEvent::CONNECT);
455-
_last_subscribe_request_cnt = 0;
456-
_last_subscribe_request_tick = 0;
587+
_deviceSubscribedToThing = true;
457588

458589
if (_shadowTopicIn != "")
459590
return State::RequestLastValues;
@@ -587,10 +718,20 @@ void ArduinoIoTCloudTCP::handleMessage(int length)
587718
bytes[i] = _mqttClient.read();
588719
}
589720

721+
/* Topic for OTA properties and device configuration */
722+
if (_deviceTopicIn == topic) {
723+
CBORDecoder::decode(_device_property_container, (uint8_t*)bytes, length);
724+
_last_device_subscribe_cnt = 0;
725+
_next_device_subscribe_attempt_tick = 0;
726+
_state = State::CheckDeviceConfig;
727+
}
728+
729+
/* Topic for user input data */
590730
if (_dataTopicIn == topic) {
591731
CBORDecoder::decode(_thing_property_container, (uint8_t*)bytes, length);
592732
}
593733

734+
/* Topic for sync Thing last values on connect */
594735
if ((_shadowTopicIn == topic) && (_state == State::RequestLastValues))
595736
{
596737
DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values received", __FUNCTION__, millis());
@@ -634,7 +775,7 @@ void ArduinoIoTCloudTCP::sendOTAPropertiesToCloud()
634775
PropertyContainer ota_property_container;
635776
unsigned int last_ota_property_index = 0;
636777

637-
std::list<String> const ota_property_list {"OTA_CAP", "OTA_ERROR", "OTA_SHA256", "OTA_URL", "OTA_REQ"};
778+
std::list<String> const ota_property_list {"OTA_CAP", "OTA_ERROR", "OTA_SHA256"};
638779
std::for_each(ota_property_list.cbegin(),
639780
ota_property_list.cend(),
640781
[this, &ota_property_container ] (String const & name)
@@ -645,7 +786,7 @@ void ArduinoIoTCloudTCP::sendOTAPropertiesToCloud()
645786
}
646787
);
647788

648-
sendPropertyContainerToCloud(_dataTopicOut, ota_property_container, last_ota_property_index);
789+
sendPropertyContainerToCloud(_deviceTopicOut, ota_property_container, last_ota_property_index);
649790
}
650791
#endif
651792

@@ -689,6 +830,16 @@ void ArduinoIoTCloudTCP::onOTARequest()
689830
}
690831
#endif
691832

833+
void ArduinoIoTCloudTCP::updateThingTopics()
834+
{
835+
_shadowTopicOut = getTopic_shadowout();
836+
_shadowTopicIn = getTopic_shadowin();
837+
_dataTopicOut = getTopic_dataout();
838+
_dataTopicIn = getTopic_datain();
839+
840+
clrThingIdOutdatedFlag();
841+
}
842+
692843
/******************************************************************************
693844
* EXTERN DEFINITION
694845
******************************************************************************/

0 commit comments

Comments
 (0)