Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport bugfixes from v0.12.0 #147

Merged
merged 3 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"arduino-libraries/ArduinoHttpClient" : "^0.4.0",
"bblanchon/StreamUtils" : "^1.7.3"
},
"version": "0.11.0",
"version": "0.11.1",
"examples": "examples/*/*.ino",
"frameworks": "arduino",
"license": "MIT"
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=ThingsBoard
version=0.11.0
version=0.11.1
author=ThingsBoard Team
maintainer=ThingsBoard Team
sentence=ThingsBoard library for Arduino.
Expand Down
File renamed without changes.
40 changes: 14 additions & 26 deletions src/OTA_Handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@
#include "OTA_Update_Callback.h"
#include "OTA_Failure_Response.h"

// Library include.
#ifdef ESP8266
#include <Updater.h>
#else
#ifdef ESP32
#include <Update.h>
#endif // ESP32
#endif // ESP8266

/// ---------------------------------
/// Constant strings in flash memory.
Expand Down Expand Up @@ -69,7 +61,7 @@ constexpr char RECEIVED_UNEXPECTED_CHUNK[] = "Received chunk (%u), not the same
constexpr char ERROR_UPDATE_BEGIN[] = "Failed to initalize flash updater";
constexpr char ERROR_UPDATE_WRITE[] = "Only wrote (%u) bytes of binary data to flash memory instead of expected (%u)";
constexpr char UPDATING_HASH_FAILED[] = "Updating hash failed";
constexpr char ERROR_UPDATE_END[] = "Error (%u) during flash updater not all bytes written";
constexpr char ERROR_UPDATE_END[] = "Error during flash updater not all bytes written";
constexpr char CHKS_VER_FAILED[] = "Checksum verification failed";
constexpr char FW_CHUNK[] = "Receive chunk (%i), with size (%u) bytes";
constexpr char HASH_ACTUAL[] = "(%s) actual checksum: (%s)";
Expand All @@ -79,6 +71,7 @@ constexpr char FW_UPDATE_ABORTED[] = "Firmware update aborted";
constexpr char FW_UPDATE_SUCCESS[] = "Update success";
#endif // THINGSBOARD_ENABLE_PROGMEM


