Skip to content

Commit

Permalink
ArduinoIoTCloudTCP: adapt state machine to use Thing process
Browse files Browse the repository at this point in the history
  • Loading branch information
pennam committed Apr 16, 2024
1 parent 20831f5 commit 0d69bbb
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 92 deletions.
1 change: 0 additions & 1 deletion src/ArduinoIoTCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ ArduinoIoTCloudClass::ArduinoIoTCloudClass()
: _connection{nullptr}
, _time_service(TimeService)
, _thing_id{""}
, _thing_id_property{nullptr}
, _lib_version{AIOT_CONFIG_LIB_VERSION}
, _device_id{""}
, _cloud_event_callback{nullptr}
Expand Down
1 change: 0 additions & 1 deletion src/ArduinoIoTCloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ class ArduinoIoTCloudClass
ConnectionHandler * _connection;
TimeServiceClass & _time_service;
String _thing_id;
Property * _thing_id_property;
String _lib_version;

void execCloudEventCallback(ArduinoIoTCloudEvent const event);
Expand Down
122 changes: 44 additions & 78 deletions src/ArduinoIoTCloudTCP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,10 @@ unsigned long getTime()
ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
: _state{State::ConnectPhy}
, _connection_attempt(0,0)
, _message_stream(std::bind(&ArduinoIoTCloudTCP::sendMessage, this, std::placeholders::_1))
, _thing(&_message_stream)
, _thing_id_property{nullptr}
, _device_property_container{0}
, _thing_property_container{0}
, _last_checked_property_index{0}
, _tz_offset{0}
, _tz_offset_property{nullptr}
, _tz_dst_until{0}
, _tz_dst_until_property{nullptr}
, _mqtt_data_buf{0}
, _mqtt_data_len{0}
, _mqtt_data_request_retransmit{false}
Expand Down Expand Up @@ -214,10 +211,8 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
#endif /* OTA_ENABLED */
p = new CloudWrapperString(_thing_id);
_thing_id_property = &addPropertyToContainer(_device_property_container, *p, "thing_id", Permission::ReadWrite, -1).writeOnDemand();
p = new CloudWrapperInt(_tz_offset);
_tz_offset_property = &addPropertyToContainer(_thing_property_container, *p, "tz_offset", Permission::ReadWrite, -1).writeOnDemand();
p = new CloudWrapperUnsignedInt(_tz_dst_until);
_tz_dst_until_property = &addPropertyToContainer(_thing_property_container, *p, "tz_dst_until", Permission::ReadWrite, -1).writeOnDemand();

_thing.begin();

#if OTA_ENABLED
_ota_cap = OTA::isCapable();
Expand Down Expand Up @@ -274,7 +269,6 @@ void ArduinoIoTCloudTCP::update()
case State::SubscribeDeviceTopic: next_state = handle_SubscribeDeviceTopic(); break;
case State::CheckDeviceConfig: next_state = handle_CheckDeviceConfig(); break;
case State::SubscribeThingTopics: next_state = handle_SubscribeThingTopics(); break;
case State::RequestLastValues: next_state = handle_RequestLastValues(); break;
case State::Connected: next_state = handle_Connected(); break;
case State::Disconnect: next_state = handle_Disconnect(); break;
}
Expand Down Expand Up @@ -478,38 +472,12 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeThingTopics()

/* Successfully subscribed to thing topics, reconfigure timers for next state and go on */
_connection_attempt.begin(AIOT_CONFIG_TIMEOUT_FOR_LASTVALUES_SYNC_ms);
return State::RequestLastValues;
}

ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_RequestLastValues()
{
if (!_mqttClient.connected() || _thing_id_property->isDifferentFromCloud())
{
return State::Disconnect;
}

/* Check whether or not we need to send a new request. */
if (_connection_attempt.isRetry() && !_connection_attempt.isExpired())
return State::RequestLastValues;

/* Track the number of times a get-last-values request was sent to the cloud.
* If no data is received within a certain number of retry-requests it's a better
* strategy to disconnect and re-establish connection from the ground up.
*/
if (_connection_attempt.getRetryCount() > AIOT_CONFIG_LASTVALUES_SYNC_MAX_RETRY_CNT)
{
return State::Disconnect;
}

_connection_attempt.retry();
requestLastValue();
DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values requested", __FUNCTION__, _time_service.getTime());
return State::RequestLastValues;
return State::Connected;
}

ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected()
{
if (!_mqttClient.connected())
if (!_mqttClient.connected() || _thing_id_property->isDifferentFromCloud() || !_thing.connected())
{
/* The last message was definitely lost, trigger a retransmit. */
_mqtt_data_request_retransmit = true;
Expand All @@ -518,20 +486,6 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected()
/* We are connected so let's to our stuff here. */
else
{
if (_thing_id_property->isDifferentFromCloud())
{
return State::Disconnect;
}

/* Check if a primitive property wrapper is locally changed.
* This function requires an existing time service which in
* turn requires an established connection. Not having that
* leads to a wrong time set in the time service which inhibits
* the connection from being established due to a wrong data
* in the reconstructed certificate.
*/
updateTimestampOnLocallyChangedProperties(_thing_property_container);

/* Retransmit data in case there was a lost transaction due
* to phy layer or MQTT connectivity loss.
*/
Expand All @@ -540,27 +494,11 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected()
_mqtt_data_request_retransmit = false;
}

/* Configure Time service with timezone data:
* _tz_offset [offset + dst]
* _tz_dst_until [posix timestamp until _tz_offset is valid]
*/
if (_tz_offset_property->isDifferentFromCloud() || _tz_dst_until_property->isDifferentFromCloud()) {
_tz_offset_property->fromCloudToLocal();
_tz_dst_until_property->fromCloudToLocal();
_time_service.setTimeZoneData(_tz_offset, _tz_dst_until);
}
/* Call CloudThing process to synchronize properties */
_thing.update();

/* Check if any properties need encoding and send them to
* the cloud if necessary.
*/
sendThingPropertiesToCloud();
return State::Connected;

unsigned long const internal_posix_time = _time_service.getTime();
if (internal_posix_time < _tz_dst_until) {
return State::Connected;
} else {
return State::RequestLastValues;
}
}
}

