From f7d8ba73c2cdca25e9e7154d5ea403f98765ebb9 Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 11 Apr 2024 17:27:55 +0200 Subject: [PATCH 1/3] Add ArduinoCloudDevice --- src/ArduinoIoTCloudDevice.cpp | 144 ++++++++++++++++++++++++++++++++++ src/ArduinoIoTCloudDevice.h | 70 +++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 src/ArduinoIoTCloudDevice.cpp create mode 100644 src/ArduinoIoTCloudDevice.h diff --git a/src/ArduinoIoTCloudDevice.cpp b/src/ArduinoIoTCloudDevice.cpp new file mode 100644 index 000000000..f72b65588 --- /dev/null +++ b/src/ArduinoIoTCloudDevice.cpp @@ -0,0 +1,144 @@ +/* + This file is part of the ArduinoIoTCloud library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include + +#ifdef HAS_TCP + +#include "ArduinoIoTCloudDevice.h" +#include "interfaces/CloudProcess.h" + +/****************************************************************************** + CTOR/DTOR + ******************************************************************************/ +ArduinoCloudDevice::ArduinoCloudDevice(MessageStream *ms) +: CloudProcess(ms), +_state{State::Init}, +_attachAttempt(0, 0), +_attached(false), +_registered(false) { +} + +void ArduinoCloudDevice::begin() { + _attachAttempt.begin(AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms, + AIOT_CONFIG_MAX_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms); +} + +void ArduinoCloudDevice::update() { + /* Run through the state machine. */ + State nextState = _state; + switch (_state) { + case State::Init: nextState = handleInit(); break; + case State::SendCapabilities: nextState = handleSendCapabilities(); break; + case State::Connected: nextState = handleConnected(); break; + case State::Disconnected: nextState = handleDisconnected(); break; + } + + /* Handle external events */ + switch (_command) { + case DeviceAttachedCmdId: + _attached = true; + _registered = true; + DEBUG_VERBOSE("CloudDevice::%s Device is attached", __FUNCTION__); + nextState = State::Connected; + break; + + case DeviceDetachedCmdId: + _attached = false; + _registered = false; + nextState = State::Init; + break; + + case DeviceRegisteredCmdId: + _registered = true; + nextState = State::Connected; + break; + + /* We have received a reset command */ + case ResetCmdId: + nextState = State::Init; + break; + + default: + break; + } + + _command = UnknownCmdId; + _state = nextState; +} + +int ArduinoCloudDevice::connected() { + return _state != State::Disconnected ? 1 : 0; +} + +void ArduinoCloudDevice::handleMessage(Message *m) { + _command = UnknownCmdId; + if (m != nullptr) { + _command = m->id; + } +} + +ArduinoCloudDevice::State ArduinoCloudDevice::handleInit() { + /* Reset attempt struct for the nex retry after disconnection */ + _attachAttempt.begin(AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms, + AIOT_CONFIG_MAX_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms); + + _attached = false; + _registered = false; + + return State::SendCapabilities; +} + +ArduinoCloudDevice::State ArduinoCloudDevice::handleSendCapabilities() { + /* Sends device capabilities message */ + Message message = { DeviceBeginCmdId }; + deliver(&message); + + /* Subscribe to device topic to request */ + message = { ThingBeginCmdId }; + deliver(&message); + + /* No device configuration received. Wait: 4s -> 8s -> 16s -> 32s -> 32s ...*/ + _attachAttempt.retry(); + DEBUG_VERBOSE("CloudDevice::%s not attached. %d next configuration request in %d ms", + __FUNCTION__, _attachAttempt.getRetryCount(), _attachAttempt.getWaitTime()); + return State::Connected; +} + +ArduinoCloudDevice::State ArduinoCloudDevice::handleConnected() { + /* Max retry than disconnect */ + if (_attachAttempt.getRetryCount() > AIOT_CONFIG_DEVICE_TOPIC_MAX_RETRY_CNT) { + return State::Disconnected; + } + + if (!_attached && _attachAttempt.isExpired()) { + if (_registered) { + /* Device configuration received, but invalid thing_id. Do not increase + * counter, but recompute delay. + * Wait: 4s -> 80s -> 160s -> 320s -> 640s -> 1280s -> 1280s ... + */ + _attachAttempt.reconfigure(AIOT_CONFIG_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms, + AIOT_CONFIG_MAX_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms); + } + return State::SendCapabilities; + } + + return State::Connected; +} + +ArduinoCloudDevice::State ArduinoCloudDevice::handleDisconnected() { + return State::Disconnected; +} + +#endif /* HAS_TCP */ diff --git a/src/ArduinoIoTCloudDevice.h b/src/ArduinoIoTCloudDevice.h new file mode 100644 index 000000000..294acfde5 --- /dev/null +++ b/src/ArduinoIoTCloudDevice.h @@ -0,0 +1,70 @@ +/* + This file is part of the Arduino_SecureElement library. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef ARDUINO_IOT_CLOUD_DEVICE_H +#define ARDUINO_IOT_CLOUD_DEVICE_H + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include "interfaces/CloudProcess.h" +#include "utility/time/TimedAttempt.h" +#include "property/PropertyContainer.h" + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class ArduinoCloudDevice : public CloudProcess { +public: + + ArduinoCloudDevice(MessageStream* stream); + virtual void update() override; + virtual void handleMessage(Message* m) override; + + virtual void begin(); + virtual int connected(); + + inline PropertyContainer &getPropertyContainer() { + return _propertyContainer; + }; + inline unsigned int &getPropertyContainerIndex() { + return _propertyContainerIndex; + } + inline bool isAttached() { + return _attached; + }; + + +private: + + enum class State { + Disconnected, + Init, + SendCapabilities, + Connected, + }; + + State _state; + CommandId _command; + TimedAttempt _attachAttempt; + PropertyContainer _propertyContainer; + unsigned int _propertyContainerIndex; + bool _attached; + bool _registered; + + State handleInit(); + State handleSendCapabilities(); + State handleConnected(); + State handleDisconnected(); +}; + +#endif /* ARDUINO_IOT_CLOUD_DEVICE_H */ From 9c881972253d1e01968fb8857f494623732e0599 Mon Sep 17 00:00:00 2001 From: pennam Date: Tue, 16 Apr 2024 11:34:01 +0200 Subject: [PATCH 2/3] ArduinoIoTCloudTCP: adapt state machine to use Device process --- src/ArduinoIoTCloudTCP.cpp | 328 +++++++++++++++---------------------- src/ArduinoIoTCloudTCP.h | 22 ++- 2 files changed, 140 insertions(+), 210 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index e06e218b4..e9ccd4a94 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -63,7 +63,7 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() , _message_stream(std::bind(&ArduinoIoTCloudTCP::sendMessage, this, std::placeholders::_1)) , _thing(&_message_stream) , _thing_id_property{nullptr} -, _device_property_container{0} +, _device(&_message_stream) , _mqtt_data_buf{0} , _mqtt_data_len{0} , _mqtt_data_request_retransmit{false} @@ -120,11 +120,6 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, _brokerAddress = brokerAddress; _brokerPort = brokerPort; -#if OTA_ENABLED - _ota_img_sha256 = OTA::getImageSHA256(); - DEBUG_VERBOSE("SHA256: HASH(%d) = %s", strlen(_ota_img_sha256.c_str()), _ota_img_sha256.c_str()); -#endif /* OTA_ENABLED */ - #if defined(BOARD_HAS_SECRET_KEY) /* If board is not configured for username and password login */ if(!_password.length()) @@ -196,27 +191,30 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, Property* p; p = new CloudWrapperString(_lib_version); - addPropertyToContainer(_device_property_container, *p, "LIB_VERSION", Permission::Read, -1); -#if OTA_ENABLED + addPropertyToContainer(_device.getPropertyContainer(), *p, "LIB_VERSION", Permission::Read, -1); + p = new CloudWrapperString(_thing_id); + _thing_id_property = &addPropertyToContainer(_device.getPropertyContainer(), *p, "thing_id", Permission::ReadWrite, -1).writeOnDemand(); + + _thing.begin(); + _device.begin(); + +#if OTA_ENABLED p = new CloudWrapperBool(_ota_cap); - addPropertyToContainer(_device_property_container, *p, "OTA_CAP", Permission::Read, -1); + addPropertyToContainer(_device.getPropertyContainer(), *p, "OTA_CAP", Permission::Read, -1); p = new CloudWrapperInt(_ota_error); - addPropertyToContainer(_device_property_container, *p, "OTA_ERROR", Permission::Read, -1); + addPropertyToContainer(_device.getPropertyContainer(), *p, "OTA_ERROR", Permission::Read, -1); p = new CloudWrapperString(_ota_img_sha256); - addPropertyToContainer(_device_property_container, *p, "OTA_SHA256", Permission::Read, -1); + addPropertyToContainer(_device.getPropertyContainer(), *p, "OTA_SHA256", Permission::Read, -1); p = new CloudWrapperString(_ota_url); - addPropertyToContainer(_device_property_container, *p, "OTA_URL", Permission::ReadWrite, -1); + addPropertyToContainer(_device.getPropertyContainer(), *p, "OTA_URL", Permission::ReadWrite, -1); p = new CloudWrapperBool(_ota_req); - addPropertyToContainer(_device_property_container, *p, "OTA_REQ", Permission::ReadWrite, -1); -#endif /* OTA_ENABLED */ - p = new CloudWrapperString(_thing_id); - _thing_id_property = &addPropertyToContainer(_device_property_container, *p, "thing_id", Permission::ReadWrite, -1).writeOnDemand(); - - _thing.begin(); + addPropertyToContainer(_device.getPropertyContainer(), *p, "OTA_REQ", Permission::ReadWrite, -1); -#if OTA_ENABLED _ota_cap = OTA::isCapable(); -#endif + + _ota_img_sha256 = OTA::getImageSHA256(); + DEBUG_VERBOSE("SHA256: HASH(%d) = %s", strlen(_ota_img_sha256.c_str()), _ota_img_sha256.c_str()); +#endif // OTA_ENABLED #ifdef BOARD_HAS_OFFLOADED_ECCX08 if (String(WiFi.firmwareVersion()) < String("1.4.4")) { @@ -257,7 +255,6 @@ void ArduinoIoTCloudTCP::update() watchdog_reset(); #endif - /* Run through the state machine. */ State next_state = _state; switch (_state) @@ -265,23 +262,13 @@ void ArduinoIoTCloudTCP::update() case State::ConnectPhy: next_state = handle_ConnectPhy(); break; case State::SyncTime: next_state = handle_SyncTime(); break; case State::ConnectMqttBroker: next_state = handle_ConnectMqttBroker(); break; - case State::SendDeviceProperties: next_state = handle_SendDeviceProperties(); break; - 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::Connected: next_state = handle_Connected(); break; case State::Disconnect: next_state = handle_Disconnect(); break; } _state = next_state; -#if OTA_ENABLED - if (_state > State::SubscribeDeviceTopic && _state <= State::Connected) { - handle_OTARequest(); - } -#endif /* OTA_ENABLED */ - /* This watchdog feed is actually needed only by the RP2040 Connect because its - * maximum watchdog window is 8389 ms; despite this we feed it for all + * maximum watchdog window is 8389 ms; despite this we feed it for all * supported ARCH to keep code aligned. */ #if defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_MBED) @@ -338,7 +325,7 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectMqttBroker() DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s connected to %s:%d", __FUNCTION__, _brokerAddress.c_str(), _brokerPort); /* Reconfigure timers for next state */ _connection_attempt.begin(AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms, AIOT_CONFIG_MAX_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms); - return State::SendDeviceProperties; + return State::Connected; } /* Can't connect to the broker. Wait: 2s -> 4s -> 8s -> 16s -> 32s -> 32s ... */ @@ -350,158 +337,38 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectMqttBroker() return State::ConnectPhy; } -ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SendDeviceProperties() -{ - if (!_mqttClient.connected()) - { - return State::Disconnect; - } - - DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s announce device to the Cloud %d", __FUNCTION__, _time_service.getTime()); - /* TODO check if write fails */ - sendDevicePropertiesToCloud(); - return State::SubscribeDeviceTopic; -} - -ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeDeviceTopic() -{ - if (!_mqttClient.connected()) - { - return State::Disconnect; - } - - if (_connection_attempt.isRetry() && !_connection_attempt.isExpired()) - return State::SubscribeDeviceTopic; - - if (_connection_attempt.isRetry()) - { - /* Configuration not received or device not attached to a valid thing. Try to resubscribe */ - DEBUG_ERROR("ArduinoIoTCloudTCP::%s device waiting for valid thing_id %d", __FUNCTION__, _time_service.getTime()); - } - - DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s request device configuration %d", __FUNCTION__, _time_service.getTime()); - - if (!_mqttClient.subscribe(_deviceTopicIn)) - { - /* If device_id is wrong the board can't connect to the broker so this condition - * should never happen. - */ - DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _deviceTopicIn.c_str()); - } - - /* Max retry than disconnect */ - if (_connection_attempt.getRetryCount() > AIOT_CONFIG_DEVICE_TOPIC_MAX_RETRY_CNT) - { - return State::Disconnect; - } - - /* No device configuration received. Wait: 4s -> 8s -> 16s -> 32s -> 32s ...*/ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - unsigned long const subscribe_retry_delay = _connection_attempt.retry(); -#pragma GCC diagnostic pop - DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s %d next configuration request in %d ms", __FUNCTION__, _connection_attempt.getRetryCount(), subscribe_retry_delay); - - return State::SubscribeDeviceTopic; -} - -ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_CheckDeviceConfig() -{ - if (!_mqttClient.connected()) - { - return State::Disconnect; - } - - updateThingTopics(); - - if (_thing_id.length() == 0) - { - /* Device configuration received, but invalid thing_id. Do not increase counter, but recompute delay. - * Device not attached. Wait: 40s -> 80s -> 160s -> 320s -> 640s -> 1280s -> 1280s ... - */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" - unsigned long const attach_retry_delay = _connection_attempt.reconfigure(AIOT_CONFIG_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms, AIOT_CONFIG_MAX_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms); -#pragma GCC diagnostic pop - DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s device not attached, next configuration request in %d ms", __FUNCTION__, attach_retry_delay); - return State::SubscribeDeviceTopic; - } - - DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s device attached to a new valid thing_id %s %d", __FUNCTION__, getThingId().c_str(), _time_service.getTime()); - - /* Received valid thing_id, reconfigure timers for next state and go on */ - _connection_attempt.begin(AIOT_CONFIG_THING_TOPICS_SUBSCRIBE_RETRY_DELAY_ms); - - return State::SubscribeThingTopics; -} - -ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeThingTopics() +ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() { - if (!_mqttClient.connected() || _thing_id_property->isDifferentFromCloud()) + if (!_mqttClient.connected() || !_thing.connected() || !_device.connected()) { return State::Disconnect; } - if (_connection_attempt.isRetry() && !_connection_attempt.isExpired()) - return State::SubscribeThingTopics; - - if (_connection_attempt.getRetryCount() > AIOT_CONFIG_THING_TOPICS_SUBSCRIBE_MAX_RETRY_CNT) - { - return State::Disconnect; + /* Retransmit data in case there was a lost transaction due + * to phy layer or MQTT connectivity loss. + */ + if (_mqtt_data_request_retransmit && (_mqtt_data_len > 0)) { + write(_dataTopicOut, _mqtt_data_buf, _mqtt_data_len); + _mqtt_data_request_retransmit = false; } - _connection_attempt.retry(); + /* Call CloudDevice process to get configuration */ + _device.update(); - if (!_mqttClient.subscribe(_dataTopicIn)) - { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _dataTopicIn.c_str()); - DEBUG_ERROR("Check your thing configuration, and press the reset button on your board."); - return State::SubscribeThingTopics; + if (_device.isAttached()) { + /* Call CloudThing process to synchronize properties */ + _thing.update(); } - if (!_mqttClient.subscribe(_shadowTopicIn)) - { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _shadowTopicIn.c_str()); - DEBUG_ERROR("Check your thing configuration, and press the reset button on your board."); - return State::SubscribeThingTopics; +#if OTA_ENABLED + if (_device.connected()) { + handle_OTARequest(); } +#endif /* OTA_ENABLED */ - DEBUG_INFO("Connected to Arduino IoT Cloud"); - DEBUG_INFO("Thing ID: %s", getThingId().c_str()); - execCloudEventCallback(ArduinoIoTCloudEvent::CONNECT); - - /* 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::Connected; } -ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() -{ - if (!_mqttClient.connected() || _thing_id_property->isDifferentFromCloud() || !_thing.connected()) - { - /* The last message was definitely lost, trigger a retransmit. */ - _mqtt_data_request_retransmit = true; - return State::Disconnect; - } - /* We are connected so let's to our stuff here. */ - else - { - /* Retransmit data in case there was a lost transaction due - * to phy layer or MQTT connectivity loss. - */ - if (_mqtt_data_request_retransmit && (_mqtt_data_len > 0)) { - write(_dataTopicOut, _mqtt_data_buf, _mqtt_data_len); - _mqtt_data_request_retransmit = false; - } - - /* Call CloudThing process to synchronize properties */ - _thing.update(); - - return State::Connected; - - } -} - #if OTA_ENABLED void ArduinoIoTCloudTCP::handle_OTARequest() { /* Request a OTA download if the hidden property @@ -538,20 +405,17 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Disconnect() if (!_mqttClient.connected()) { DEBUG_ERROR("ArduinoIoTCloudTCP::%s MQTT client connection lost", __FUNCTION__); } else { - _mqttClient.unsubscribe(_shadowTopicIn); - _mqttClient.unsubscribe(_dataTopicIn); - /* TODO add device topic */ + /* No need to manually unsubscribe because we are using clean sessions */ _mqttClient.stop(); } Message message = { ResetCmdId }; _thing.handleMessage(&message); + _device.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; @@ -574,11 +438,28 @@ void ArduinoIoTCloudTCP::handleMessage(int length) /* Topic for OTA properties and device configuration */ if (_deviceTopicIn == topic) { - CBORDecoder::decode(_device_property_container, (uint8_t*)bytes, length); - if (_thing_id_property->isDifferentFromCloud() && (_thing_id.length() != 0)) { - _state = State::Disconnect; - } else { - _state = State::CheckDeviceConfig; + CBORDecoder::decode(_device.getPropertyContainer(), (uint8_t*)bytes, length); + + /* Temporary check to avoid flooding device state machine with usless messages */ + if (_thing_id_property->isDifferentFromCloud()) { + _thing_id_property->fromCloudToLocal(); + + Message message; + /* If we are attached we need first to detach */ + if (_device.isAttached()) { + detachThing(); + message = { DeviceDetachedCmdId }; + } + /* If received thing id is valid attach to the new thing */ + if (_thing_id.length()) { + attachThing(); + message = { DeviceAttachedCmdId }; + } else { + /* Send message to device state machine to inform we have received a null thing-id */ + _thing_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; + message = { DeviceRegisteredCmdId }; + } + _device.handleMessage(&message); } } @@ -588,14 +469,15 @@ void ArduinoIoTCloudTCP::handleMessage(int length) } /* Topic for sync Thing last values on connect */ - if (_shadowTopicIn == topic) - { + if (_shadowTopicIn == topic) { DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values received", __FUNCTION__, millis()); + /* Decode last values property array */ CBORDecoder::decode(_thing.getPropertyContainer(), (uint8_t*)bytes, length, true); + /* Unlock thing state machine waiting last values */ Message message = { LastValuesUpdateCmdId }; _thing.handleMessage(&message); + /* Call ArduinoIoTCloud sync user callback*/ execCloudEventCallback(ArduinoIoTCloudEvent::SYNC); - _state = State::Connected; } } @@ -603,14 +485,22 @@ void ArduinoIoTCloudTCP::sendMessage(Message * msg) { switch (msg->id) { - case PropertiesUpdateCmdId: - sendThingPropertiesToCloud(); + case DeviceBeginCmdId: + sendDevicePropertiesToCloud(); + break; + + case ThingBeginCmdId: + requestThingId(); break; case LastValuesBeginCmdId: requestLastValue(); break; + case PropertiesUpdateCmdId: + sendThingPropertiesToCloud(); + break; + default: break; } @@ -622,6 +512,7 @@ void ArduinoIoTCloudTCP::sendPropertyContainerToCloud(String const topic, Proper uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE]; if (CBOREncoder::encode(property_container, data, sizeof(data), bytes_encoded, current_property_index, false) == CborNoError) + { if (bytes_encoded > 0) { /* If properties have been encoded store them in the back-up buffer @@ -632,6 +523,7 @@ void ArduinoIoTCloudTCP::sendPropertyContainerToCloud(String const topic, Proper /* Transmit the properties to the MQTT broker */ write(topic, _mqtt_data_buf, _mqtt_data_len); } + } } void ArduinoIoTCloudTCP::sendThingPropertiesToCloud() @@ -649,12 +541,11 @@ void ArduinoIoTCloudTCP::sendDevicePropertiesToCloud() ro_device_property_list.end(), [this, &ro_device_property_container ] (String const & name) { - Property* p = getProperty(this->_device_property_container, name); + Property* p = getProperty(this->_device.getPropertyContainer(), name); if(p != nullptr) addPropertyToContainer(ro_device_property_container, *p, p->name(), p->isWriteableByCloud() ? Permission::ReadWrite : Permission::Read); } ); - sendPropertyContainerToCloud(_deviceTopicOut, ro_device_property_container, last_device_property_index); } @@ -664,7 +555,7 @@ void ArduinoIoTCloudTCP::sendDevicePropertyToCloud(String const name) PropertyContainer temp_device_property_container; unsigned int last_device_property_index = 0; - Property* p = getProperty(this->_device_property_container, name); + Property* p = getProperty(this->_device.getPropertyContainer(), name); if(p != nullptr) { addPropertyToContainer(temp_device_property_container, *p, p->name(), p->isWriteableByCloud() ? Permission::ReadWrite : Permission::Read); @@ -682,6 +573,57 @@ void ArduinoIoTCloudTCP::requestLastValue() write(_shadowTopicOut, CBOR_REQUEST_LAST_VALUE_MSG, sizeof(CBOR_REQUEST_LAST_VALUE_MSG)); } +void ArduinoIoTCloudTCP::requestThingId() +{ + if (!_mqttClient.subscribe(_deviceTopicIn)) + { + /* If device_id is wrong the board can't connect to the broker so this condition + * should never happen. + */ + DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _deviceTopicIn.c_str()); + } +} + +void ArduinoIoTCloudTCP::attachThing() +{ + + _dataTopicIn = getTopic_datain(); + _dataTopicOut = getTopic_dataout(); + if (!_mqttClient.subscribe(_dataTopicIn)) { + DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _dataTopicIn.c_str()); + DEBUG_ERROR("Check your thing configuration, and press the reset button on your board."); + return; + } + + _shadowTopicIn = getTopic_shadowin(); + _shadowTopicOut = getTopic_shadowout(); + if (!_mqttClient.subscribe(_shadowTopicIn)) { + DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _shadowTopicIn.c_str()); + DEBUG_ERROR("Check your thing configuration, and press the reset button on your board."); + return; + } + + DEBUG_INFO("Connected to Arduino IoT Cloud"); + DEBUG_INFO("Thing ID: %s", getThingId().c_str()); + execCloudEventCallback(ArduinoIoTCloudEvent::CONNECT); +} + +void ArduinoIoTCloudTCP::detachThing() +{ + if (!_mqttClient.unsubscribe(_dataTopicIn)) { + DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not unsubscribe from %s", __FUNCTION__, _dataTopicIn.c_str()); + return; + } + + if (!_mqttClient.unsubscribe(_shadowTopicIn)) { + DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not unsubscribe from %s", __FUNCTION__, _shadowTopicIn.c_str()); + return; + } + + DEBUG_INFO("Disconnected from Arduino IoT Cloud"); + execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); +} + int ArduinoIoTCloudTCP::write(String const topic, byte const data[], int const length) { if (_mqttClient.beginMessage(topic, length, false, 0)) { @@ -694,16 +636,6 @@ int ArduinoIoTCloudTCP::write(String const topic, byte const data[], int const l return 0; } -void ArduinoIoTCloudTCP::updateThingTopics() -{ - _thing_id_property->fromCloudToLocal(); - - _shadowTopicOut = getTopic_shadowout(); - _shadowTopicIn = getTopic_shadowin(); - _dataTopicOut = getTopic_dataout(); - _dataTopicIn = getTopic_datain(); -} - /****************************************************************************** * EXTERN DEFINITION ******************************************************************************/ diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index 3d23e56a1..1c13592df 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -26,6 +26,7 @@ #include #include #include +#include #if defined(BOARD_HAS_SECURE_ELEMENT) #include @@ -51,6 +52,10 @@ #include #endif +#if OTA_ENABLED +#include +#endif + /****************************************************************************** CONSTANTS ******************************************************************************/ @@ -101,8 +106,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass _get_ota_confirmation = cb; _ask_user_before_executing_ota = true; } - - void handle_OTARequest(); + void handle_OTARequest(); #endif private: @@ -113,10 +117,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass ConnectPhy, SyncTime, ConnectMqttBroker, - SendDeviceProperties, - SubscribeDeviceTopic, - CheckDeviceConfig, - SubscribeThingTopics, Connected, Disconnect, }; @@ -126,7 +126,7 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass MessageStream _message_stream; ArduinoCloudThing _thing; Property * _thing_id_property; - PropertyContainer _device_property_container; + ArduinoCloudDevice _device; String _brokerAddress; uint16_t _brokerPort; @@ -190,10 +190,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass State handle_ConnectPhy(); State handle_SyncTime(); State handle_ConnectMqttBroker(); - State handle_SendDeviceProperties(); - State handle_CheckDeviceConfig(); - State handle_SubscribeDeviceTopic(); - State handle_SubscribeThingTopics(); State handle_Connected(); State handle_Disconnect(); @@ -204,13 +200,15 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass void sendThingPropertiesToCloud(); void sendDevicePropertiesToCloud(); void requestLastValue(); + void requestThingId(); + void attachThing(); + void detachThing(); int write(String const topic, byte const data[], int const length); #if OTA_ENABLED void sendDevicePropertyToCloud(String const name); #endif - void updateThingTopics(); }; /****************************************************************************** From 181bba7a0da3f564b173e85f2d703d18e51ca967 Mon Sep 17 00:00:00 2001 From: pennam Date: Mon, 15 Apr 2024 11:39:45 +0200 Subject: [PATCH 3/3] Cloud: initialize thing id to non null --- src/ArduinoIoTCloud.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ArduinoIoTCloud.cpp b/src/ArduinoIoTCloud.cpp index 0603b6759..a565cac44 100644 --- a/src/ArduinoIoTCloud.cpp +++ b/src/ArduinoIoTCloud.cpp @@ -28,9 +28,9 @@ ArduinoIoTCloudClass::ArduinoIoTCloudClass() : _connection{nullptr} , _time_service(TimeService) -, _thing_id{""} +, _thing_id{"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"} , _lib_version{AIOT_CONFIG_LIB_VERSION} -, _device_id{""} +, _device_id{"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"} , _cloud_event_callback{nullptr} {