/// @brief Handles actually writing the received firmware packets into flash memory
/// @tparam Logger Logging class that should be used to print messages generated by ThingsBoard
template<typename Logger>
Expand Down Expand Up @@ -119,8 +112,9 @@ class OTA_Handler {
m_fw_algorithm = fw_algorithm;
m_fw_checksum = fw_checksum;
m_fw_checksum_algorithm = fw_checksum_algorithm;
m_fw_updater = m_fw_callback->Get_Updater();

if (!m_publish_callback || !m_send_fw_state_callback || !m_finish_callback) {
if (!m_publish_callback || !m_send_fw_state_callback || !m_finish_callback || !m_fw_updater) {
Logger::log(OTA_CB_IS_NULL);
(void)m_send_fw_state_callback(FW_STATE_FAILED, OTA_CB_IS_NULL);
return Handle_Failure(OTA_Failure_Response::RETRY_NOTHING);
Expand All @@ -131,9 +125,7 @@ class OTA_Handler {
/// @brief Stops the firmware update
inline void Stop_Firmware_Update() {
m_watchdog.detach();
#ifdef ESP32
Update.abort();
#endif
m_fw_updater->reset();
Logger::log(FW_UPDATE_ABORTED);
(void)m_send_fw_state_callback(FW_STATE_FAILED, FW_UPDATE_ABORTED);
Handle_Failure(OTA_Failure_Response::RETRY_NOTHING);
Expand All @@ -145,7 +137,7 @@ class OTA_Handler {
/// @param current_chunk Index of the chunk we recieved the binary data for
/// @param payload Firmware packet data of the current chunk
/// @param total_bytes Amount of bytes in the current firmware packet data
inline void Process_Firmware_Packet(const uint32_t& current_chunk, uint8_t *payload, const uint32_t& total_bytes) {
inline void Process_Firmware_Packet(const uint32_t& current_chunk, uint8_t *payload, const unsigned int& total_bytes) {
(void)m_send_fw_state_callback(FW_STATE_DOWNLOADING, nullptr);

if (current_chunk != m_requested_chunks) {
Expand All @@ -163,15 +155,15 @@ class OTA_Handler {

if (current_chunk == 0U) {
// Initialize Flash
if (!Update.begin(m_fw_size)) {
if (!m_fw_updater->begin(m_fw_size)) {
Logger::log(ERROR_UPDATE_BEGIN);
(void)m_send_fw_state_callback(FW_STATE_FAILED, ERROR_UPDATE_BEGIN);
return Handle_Failure(OTA_Failure_Response::RETRY_UPDATE);
}
}

// Write received binary data to flash partition
const size_t written_bytes = Update.write(payload, total_bytes);
const size_t written_bytes = m_fw_updater->write(payload, total_bytes);
if (written_bytes != total_bytes) {
char message[Helper::detectSize(ERROR_UPDATE_WRITE, written_bytes, total_bytes)];
snprintf_P(message, sizeof(message), ERROR_UPDATE_WRITE, written_bytes, total_bytes);
Expand Down Expand Up @@ -211,6 +203,7 @@ class OTA_Handler {
std::string m_fw_algorithm;
std::string m_fw_checksum;
mbedtls_md_type_t m_fw_checksum_algorithm;
IUpdater *m_fw_updater;
HashGenerator m_hash;
uint32_t m_total_chunks;
uint32_t m_requested_chunks;
Expand All @@ -222,9 +215,7 @@ class OTA_Handler {
m_retries = m_fw_callback->Get_Chunk_Retries();
m_hash.start(m_fw_checksum_algorithm);
m_watchdog.detach();
#ifdef ESP32
Update.abort();
#endif
m_fw_updater->reset();
Request_Next_Firmware_Packet();
}

Expand All @@ -240,7 +231,7 @@ class OTA_Handler {
(void)m_send_fw_state_callback(FW_STATE_FAILED, UNABLE_TO_REQUEST_CHUNCKS);
}

// Watchdog gets started no matter if publishing request was successfull or not in hopes,
// Watchdog gets started no matter if publishing request was successful or not in hopes,
// that after the given timeout the callback calls this method again and can then publish the request successfully.
m_watchdog.once(m_fw_callback->Get_Timeout());
}
Expand All @@ -267,12 +258,9 @@ class OTA_Handler {

Logger::log(CHKS_VER_SUCCESS);

if (!Update.end()) {
const uint8_t error = Update.getError();
char message[Helper::detectSize(ERROR_UPDATE_END, error)];
snprintf_P(message, sizeof(message), ERROR_UPDATE_END, error);
Logger::log(message);
(void)m_send_fw_state_callback(FW_STATE_FAILED, message);
if (!m_fw_updater->end()) {
Logger::log(ERROR_UPDATE_END);
(void)m_send_fw_state_callback(FW_STATE_FAILED, ERROR_UPDATE_END);
return Handle_Failure(OTA_Failure_Response::RETRY_UPDATE);
}

Expand Down
46 changes: 40 additions & 6 deletions src/ThingsBoard.h
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,30 @@ class ThingsBoardSized {
return m_client.unsubscribe(ATTRIBUTE_TOPIC);
}

/// @brief Clears all currently subscribed callbacks and unsubscribed from all
/// currently subscribed MQTT topics, any response that will stil be received is discarded
/// and any ongoing firmware update is aborted and will not be finished.
/// Was previously done automatically in the connect() method, but is not done anymore,
/// because connect() method now reconencts to all previously subscribed MQTT topics instead,
/// therefore there is no need anymore to discard all previously subscribed callbacks and letting the user resubscribe
inline void Cleanup_Subscriptions() {
// Cleanup all server-side RPC subscriptions
this->RPC_Unsubscribe();
// Cleanup all client-side RPC requests
this->RPC_Request_Unsubscribe();
// Cleanup all shared attributes subscriptions
this->Shared_Attributes_Unsubscribe();
// Cleanup all client-side or shared attributes requests
this->Attributes_Request_Unsubscribe();
// Cleanup all provision requests
this->Provision_Unsubscribe();
// Stop any ongoing Firmware update,
// which will in turn cleanup the internal member variables of the OTAHandler class
// as well as all firmware subscriptions
// and inform the user of the failed firmware update
this->Stop_Firmware_Update();
}

private:

#if THINGSBOARD_ENABLE_STREAM_UTILS
Expand Down Expand Up @@ -1369,20 +1393,30 @@ class ThingsBoardSized {
/// @return Whether connecting to ThingsBoard was successful or not
inline bool connect_to_host(const char *access_token, const char *client_id, const char *password) {
const bool connection_result = m_client.connect(client_id, access_token, password);

if (!connection_result) {
Logger::log(CONNECT_FAILED);
return connection_result;
}

this->RPC_Unsubscribe(); // Cleanup all server-side RPC subscriptions
this->RPC_Request_Unsubscribe(); // Cleanup all client-side RPC requests
this->Shared_Attributes_Unsubscribe(); // Cleanup all shared attributes subscriptions
this->Attributes_Request_Unsubscribe(); // Cleanup all client-side or shared attributes requests
this->Provision_Unsubscribe(); // Cleanup all provision subscriptions
// Firmware subscriptions are not cleaned up to ensure that it can be continued if the connection drops while the update is ongoing
// Only attempt to resubscribe if we connected successfully
Resubscribe_Topics();
return connection_result;
}

/// @brief Resubscribes to topics that establish a permanent connection with MQTT, meaning they may receive more than one event over their lifetime,
/// whereas other events that are only ever called once and then deleted after they have been handled are not resubscribed.
/// This is done, because the chance of disconnecting the moment when a request event (provisioning, attribute request, client-side rpc) was sent
/// and then reconnecting and resubscribing to that topic fast enough to still receive the message is not feasible
inline void Resubscribe_Topics() {
if (!m_rpc_callbacks.empty()) {
m_client.subscribe(RPC_SUBSCRIBE_TOPIC);
}
if (!m_shared_attribute_update_callbacks.empty()) {
m_client.subscribe(ATTRIBUTE_TOPIC);
}
}

#if !THINGSBOARD_ENABLE_DYNAMIC
/// @brief Reserves size for the given amount of items in our internal callback vectors beforehand for performance reasons,
/// this ensures the internal memory blocks do not have to move if new data is inserted,
Expand Down
Loading