diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index e47a8ac2b..f4fbd2a6a 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -139,11 +139,13 @@ #define AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms (1000UL) #define AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms (32000UL) -#define AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms (5*1000UL) +#define AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms (2000UL) #define AIOT_CONFIG_MAX_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms (32000UL) +#define AIOT_CONFIG_DEVICE_TOPIC_MAX_RETRY_CNT (10UL) +#define AIOT_CONFIG_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms (20000UL) +#define AIOT_CONFIG_MAX_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms (1280000UL) #define AIOT_CONFIG_THING_TOPICS_SUBSCRIBE_RETRY_DELAY_ms (1000UL) #define AIOT_CONFIG_THING_TOPICS_SUBSCRIBE_MAX_RETRY_CNT (10UL) -#define AIOT_CONFIG_MAX_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms (1280000UL) #define AIOT_CONFIG_TIMEOUT_FOR_LASTVALUES_SYNC_ms (30000UL) #define AIOT_CONFIG_LASTVALUES_SYNC_MAX_RETRY_CNT (10UL) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 87b1c555e..794be824c 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -59,18 +59,11 @@ unsigned long getTime() ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() : _state{State::ConnectPhy} +, _connection_attempt(0,0) , _tz_offset{0} , _tz_offset_property{nullptr} , _tz_dst_until{0} , _tz_dst_until_property{nullptr} -, _next_connection_attempt_tick{0} -, _last_connection_attempt_cnt{0} -, _next_device_subscribe_attempt_tick{0} -, _last_device_subscribe_cnt{0} -, _last_sync_request_tick{0} -, _last_sync_request_cnt{0} -, _last_subscribe_request_tick{0} -, _last_subscribe_request_cnt{0} , _mqtt_data_buf{0} , _mqtt_data_len{0} , _mqtt_data_request_retransmit{false} @@ -87,7 +80,6 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP() , _shadowTopicIn("") , _dataTopicOut("") , _dataTopicIn("") -, _deviceSubscribedToThing{false} #if OTA_ENABLED , _ota_cap{false} , _ota_error{static_cast(OTAError::None)} @@ -114,7 +106,12 @@ int ArduinoIoTCloudTCP::begin(ConnectionHandler & connection, bool const enable_ #else _brokerPort = brokerPort; #endif + + /* Setup TimeService */ _time_service.begin(&connection); + + /* Setup retry timers */ + _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); return begin(enable_watchdog, _brokerAddress, _brokerPort); } @@ -133,15 +130,16 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, if(!_password.length()) { #endif + #if defined(BOARD_HAS_SECURE_ELEMENT) if (!_selement.begin()) { DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not initialize secure element.", __FUNCTION__); -#if defined(ARDUINO_UNOWIFIR4) + #if defined(ARDUINO_UNOWIFIR4) if (String(WiFi.firmwareVersion()) < String("0.4.1")) { DEBUG_ERROR("ArduinoIoTCloudTCP::%s In order to read device certificate, WiFi firmware needs to be >= 0.4.1, current %s", __FUNCTION__, WiFi.firmwareVersion()); } -#endif + #endif return 0; } if (!SElementArduinoCloudDeviceId::read(_selement, getDeviceId(), SElementArduinoCloudSlot::DeviceId)) @@ -271,7 +269,6 @@ void ArduinoIoTCloudTCP::update() 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::WaitDeviceConfig: next_state = handle_WaitDeviceConfig(); 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; @@ -319,8 +316,7 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectPhy() { if (_connection->check() == NetworkConnectionState::CONNECTED) { - bool const is_retry_attempt = (_last_connection_attempt_cnt > 0); - if (!is_retry_attempt || (is_retry_attempt && (millis() > _next_connection_attempt_tick))) + if (!_connection_attempt.isRetry() || (_connection_attempt.isRetry() && _connection_attempt.isExpired())) return State::SyncTime; } @@ -341,17 +337,18 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectMqttBroker() { if (_mqttClient.connect(_brokerAddress.c_str(), _brokerPort)) { - _last_connection_attempt_cnt = 0; + 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; } - _last_connection_attempt_cnt++; - unsigned long reconnection_retry_delay = (1 << _last_connection_attempt_cnt) * AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms; - reconnection_retry_delay = min(reconnection_retry_delay, static_cast(AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms)); - _next_connection_attempt_tick = millis() + reconnection_retry_delay; + /* Can't connect to the broker. Wait: 2s -> 4s -> 8s -> 16s -> 32s -> 32s ... */ + _connection_attempt.retry(); DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not connect to %s:%d", __FUNCTION__, _brokerAddress.c_str(), _brokerPort); - DEBUG_ERROR("ArduinoIoTCloudTCP::%s %d connection attempt at tick time %d", __FUNCTION__, _last_connection_attempt_cnt, _next_connection_attempt_tick); + DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s %d next connection attempt in %d ms", __FUNCTION__, _connection_attempt.getRetryCount(), _connection_attempt.getWaitTime()); + /* Go back to ConnectPhy and retry to get time from network (invalid time for SSL handshake?)*/ return State::ConnectPhy; } @@ -362,6 +359,8 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SendDeviceProperties() 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; } @@ -373,52 +372,39 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeDeviceTopic() return State::Disconnect; } - if (!_mqttClient.subscribe(_deviceTopicIn)) - { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s could not subscribe to %s", __FUNCTION__, _deviceTopicIn.c_str()); + if (_connection_attempt.isRetry() && !_connection_attempt.isExpired()) return State::SubscribeDeviceTopic; - } - if (_last_device_subscribe_cnt > AIOT_CONFIG_LASTVALUES_SYNC_MAX_RETRY_CNT) + if (_connection_attempt.isRetry()) { - _last_device_subscribe_cnt = 0; - _next_device_subscribe_attempt_tick = 0; - _mqttClient.stop(); - execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); - return State::ConnectPhy; + /* 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()); } - /* No device configuration reply. Wait: 5s -> 10s -> 20s -> 30s */ - unsigned long subscribe_retry_delay = (1 << _last_device_subscribe_cnt) * AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms; - subscribe_retry_delay = min(subscribe_retry_delay, static_cast(AIOT_CONFIG_MAX_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms)); - _next_device_subscribe_attempt_tick = millis() + subscribe_retry_delay; - _last_device_subscribe_cnt++; - - return State::WaitDeviceConfig; -} + DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s request device configuration %d", __FUNCTION__, _time_service.getTime()); -ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_WaitDeviceConfig() -{ - if (!_mqttClient.connected()) + if (!_mqttClient.subscribe(_deviceTopicIn)) { - return State::Disconnect; + /* 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()); } - if (_thing_id_property->isDifferentFromCloud()) + /* Max retry than disconnect */ + if (_connection_attempt.getRetryCount() > AIOT_CONFIG_DEVICE_TOPIC_MAX_RETRY_CNT) { - return State::CheckDeviceConfig; + return State::Disconnect; } - if (millis() > _next_device_subscribe_attempt_tick) - { - /* Configuration not received or device not attached to a valid thing. Try to resubscribe */ - if (_mqttClient.unsubscribe(_deviceTopicIn)) - { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s device waiting for valid thing_id", __FUNCTION__); - return State::SubscribeDeviceTopic; - } - } - return State::WaitDeviceConfig; + /* 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() @@ -428,66 +414,45 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_CheckDeviceConfig() return State::Disconnect; } - if(_deviceSubscribedToThing == true) - { - /* Unsubscribe from old things topics and go on with a new subscription */ - _mqttClient.unsubscribe(_shadowTopicIn); - _mqttClient.unsubscribe(_dataTopicIn); - _deviceSubscribedToThing = false; - DEBUG_INFO("Disconnected from Arduino IoT Cloud"); - execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); - } - updateThingTopics(); if (_thing_id.length() == 0) { - /* Configuration received but device not attached. Wait: 40s */ - unsigned long attach_retry_delay = (1 << _last_device_attach_cnt) * AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms; - attach_retry_delay = min(attach_retry_delay, static_cast(AIOT_CONFIG_MAX_DEVICE_TOPIC_ATTACH_RETRY_DELAY_ms)); - _next_device_subscribe_attempt_tick = millis() + attach_retry_delay; - _last_device_attach_cnt++; - return State::WaitDeviceConfig; + /* 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 %s", __FUNCTION__, getThingId().c_str()); - _last_device_attach_cnt = 0; + 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() { - if (!_mqttClient.connected()) + if (!_mqttClient.connected() || _thing_id_property->isDifferentFromCloud()) { return State::Disconnect; } - if (_thing_id_property->isDifferentFromCloud()) - { - return State::CheckDeviceConfig; - } - - unsigned long const now = millis(); - bool const is_subscribe_retry_delay_expired = (now - _last_subscribe_request_tick) > AIOT_CONFIG_THING_TOPICS_SUBSCRIBE_RETRY_DELAY_ms; - bool const is_first_subscribe_request = (_last_subscribe_request_cnt == 0); - - if (!is_first_subscribe_request && !is_subscribe_retry_delay_expired) - { + if (_connection_attempt.isRetry() && !_connection_attempt.isExpired()) return State::SubscribeThingTopics; - } - if (_last_subscribe_request_cnt > AIOT_CONFIG_THING_TOPICS_SUBSCRIBE_MAX_RETRY_CNT) + if (_connection_attempt.getRetryCount() > AIOT_CONFIG_THING_TOPICS_SUBSCRIBE_MAX_RETRY_CNT) { - _last_subscribe_request_cnt = 0; - _last_subscribe_request_tick = 0; - _mqttClient.stop(); - execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); - return State::ConnectPhy; + return State::Disconnect; } - _last_subscribe_request_tick = now; - _last_subscribe_request_cnt++; + _connection_attempt.retry(); if (!_mqttClient.subscribe(_dataTopicIn)) { @@ -506,48 +471,35 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_SubscribeThingTopics() DEBUG_INFO("Connected to Arduino IoT Cloud"); DEBUG_INFO("Thing ID: %s", getThingId().c_str()); execCloudEventCallback(ArduinoIoTCloudEvent::CONNECT); - _deviceSubscribedToThing = true; - /*Add retry wait time otherwise we are trying to reconnect every 250 ms...*/ + /* 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()) + if (!_mqttClient.connected() || _thing_id_property->isDifferentFromCloud()) { return State::Disconnect; } - if (_thing_id_property->isDifferentFromCloud()) - { - return State::CheckDeviceConfig; - } - /* Check whether or not we need to send a new request. */ - unsigned long const now = millis(); - bool const is_sync_request_timeout = (now - _last_sync_request_tick) > AIOT_CONFIG_TIMEOUT_FOR_LASTVALUES_SYNC_ms; - bool const is_first_sync_request = (_last_sync_request_cnt == 0); - if (is_first_sync_request || is_sync_request_timeout) + 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) { - DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values requested", __FUNCTION__, now); - requestLastValue(); - _last_sync_request_tick = now; - /* 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. - */ - _last_sync_request_cnt++; - if (_last_sync_request_cnt > AIOT_CONFIG_LASTVALUES_SYNC_MAX_RETRY_CNT) - { - _last_sync_request_cnt = 0; - _last_sync_request_tick = 0; - _mqttClient.stop(); - execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); - return State::ConnectPhy; - } + return State::Disconnect; } + _connection_attempt.retry(); + requestLastValue(); + DEBUG_VERBOSE("ArduinoIoTCloudTCP::%s [%d] last values requested", __FUNCTION__, _time_service.getTime()); return State::RequestLastValues; } @@ -564,7 +516,7 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Connected() { if (_thing_id_property->isDifferentFromCloud()) { - return State::CheckDeviceConfig; + return State::Disconnect; } /* Check if a primitive property wrapper is locally changed. @@ -641,9 +593,19 @@ void ArduinoIoTCloudTCP::handle_OTARequest() { ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Disconnect() { - DEBUG_ERROR("ArduinoIoTCloudTCP::%s MQTT client connection lost", __FUNCTION__); - _mqttClient.stop(); + if (!_mqttClient.connected()) { + DEBUG_ERROR("ArduinoIoTCloudTCP::%s MQTT client connection lost", __FUNCTION__); + } else { + _mqttClient.unsubscribe(_shadowTopicIn); + _mqttClient.unsubscribe(_dataTopicIn); + /* TODO add device topic */ + _mqttClient.stop(); + } + DEBUG_INFO("Disconnected from Arduino IoT Cloud"); execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT); + + /* 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; } @@ -665,8 +627,7 @@ void ArduinoIoTCloudTCP::handleMessage(int length) /* Topic for OTA properties and device configuration */ if (_deviceTopicIn == topic) { CBORDecoder::decode(_device_property_container, (uint8_t*)bytes, length); - _last_device_subscribe_cnt = 0; - _next_device_subscribe_attempt_tick = 0; + _state = State::CheckDeviceConfig; } /* Topic for user input data */ @@ -681,8 +642,6 @@ void ArduinoIoTCloudTCP::handleMessage(int length) CBORDecoder::decode(_thing_property_container, (uint8_t*)bytes, length, true); _time_service.setTimeZoneData(_tz_offset, _tz_dst_until); execCloudEventCallback(ArduinoIoTCloudEvent::SYNC); - _last_sync_request_cnt = 0; - _last_sync_request_tick = 0; _state = State::Connected; } } diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index d00bb0ca6..9b5ffa6c0 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -25,6 +25,7 @@ #include #include #include +#include #if defined(BOARD_HAS_SECURE_ELEMENT) #include @@ -113,7 +114,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass ConnectMqttBroker, SendDeviceProperties, SubscribeDeviceTopic, - WaitDeviceConfig, CheckDeviceConfig, SubscribeThingTopics, RequestLastValues, @@ -122,21 +122,13 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass }; State _state; + TimedAttempt _connection_attempt; int _tz_offset; Property * _tz_offset_property; unsigned int _tz_dst_until; Property * _tz_dst_until_property; - unsigned long _next_connection_attempt_tick; - unsigned int _last_connection_attempt_cnt; - unsigned long _next_device_subscribe_attempt_tick; - unsigned int _last_device_subscribe_cnt; - unsigned int _last_device_attach_cnt; - unsigned long _last_sync_request_tick; - unsigned int _last_sync_request_cnt; - unsigned long _last_subscribe_request_tick; - unsigned int _last_subscribe_request_cnt; String _brokerAddress; uint16_t _brokerPort; uint8_t _mqtt_data_buf[MQTT_TRANSMIT_BUFFER_SIZE]; @@ -179,8 +171,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass String _dataTopicOut; String _dataTopicIn; - bool _deviceSubscribedToThing; - #if OTA_ENABLED bool _ota_cap; int _ota_error; @@ -202,7 +192,6 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass State handle_SyncTime(); State handle_ConnectMqttBroker(); State handle_SendDeviceProperties(); - State handle_WaitDeviceConfig(); State handle_CheckDeviceConfig(); State handle_SubscribeDeviceTopic(); State handle_SubscribeThingTopics(); diff --git a/src/utility/time/TimedAttempt.cpp b/src/utility/time/TimedAttempt.cpp new file mode 100644 index 000000000..01da506d7 --- /dev/null +++ b/src/utility/time/TimedAttempt.cpp @@ -0,0 +1,79 @@ +/* + 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/. +*/ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "TimedAttempt.h" + +/****************************************************************************** + * CTOR/DTOR + ******************************************************************************/ + +TimedAttempt::TimedAttempt(unsigned long minDelay, unsigned long maxDelay) +: _minDelay(minDelay) +, _maxDelay(maxDelay) { +} + +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ + +void TimedAttempt::begin(unsigned long delay) { + _retryCount = 0; + _minDelay = delay; + _maxDelay = delay; +} + +void TimedAttempt::begin(unsigned long minDelay, unsigned long maxDelay) { + _retryCount = 0; + _minDelay = minDelay; + _maxDelay = maxDelay; +} + +unsigned long TimedAttempt::reconfigure(unsigned long minDelay, unsigned long maxDelay) { + _minDelay = minDelay; + _maxDelay = maxDelay; + return reload(); +} + +unsigned long TimedAttempt::retry() { + _retryCount++; + return reload(); +} + +unsigned long TimedAttempt::reload() { + unsigned long retryDelay = (1 << _retryCount) * _minDelay; + _retryDelay = min(retryDelay, _maxDelay); + _nextRetryTick = millis() + retryDelay; + return retryDelay; +} + +void TimedAttempt::reset() { + _retryCount = 0; +} + +bool TimedAttempt::isRetry() { + return _retryCount > 0; +} + +bool TimedAttempt::isExpired() { + return millis() > _nextRetryTick; +} + +unsigned int TimedAttempt::getRetryCount() { + return _retryCount; +} + +unsigned int TimedAttempt::getWaitTime() { + return _retryDelay; +} diff --git a/src/utility/time/TimedAttempt.h b/src/utility/time/TimedAttempt.h new file mode 100644 index 000000000..67a1931c0 --- /dev/null +++ b/src/utility/time/TimedAttempt.h @@ -0,0 +1,42 @@ +/* + 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 TIMED_ATTEMPT_H +#define TIMED_ATTEMPT_H + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class TimedAttempt { + +public: + TimedAttempt(unsigned long minDelay, unsigned long maxDelay); + + void begin(unsigned long delay); + void begin(unsigned long minDelay, unsigned long maxDelay); + unsigned long reconfigure(unsigned long minDelay, unsigned long maxDelay); + unsigned long retry(); + unsigned long reload(); + void reset(); + bool isRetry(); + bool isExpired(); + unsigned int getRetryCount(); + unsigned int getWaitTime(); + +private: + unsigned long _minDelay; + unsigned long _maxDelay; + unsigned long _nextRetryTick; + unsigned long _retryDelay; + unsigned int _retryCount; +}; + +#endif /* TIMED_ATTEMPT_H */