Expand Down Expand Up @@ -605,9 +543,15 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Disconnect()
/* TODO add device topic */
_mqttClient.stop();
}

Message message = { ResetCmdId };
_thing.handleMessage(&message);

DEBUG_INFO("Disconnected from Arduino IoT Cloud");
execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);

updateThingTopics();

/* Setup timer for broker connection and restart */
_connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms);
return State::ConnectPhy;
Expand All @@ -631,25 +575,47 @@ void ArduinoIoTCloudTCP::handleMessage(int length)
/* Topic for OTA properties and device configuration */
if (_deviceTopicIn == topic) {
CBORDecoder::decode(_device_property_container, (uint8_t*)bytes, length);
_state = State::CheckDeviceConfig;
if (_thing_id_property->isDifferentFromCloud() && (_thing_id.length() != 0)) {
_state = State::Disconnect;
} else {
_state = State::CheckDeviceConfig;
}
}

/* Topic for user input data */
if (_dataTopicIn == topic) {
CBORDecoder::decode(_thing_property_container, (uint8_t*)bytes, length);
CBORDecoder::decode(_thing.getPropertyContainer(), (uint8_t*)bytes, length);
}

/* Topic for sync Thing last values on connect */
if ((_shadowTopicIn == topic) && (_state == State::RequestLastValues))
if (_shadowTopicIn == topic)
{
DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values received", __FUNCTION__, millis());
CBORDecoder::decode(_thing_property_container, (uint8_t*)bytes, length, true);
_time_service.setTimeZoneData(_tz_offset, _tz_dst_until);
CBORDecoder::decode(_thing.getPropertyContainer(), (uint8_t*)bytes, length, true);
Message message = { LastValuesUpdateCmdId };
_thing.handleMessage(&message);
execCloudEventCallback(ArduinoIoTCloudEvent::SYNC);
_state = State::Connected;
}
}

void ArduinoIoTCloudTCP::sendMessage(Message * msg)
{
switch (msg->id)
{
case PropertiesUpdateCmdId:
sendThingPropertiesToCloud();
break;

case LastValuesBeginCmdId:
requestLastValue();
break;

default:
break;
}
}

void ArduinoIoTCloudTCP::sendPropertyContainerToCloud(String const topic, PropertyContainer & property_container, unsigned int & current_property_index)
{
int bytes_encoded = 0;
Expand All @@ -670,7 +636,7 @@ void ArduinoIoTCloudTCP::sendPropertyContainerToCloud(String const topic, Proper

void ArduinoIoTCloudTCP::sendThingPropertiesToCloud()
{
sendPropertyContainerToCloud(_dataTopicOut, _thing_property_container, _last_checked_property_index);
sendPropertyContainerToCloud(_dataTopicOut, _thing.getPropertyContainer(), _thing.getPropertyContainerIndex());
}

void ArduinoIoTCloudTCP::sendDevicePropertiesToCloud()
Expand Down
18 changes: 6 additions & 12 deletions src/ArduinoIoTCloudTCP.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include <AIoTC_Config.h>
#include <ArduinoIoTCloud.h>
#include <ArduinoMqttClient.h>
#include <utility/time/TimedAttempt.h>
#include <ArduinoIoTCloudThing.h>

#if defined(BOARD_HAS_SECURE_ELEMENT)
#include <Arduino_SecureElement.h>
Expand Down Expand Up @@ -75,7 +75,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
ArduinoIoTCloudTCP();
virtual ~ArduinoIoTCloudTCP() { }


virtual void update () override;
virtual int connected () override;
virtual void printDebugInfo() override;
Expand All @@ -91,7 +90,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
inline String getBrokerAddress() const { return _brokerAddress; }
inline uint16_t getBrokerPort () const { return _brokerPort; }

inline PropertyContainer &getThingPropertyContainer() { return _thing_property_container; }
inline PropertyContainer &getThingPropertyContainer() { return _thing.getPropertyContainer(); }

#if OTA_ENABLED
/* The callback is triggered when the OTA is initiated and it gets executed until _ota_req flag is cleared.
Expand All @@ -118,21 +117,16 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
SubscribeDeviceTopic,
CheckDeviceConfig,
SubscribeThingTopics,
RequestLastValues,
Connected,
Disconnect,
};

State _state;
TimedAttempt _connection_attempt;
MessageStream _message_stream;
ArduinoCloudThing _thing;
Property * _thing_id_property;
PropertyContainer _device_property_container;
PropertyContainer _thing_property_container;
unsigned int _last_checked_property_index;

int _tz_offset;
Property * _tz_offset_property;
unsigned int _tz_dst_until;
Property * _tz_dst_until_property;

String _brokerAddress;
uint16_t _brokerPort;
Expand Down Expand Up @@ -200,12 +194,12 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
State handle_CheckDeviceConfig();
State handle_SubscribeDeviceTopic();
State handle_SubscribeThingTopics();
State handle_RequestLastValues();
State handle_Connected();
State handle_Disconnect();

static void onMessage(int length);
void handleMessage(int length);
void sendMessage(Message * msg);
void sendPropertyContainerToCloud(String const topic, PropertyContainer & property_container, unsigned int & current_property_index);
void sendThingPropertiesToCloud();
void sendDevicePropertiesToCloud();
Expand Down

0 comments on commit 0d69bbb

Please sign in to comment.