From ddea7012f33c469d52ed8bb835bc342211f74439 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 3 Sep 2023 13:19:06 +0200 Subject: [PATCH 01/80] Removedirect dependency on PubSubClient --- .../0000-arduino_send_telemetry.ino | 5 +- .../0001-arduino_send_batch.ino | 3 + .../0002-arduino_rpc/0002-arduino_rpc.ino | 3 + .../0003-esp8266_esp32_send_data.ino | 7 +- .../0004-arduino-sim900_send_telemetry.ino | 6 +- ...005-arduino-sim900_send_telemetry_http.ino | 7 +- ..._esp32_process_shared_attribute_update.ino | 5 +- .../0007-esp8266_esp32_claim_device.ino | 5 +- .../0008-esp8266_esp32_provision_device.ino | 5 +- .../0009-esp8266_esp32_process_OTA_MQTT.ino | 5 +- .../0010-esp8266_esp32_rpc.ino | 5 +- .../0011-esp8266_esp32_subscribe_OTA_MQTT.ino | 5 +- ...esp8266_esp32_request_shared_attribute.ino | 5 +- .../0013-esp8266_esp32_request_rpc.ino | 5 +- library.json | 1 - library.properties | 2 +- src/Arduino_MQTT_Client.cpp | 82 +++++++++ src/Arduino_MQTT_Client.h | 75 +++++++++ src/ESP32_Updater.h | 4 +- src/ESP8266_Updater.h | 4 +- src/IMQTT_Client.h | 155 ++++++++++++++++++ src/IUpdater.h | 6 +- src/ThingsBoard.h | 102 +++++------- 23 files changed, 416 insertions(+), 86 deletions(-) create mode 100644 src/Arduino_MQTT_Client.cpp create mode 100644 src/Arduino_MQTT_Client.h create mode 100644 src/IMQTT_Client.h diff --git a/examples/0000-arduino_send_telemetry/0000-arduino_send_telemetry.ino b/examples/0000-arduino_send_telemetry/0000-arduino_send_telemetry.ino index 411038a8..63738b68 100644 --- a/examples/0000-arduino_send_telemetry/0000-arduino_send_telemetry.ino +++ b/examples/0000-arduino_send_telemetry/0000-arduino_send_telemetry.ino @@ -6,6 +6,7 @@ // - ESP8266 connected to Arduino Uno #include +#include #include #include #include @@ -75,8 +76,10 @@ constexpr char HUMIDITY_KEY[] = "humidity"; SoftwareSerial soft(2U, 3U); // RX, TX // Initialize the Ethernet client object WiFiEspClient espClient; +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); /// @brief Initalizes WiFi connection, diff --git a/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino b/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino index 96986967..e4bda380 100644 --- a/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino +++ b/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino @@ -6,6 +6,7 @@ // - ESP8266 connected to Arduino Uno #include +#include #include #include #include @@ -81,6 +82,8 @@ constexpr char SENSOR_VALUE[] = "sensor"; SoftwareSerial soft(2U, 3U); // RX, TX // Initialize the Ethernet client object WiFiEspClient espClient; +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); diff --git a/examples/0002-arduino_rpc/0002-arduino_rpc.ino b/examples/0002-arduino_rpc/0002-arduino_rpc.ino index 29d30bda..500f4c84 100644 --- a/examples/0002-arduino_rpc/0002-arduino_rpc.ino +++ b/examples/0002-arduino_rpc/0002-arduino_rpc.ino @@ -6,6 +6,7 @@ // - ESP8266 connected to Arduino Uno #include +#include #include #include #include @@ -81,6 +82,8 @@ constexpr const char RPC_RESPONSE_KEY[] = "example_response"; SoftwareSerial soft(2U, 3U); // RX, TX // Initialize the Ethernet client object WiFiEspClient espClient; +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); diff --git a/examples/0003-esp8266_esp32_send_data/0003-esp8266_esp32_send_data.ino b/examples/0003-esp8266_esp32_send_data/0003-esp8266_esp32_send_data.ino index 58a323b3..7bd6c671 100644 --- a/examples/0003-esp8266_esp32_send_data/0003-esp8266_esp32_send_data.ino +++ b/examples/0003-esp8266_esp32_send_data/0003-esp8266_esp32_send_data.ino @@ -27,6 +27,7 @@ #define THINGSBOARD_ENABLE_STREAM_UTILS 1 +#include #if USING_HTTPS #include #else @@ -197,11 +198,13 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size #if USING_HTTPS -ThingsBoardHttp tb(espClient, TOKEN, THINGSBOARD_SERVER, THINGSBOARD_PORT); +ThingsBoardHttp tb(mqttClient, TOKEN, THINGSBOARD_SERVER, THINGSBOARD_PORT); #else -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); #endif diff --git a/examples/0004-arduino-sim900_send_telemetry/0004-arduino-sim900_send_telemetry.ino b/examples/0004-arduino-sim900_send_telemetry/0004-arduino-sim900_send_telemetry.ino index fb5b8a26..d9d86615 100644 --- a/examples/0004-arduino-sim900_send_telemetry/0004-arduino-sim900_send_telemetry.ino +++ b/examples/0004-arduino-sim900_send_telemetry/0004-arduino-sim900_send_telemetry.ino @@ -18,6 +18,7 @@ #include #include +#include #include @@ -92,8 +93,11 @@ TinyGsm modem(serialGsm); // Initialize GSM client TinyGsmClient client(modem); +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(client); + // Initialize ThingsBoard instance -ThingsBoard tb(client, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Set to true, if modem is connected bool modemConnected = false; diff --git a/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino b/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino index 48b67722..ead8425d 100644 --- a/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino +++ b/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino @@ -16,8 +16,10 @@ // #define TINY_GSM_MODEM_M590 // #define TINY_GSM_MODEM_ESP8266 +#include #include #include +#include #include @@ -88,8 +90,11 @@ SoftwareSerial serialGsm(7U, 8U); // RX, TX pins for communicating with modem // Initialize GSM client TinyGsmClient client(modem); +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(client); + // Initialize ThingsBoard instance -ThingsBoardHttp tb(client, TOKEN, THINGSBOARD_SERVER, THINGSBOARD_PORT); +ThingsBoardHttp tb(mqttClient, TOKEN, THINGSBOARD_SERVER, THINGSBOARD_PORT); // Set to true, if modem is connected bool modemConnected = false; diff --git a/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino b/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino index 08d381b1..2fbd4fd6 100644 --- a/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino +++ b/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include @@ -173,8 +174,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for subscribing to shared attributes bool subscribed = false; diff --git a/examples/0007-esp8266_esp32_claim_device/0007-esp8266_esp32_claim_device.ino b/examples/0007-esp8266_esp32_claim_device/0007-esp8266_esp32_claim_device.ino index 019229d4..85700e42 100644 --- a/examples/0007-esp8266_esp32_claim_device/0007-esp8266_esp32_claim_device.ino +++ b/examples/0007-esp8266_esp32_claim_device/0007-esp8266_esp32_claim_device.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include @@ -180,8 +181,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for claiming bool claimingRequestSent = false; diff --git a/examples/0008-esp8266_esp32_provision_device/0008-esp8266_esp32_provision_device.ino b/examples/0008-esp8266_esp32_provision_device/0008-esp8266_esp32_provision_device.ino index fd0c61fa..64f7f444 100644 --- a/examples/0008-esp8266_esp32_provision_device/0008-esp8266_esp32_provision_device.ino +++ b/examples/0008-esp8266_esp32_provision_device/0008-esp8266_esp32_provision_device.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include @@ -195,8 +196,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); uint32_t previous_processing_time = 0U; diff --git a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino index 5aa85d95..ff866efb 100644 --- a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino +++ b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include #ifdef ESP8266 @@ -189,8 +190,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for updating bool currentFWSent = false; diff --git a/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino b/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino index da001763..5a328a92 100644 --- a/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino +++ b/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include @@ -171,8 +172,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for subscribing to rpc bool subscribed = false; diff --git a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino index 553639d7..4494a9ab 100644 --- a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino +++ b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include #ifdef ESP8266 @@ -189,8 +190,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for updating bool currentFWSent = false; diff --git a/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino b/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino index e4da32cb..5102798c 100644 --- a/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino +++ b/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include @@ -180,8 +181,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for requesting of attributes bool requestedClient = false; diff --git a/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino b/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino index c90f27e6..4c136304 100644 --- a/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino +++ b/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino @@ -10,6 +10,7 @@ #endif // ESP32 #endif // ESP8266 +#include #include @@ -163,8 +164,10 @@ WiFiClientSecure espClient; #else WiFiClient espClient; #endif +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for subscribing to rpc bool subscribed = false; diff --git a/library.json b/library.json index 90db693c..7511ce9b 100644 --- a/library.json +++ b/library.json @@ -8,7 +8,6 @@ }, "dependencies": { "bblanchon/ArduinoJson": "^6.21.2", - "thingsboard/TBPubSubClient": "https://github.com/thingsboard/pubsubclient.git#v2.9.2", "arduino-libraries/ArduinoHttpClient" : "^0.4.0", "bblanchon/StreamUtils" : "^1.7.3" }, diff --git a/library.properties b/library.properties index 226ba512..31a25100 100644 --- a/library.properties +++ b/library.properties @@ -8,4 +8,4 @@ category=Communication url=https://github.com/thingsboard/thingsboard-arduino-sdk architectures=* includes=ThingsBoard.h -depends=ArduinoHttpClient, TBPubSubClient, ArduinoJson, StreamUtils +depends=ArduinoHttpClient, ArduinoJson, StreamUtils diff --git a/src/Arduino_MQTT_Client.cpp b/src/Arduino_MQTT_Client.cpp new file mode 100644 index 00000000..3bb74d66 --- /dev/null +++ b/src/Arduino_MQTT_Client.cpp @@ -0,0 +1,82 @@ +// Header include. +#include "Arduino_MQTT_Client.h" + +Arduino_MQTT_Client::Arduino_MQTT_Client() : + m_mqtt_client() +{ + // Nothing to do +} + +Arduino_MQTT_Client::Arduino_MQTT_Client(Client& transport_client) : + m_mqtt_client(transport_client) +{ + // Nothing to do +} + +void Arduino_MQTT_Client::set_client(Client& transport_client) { + m_mqtt_client.setClient(transport_client); +} + +void Arduino_MQTT_Client::set_callback(function cb) { + m_mqtt_client.setCallback(cb); +} + +bool Arduino_MQTT_Client::set_buffer_size(const uint16_t& buffer_size) { + return m_mqtt_client.setBufferSize(buffer_size); +} + +uint16_t Arduino_MQTT_Client::get_buffer_size() { + return m_mqtt_client.getBufferSize(); +} + +void Arduino_MQTT_Client::set_server(const char *domain, const uint16_t& port) { + m_mqtt_client.setServer(domain, port); +} + +bool Arduino_MQTT_Client::connect(const char *client_id, const char *user_name, const char *password) { + return m_mqtt_client.connect(client_id, user_name, password); +} + +void Arduino_MQTT_Client::disconnect() { + m_mqtt_client.disconnect(); +} + +bool Arduino_MQTT_Client::loop() { + return m_mqtt_client.loop(); +} + +bool Arduino_MQTT_Client::publish(const char *topic, const uint8_t *payload, const uint32_t& length) { + return m_mqtt_client.publish(topic, payload, length, false); +} + +bool Arduino_MQTT_Client::subscribe(const char *topic) { + return m_mqtt_client.subscribe(topic); +} + +bool Arduino_MQTT_Client::unsubscribe(const char *topic) { + return m_mqtt_client.unsubscribe(topic); +} + +bool Arduino_MQTT_Client::connected() { + return m_mqtt_client.connected(); +} + +#if THINGSBOARD_ENABLE_STREAM_UTILS + +bool Arduino_MQTT_Client::begin_publish(const char *topic, const uint32_t& length) { + return m_mqtt_client.beginPublish(topic, length, false); +} + +bool Arduino_MQTT_Client::end_publish() { + return m_mqtt_client.endPublish(); +} + +size_t Arduino_MQTT_Client::write(uint8_t payload_byte) { + return m_mqtt_client.write(payload_byte); +} + +size_t Arduino_MQTT_Client::write(const uint8_t *buffer, size_t size) { + return m_mqtt_client.write(buffer, size); +} + +#endif // THINGSBOARD_ENABLE_STREAM_UTILS diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h new file mode 100644 index 00000000..b05c58ae --- /dev/null +++ b/src/Arduino_MQTT_Client.h @@ -0,0 +1,75 @@ +/* + Arduino_MQTT_Client.h - Library API for sending data to the ThingsBoard + Based on PubSub MQTT library. + Created by Olender M. Oct 2018. + Released into the public domain. +*/ +#ifndef Arduino_MQTT_Client_h +#define Arduino_MQTT_Client_h + +// Local includes. +#include "IMQTT_Client.h" + +// Library include +#include + +/// @brief MQTT Client interface imnplementation that uses the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), +/// under the hood to establis and communicate over a MQTT connection. The fork includes fixes to solve issues with using std::function callbacks for non ESP boards +class Arduino_MQTT_Client : public IMQTT_Client { + public: + /// @brief Constructs a IMQTT_Client implementation without a network client, meaning it has to be added later with the set_client() and set_buffer_size() method + Arduino_MQTT_Client(); + + /// @brief Constructs a IMQTT_Client implementation with the given network client + /// @param transport_client Client that is used to send the actual payload via. MQTT, needs to implement the client interface, + /// but the actual type of connection does not matter (Ethernet or WiFi) + Arduino_MQTT_Client(Client& transport_client); + + /// @brief Sets the client has to be used if the empty constructor was used initally + /// @param transport_client Client that is used to send the actual payload via. MQTT, needs to implement the client interface, + /// but the actual type of connection does not matter (Ethernet or WiFi) + void set_client(Client& transport_client); + + void set_callback(function cb) override; + + bool set_buffer_size(const uint16_t& buffer_size) override; + + uint16_t get_buffer_size() override; + + void set_server(const char *domain, const uint16_t& port) override; + + bool connect(const char *client_id, const char *user_name, const char *password) override; + + void disconnect() override; + + bool loop() override; + + bool publish(const char *topic, const uint8_t *payload, const uint32_t& length) override; + + bool subscribe(const char *topic) override; + + bool unsubscribe(const char *topic) override; + + bool connected() override; + +#if THINGSBOARD_ENABLE_STREAM_UTILS + + bool begin_publish(const char *topic, const uint32_t& length) override; + + bool end_publish() override; + + //---------------------------------------------------------------------------- + // Print interface + //---------------------------------------------------------------------------- + + size_t write(uint8_t payload_byte) override; + + size_t write(const uint8_t *buffer, size_t size) override; + +#endif // THINGSBOARD_ENABLE_STREAM_UTILS + + private: + PubSubClient m_mqtt_client; +}; + +#endif // Arduino_MQTT_Client_h diff --git a/src/ESP32_Updater.h b/src/ESP32_Updater.h index 258bc386..7565e5eb 100644 --- a/src/ESP32_Updater.h +++ b/src/ESP32_Updater.h @@ -14,8 +14,8 @@ #ifdef ESP32 -// Library include. -#include +// Local include. +#include "IUpdater.h" class ESP32_Updater : public IUpdater { public: diff --git a/src/ESP8266_Updater.h b/src/ESP8266_Updater.h index 2e8135d8..1c4fbae5 100644 --- a/src/ESP8266_Updater.h +++ b/src/ESP8266_Updater.h @@ -14,8 +14,8 @@ #ifdef ESP8266 -// Library include. -#include +// Local include. +#include "IUpdater.h" class ESP8266_Updater : public IUpdater { public: diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h new file mode 100644 index 00000000..13079c5f --- /dev/null +++ b/src/IMQTT_Client.h @@ -0,0 +1,155 @@ +/* + IMQTT_Client.h - Library API for sending data to the ThingsBoard + Based on PubSub MQTT library. + Created by Olender M. Oct 2018. + Released into the public domain. +*/ +#ifndef IMQTT_Client_h +#define IMQTT_Client_h + +// Local include. +#include "Configuration.h" + +// Library include. +#if THINGSBOARD_ENABLE_STL +#include +#endif // THINGSBOARD_ENABLE_STL +#if THINGSBOARD_ENABLE_STREAM_UTILS +#include +#endif // THINGSBOARD_ENABLE_STREAM_UTILS +#include + +/// @brief MQTT Client interface that contains the method that a class that can be used to send and receive data over an MQTT connection should implement. +/// Seperates the specific implementation used from the ThingsBoard client, allows to use differnt clients depending on different needs. +/// In this case the main use case of the seperation is to both support Espressif IDF and Arduino with the following libraries as recommendations. +/// The default MQTT Client for Arduino is the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), +/// it includes fixes to solve issues with using std::function callbacks for non ESP boards. +/// For Espressif IDF however the default MQTT Client is the esp-mqtt (https://github.com/espressif/esp-mqtt) component. +/// When using an implementation that does not allow for Arduino it is additional important to disable support for THINGSBOARD_ENABLE_STREAM_UTILS, +/// because this feature relies on Arduino as it improves the underlying data streams to directly write the data into the MQTT Client, +/// but wrirting each byte one by one, would be too slow, therefore the ArduinoStreamUtils (https://github.com/bblanchon/ArduinoStreamUtils) library is used to buffer thoose calls into bigger packets. +/// This allows sending data that is very big without requiring to allocate that much memory, because it is sent in smaller packets. +/// To support this feature, however this interface needs to additionally implement the Print interface, because that is required by the wrapper class BufferingPrint +#if THINGSBOARD_ENABLE_STREAM_UTILS +class IMQTT_Client : Print { +#else +class IMQTT_Client { +#endif // THINGSBOARD_ENABLE_STREAM_UTILS + public: + /// @brief Callback signature +#if THINGSBOARD_ENABLE_STL + using function = std::function; +#else + using function = void (*)(char *topic, uint8_t *payload, uint32_t length); +#endif // THINGSBOARD_ENABLE_STL + + /// @brief Sets the callback that is called, if any message is received by the MQTT broker, including the topic string that the message was received over, + /// as well as the payload data and the size of that payload data + /// @param cb Method that should be called on received MQTT response + virtual void set_callback(function cb) = 0; + + /// @brief Changes the size of the buffer for sent and received MQTT messages, + /// using a bigger value than uint16_t for passing the buffer size does not make any sense because the maximum message size received + /// or sent by MQTT can never be bigger than 64K, because it relies on TCP and the TCP size limit also uses a uint16_t internally for the size parameter + /// @param buffer_size Maximum amount of data that can be either received or sent via MQTT at once, + /// expected behaviour is that, if bigger packets are received they are discarded and a warning is printed to the console + /// and if we attempt to send data that is bigger, it will simply not be sent and a message is printed to the console instead + /// @return Whether allocating the needed memory for the given buffer_size was successful or not + virtual bool set_buffer_size(const uint16_t& buffer_size) = 0; + + /// @brief Gets the previously set size of the internal buffer size for sent and received MQTT + /// @return Internal size of the buffer + virtual uint16_t get_buffer_size() = 0; + + /// @brief Configures the server and port that the client should connect to MQTT over, + /// should be called atleast once before calling connect() so it is clear which server to connect too + /// @param domain Server instance name the client should connect too + /// @param port Port that will be used to establish a connection and send / receive data + /// Should be either 1883 for unencrypted MQTT or 8883 for MQTT with TLS/SSL encryption. + /// The latter is recommended if relevant data is sent or if the client receives and handles Remote Procedure Calls or Shared Attribute Update Callbacks from the server, + /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. + /// However if Over the Air udpates are enabled secure communication should definetly be enabled, because if that is not done a 3rd party might impersonate the server sending a malicious payload, + /// which is then flashed onto the device instead of the real firmware. Which depeding on the payload might even be able to destroy the device or it make it otherwise unusable. + /// See https://stackoverflow.blog/2020/12/14/security-considerations-for-ota-software-updates-for-iot-gateway-devices/ for more information on the aforementioned security risk + virtual void set_server(const char *domain, const uint16_t& port) = 0; + + /// @brief Connects to the previously with set_server configured server instance that should be connected to over the previously defined port + /// @param id Client identification code, that allows to differentiate which MQTT device is sending the traffic to the MQTT broker + /// @param user Client usernam that is used to authenticate, who is connecting over MQTT + /// @param pass Client password that isused to authenticate, who is connecting over MQTT + /// @return Wether the client could establish the connection successfully or not + virtual bool connect(const char *client_id, const char *user_name, const char *password) = 0; + + /// @brief Disconnects from a previously connected server and should release all used resources + virtual void disconnect() = 0; + + /// @brief Receives and sends any outstanding messages from and to the MQTT broker + /// @return Wether sending or receiving the oustanding the messages was successfull or not, + /// should return false if an internal error occured or the connection has been lost + virtual bool loop() = 0; + + /// @brief Sends the given payload over the previously established connection with connect + /// @param topic Topic that the message is sent over, where different MQTT topics expect a different kind of payload + /// @param payload Payload containg the json data that should be sent + /// @param length Length of the payload in bytes + /// @return Wether publishing the payload on the given topic was successfull or not + virtual bool publish(const char *topic, const uint8_t *payload, const uint32_t& length) = 0; + + /// @brief Subscribes to MQTT message on the given topic, which will cause an internal callback to be called for each message received on that topic from the server, + /// it should then, call the previously configured callback with set_callback() with the received data + /// @param topic Topic we want to receive a notification about if messages are sent by the server + /// @return Wheter subscribing the given topic was possible or not, should return false and a warning should be printed, + /// if the connection has been lost or the topic does not exist + virtual bool subscribe(const char *topic) = 0; + + /// @brief Unsubscribes to previously subscribed MQTT message on the given topic + /// @param topic Topic we want to stop receiving a notification about if messages are sent by the server + /// @return Wheter unsubscribing the given topic was possible or not, should return false and a warning should be printed, + /// if the connection has been lost or the topic was not previously subscribed + virtual bool unsubscribe(const char *topic) = 0; + + /// @brief Returns our current connection status to MQTT, true meaning we are connected, + /// false meaning we have been disconnected or have not established a connection yet + /// @return Whether the client is currently connected or not + virtual bool connected() = 0; + +#if THINGSBOARD_ENABLE_STREAM_UTILS + + /// @brief Start to publish a message over a given topic, without being restricted to the internal buffer size. + /// Meaning it allows for arbitrarily large payloads to be sent without them having to be copied into a new buffer and held in memory. + /// To use this feature first call begin_publish(), followed by multiple calls to write() and then ending with a call to end_publish() + /// @param topic Topic that the message is sent over, where different MQTT topics expect a different kind of payload + /// @param length Length of the payload in bytes + /// @return Wether starting to publish on the given topic was successfull or not + virtual bool begin_publish(const char *topic, const uint32_t& length) = 0; + + /// @brief Finishes any publish message started with begin_publish() + /// @return Wether the complete packet was sent successfully or not + virtual bool end_publish() = 0; + + //---------------------------------------------------------------------------- + // Print interface + //---------------------------------------------------------------------------- + + /// @brief Sends a single byte of payload to be published, is meant to be used after having calling begin_publish() + /// Once the complete payload has been written ensure to call end_publish() to send any remaining bytes. + /// Because payload bytes are sent one by one this method is extremly inefficient, + /// if possible package the payload into bigger chunks and use the write() method with arrays instead + /// @param payload_byte Byte containg part of the payload that should be sent + /// @return The amount of bytes successfully written + virtual size_t write(uint8_t payload_byte) = 0; + + // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish) + // Returns the number of bytes written + + /// @brief Sends a buffer containg multiple bytes of payload to be published, is meant to be used after having calling begin_publish() + /// Once the complete payload has been written ensure to call end_publish() to send any remaining bytes + /// @param buffer Buffer containg part of the payload that should be sent + /// @param size Amount of bytes contained in the buffer that should be sent + /// @return The amount of bytes successfully written + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + +#endif // THINGSBOARD_ENABLE_STREAM_UTILS +}; + +#endif // IMQTT_Client_h diff --git a/src/IUpdater.h b/src/IUpdater.h index ffe4b297..56819f4d 100644 --- a/src/IUpdater.h +++ b/src/IUpdater.h @@ -21,7 +21,7 @@ class IUpdater { public: /// @brief Initalizes the writing of the given data /// @param firmware_size Total size of the data that should be written, is done in multiple packets - /// @return Wheter initalizing the update was successfull or not + /// @return Wether initalizing the update was successfull or not virtual bool begin(const size_t& firmware_size) = 0; /// @brief Writes the given amount of bytes of the packet data @@ -31,11 +31,11 @@ class IUpdater { virtual size_t write(uint8_t* payload, const size_t& total_bytes) = 0; /// @brief Resets the writing of the given data so it can be restarted with begin - /// @return Wheter reseting was successful or not + /// @return Wether reseting was successful or not virtual bool reset() = 0; /// @brief Ends the update and returns wheter it was successfully completed - /// @return Wheter the complete amount of bytes initally given was successfully written or not + /// @return Wether the complete amount of bytes initally given was successfully written or not virtual bool end() = 0; }; diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 9418cbb8..915ba349 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -18,9 +18,9 @@ #include "RPC_Request_Callback.h" #include "Provision_Callback.h" #include "OTA_Handler.h" +#include "IMQTT_Client.h" // Library includes. -#include #if THINGSBOARD_ENABLE_STREAM_UTILS #include #endif // THINGSBOARD_ENABLE_STREAM_UTILS @@ -120,9 +120,7 @@ constexpr char RPC_EMPTY_PARAMS_VALUE[] = "{}"; // Log messages. #if THINGSBOARD_ENABLE_PROGMEM constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] PROGMEM = "Unable to de-serialize received json data with error (%s)"; -#if !THINGSBOARD_ENABLE_STREAM_UTILS constexpr char INVALID_BUFFER_SIZE[] PROGMEM = "Buffer size (%u) to small for the given payloads size (%u), increase with setBufferSize accordingly or set THINGSBOARD_ENABLE_STREAM_UTILS to 1 before including ThingsBoard"; -#endif // !THINGSBOARD_ENABLE_STREAM_UTILS constexpr char NUMBER_PRINTF[] PROGMEM = "%u"; #if !THINGSBOARD_ENABLE_DYNAMIC constexpr char MAX_RPC_EXCEEDED[] PROGMEM = "Too many server-side RPC subscriptions, increase MaxFieldsAmt or unsubscribe"; @@ -151,9 +149,7 @@ constexpr char SEND_SERIALIZED[] PROGMEM = "Hidden, because json data is bigger #endif // THINGSBOARD_ENABLE_DEBUG #else constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] = "Unable to de-serialize received json data with error (%s)"; -#if !THINGSBOARD_ENABLE_STREAM_UTILS constexpr char INVALID_BUFFER_SIZE[] = "Buffer size (%u) to small for the given payloads size (%u), increase with setBufferSize accordingly or set THINGSBOARD_ENABLE_STREAM_UTILS to 1 before including ThingsBoard"; -#endif // !THINGSBOARD_ENABLE_STREAM_UTILS constexpr char NUMBER_PRINTF[] = "%u"; #if !THINGSBOARD_ENABLE_DYNAMIC constexpr char MAX_RPC_EXCEEDED[] = "Too many server-side RPC subscriptions, increase MaxFieldsAmt or unsubscribe"; @@ -299,14 +295,14 @@ constexpr char DOWNLOADING_FW[] = "Attempting to download over MQTT..."; #endif // THINGSBOARD_ENABLE_OTA #if THINGSBOARD_ENABLE_DYNAMIC -/// @brief Wrapper around the PubSubClient to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. +/// @brief Wrapper around any arbitrary MQTT Client implementing the IMQTT_Client interface, to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. /// BufferSize of the underlying data buffer as well as the maximum amount of data points that can ever be sent are either dynamic or can be changed during runtime. /// If this feature is not needed and the values can be sent once as template arguements it is recommended to use the static ThingsBoard instance instead. /// Simply set THINGSBOARD_ENABLE_DYNAMIC to 0, before including ThingsBoard.h /// @tparam Logger Logging class that should be used to print messages generated by ThingsBoard, default = ThingsBoardDefaultLogger template #else -/// @brief Wrapper around the PubSubClient to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. +/// @brief Wrapper around any arbitrary MQTT Client implementing the IMQTT_Client interface, to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. /// BufferSize of the underlying data buffer as well as the maximum amount of data points that can ever be sent have to defined as template arguments. /// Changing is only possible if a new instance of this class is created. If theese values should be changeable and dynamic instead. /// Simply set THINGSBOARD_ENABLE_DYNAMIC to 1, before including ThingsBoard.h @@ -317,24 +313,14 @@ template(json), jsonSize, false); + return m_client.publish(topic, reinterpret_cast(json), jsonSize); } //---------------------------------------------------------------------------- @@ -1054,7 +1028,7 @@ class ThingsBoardSized { /// @return Whether sending the data was successful or not template inline bool Serialize_Json(const char* topic, const TSource& source, const uint32_t& jsonSize) { - if (!m_client.beginPublish(topic, jsonSize, false)) { + if (!m_client.begin_publish(topic, jsonSize, false)) { Logger::log(UNABLE_TO_SERIALIZE_JSON); return false; } @@ -1065,7 +1039,7 @@ class ThingsBoardSized { return false; } buffered_print.flush(); - return m_client.endPublish(); + return m_client.end_publish(); } #endif // THINGSBOARD_ENABLE_STREAM_UTILS @@ -1074,7 +1048,7 @@ class ThingsBoardSized { /// @brief Publishes a request via MQTT to request the given firmware chunk /// @param request_chunck Chunk index that should be requested from the server - /// @return Wheter publishing the message was successfull or not + /// @return Wether publishing the message was successfull or not inline bool Publish_Chunk_Request(const uint32_t& request_chunck) { // Calculate the number of chuncks we need to request, // in order to download the complete firmware binary @@ -1089,7 +1063,7 @@ class ThingsBoardSized { char topic[Helper::detectSize(FIRMWARE_REQUEST_TOPIC, request_chunck)]; snprintf_P(topic, sizeof(topic), FIRMWARE_REQUEST_TOPIC, request_chunck); - return m_client.publish(topic, reinterpret_cast(size), jsonSize, false); + return m_client.publish(topic, reinterpret_cast(size), jsonSize); } #endif // THINGSBOARD_ENABLE_OTA @@ -1254,7 +1228,7 @@ class ThingsBoardSized { // to allow to receive ota chunck packets that might be much bigger than the normal // buffer size would allow, therefore we return to the previous value to decrease overall memory usage if (m_change_buffer_size) { - m_client.setBufferSize(m_previous_buffer_size); + m_client.set_buffer_size(m_previous_buffer_size); } // Reset now not needed private member variables m_fw_callback = nullptr; @@ -1295,13 +1269,13 @@ class ThingsBoardSized { // If firmware version and title is the same, we do not initiate an update, because we expect the binary to be the same one we are currently using else if (strncmp_P(curr_fw_title, fw_title, JSON_STRING_SIZE(strlen(curr_fw_title))) == 0 && strncmp_P(curr_fw_version, fw_version, JSON_STRING_SIZE(strlen(curr_fw_version))) == 0) { Logger::log(FW_UP_TO_DATE); - Firmware_Send_State(FW_STATE_UPDATED, FW_UP_TO_DATE); + Firmware_Send_State(FW_STATE_FAILED, FW_UP_TO_DATE); return; } // If firmware title is not the same, we do not initiate an update, because we expect the binary to be for another device type else if (strncmp_P(curr_fw_title, fw_title, JSON_STRING_SIZE(strlen(curr_fw_title))) != 0) { Logger::log(FW_NOT_FOR_US); - Firmware_Send_State(FW_STATE_UPDATED, FW_NOT_FOR_US); + Firmware_Send_State(FW_STATE_FAILED, FW_NOT_FOR_US); return; } @@ -1346,11 +1320,11 @@ class ThingsBoardSized { const uint16_t& chunk_size = m_fw_callback->Get_Chunk_Size(); // Get the previous buffer size and cache it so the previous settings can be restored. - m_previous_buffer_size = m_client.getBufferSize(); + m_previous_buffer_size = m_client.get_buffer_size(); m_change_buffer_size = m_previous_buffer_size < (chunk_size + 50U); // Increase size of receive buffer - if (m_change_buffer_size && !m_client.setBufferSize(chunk_size + 50U)) { + if (m_change_buffer_size && !m_client.set_buffer_size(chunk_size + 50U)) { Logger::log(NOT_ENOUGH_RAM); Firmware_Send_State(FW_STATE_FAILED, NOT_ENOUGH_RAM); return; @@ -1836,7 +1810,7 @@ class ThingsBoardSized { return telemetry ? sendTelemetryJson(object, Helper::Measure_Json(object)) : sendAttributeJSON(object, Helper::Measure_Json(object)); } - PubSubClient m_client; // PubSub MQTT client instance. + IMQTT_Client& m_client; // MQTT client instance. uint32_t m_max_stack; // Maximum stack size we allocate at once. size_t m_buffering_size; // Buffering size used to serialize directly into client. From 72bf223d65fcc2fee06be2982dc8b10c6c9a717a Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 3 Sep 2023 13:30:21 +0200 Subject: [PATCH 02/80] Fix incorrect function arguments --- src/Constants.h | 4 ++-- src/IMQTT_Client.h | 4 ++-- src/OTA_Handler.h | 2 +- src/ThingsBoard.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Constants.h b/src/Constants.h index cf214c6b..39554f61 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -39,7 +39,7 @@ class ThingsBoardDefaultLogger; #if THINGSBOARD_ENABLE_PROGMEM constexpr char UNABLE_TO_SERIALIZE[] PROGMEM = "Unable to serialize key-value json"; #if !THINGSBOARD_ENABLE_DYNAMIC -constexpr char TOO_MANY_JSON_FIELDS[] PROGMEM = "Too many JSON fields passed (%u), increase MaxFieldsAmt (%u) accordingly"; +constexpr char TOO_MANY_JSON_FIELDS[] PROGMEM = "Too many JSON fields passed (%lu), increase MaxFieldsAmt (%u) accordingly"; #endif // !THINGSBOARD_ENABLE_DYNAMIC constexpr char CONNECT_FAILED[] PROGMEM = "Connecting to server failed"; constexpr char UNABLE_TO_SERIALIZE_JSON[] PROGMEM = "Unable to serialize json data"; @@ -47,7 +47,7 @@ constexpr char UNABLE_TO_ALLOCATE_MEMORY[] PROGMEM = "Allocating memory for the #else constexpr char UNABLE_TO_SERIALIZE[] = "Unable to serialize key-value json"; #if !THINGSBOARD_ENABLE_DYNAMIC -constexpr char TOO_MANY_JSON_FIELDS[] = "Too many JSON fields passed (%u), increase MaxFieldsAmt (%u) accordingly"; +constexpr char TOO_MANY_JSON_FIELDS[] = "Too many JSON fields passed (%lu), increase MaxFieldsAmt (%u) accordingly"; #endif // !THINGSBOARD_ENABLE_DYNAMIC constexpr char CONNECT_FAILED[] = "Connecting to server failed"; constexpr char UNABLE_TO_SERIALIZE_JSON[] = "Unable to serialize json data"; diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index 13079c5f..ab5d3309 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -38,9 +38,9 @@ class IMQTT_Client { public: /// @brief Callback signature #if THINGSBOARD_ENABLE_STL - using function = std::function; + using function = std::function; #else - using function = void (*)(char *topic, uint8_t *payload, uint32_t length); + using function = void (*)(char *topic, uint8_t *payload, unsigned int length); #endif // THINGSBOARD_ENABLE_STL /// @brief Sets the callback that is called, if any message is received by the MQTT broker, including the topic string that the message was received over, diff --git a/src/OTA_Handler.h b/src/OTA_Handler.h index 99c242b6..ba54df89 100644 --- a/src/OTA_Handler.h +++ b/src/OTA_Handler.h @@ -145,7 +145,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) { diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 915ba349..f95544cc 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -1583,7 +1583,7 @@ class ThingsBoardSized { /// @param topic Previously subscribed topic, we got the response over /// @param payload Payload that was sent over the cloud and received over the given topic /// @param length Total length of the received payload - inline void process_firmware_response(char *topic, uint8_t *payload, const uint32_t length) { + inline void process_firmware_response(char *topic, uint8_t *payload, const unsigned int& length) { // Remove the not needed part of the topic const size_t index = strlen(FIRMWARE_RESPONSE_TOPIC) + 1U; #if THINGSBOARD_ENABLE_STL @@ -1850,7 +1850,7 @@ class ThingsBoardSized { /// @param topic Previously subscribed topic, we got the response over /// @param payload Payload that was sent over the cloud and received over the given topic /// @param length Total length of the received payload - inline void onMQTTMessage(char *topic, uint8_t *payload, uint32_t length) { + inline void onMQTTMessage(char *topic, uint8_t *payload, unsigned int length) { #if THINGSBOARD_ENABLE_DEBUG char message[JSON_STRING_SIZE(strlen(RECEIVE_MESSAGE)) + JSON_STRING_SIZE(strlen(topic))]; snprintf_P(message, sizeof(message), RECEIVE_MESSAGE, topic); From c6ae5511eb29e8e7cda2932cefeadb3f867cabd2 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 3 Sep 2023 13:43:57 +0200 Subject: [PATCH 03/80] Use updater interface --- src/ESP32_Updater.cpp | 3 +-- src/ESP32_Updater.h | 2 +- src/ESP6266_Updater.cpp | 4 ++-- src/ESP8266_Updater.h | 2 +- src/IUpdater.h | 3 +-- src/OTA_Handler.h | 36 +++++++++++------------------------- 6 files changed, 17 insertions(+), 33 deletions(-) diff --git a/src/ESP32_Updater.cpp b/src/ESP32_Updater.cpp index 91604cbb..feed709d 100644 --- a/src/ESP32_Updater.cpp +++ b/src/ESP32_Updater.cpp @@ -16,9 +16,8 @@ size_t ESP32_Updater::write(uint8_t* payload, const size_t& total_bytes) { return Update.write(payload, total_bytes); } -bool ESP32_Updater::reset() { +void ESP32_Updater::reset() { Update.abort(); - return true; } bool ESP32_Updater::end() { diff --git a/src/ESP32_Updater.h b/src/ESP32_Updater.h index 7565e5eb..a54a1fe1 100644 --- a/src/ESP32_Updater.h +++ b/src/ESP32_Updater.h @@ -23,7 +23,7 @@ class ESP32_Updater : public IUpdater { size_t write(uint8_t* payload, const size_t& total_bytes) override; - bool reset() override; + void reset() override; bool end() override; }; diff --git a/src/ESP6266_Updater.cpp b/src/ESP6266_Updater.cpp index 0e64e15d..254e6091 100644 --- a/src/ESP6266_Updater.cpp +++ b/src/ESP6266_Updater.cpp @@ -16,8 +16,8 @@ size_t ESP8266_Updater::write(uint8_t* payload, const size_t& total_bytes) { return Update.write(payload, total_bytes); } -bool ESP8266_Updater::reset() { - return true; +void ESP8266_Updater::reset() { + // Nothing to do } bool ESP8266_Updater::end() { diff --git a/src/ESP8266_Updater.h b/src/ESP8266_Updater.h index 1c4fbae5..a7440d95 100644 --- a/src/ESP8266_Updater.h +++ b/src/ESP8266_Updater.h @@ -23,7 +23,7 @@ class ESP8266_Updater : public IUpdater { size_t write(uint8_t* payload, const size_t& total_bytes) override; - bool reset() override; + void reset() override; bool end() override; }; diff --git a/src/IUpdater.h b/src/IUpdater.h index 56819f4d..8340e9ae 100644 --- a/src/IUpdater.h +++ b/src/IUpdater.h @@ -31,8 +31,7 @@ class IUpdater { virtual size_t write(uint8_t* payload, const size_t& total_bytes) = 0; /// @brief Resets the writing of the given data so it can be restarted with begin - /// @return Wether reseting was successful or not - virtual bool reset() = 0; + virtual void reset() = 0; /// @brief Ends the update and returns wheter it was successfully completed /// @return Wether the complete amount of bytes initally given was successfully written or not diff --git a/src/OTA_Handler.h b/src/OTA_Handler.h index ba54df89..cfc7045b 100644 --- a/src/OTA_Handler.h +++ b/src/OTA_Handler.h @@ -19,15 +19,6 @@ #include "OTA_Update_Callback.h" #include "OTA_Failure_Response.h" -// Library include. -#ifdef ESP8266 -#include -#else -#ifdef ESP32 -#include -#endif // ESP32 -#endif // ESP8266 - /// --------------------------------- /// Constant strings in flash memory. /// --------------------------------- @@ -69,7 +60,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)"; @@ -119,8 +110,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); @@ -131,9 +123,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); @@ -163,7 +153,7 @@ 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); @@ -171,7 +161,7 @@ class OTA_Handler { } // 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); @@ -211,6 +201,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; @@ -222,9 +213,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(); } @@ -267,12 +256,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); } From 35855355b0061fc9cacfb0bdb1ab12a5113cc343 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 3 Sep 2023 14:30:40 +0200 Subject: [PATCH 04/80] Fix typos and adjust documentation --- README.md | 26 ++++++++++++++----- .../0001-arduino_send_batch.ino | 2 +- .../0002-arduino_rpc/0002-arduino_rpc.ino | 2 +- src/Constants.h | 4 +-- src/IMQTT_Client.h | 2 +- src/ThingsBoard.h | 2 +- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f2259ab6..6814e231 100644 --- a/README.md +++ b/README.md @@ -95,11 +95,14 @@ If that's a case, the buffer size for serialization should be increased. To do s // For the sake of example WiFiEspClient espClient; +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); + // The SDK setup with 64 bytes for JSON buffer -// ThingsBoard tb(espClient); +// ThingsBoard tb(mqttClient); // The SDK setup with 128 bytes for JSON buffer -ThingsBoard tb(espClient, 128); +ThingsBoard tb(mqttClient, 128); void setup() { // Increase internal buffer size after inital creation. @@ -116,8 +119,11 @@ Alternatively it is possible to enable the mentioned `THINGSBOARD_ENABLE_STREAM_ // For the sake of example WiFiEspClient espClient; +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); + // The SDK setup with 64 bytes for JSON buffer -ThingsBoard tb(espClient); +ThingsBoard tb(mqttClient); ``` ### Too much data fields must be serialized @@ -134,11 +140,14 @@ The solution is to use `ThingsBoardSized` class instead of `ThingsBoard`. **Note // For the sake of example WiFiEspClient espClient; +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); + // The SDK setup with 8 fields for JSON object -// ThingsBoard tb(espClient); +// ThingsBoard tb(mqttClient); // The SDK setup with 128 bytes for JSON payload and 32 fields for JSON object. -ThingsBoardSized<32> tb(espClient, 128); +ThingsBoardSized<32> tb(mqttClient, 128); ``` ## Tips and Tricks @@ -165,11 +174,14 @@ After that, you can use it in place of regular `ThingsBoard` class. **Note that // For the sake of example WiFiEspClient espClient; +// Initalize the Mqtt client instance +Arduino_MQTT_Client mqttClient(espClient); + // The SDK setup with 8 fields for JSON object -// ThingsBoard tb(espClient); +// ThingsBoard tb(mqttClient); // The SDK setup with 128 bytes for JSON payload and 32 fields for JSON object. -ThingsBoardSized<32, CustomLogger> tb(espClient, 128); +ThingsBoardSized<32, CustomLogger> tb(mqttClient, 128); ``` ## Have a question or proposal? diff --git a/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino b/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino index e4bda380..6b567f8d 100644 --- a/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino +++ b/examples/0001-arduino_send_batch/0001-arduino_send_batch.ino @@ -85,7 +85,7 @@ WiFiEspClient espClient; // Initalize the Mqtt client instance Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); /// @brief Initalizes WiFi connection, diff --git a/examples/0002-arduino_rpc/0002-arduino_rpc.ino b/examples/0002-arduino_rpc/0002-arduino_rpc.ino index 500f4c84..a6b33566 100644 --- a/examples/0002-arduino_rpc/0002-arduino_rpc.ino +++ b/examples/0002-arduino_rpc/0002-arduino_rpc.ino @@ -85,7 +85,7 @@ WiFiEspClient espClient; // Initalize the Mqtt client instance Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance -ThingsBoard tb(espClient, MAX_MESSAGE_SIZE); +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); // Statuses for subscribing to rpc bool subscribed = false; diff --git a/src/Constants.h b/src/Constants.h index 39554f61..cf214c6b 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -39,7 +39,7 @@ class ThingsBoardDefaultLogger; #if THINGSBOARD_ENABLE_PROGMEM constexpr char UNABLE_TO_SERIALIZE[] PROGMEM = "Unable to serialize key-value json"; #if !THINGSBOARD_ENABLE_DYNAMIC -constexpr char TOO_MANY_JSON_FIELDS[] PROGMEM = "Too many JSON fields passed (%lu), increase MaxFieldsAmt (%u) accordingly"; +constexpr char TOO_MANY_JSON_FIELDS[] PROGMEM = "Too many JSON fields passed (%u), increase MaxFieldsAmt (%u) accordingly"; #endif // !THINGSBOARD_ENABLE_DYNAMIC constexpr char CONNECT_FAILED[] PROGMEM = "Connecting to server failed"; constexpr char UNABLE_TO_SERIALIZE_JSON[] PROGMEM = "Unable to serialize json data"; @@ -47,7 +47,7 @@ constexpr char UNABLE_TO_ALLOCATE_MEMORY[] PROGMEM = "Allocating memory for the #else constexpr char UNABLE_TO_SERIALIZE[] = "Unable to serialize key-value json"; #if !THINGSBOARD_ENABLE_DYNAMIC -constexpr char TOO_MANY_JSON_FIELDS[] = "Too many JSON fields passed (%lu), increase MaxFieldsAmt (%u) accordingly"; +constexpr char TOO_MANY_JSON_FIELDS[] = "Too many JSON fields passed (%u), increase MaxFieldsAmt (%u) accordingly"; #endif // !THINGSBOARD_ENABLE_DYNAMIC constexpr char CONNECT_FAILED[] = "Connecting to server failed"; constexpr char UNABLE_TO_SERIALIZE_JSON[] = "Unable to serialize json data"; diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index ab5d3309..58341d9b 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -31,7 +31,7 @@ /// This allows sending data that is very big without requiring to allocate that much memory, because it is sent in smaller packets. /// To support this feature, however this interface needs to additionally implement the Print interface, because that is required by the wrapper class BufferingPrint #if THINGSBOARD_ENABLE_STREAM_UTILS -class IMQTT_Client : Print { +class IMQTT_Client : public Print { #else class IMQTT_Client { #endif // THINGSBOARD_ENABLE_STREAM_UTILS diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index f95544cc..18b92407 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -1028,7 +1028,7 @@ class ThingsBoardSized { /// @return Whether sending the data was successful or not template inline bool Serialize_Json(const char* topic, const TSource& source, const uint32_t& jsonSize) { - if (!m_client.begin_publish(topic, jsonSize, false)) { + if (!m_client.begin_publish(topic, jsonSize)) { Logger::log(UNABLE_TO_SERIALIZE_JSON); return false; } From 94239afc4584ad2bdfc73a9340fb12c7a69680e4 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 3 Sep 2023 18:39:58 +0200 Subject: [PATCH 05/80] Added HTTP interface as well --- README.md | 169 ++++++++++++++++++ ...005-arduino-sim900_send_telemetry_http.ino | 7 +- library.json | 3 +- library.properties | 4 +- src/Arduino_HTTP_Client.cpp | 38 ++++ src/Arduino_HTTP_Client.h | 48 +++++ src/Arduino_MQTT_Client.h | 2 +- src/HashGenerator.h | 2 +- src/IHTTP_Client.h | 62 +++++++ src/IMQTT_Client.h | 12 +- src/IUpdater.h | 4 +- src/OTA_Handler.h | 2 +- src/Telemetry.h | 2 +- src/ThingsBoard.h | 4 +- src/ThingsBoardHttp.h | 36 ++-- 15 files changed, 351 insertions(+), 44 deletions(-) create mode 100644 src/Arduino_HTTP_Client.cpp create mode 100644 src/Arduino_HTTP_Client.h create mode 100644 src/IHTTP_Client.h diff --git a/README.md b/README.md index 6814e231..f2f66154 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,175 @@ ThingsBoardSized<32> tb(mqttClient, 128); ## Tips and Tricks +### Custom Updater Instance + +When using the `ThingsBoard` class instance, the class used to flash the binary data onto the device is not hard coded, +but instead the `OTA_Update_Callback` class expects an optional argument, the `IUpdater` implementation. + +Thanks to it being a `interface` it allows an arbitrary implementation, +meaning as long as the device can flash binary data and supports the C++ STL it supports OTA updates, with the `ThingsBoard` library. + +Currently, implemented in the library itself are the `ESP32_Updater`, which is used for flashing the binary data when using a `ESP32` and the `ESP8266_Updater` which is used with the `ESP8266`. +If another device wants to be supported, a custom interface implementation needs to be created. For that a `class` that inherits the `IUpdater` interface needs to be created and `override` the needed methods. + +```c++ +#include + +class Custom_Updater : public IUpdater { + public: + bool begin(const size_t& firmware_size) override { + return true; + } + + size_t write(uint8_t* payload, const size_t& total_bytes) override { + return total_bytes; + } + + void reset() override { + // Nothing to do + } + + bool end() override { + return true; + } +}; +``` + +### Custom HTTP Instance + +When using the `ThingsBoardHttp` class instance, the protocol used to send the data to the HTTP broker is not hard coded, +but instead the `ThingsBoardHttp` class expects the argument to a `IHTTP_Client` implementation. + +Thanks to it being a `interface` it allows an arbitrary implementation, +meaning the underlying HTTP client can be whatever the user decides, so it can for example be used to support platforms using `Arduino` or even `Espressif IDF`. + +Currently, implemented in the library itself is the `Arduino_HTTP_Client`, which is simply a wrapper around the [`ArduinoHttpClient`](https://github.com/arduino-libraries/ArduinoHttpClient), see [dependencies](https://github.com/arduino-libraries/ArduinoHttpClient?tab=readme-ov-file#dependencies) for whether the board you are using is supported or not. If another device wants to be supported, a custom interface implementation needs to be created. +For that a `class` that inherits the `IHTTP_Client` interface needs to be created and `override` the needed methods. + + +```c++ +#include + +class Custom_HTTP_Client : public IHTTP_Client { + public: + void set_keep_alive(const bool& keep_alive) override { + // Nothing to do + } + + int connect(const char *host, const uint16_t& port) override { + return 0; + } + + void stop() override { + // Nothing to do + } + + int post(const char *url_path, const char *content_type, const char *request_body) override { + return 0; + } + + int get_response_status_code() override { + return 200; + } + + int get(const char *url_path) override{ + return 0; + } + + String get_response_body() override{ + return String(); + } +}; +``` + +### Custom MQTT Instance + +When using the `ThingsBoard` class instance, the protocol used to send the data to the MQTT broker is not hard coded, +but instead the `ThingsBoard` class expects the argument to a `IMQTT_Client` implementation. + +Thanks to it being a `interface` it allows an arbitrary implementation, +meaning the underlying MQTT client can be whatever the user decides, so it can for example be used to support platforms using `Arduino` or even `Espressif IDF`. + +Currently, implemented in the library itself is the `Arduino_MQTT_Client`, which is simply a wrapper around the [`TBPubSubClient`](https://github.com/thingsboard/pubsubclient), see [compatible Hardware](https://github.com/thingsboard/pubsubclient?tab=readme-ov-file#compatible-hardware) for whether the board you are using is supported or not. If another device wants to be supported, a custom interface implementation needs to be created. +For that a `class` that inherits the `IMQTT_Client` interface needs to be created and `override` the needed methods. + +```c++ +#include + +class Custom_MQTT_Client : public IMQTT_Client { + public: + void set_callback(function cb) override { + // Nothing to do + } + + bool set_buffer_size(const uint16_t& buffer_size) override{ + return true; + } + + uint16_t get_buffer_size() override { + return 0U; + } + + void set_server(const char *domain, const uint16_t& port) override { + // Nothing to do + } + + bool connect(const char *client_id, const char *user_name, const char *password) override { + return true; + } + + void disconnect() override { + // Nothing to do + } + + bool loop() override { + return true; + } + + bool publish(const char *topic, const uint8_t *payload, const uint32_t& length) override { + return true; + } + + bool subscribe(const char *topic) override { + return true; + } + + bool unsubscribe(const char *topic) override { + return true; + } + + bool connected() override { + return true; + } + +#if THINGSBOARD_ENABLE_STREAM_UTILS + + bool begin_publish(const char *topic, const uint32_t& length) override { + return true; + } + + bool end_publish() override { + return true; + } + + //---------------------------------------------------------------------------- + // Print interface + //---------------------------------------------------------------------------- + + size_t write(uint8_t payload_byte) override { + return 1U; + } + + size_t write(const uint8_t *buffer, size_t size) override { + return size; + } + +#endif // THINGSBOARD_ENABLE_STREAM_UTILS +}; +``` + +### Custom Logger Instance + To use your own logger you have to create a class and pass it as second template parameter `Logger` to your `ThingsBoardSized` class instance. **For example:** diff --git a/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino b/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino index ead8425d..286f3499 100644 --- a/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino +++ b/examples/0005-arduino-sim900_send_telemetry_http/0005-arduino-sim900_send_telemetry_http.ino @@ -16,10 +16,9 @@ // #define TINY_GSM_MODEM_M590 // #define TINY_GSM_MODEM_ESP8266 -#include #include #include -#include +#include #include @@ -91,10 +90,10 @@ SoftwareSerial serialGsm(7U, 8U); // RX, TX pins for communicating with modem TinyGsmClient client(modem); // Initalize the Mqtt client instance -Arduino_MQTT_Client mqttClient(client); +Arduino_HTTP_Client httpClient(client, THINGSBOARD_SERVER, THINGSBOARD_PORT); // Initialize ThingsBoard instance -ThingsBoardHttp tb(mqttClient, TOKEN, THINGSBOARD_SERVER, THINGSBOARD_PORT); +ThingsBoardHttp tb(httpClient, TOKEN, THINGSBOARD_SERVER, THINGSBOARD_PORT); // Set to true, if modem is connected bool modemConnected = false; diff --git a/library.json b/library.json index 7511ce9b..0b1e9b1c 100644 --- a/library.json +++ b/library.json @@ -8,10 +8,9 @@ }, "dependencies": { "bblanchon/ArduinoJson": "^6.21.2", - "arduino-libraries/ArduinoHttpClient" : "^0.4.0", "bblanchon/StreamUtils" : "^1.7.3" }, - "version": "0.11.0", + "version": "0.12.0", "examples": "examples/*/*.ino", "frameworks": "arduino", "license": "MIT" diff --git a/library.properties b/library.properties index 31a25100..1d1c851e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ThingsBoard -version=0.11.0 +version=0.12.0 author=ThingsBoard Team maintainer=ThingsBoard Team sentence=ThingsBoard library for Arduino. @@ -8,4 +8,4 @@ category=Communication url=https://github.com/thingsboard/thingsboard-arduino-sdk architectures=* includes=ThingsBoard.h -depends=ArduinoHttpClient, ArduinoJson, StreamUtils +depends=ArduinoJson, StreamUtils diff --git a/src/Arduino_HTTP_Client.cpp b/src/Arduino_HTTP_Client.cpp new file mode 100644 index 00000000..81ab7138 --- /dev/null +++ b/src/Arduino_HTTP_Client.cpp @@ -0,0 +1,38 @@ +// Header include. +#include "Arduino_HTTP_Client.h" + +Arduino_HTTP_Client::Arduino_HTTP_Client(Client& transport_client, const char *host, const uint16_t& port) : + m_http_client(transport_client, host, port) +{ + // Nothing to do +} + +void Arduino_HTTP_Client::set_keep_alive(const bool& keep_alive) { + if (keep_alive) { + m_http_client.connectionKeepAlive(); + } +} + +int Arduino_HTTP_Client::connect(const char *host, const uint16_t& port) { + return m_client.connect(host, port); +} + +void Arduino_HTTP_Client::stop() { + m_client.stop(); +} + +int Arduino_HTTP_Client::post(const char *url_path, const char *content_type, const char *request_body) { + return m_client.post(url_path, content_type, request_body); +} + +int Arduino_HTTP_Client::get_response_status_code() { + return m_client.responseStatusCode(); +} + +int Arduino_HTTP_Client::get(const char *url_path) { + return m_client.get(url_path); +} + +String Arduino_HTTP_Client::get_response_body() { + return m_client.responseBody(); +} diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h new file mode 100644 index 00000000..ab101816 --- /dev/null +++ b/src/Arduino_HTTP_Client.h @@ -0,0 +1,48 @@ +/* + Arduino_HTTP_Client.h - Library API for sending data to the ThingsBoard + Based on PubSub MQTT library. + Created by Olender M. Oct 2018. + Released into the public domain. +*/ +#ifndef Arduino_HTTP_Client_h +#define Arduino_HTTP_Client_h + +// Library includes. +#include + +// Local includes. +#include "IHTTP_Client.h" + +/// @brief HTTP Client interface imnplementation that uses the ArduinoHttpClient (https://github.com/arduino-libraries/ArduinoHttpClient), +/// under the hood to establish and communicate over a HTTP connection +class Arduino_HTTP_Client : public IHTTP_Client { + public: + /// @brief Constructs a IHTTP_Client implementation with the given network client + /// @param transport_client Client that is used to send the actual payload via. HTTP, needs to implement the client interface, + /// but the actual type of connection does not matter (Ethernet or WiFi) + /// @param host Server instance name the client should connect too + /// @param port Port that will be used to establish a connection and send / receive data + /// Should be either 80 for unencrypted HTTP or 443 for HTTPS with encryption. + /// The latter is recommended if relevant data is sent or if the client receives and handles requests from the server, + /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. + Arduino_HTTP_Client(Client& transport_client, const char *host, const uint16_t& port); + + void set_keep_alive(const bool& keep_alive) override; + + int connect(const char *host, const uint16_t& port) override; + + void stop() override; + + int post(const char *url_path, const char *content_type, const char *request_body) override; + + int get_response_status_code() override; + + int get(const char *url_path) override; + + String get_response_body() override; + + private: + HttpClient m_http_client; +}; + +#endif // Arduino_HTTP_Client_h diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index b05c58ae..a19f305d 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -14,7 +14,7 @@ #include /// @brief MQTT Client interface imnplementation that uses the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), -/// under the hood to establis and communicate over a MQTT connection. The fork includes fixes to solve issues with using std::function callbacks for non ESP boards +/// under the hood to establish and communicate over a MQTT connection. The fork includes fixes to solve issues with using std::function callbacks for non ESP boards class Arduino_MQTT_Client : public IMQTT_Client { public: /// @brief Constructs a IMQTT_Client implementation without a network client, meaning it has to be added later with the set_client() and set_buffer_size() method diff --git a/src/HashGenerator.h b/src/HashGenerator.h index 57329e3a..b89808fc 100644 --- a/src/HashGenerator.h +++ b/src/HashGenerator.h @@ -37,7 +37,7 @@ class HashGenerator { /// @brief Update the current hash value with new data /// @param data Data that should be added to generate the hash /// @param len Length of data entered - /// @return Whether updating the hash for the given bytes was successfull or not + /// @return Whether updating the hash for the given bytes was successful or not bool update(const uint8_t* data, const size_t& len); /// @brief Returns the final hash value as a string diff --git a/src/IHTTP_Client.h b/src/IHTTP_Client.h new file mode 100644 index 00000000..a3a71fe4 --- /dev/null +++ b/src/IHTTP_Client.h @@ -0,0 +1,62 @@ +/* + IHTTP_Client.h - Library API for sending data to the ThingsBoard + Based on PubSub MQTT library. + Created by Olender M. Oct 2018. + Released into the public domain. +*/ +#ifndef IHTTP_Client_h +#define IHTTP_Client_h + +// Library include. +#include + +/// @brief HTTP Client interface that contains the method that a class that can be used to send and receive data over an HTTP conection should implement. +/// Seperates the specific implementation used from the ThingsBoardHttp client, allows to use differnt clients depending on different needs. +/// In this case the main use case of the seperation is to both support Espressif IDF and Arduino with the following libraries as recommendations. +/// The default HTTP Client for Arduino is the ArduinoHttpClient (https://github.com/arduino-libraries/ArduinoHttpClient), +/// For Espressif IDF however the default HTTP Client is the esp-http-client (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_http_client.html) component. +class IHTTP_Client { + public: + /// @brief Sets whether to close the HTTP connection for every single request and reconnect once a new request is sent + /// @param keep_alive Enable or disable to keep the connection alive after a message has been sent. + /// Is recommended to be always enabled to improve performance and speed, because opening a new connection takes a while especially when using HTTPS + virtual void set_keep_alive(const bool& keep_alive) = 0; + + /// @brief Connects to the given server instance that should be connected to over the defined port + /// @param host Server instance name the client should connect too + /// @param port Port that will be used to establish a connection and send / receive data + /// Should be either 80 for unencrypted HTTP or 443 for HTTPS with encryption. + /// The latter is recommended if relevant data is sent or if the client receives and handles requests from the server, + /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. + /// @return Whether the client could establish the connection successfully or not + virtual int connect(const char *host, const uint16_t& port) = 0; + + /// @brief Disconnects the given device from the current host and clears about any remaining bytes still in the reponse body + virtual void stop() = 0; + + /// @brief Connects to the server and sends a POST request with a body and content type + /// @param url_path URL the POST request should be sent too + /// @param content_type Type of the content that is sent will be JSON data most of the time + /// @param request_body Request body containing data of the given content type + /// @return Whether the request was successful or not, returns 0 if successful or if not the internal error code + virtual int post(const char *url_path, const char *content_type, const char *request_body) = 0; + + /// @brief Gets the HTTP status code contained in the server response. + /// Should follow the HTTP standard, meaning 200 for a successful request or 404 for file not found, + /// see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status for more information on all standard status codes + /// @return Current HTTP status code from the server response + virtual int get_response_status_code() = 0; + + /// @brief Connects to the server and sends a GET request + /// @param url_path URL the GET request should be sent too + /// @return hether the request was successful or not, returns 0 if successful or if not the internal error code + virtual int get(const char *url_path) = 0; + + /// @brief Returns the response body of a previously sent message as a String object, + /// skips any response headers if they have not been read already, + /// should be called after calling get_response_status_code() and ensuring the request was successful + /// @return Response body of a request + virtual String get_response_body() = 0; +}; + +#endif // IHTTP_Client_h diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index 58341d9b..422f4fab 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -24,7 +24,7 @@ /// In this case the main use case of the seperation is to both support Espressif IDF and Arduino with the following libraries as recommendations. /// The default MQTT Client for Arduino is the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), /// it includes fixes to solve issues with using std::function callbacks for non ESP boards. -/// For Espressif IDF however the default MQTT Client is the esp-mqtt (https://github.com/espressif/esp-mqtt) component. +/// For Espressif IDF however the default MQTT Client is the esp-mqtt (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html) component. /// When using an implementation that does not allow for Arduino it is additional important to disable support for THINGSBOARD_ENABLE_STREAM_UTILS, /// because this feature relies on Arduino as it improves the underlying data streams to directly write the data into the MQTT Client, /// but wrirting each byte one by one, would be too slow, therefore the ArduinoStreamUtils (https://github.com/bblanchon/ArduinoStreamUtils) library is used to buffer thoose calls into bigger packets. @@ -77,14 +77,14 @@ class IMQTT_Client { /// @param id Client identification code, that allows to differentiate which MQTT device is sending the traffic to the MQTT broker /// @param user Client usernam that is used to authenticate, who is connecting over MQTT /// @param pass Client password that isused to authenticate, who is connecting over MQTT - /// @return Wether the client could establish the connection successfully or not + /// @return Whether the client could establish the connection successfully or not virtual bool connect(const char *client_id, const char *user_name, const char *password) = 0; /// @brief Disconnects from a previously connected server and should release all used resources virtual void disconnect() = 0; /// @brief Receives and sends any outstanding messages from and to the MQTT broker - /// @return Wether sending or receiving the oustanding the messages was successfull or not, + /// @return Whether sending or receiving the oustanding the messages was successful or not, /// should return false if an internal error occured or the connection has been lost virtual bool loop() = 0; @@ -92,7 +92,7 @@ class IMQTT_Client { /// @param topic Topic that the message is sent over, where different MQTT topics expect a different kind of payload /// @param payload Payload containg the json data that should be sent /// @param length Length of the payload in bytes - /// @return Wether publishing the payload on the given topic was successfull or not + /// @return Whether publishing the payload on the given topic was successful or not virtual bool publish(const char *topic, const uint8_t *payload, const uint32_t& length) = 0; /// @brief Subscribes to MQTT message on the given topic, which will cause an internal callback to be called for each message received on that topic from the server, @@ -120,11 +120,11 @@ class IMQTT_Client { /// To use this feature first call begin_publish(), followed by multiple calls to write() and then ending with a call to end_publish() /// @param topic Topic that the message is sent over, where different MQTT topics expect a different kind of payload /// @param length Length of the payload in bytes - /// @return Wether starting to publish on the given topic was successfull or not + /// @return Whether starting to publish on the given topic was successful or not virtual bool begin_publish(const char *topic, const uint32_t& length) = 0; /// @brief Finishes any publish message started with begin_publish() - /// @return Wether the complete packet was sent successfully or not + /// @return Whether the complete packet was sent successfully or not virtual bool end_publish() = 0; //---------------------------------------------------------------------------- diff --git a/src/IUpdater.h b/src/IUpdater.h index 8340e9ae..5759456d 100644 --- a/src/IUpdater.h +++ b/src/IUpdater.h @@ -21,7 +21,7 @@ class IUpdater { public: /// @brief Initalizes the writing of the given data /// @param firmware_size Total size of the data that should be written, is done in multiple packets - /// @return Wether initalizing the update was successfull or not + /// @return Whether initalizing the update was successful or not virtual bool begin(const size_t& firmware_size) = 0; /// @brief Writes the given amount of bytes of the packet data @@ -34,7 +34,7 @@ class IUpdater { virtual void reset() = 0; /// @brief Ends the update and returns wheter it was successfully completed - /// @return Wether the complete amount of bytes initally given was successfully written or not + /// @return Whether the complete amount of bytes initally given was successfully written or not virtual bool end() = 0; }; diff --git a/src/OTA_Handler.h b/src/OTA_Handler.h index cfc7045b..22cc42cf 100644 --- a/src/OTA_Handler.h +++ b/src/OTA_Handler.h @@ -229,7 +229,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()); } diff --git a/src/Telemetry.h b/src/Telemetry.h index 6cd403ad..8bd5ffef 100644 --- a/src/Telemetry.h +++ b/src/Telemetry.h @@ -80,7 +80,7 @@ class Telemetry { /// @brief Serializes the key-value pair depending on the constructor used /// @param jsonObj Object the value will be copied into with the given key - /// @return Whether serializing was successfull or not + /// @return Whether serializing was successful or not bool SerializeKeyValue(const JsonVariant &jsonObj) const; private: diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 18b92407..fd6c98a9 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -422,7 +422,7 @@ class ThingsBoardSized { } /// @brief Receives / sends any outstanding messages from and to the MQTT broker - /// @return Wether sending or receiving the oustanding the messages was successfull or not + /// @return Whether sending or receiving the oustanding the messages was successful or not inline bool loop() { return m_client.loop(); } @@ -1048,7 +1048,7 @@ class ThingsBoardSized { /// @brief Publishes a request via MQTT to request the given firmware chunk /// @param request_chunck Chunk index that should be requested from the server - /// @return Wether publishing the message was successfull or not + /// @return Whether publishing the message was successful or not inline bool Publish_Chunk_Request(const uint32_t& request_chunck) { // Calculate the number of chuncks we need to request, // in order to download the complete firmware binary diff --git a/src/ThingsBoardHttp.h b/src/ThingsBoardHttp.h index 944ce923..44f18db3 100644 --- a/src/ThingsBoardHttp.h +++ b/src/ThingsBoardHttp.h @@ -12,10 +12,7 @@ #include "ThingsBoardDefaultLogger.h" #include "Telemetry.h" #include "Helper.h" - -// Library includes. -#include -#include +#include "IHTTP_Client.h" /// --------------------------------- /// Constant strings in flash memory. @@ -25,12 +22,14 @@ constexpr char HTTP_TELEMETRY_TOPIC[] PROGMEM = "/api/v1/%s/telemetry"; constexpr char HTTP_ATTRIBUTES_TOPIC[] PROGMEM = "/api/v1/%s/attributes"; constexpr char HTTP_POST_PATH[] PROGMEM = "application/json"; -constexpr int HTTP_RESPONSE_SUCCESS_CODE PROGMEM = 200; +constexpr int HTTP_RESPONSE_SUCCESS_RANGE_START PROGMEM = 200; +constexpr int HTTP_RESPONSE_SUCCESS_RANGE_END PROGMEM = 299; #else constexpr char HTTP_TELEMETRY_TOPIC[] = "/api/v1/%s/telemetry"; constexpr char HTTP_ATTRIBUTES_TOPIC[] = "/api/v1/%s/attributes"; constexpr char HTTP_POST_PATH[] = "application/json"; -constexpr int HTTP_RESPONSE_SUCCESS_CODE = 200; +constexpr int HTTP_RESPONSE_SUCCESS_RANGE_START = 200; +constexpr int HTTP_RESPONSE_SUCCESS_RANGE_END = 299; #endif // THINGSBOARD_ENABLE_PROGMEM // Log messages. @@ -75,25 +74,18 @@ class ThingsBoardHttpSized { /// @param port Port we want to establish a connection over (80 for HTTP, 443 for HTTPS) /// @param keepAlive Attempts to keep the establishes TCP connection alive to make sending data faster /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack, default = Default_Max_Stack_Size - inline ThingsBoardHttpSized(Client &client, const char *access_token, + inline ThingsBoardHttpSized(IHTTP_Client& client, const char *access_token, const char *host, const uint16_t& port = 80U, const bool& keepAlive = true, const uint32_t& maxStackSize = Default_Max_Stack_Size) - : m_client(client, host, port) + : m_client(client) , m_max_stack(maxStackSize) , m_host(host) , m_port(port) , m_token(access_token) { - if (keepAlive) { - m_client.connectionKeepAlive(); - } + m_client.set_keep_alive(keepAlive); m_client.connect(m_host, m_port); } - /// @brief Destructor - inline ~ThingsBoardHttpSized() { - // Nothing to do - } - /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before allocating on the heap instead /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack inline void setMaximumStackSize(const uint32_t& maxStackSize) { @@ -279,9 +271,9 @@ class ThingsBoardHttpSized { bool result = true; const int success = m_client.post(path, HTTP_POST_PATH, json); - const int status = m_client.responseStatusCode(); + const int status = m_client.get_response_status_code(); - if (success != HTTP_SUCCESS || status != HTTP_RESPONSE_SUCCESS_CODE) { + if (!success || status < HTTP_RESPONSE_SUCCESS_RANGE_START || status > HTTP_RESPONSE_SUCCESS_RANGE_END) { char message[Helper::detectSize(HTTP_FAILED, POST, status)]; snprintf_P(message, sizeof(message), HTTP_FAILED, POST, status); Logger::log(message); @@ -301,9 +293,9 @@ class ThingsBoardHttpSized { bool result = true; const bool success = m_client.get(path); - const int status = m_client.responseStatusCode(); + const int status = m_client.get_response_status_code(); - if (!success || status != HTTP_SUCCESS) { + if (!success || status < HTTP_RESPONSE_SUCCESS_RANGE_START || status > HTTP_RESPONSE_SUCCESS_RANGE_END) { char message[Helper::detectSize(HTTP_FAILED, GET, status)]; snprintf_P(message, sizeof(message), HTTP_FAILED, GET, status); Logger::log(message); @@ -311,7 +303,7 @@ class ThingsBoardHttpSized { goto cleanup; } - response = m_client.responseBody(); + response = m_client.get_response_body(); cleanup: clearConnection(); @@ -374,7 +366,7 @@ class ThingsBoardHttpSized { return telemetry ? sendTelemetryJson(object, Helper::Measure_Json(object)) : sendAttributeJSON(object, Helper::Measure_Json(object)); } - HttpClient m_client; // HttpClient instance + IHTTP_Client& m_client; // HttpClient instance uint32_t m_max_stack; // Maximum stack size we allocate at once on the stack. const char *m_host; // Host address we connect too const uint16_t m_port; // Port we connect over From 0b9200ae6bd0ff08460b275f221be7292236faf5 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 3 Sep 2023 18:45:13 +0200 Subject: [PATCH 06/80] Fix typos --- src/Arduino_HTTP_Client.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Arduino_HTTP_Client.cpp b/src/Arduino_HTTP_Client.cpp index 81ab7138..a969a188 100644 --- a/src/Arduino_HTTP_Client.cpp +++ b/src/Arduino_HTTP_Client.cpp @@ -14,25 +14,25 @@ void Arduino_HTTP_Client::set_keep_alive(const bool& keep_alive) { } int Arduino_HTTP_Client::connect(const char *host, const uint16_t& port) { - return m_client.connect(host, port); + return m_http_client.connect(host, port); } void Arduino_HTTP_Client::stop() { - m_client.stop(); + m_http_client.stop(); } int Arduino_HTTP_Client::post(const char *url_path, const char *content_type, const char *request_body) { - return m_client.post(url_path, content_type, request_body); + return m_http_client.post(url_path, content_type, request_body); } int Arduino_HTTP_Client::get_response_status_code() { - return m_client.responseStatusCode(); + return m_http_client.responseStatusCode(); } int Arduino_HTTP_Client::get(const char *url_path) { - return m_client.get(url_path); + return m_http_client.get(url_path); } String Arduino_HTTP_Client::get_response_body() { - return m_client.responseBody(); + return m_http_client.responseBody(); } From aa06d32b4a26a9cce45d08545ff00b9a4c3af40a Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:14:33 +0200 Subject: [PATCH 07/80] Adjust arduino compile to increase board flash. --- .github/workflows/arduino-compile.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index 5137707d..70394f5b 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -22,7 +22,7 @@ env: jobs: build: - name: Arduino Uno + name: ${{ matrix.board.fqbn }} runs-on: ubuntu-latest env: LIBRARIES: | @@ -31,16 +31,22 @@ jobs: - name: TBPubSubClient - name: ArduinoHttpClient - name: ArduinoJson - - name: WiFiEsp - - name: TinyGSM - name: StreamUtils + strategy: + matrix: + board: + - fqbn: "arduino:renesas_uno:unor4wifi" + platform-name: arduino:renesas_uno + steps: - uses: actions/checkout@v3 - name: Compile examples uses: arduino/compile-sketches@v1 with: + platforms: ${{ matrix.platforms }} + fqbn: ${{ matrix.board.fqbn }} libraries: ${{ env.LIBRARIES }} enable-deltas-report: true sketch-paths: | From d2c97e15197dbdc7da13ca6f7b367d4769efec79 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:29:47 +0200 Subject: [PATCH 08/80] Update libraries --- .github/workflows/arduino-compile.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index 70394f5b..91503108 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -32,6 +32,7 @@ jobs: - name: ArduinoHttpClient - name: ArduinoJson - name: StreamUtils + - name: Ticker strategy: matrix: From 77da1ea98aa6cf4ad70f81b4a3fa7eaff586a258 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:32:42 +0200 Subject: [PATCH 09/80] Adding more missing dependencies --- .github/workflows/arduino-compile.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index 91503108..f5ff0a7b 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -31,6 +31,7 @@ jobs: - name: TBPubSubClient - name: ArduinoHttpClient - name: ArduinoJson + - name: Seeed_Arduino_mbedtls - name: StreamUtils - name: Ticker From c18a61e961a1b279e939163967a1cc68cf907e47 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:40:17 +0200 Subject: [PATCH 10/80] Add dependencies --- .github/workflows/arduino-compile.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index f5ff0a7b..912e1413 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -34,6 +34,8 @@ jobs: - name: Seeed_Arduino_mbedtls - name: StreamUtils - name: Ticker + - name: WiFiEsp + - name: TinyGsmClient strategy: matrix: From de2b86bb080005801d93602bf1c919635bef3dcd Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:42:48 +0200 Subject: [PATCH 11/80] Fix typo --- .github/workflows/arduino-compile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index 912e1413..166773b9 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -35,7 +35,7 @@ jobs: - name: StreamUtils - name: Ticker - name: WiFiEsp - - name: TinyGsmClient + - name: TinyGSM strategy: matrix: From e321cb054434727e76cd3fde82d450de83a03e33 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:53:23 +0200 Subject: [PATCH 12/80] Attempt to compile arduino uno --- .github/workflows/arduino-compile.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index 166773b9..28c21a88 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -31,17 +31,12 @@ jobs: - name: TBPubSubClient - name: ArduinoHttpClient - name: ArduinoJson - - name: Seeed_Arduino_mbedtls - - name: StreamUtils - - name: Ticker - - name: WiFiEsp - - name: TinyGSM strategy: matrix: board: - - fqbn: "arduino:renesas_uno:unor4wifi" - platform-name: arduino:renesas_uno + - fqbn: "arduino:uno" + platform-name: uno steps: - uses: actions/checkout@v3 From 5bfbf067b0333ca4bc8db4a44509c4c93c0c20c8 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:59:41 +0200 Subject: [PATCH 13/80] Quess fully qualified board name --- .github/workflows/arduino-compile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index 28c21a88..af784945 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -35,8 +35,8 @@ jobs: strategy: matrix: board: - - fqbn: "arduino:uno" - platform-name: uno + - fqbn: "arduino:avr:nano" + platform-name: avr:nano steps: - uses: actions/checkout@v3 From 9d2dfd8ee53b63b21e1ef6188058d8f7efbaac20 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:02:03 +0200 Subject: [PATCH 14/80] Add libraries --- .github/workflows/arduino-compile.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index af784945..deb47144 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -31,6 +31,9 @@ jobs: - name: TBPubSubClient - name: ArduinoHttpClient - name: ArduinoJson + - name: StreamUtils + - name: WiFiEsp + - name: TinyGSM strategy: matrix: From 00cc59db3d730bc20d67c3f898230514ceacf594 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 4 Sep 2023 21:40:31 +0200 Subject: [PATCH 15/80] Move callbackwatchdog to esp_timer --- src/Callback_Watchdog.cpp | 37 ++++++++++++++++++++++++++++++++----- src/Callback_Watchdog.h | 25 ++++++++++++++++--------- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/Callback_Watchdog.cpp b/src/Callback_Watchdog.cpp index 1b93ff80..3ea8d549 100644 --- a/src/Callback_Watchdog.cpp +++ b/src/Callback_Watchdog.cpp @@ -3,24 +3,51 @@ #if THINGSBOARD_ENABLE_OTA +// Library includes. +#include +#include + +#if THINGSBOARD_ENABLE_PROGMEM +constexpr char WATCHDOG_TIMER_NAME[] PROGMEM = "watchdog_timer"; +#else +constexpr char WATCHDOG_TIMER_NAME[] = "watchdog_timer"; +#endif // THINGSBOARD_ENABLE_PROGMEM + Callback_Watchdog *Callback_Watchdog::m_instance = nullptr; Callback_Watchdog::Callback_Watchdog(std::function callback) : m_callback(callback), - m_ticker() + m_oneshot_timer(nullptr) { m_instance = this; + const esp_timer_create_args_t oneshot_timer_args = { + .callback = &oneshot_timer_callback, + .name = WATCHDOG_TIMER_NAME + }; + // Temporary handle is used, because it allows using a void* as the actual oneshot_timer, + // allowing us to only include the esp_timer header in the defintion (.cpp) file, + // instead of also needing to declare it in the declaration (.h) header file + esp_timer_handle_t temp_handle; + (void)esp_timer_create(&oneshot_timer_args, &temp_handle); + m_oneshot_timer = temp_handle; +} + +Callback_Watchdog::~Callback_Watchdog() { + // Timer only has to deleted at the end of the lifetime of this object, to ensure no memory leak occurs. + // But besides that the same timer can simply be stopped and restarted without needing to delete and create the timer again everytime. + (void)esp_timer_delete(static_cast(m_oneshot_timer)); + m_instance = nullptr; } -void Callback_Watchdog::once(const uint32_t& timeout_millis) { - m_ticker.once_ms(timeout_millis, &Callback_Watchdog::timerCallback); +void Callback_Watchdog::once(const uint64_t& timeout_microseconds) { + (void)esp_timer_start_once(static_cast(m_oneshot_timer), timeout_microseconds); } void Callback_Watchdog::detach() { - m_ticker.detach(); + (void)esp_timer_stop(static_cast(m_oneshot_timer)); } -void Callback_Watchdog::timerCallback() { +void Callback_Watchdog::oneshot_timer_callback(void *arg) { if (m_instance == nullptr) { return; } diff --git a/src/Callback_Watchdog.h b/src/Callback_Watchdog.h index 68bc5826..35610a47 100644 --- a/src/Callback_Watchdog.h +++ b/src/Callback_Watchdog.h @@ -14,29 +14,36 @@ // Library includes. #include -#include +/// @brief Wrapper class around the esp_timer, allowing to start and stop timers. Is meant to be started with once(), +/// which will then call the registered callback after timeout if the detach() method has not been called before the time passed. +/// Resulting in behaviour similair to a watchdog but without as high of an accuracy and without restarting the device, +/// allowing to fail and handle the error case silently class Callback_Watchdog { public: /// @brief Constructor - /// @param callback Callback method that will be called if the timeout time passes without being fed + /// @param callback Callback method that will be called if the timeout time passes without detach() being called Callback_Watchdog(std::function callback); - /// @brief Starts the watchdog timer once for the initally given timeout - /// @param timeout_millis Amount of milliseconds until the feed method is excpected to have been called or the given callback method will be called - void once(const uint32_t& timeout_millis); + /// @brief Destructor + ~Callback_Watchdog(); - /// @brief Stops any currently ongoing watchdog timer + /// @brief Starts the watchdog timer once for the given timeout + /// @param timeout_microseconds Amount of microseconds until the detach() method is excpected to have been called or the given callback method will be called + void once(const uint64_t& timeout_microseconds); + + /// @brief Stops the currently ongoing watchdog timer and ensures the callback is not called. Timer can simply be restarted with calling once() again. void detach(); private: std::function m_callback; - Ticker m_ticker; + void *m_oneshot_timer; static Callback_Watchdog *m_instance; - /// @brief Static callback used to call the initally subscribed callback, if the internal watchdog has not been fed - static void timerCallback(); + /// @brief Static callback used to call the initally subscribed callback, if the internal watchdog has not been fed with detach() + /// @param arg Possible argument passed to the timer callback + static void oneshot_timer_callback(void *arg); }; #endif // THINGSBOARD_ENABLE_OTA From bbe64282c4c576a325a680d0977f269c686a6bbc Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:35:03 +0200 Subject: [PATCH 16/80] Only use esp_timer if the header exists --- src/Arduino_HTTP_Client.h | 1 + src/Arduino_MQTT_Client.h | 1 + src/Attribute_Request_Callback.h | 2 ++ src/Callback.h | 1 + src/Callback_Watchdog.cpp | 23 +++++++++++++++++++++++ src/Callback_Watchdog.h | 12 ++++++++++++ src/Configuration.h | 32 ++++++++++++++++++++++++++++++++ src/Constants.h | 3 +++ src/ESP32_Updater.h | 1 + src/ESP8266_Updater.h | 1 + src/HashGenerator.h | 5 +++-- src/Helper.h | 1 + src/IHTTP_Client.h | 1 + src/IMQTT_Client.h | 1 + src/IUpdater.h | 1 + src/OTA_Failure_Response.h | 1 + src/OTA_Handler.h | 2 ++ src/OTA_Update_Callback.cpp | 10 +++++----- src/OTA_Update_Callback.h | 26 ++++++++++++++------------ src/Provision_Callback.h | 2 ++ src/RPC_Callback.h | 2 ++ src/RPC_Request_Callback.h | 1 + src/RPC_Response.h | 1 + src/Shared_Attribute_Callback.h | 2 ++ src/Telemetry.h | 1 + src/ThingsBoard.h | 2 ++ src/Vector.h | 1 + 27 files changed, 118 insertions(+), 19 deletions(-) diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index ab101816..0800b81f 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -13,6 +13,7 @@ // Local includes. #include "IHTTP_Client.h" + /// @brief HTTP Client interface imnplementation that uses the ArduinoHttpClient (https://github.com/arduino-libraries/ArduinoHttpClient), /// under the hood to establish and communicate over a HTTP connection class Arduino_HTTP_Client : public IHTTP_Client { diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index a19f305d..e2d996f0 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -13,6 +13,7 @@ // Library include #include + /// @brief MQTT Client interface imnplementation that uses the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), /// under the hood to establish and communicate over a MQTT connection. The fork includes fixes to solve issues with using std::function callbacks for non ESP boards class Arduino_MQTT_Client : public IMQTT_Client { diff --git a/src/Attribute_Request_Callback.h b/src/Attribute_Request_Callback.h index f3f09186..599f08bc 100644 --- a/src/Attribute_Request_Callback.h +++ b/src/Attribute_Request_Callback.h @@ -16,6 +16,7 @@ #include #endif // THINGSBOARD_ENABLE_STL + /// --------------------------------- /// Constant strings in flash memory. /// --------------------------------- @@ -25,6 +26,7 @@ constexpr char ATT_REQUEST_CB_IS_NULL[] PROGMEM = "Client-side or shared attribu constexpr char ATT_REQUEST_CB_IS_NULL[] = "Client-side or shared attribute request callback is NULL"; #endif // THINGSBOARD_ENABLE_PROGMEM + // Convenient aliases // JSON object const (read only twice as small as JSON object), is used to communicate Shared Attributes and Provision Data to the client using Attribute_Data = const JsonObjectConst; diff --git a/src/Callback.h b/src/Callback.h index f26212d8..5b1fc9ab 100644 --- a/src/Callback.h +++ b/src/Callback.h @@ -15,6 +15,7 @@ #include #endif // THINGSBOARD_ENABLE_STL + /// @brief General purpose callback wrapper /// @tparam returnType Type the given callback method should return /// @tparam argumentTypes Types the given callback method should receive diff --git a/src/Callback_Watchdog.cpp b/src/Callback_Watchdog.cpp index 3ea8d549..94804267 100644 --- a/src/Callback_Watchdog.cpp +++ b/src/Callback_Watchdog.cpp @@ -17,9 +17,14 @@ Callback_Watchdog *Callback_Watchdog::m_instance = nullptr; Callback_Watchdog::Callback_Watchdog(std::function callback) : m_callback(callback), +#if THINGSBOARD_USE_ESP_TIMER m_oneshot_timer(nullptr) +#else + m_oneshot_timer() +#endif // THINGSBOARD_USE_ESP_TIMER { m_instance = this; +#if THINGSBOARD_USE_ESP_TIMER const esp_timer_create_args_t oneshot_timer_args = { .callback = &oneshot_timer_callback, .name = WATCHDOG_TIMER_NAME @@ -30,24 +35,42 @@ Callback_Watchdog::Callback_Watchdog(std::function callback) : esp_timer_handle_t temp_handle; (void)esp_timer_create(&oneshot_timer_args, &temp_handle); m_oneshot_timer = temp_handle; +#endif // THINGSBOARD_USE_ESP_TIMER } Callback_Watchdog::~Callback_Watchdog() { +#if THINGSBOARD_USE_ESP_TIMER // Timer only has to deleted at the end of the lifetime of this object, to ensure no memory leak occurs. // But besides that the same timer can simply be stopped and restarted without needing to delete and create the timer again everytime. (void)esp_timer_delete(static_cast(m_oneshot_timer)); +#else + m_oneshot_timer.detach(); +#endif // THINGSBOARD_USE_ESP_TIMER m_instance = nullptr; } void Callback_Watchdog::once(const uint64_t& timeout_microseconds) { +#if THINGSBOARD_USE_ESP_TIMER (void)esp_timer_start_once(static_cast(m_oneshot_timer), timeout_microseconds); +#else + const uint32_t timeout_millis = timeout_microseconds / 1000U; + m_oneshot_timer.once_ms(timeout_millis, &Callback_Watchdog::oneshot_timer_callback); +#endif // THINGSBOARD_USE_ESP_TIMER } void Callback_Watchdog::detach() { +#if THINGSBOARD_USE_ESP_TIMER (void)esp_timer_stop(static_cast(m_oneshot_timer)); +#else + m_oneshot_timer.detach(); +#endif // THINGSBOARD_USE_ESP_TIMER } +#if THINGSBOARD_USE_ESP_TIMER void Callback_Watchdog::oneshot_timer_callback(void *arg) { +#else +void Callback_Watchdog::oneshot_timer_callback() { +#endif // THINGSBOARD_USE_ESP_TIMER if (m_instance == nullptr) { return; } diff --git a/src/Callback_Watchdog.h b/src/Callback_Watchdog.h index 35610a47..45124b42 100644 --- a/src/Callback_Watchdog.h +++ b/src/Callback_Watchdog.h @@ -14,6 +14,10 @@ // Library includes. #include +#if !THINGSBOARD_USE_ESP_TIMER +#include +#endif // !THINGSBOARD_USE_ESP_TIMER + /// @brief Wrapper class around the esp_timer, allowing to start and stop timers. Is meant to be started with once(), /// which will then call the registered callback after timeout if the detach() method has not been called before the time passed. @@ -37,13 +41,21 @@ class Callback_Watchdog { private: std::function m_callback; +#if THINGSBOARD_USE_ESP_TIMER void *m_oneshot_timer; +#else + Ticker m_oneshot_timer; +#endif // THINGSBOARD_USE_ESP_TIMER static Callback_Watchdog *m_instance; /// @brief Static callback used to call the initally subscribed callback, if the internal watchdog has not been fed with detach() +#if THINGSBOARD_USE_ESP_TIMER /// @param arg Possible argument passed to the timer callback static void oneshot_timer_callback(void *arg); +#else + static void oneshot_timer_callback(); +#endif // THINGSBOARD_USE_ESP_TIMER }; #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/Configuration.h b/src/Configuration.h index 120ca040..1a826179 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -44,6 +44,38 @@ # endif # endif +// Use the esp_timer header internally for handling timeouts and callbacks, as long as the header exists, because it is more efficient than the Arduino Ticker implementation, +// because we can stop the timer without having to delete it, removing the need to create a new timer to restart it. Because instead we can simply stop and start again. +# ifdef __has_include +# if __has_include() +# ifndef THINGSBOARD_USE_ESP_TIMER +# define THINGSBOARD_USE_ESP_TIMER 1 +# endif +# else +# ifndef THINGSBOARD_USE_ESP_TIMER +# define THINGSBOARD_USE_ESP_TIMER 0 +# endif +# endif +# else +# define THINGSBOARD_USE_ESP_TIMER 0 +# endif + +// Use the mbed_tls header internally for handling the creation of hashes from binary data, as long as the header exists, +// because if it is already included we do not need to rely on and incude external lbiraries like Seeed_mbedtls.h, which implements the same features. +# ifdef __has_include +# if __has_include() +# ifndef THINGSBOARD_USE_MBED_TLS +# define THINGSBOARD_USE_MBED_TLS 1 +# endif +# else +# ifndef THINGSBOARD_USE_MBED_TLS +# define THINGSBOARD_USE_MBED_TLS 0 +# endif +# endif +# else +# define THINGSBOARD_USE_MBED_TLS 0 +# endif + // Enable the usage of the PROGMEM header for constants variables (variables are placed into flash memory instead of sram). # ifdef __has_include # if __has_include() diff --git a/src/Constants.h b/src/Constants.h index cf214c6b..82f2e7b0 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -14,6 +14,7 @@ #include #include + #define Default_Max_Stack_Size 1023 // 10 bytes = 2^10 - 1 #define Default_Buffering_Size 64 #define Default_Payload 64 @@ -31,6 +32,7 @@ class ThingsBoardDefaultLogger; #endif // vsnprintf_P #endif // THINGSBOARD_ENABLE_PROGMEM + /// --------------------------------- /// Constant strings in flash memory, /// used by both ThingsBoard and ThingsBoardHttp. @@ -54,6 +56,7 @@ constexpr char UNABLE_TO_SERIALIZE_JSON[] = "Unable to serialize json data"; constexpr char UNABLE_TO_ALLOCATE_MEMORY[] = "Allocating memory for the JsonDocument failed, passed JsonObject or JsonVariant is NULL"; #endif // THINGSBOARD_ENABLE_PROGMEM + #if THINGSBOARD_ENABLE_PSRAM #include diff --git a/src/ESP32_Updater.h b/src/ESP32_Updater.h index a54a1fe1..8731a0aa 100644 --- a/src/ESP32_Updater.h +++ b/src/ESP32_Updater.h @@ -17,6 +17,7 @@ // Local include. #include "IUpdater.h" + class ESP32_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; diff --git a/src/ESP8266_Updater.h b/src/ESP8266_Updater.h index a7440d95..e602af82 100644 --- a/src/ESP8266_Updater.h +++ b/src/ESP8266_Updater.h @@ -17,6 +17,7 @@ // Local include. #include "IUpdater.h" + class ESP8266_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; diff --git a/src/HashGenerator.h b/src/HashGenerator.h index b89808fc..272c10cc 100644 --- a/src/HashGenerator.h +++ b/src/HashGenerator.h @@ -13,13 +13,14 @@ #if THINGSBOARD_ENABLE_OTA // Library includes. -#ifdef ESP32 +#if THINGSBOARD_USE_MBED_TLS #include #else #include -#endif // ESP32 +#endif // THINGSBOARD_USE_MBED_TLS #include + /// @brief Allows generating a hash of the given type /// with partial data and then retreiving the completed hash once it has been completed class HashGenerator { diff --git a/src/Helper.h b/src/Helper.h index 32e9abf4..175322a9 100644 --- a/src/Helper.h +++ b/src/Helper.h @@ -14,6 +14,7 @@ #include #include + /// @brief Static helper class that includes some uniliterally used functionalities in multiple places class Helper { public: diff --git a/src/IHTTP_Client.h b/src/IHTTP_Client.h index a3a71fe4..9368564f 100644 --- a/src/IHTTP_Client.h +++ b/src/IHTTP_Client.h @@ -10,6 +10,7 @@ // Library include. #include + /// @brief HTTP Client interface that contains the method that a class that can be used to send and receive data over an HTTP conection should implement. /// Seperates the specific implementation used from the ThingsBoardHttp client, allows to use differnt clients depending on different needs. /// In this case the main use case of the seperation is to both support Espressif IDF and Arduino with the following libraries as recommendations. diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index 422f4fab..a59ce4a5 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -19,6 +19,7 @@ #endif // THINGSBOARD_ENABLE_STREAM_UTILS #include + /// @brief MQTT Client interface that contains the method that a class that can be used to send and receive data over an MQTT connection should implement. /// Seperates the specific implementation used from the ThingsBoard client, allows to use differnt clients depending on different needs. /// In this case the main use case of the seperation is to both support Espressif IDF and Arduino with the following libraries as recommendations. diff --git a/src/IUpdater.h b/src/IUpdater.h index 5759456d..7aa536b7 100644 --- a/src/IUpdater.h +++ b/src/IUpdater.h @@ -16,6 +16,7 @@ #include #include + /// @brief Updater interface that contains the method that a class that can be used to flash given binary data onto a device has to implement class IUpdater { public: diff --git a/src/OTA_Failure_Response.h b/src/OTA_Failure_Response.h index e9634489..5d9620d9 100644 --- a/src/OTA_Failure_Response.h +++ b/src/OTA_Failure_Response.h @@ -10,6 +10,7 @@ // Library include. #include + enum class OTA_Failure_Response : const uint8_t { RETRY_CHUNK, RETRY_UPDATE, diff --git a/src/OTA_Handler.h b/src/OTA_Handler.h index 22cc42cf..c8f4459a 100644 --- a/src/OTA_Handler.h +++ b/src/OTA_Handler.h @@ -19,6 +19,7 @@ #include "OTA_Update_Callback.h" #include "OTA_Failure_Response.h" + /// --------------------------------- /// Constant strings in flash memory. /// --------------------------------- @@ -70,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 diff --git a/src/OTA_Update_Callback.cpp b/src/OTA_Update_Callback.cpp index 0e35fe8e..fee09ca7 100644 --- a/src/OTA_Update_Callback.cpp +++ b/src/OTA_Update_Callback.cpp @@ -9,13 +9,13 @@ OTA_Update_Callback::OTA_Update_Callback() : // Nothing to do } -OTA_Update_Callback::OTA_Update_Callback(function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries, const uint16_t &chunkSize, const uint16_t &timeout) : +OTA_Update_Callback::OTA_Update_Callback(function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries, const uint16_t &chunkSize, const uint64_t &timeout) : OTA_Update_Callback(nullptr, endCb, currFwTitle, currFwVersion, updater, chunkRetries, chunkSize, timeout) { // Nothing to do } -OTA_Update_Callback::OTA_Update_Callback(progressFn progressCb, function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries, const uint16_t &chunkSize, const uint16_t &timeout) : +OTA_Update_Callback::OTA_Update_Callback(progressFn progressCb, function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries, const uint16_t &chunkSize, const uint64_t &timeout) : Callback(endCb, OTA_CB_IS_NULL), m_progressCb(progressCb), m_fwTitel(currFwTitle), @@ -72,12 +72,12 @@ void OTA_Update_Callback::Set_Chunk_Size(const uint16_t &chunkSize) { m_size = chunkSize; } -const uint16_t& OTA_Update_Callback::Get_Timeout() const { +const uint64_t& OTA_Update_Callback::Get_Timeout() const { return m_timeout; } -void OTA_Update_Callback::Set_Timeout(const uint16_t &timeout) { - m_timeout = timeout; +void OTA_Update_Callback::Set_Timeout(const uint64_t &timeout_microseconds) { + m_timeout = timeout_microseconds; } #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/OTA_Update_Callback.h b/src/OTA_Update_Callback.h index b5febde1..4f48eddd 100644 --- a/src/OTA_Update_Callback.h +++ b/src/OTA_Update_Callback.h @@ -20,6 +20,7 @@ #include #endif // THINGSBOARD_ENABLE_PROGMEM + /// --------------------------------- /// Constant strings in flash memory. /// --------------------------------- @@ -32,14 +33,15 @@ constexpr char OTA_CB_IS_NULL[] = "OTA update callback is NULL"; // OTA default values. #if THINGSBOARD_ENABLE_PROGMEM constexpr uint8_t CHUNK_RETRIES PROGMEM = 12U; -constexpr uint16_t CHUNK_SIZE PROGMEM = 4096U; -constexpr uint16_t REQUEST_TIMEOUT PROGMEM = 5000U; +constexpr uint16_t CHUNK_SIZE PROGMEM = (4U * 1024U); +constexpr uint64_t REQUEST_TIMEOUT PROGMEM = (5U * 1000U * 1000U); #else constexpr uint8_t CHUNK_RETRIES = 12U; -constexpr uint16_t CHUNK_SIZE = 4096U; -constexpr uint16_t REQUEST_TIMEOUT = 5000U; +constexpr uint16_t CHUNK_SIZE = (4U * 1024U); +constexpr uint64_t REQUEST_TIMEOUT = (5U * 1000U * 1000U); #endif // THINGSBOARD_ENABLE_PROGMEM + /// @brief OTA firmware update callback wrapper class OTA_Update_Callback : public Callback { public: @@ -64,7 +66,7 @@ class OTA_Update_Callback : public Callback { // because the whole chunk is saved into the heap before it can be processed and is then cleared again /// @param timeout Maximum amount of time in millseconds for the OTA firmware update for each seperate chunk, /// until that chunk counts as a timeout, retries is then subtraced by one and the download is retried - OTA_Update_Callback(function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries = CHUNK_RETRIES, const uint16_t &chunkSize = CHUNK_SIZE, const uint16_t &timeout = REQUEST_TIMEOUT); + OTA_Update_Callback(function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries = CHUNK_RETRIES, const uint16_t &chunkSize = CHUNK_SIZE, const uint64_t &timeout = REQUEST_TIMEOUT); /// @brief Constructs callbacks that will be called when the OTA firmware data, /// has been completly sent by the cloud, received by the client and written to the flash partition as well as callback @@ -82,7 +84,7 @@ class OTA_Update_Callback : public Callback { // because the whole chunk is saved into the heap before it can be processed and is then cleared again /// @param timeout Maximum amount of time in millseconds for the OTA firmware update for each seperate chunk, /// until that chunk counts as a timeout, retries is then subtraced by one and the download is retried - OTA_Update_Callback(progressFn progressCb, function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries = CHUNK_RETRIES, const uint16_t &chunkSize = CHUNK_SIZE, const uint16_t &timeout = REQUEST_TIMEOUT); + OTA_Update_Callback(progressFn progressCb, function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries = CHUNK_RETRIES, const uint16_t &chunkSize = CHUNK_SIZE, const uint64_t &timeout = REQUEST_TIMEOUT); /// @brief Calls the progress callback that was subscribed, when this class instance was initally created /// @tparam Logger Logging class that should be used to print messages @@ -153,13 +155,13 @@ class OTA_Update_Callback : public Callback { /// @param chunkSize Size of each single chunk to be downloaded void Set_Chunk_Size(const uint16_t &chunkSize); - /// @brief Gets the time in milliseconds we wait until we declare a single chunk we attempted to download as a failure + /// @brief Gets the time in microseconds we wait until we declare a single chunk we attempted to download as a failure /// @return Gets the timeout time for each single chunk to be downloaded - const uint16_t& Get_Timeout() const; + const uint64_t& Get_Timeout() const; - /// @brief Sets the time we wait until we decleare a single chunk we attempted to download as a timeout - /// @param timeout Gets the timeout time for each single chunk to be downloaded - void Set_Timeout(const uint16_t &timeout); + /// @brief Sets the time in microseconds we wait until we decleare a single chunk we attempted to download as a timeout + /// @param timeout_microseconds Gets the timeout time for each single chunk to be downloaded + void Set_Timeout(const uint64_t &timeout_microseconds); private: progressFn m_progressCb; // Progress callback to call @@ -168,7 +170,7 @@ class OTA_Update_Callback : public Callback { IUpdater *m_updater; // Updater implementation used to write firmware data uint8_t m_retries; // Maximum amount of retries uint16_t m_size; // Maximum size of the chuncks we are downloading - uint16_t m_timeout; // How long we maximum wait for each chunck to arrive + uint64_t m_timeout; // How long we maximum wait for each chunck to arrive }; #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/Provision_Callback.h b/src/Provision_Callback.h index 38844a69..7972f720 100644 --- a/src/Provision_Callback.h +++ b/src/Provision_Callback.h @@ -13,6 +13,7 @@ // Library includes. #include + // Convenient aliases using Provision_Data = const JsonObjectConst; @@ -22,6 +23,7 @@ struct Device_Access_Token{}; struct Basic_MQTT_Credentials{}; struct X509_Certificate{}; + /// @brief Provisioning callback wrapper class Provision_Callback : public Callback { public: diff --git a/src/RPC_Callback.h b/src/RPC_Callback.h index ad0711ad..b4659a44 100644 --- a/src/RPC_Callback.h +++ b/src/RPC_Callback.h @@ -11,9 +11,11 @@ #include "Callback.h" #include "RPC_Response.h" + // JSON variant const (read only twice as small as JSON variant), is used to communicate RPC parameters to the client using RPC_Data = const JsonVariantConst; + /// @brief RPC callback wrapper class RPC_Callback : public Callback { public: diff --git a/src/RPC_Request_Callback.h b/src/RPC_Request_Callback.h index e9441955..1b245a2c 100644 --- a/src/RPC_Request_Callback.h +++ b/src/RPC_Request_Callback.h @@ -13,6 +13,7 @@ // Library includes. #include + /// @brief RPC request callback wrapper class RPC_Request_Callback : public Callback { public: diff --git a/src/RPC_Response.h b/src/RPC_Response.h index f43781e2..c1178d1c 100644 --- a/src/RPC_Response.h +++ b/src/RPC_Response.h @@ -10,6 +10,7 @@ // Local includes. #include "Telemetry.h" + class RPC_Response : public JsonVariant { public: /// @brief Constructor diff --git a/src/Shared_Attribute_Callback.h b/src/Shared_Attribute_Callback.h index 0f3adebf..1d04e75a 100644 --- a/src/Shared_Attribute_Callback.h +++ b/src/Shared_Attribute_Callback.h @@ -16,6 +16,7 @@ #include #endif // THINGSBOARD_ENABLE_STL + /// --------------------------------- /// Constant strings in flash memory. /// --------------------------------- @@ -29,6 +30,7 @@ constexpr char ATT_CB_IS_NULL[] = "Shared attribute update callback is NULL"; // JSON object const (read only twice as small as JSON object), is used to communicate Shared Attributes and Provision Data to the client using Shared_Attribute_Data = const JsonObjectConst; + /// @brief Shared attributes callback wrapper class Shared_Attribute_Callback : public Callback { public: diff --git a/src/Telemetry.h b/src/Telemetry.h index 8bd5ffef..b374ec26 100644 --- a/src/Telemetry.h +++ b/src/Telemetry.h @@ -16,6 +16,7 @@ #include #endif // THINGSBOARD_ENABLE_STL + /// @brief Telemetry record class, allows to store different data using a common interface. class Telemetry { public: diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index fd6c98a9..dd93c68d 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -25,6 +25,7 @@ #include #endif // THINGSBOARD_ENABLE_STREAM_UTILS + /// --------------------------------- /// Constant strings in flash memory. /// --------------------------------- @@ -294,6 +295,7 @@ constexpr char DOWNLOADING_FW[] = "Attempting to download over MQTT..."; #endif // THINGSBOARD_ENABLE_OTA + #if THINGSBOARD_ENABLE_DYNAMIC /// @brief Wrapper around any arbitrary MQTT Client implementing the IMQTT_Client interface, to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. /// BufferSize of the underlying data buffer as well as the maximum amount of data points that can ever be sent are either dynamic or can be changed during runtime. diff --git a/src/Vector.h b/src/Vector.h index af2cfbca..3494fb4a 100644 --- a/src/Vector.h +++ b/src/Vector.h @@ -15,6 +15,7 @@ // Library includes. #include + /// @brief Replacement data container for boards that do not support the C++ STL. /// @tparam T Type of the underlying data the list should point too. template From 22d37b1a25671fa29e77dcf05762bedbe76acb13 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 5 Sep 2023 20:32:10 +0200 Subject: [PATCH 17/80] Remove all not needed dependencies --- src/Arduino_HTTP_Client.cpp | 5 +++++ src/Arduino_HTTP_Client.h | 4 ++++ src/Callback_Watchdog.cpp | 4 +++- src/Configuration.h | 6 +++--- src/Constants.h | 8 ++++++-- src/Helper.cpp | 4 +--- src/IHTTP_Client.h | 10 +++++++++- src/ThingsBoardHttp.h | 8 ++++++++ 8 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/Arduino_HTTP_Client.cpp b/src/Arduino_HTTP_Client.cpp index a969a188..01773053 100644 --- a/src/Arduino_HTTP_Client.cpp +++ b/src/Arduino_HTTP_Client.cpp @@ -33,6 +33,11 @@ int Arduino_HTTP_Client::get(const char *url_path) { return m_http_client.get(url_path); } +#if THINGSBOARD_ENABLE_STL String Arduino_HTTP_Client::get_response_body() { return m_http_client.responseBody(); +#else +std::string Arduino_HTTP_Client::get_response_body() { + return m_http_client.responseBody().c_str(); +#endif // THINGSBOARD_ENABLE_STL } diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index 0800b81f..8a022f3a 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -40,7 +40,11 @@ class Arduino_HTTP_Client : public IHTTP_Client { int get(const char *url_path) override; +#if THINGSBOARD_ENABLE_STL String get_response_body() override; +#else + std::string get_response_body() override; +#endif // THINGSBOARD_ENABLE_STL private: HttpClient m_http_client; diff --git a/src/Callback_Watchdog.cpp b/src/Callback_Watchdog.cpp index 94804267..4168adfd 100644 --- a/src/Callback_Watchdog.cpp +++ b/src/Callback_Watchdog.cpp @@ -5,7 +5,9 @@ // Library includes. #include -#include +#if THINGSBOARD_ENABLE_PROGMEM +#include +#endif // THINGSBOARD_ENABLE_PROGMEM #if THINGSBOARD_ENABLE_PROGMEM constexpr char WATCHDOG_TIMER_NAME[] PROGMEM = "watchdog_timer"; diff --git a/src/Configuration.h b/src/Configuration.h index 1a826179..522dc965 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -114,9 +114,9 @@ # endif // Enables the ThingsBoard class to save the allocated memory of the DynamicJsonDocument into psram instead of onto the sram. -// Enabled by default if THINGSBOARD_ENABLE_DYNAMIC has been set, because it requries DynamicJsonDocument to work. -// If enabled the program might be slightly slower and all the memory will be placed onto psram instead of sram. -// See https://arduinojson.org/v6/api/basicjsondocument/ for the main difference in the underlying code. +// Enabled by default if THINGSBOARD_ENABLE_DYNAMIC has been set and the esp_heap_caps header exists, because it requries DynamicJsonDocument to work. +// If enabled the program might be slightly slower, but all the memory will be placed onto psram instead of sram, meaning the sram can be allocated for other things. +// See https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/ and https://arduinojson.org/v6/api/basicjsondocument/ for for the main difference in the underlying code. # ifdef __has_include # if THINGSBOARD_ENABLE_DYNAMIC && __has_include() # ifndef THINGSBOARD_ENABLE_PSRAM diff --git a/src/Constants.h b/src/Constants.h index 82f2e7b0..77ed60ef 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -11,8 +11,12 @@ #include "Configuration.h" // Library includes. -#include +#if THINGSBOARD_ENABLE_PROGMEM +#include +#endif // THINGSBOARD_ENABLE_PROGMEM +#if THINGSBOARD_ENABLE_PSRAM || THINGSBOARD_ENABLE_DYNAMIC #include +#endif // THINGSBOARD_ENABLE_PSRAM || THINGSBOARD_ENABLE_DYNAMIC #define Default_Max_Stack_Size 1023 // 10 bytes = 2^10 - 1 @@ -75,7 +79,7 @@ constexpr char UNABLE_TO_ALLOCATE_MEMORY[] = "Allocating memory for the JsonDocu }; using TBJsonDocument = BasicJsonDocument; -#else +#elif THINGSBOARD_ENABLE_DYNAMIC using TBJsonDocument = DynamicJsonDocument; #endif diff --git a/src/Helper.cpp b/src/Helper.cpp index 002905a9..041af3d6 100644 --- a/src/Helper.cpp +++ b/src/Helper.cpp @@ -2,13 +2,12 @@ #include "Helper.h" // Local includes. -#include "Configuration.h" +#include "Constants.h" // Library includes. #include #include #include -#include #include uint8_t Helper::detectSize(const char *msg, ...) { @@ -21,7 +20,6 @@ uint8_t Helper::detectSize(const char *msg, ...) { assert(result >= 0); va_end(args); return result; - } uint32_t Helper::getOccurences(const char *str, char symbol) { diff --git a/src/IHTTP_Client.h b/src/IHTTP_Client.h index 9368564f..78dd9485 100644 --- a/src/IHTTP_Client.h +++ b/src/IHTTP_Client.h @@ -8,7 +8,11 @@ #define IHTTP_Client_h // Library include. +#if THINGSBOARD_ENABLE_STL #include +#else +#include +#endif // THINGSBOARD_ENABLE_STL /// @brief HTTP Client interface that contains the method that a class that can be used to send and receive data over an HTTP conection should implement. @@ -53,11 +57,15 @@ class IHTTP_Client { /// @return hether the request was successful or not, returns 0 if successful or if not the internal error code virtual int get(const char *url_path) = 0; - /// @brief Returns the response body of a previously sent message as a String object, + /// @brief Returns the response body of a previously sent message as a string object, /// skips any response headers if they have not been read already, /// should be called after calling get_response_status_code() and ensuring the request was successful /// @return Response body of a request +#if THINGSBOARD_ENABLE_STL virtual String get_response_body() = 0; +#else + virtual std::string get_response_body() = 0; +#endif // THINGSBOARD_ENABLE_STL }; #endif // IHTTP_Client_h diff --git a/src/ThingsBoardHttp.h b/src/ThingsBoardHttp.h index 44f18db3..27d8376b 100644 --- a/src/ThingsBoardHttp.h +++ b/src/ThingsBoardHttp.h @@ -199,7 +199,11 @@ class ThingsBoardHttpSized { /// @param response String the GET response will be copied into, /// will not be changed if the GET request wasn't successful /// @return Whetherr sending the GET request was successful or not +#if THINGSBOARD_ENABLE_STL + inline bool sendGetRequest(const char* path, std::string& response) { +#else inline bool sendGetRequest(const char* path, String& response) { +#endif // THINGSBOARD_ENABLE_STL return getMessage(path, response); } @@ -289,7 +293,11 @@ class ThingsBoardHttpSized { /// @param response String the GET response will be copied into, /// will not be changed if the GET request wasn't successful /// @return Whetherr sending the GET request was successful or not +#if THINGSBOARD_ENABLE_STL + inline bool getMessage(const char* path, std::string& response) { +#else inline bool getMessage(const char* path, String& response) { +#endif // THINGSBOARD_ENABLE_STL bool result = true; const bool success = m_client.get(path); From bb27bb210654ee9245c4e119dc1101aa5f224220 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 5 Sep 2023 20:36:38 +0200 Subject: [PATCH 18/80] Fix typo --- src/Arduino_HTTP_Client.cpp | 6 +++--- src/Arduino_HTTP_Client.h | 4 ++-- src/IHTTP_Client.h | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Arduino_HTTP_Client.cpp b/src/Arduino_HTTP_Client.cpp index 01773053..f9c05a99 100644 --- a/src/Arduino_HTTP_Client.cpp +++ b/src/Arduino_HTTP_Client.cpp @@ -34,10 +34,10 @@ int Arduino_HTTP_Client::get(const char *url_path) { } #if THINGSBOARD_ENABLE_STL -String Arduino_HTTP_Client::get_response_body() { - return m_http_client.responseBody(); -#else std::string Arduino_HTTP_Client::get_response_body() { return m_http_client.responseBody().c_str(); +#else +String Arduino_HTTP_Client::get_response_body() { + return m_http_client.responseBody(); #endif // THINGSBOARD_ENABLE_STL } diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index 8a022f3a..b531e8df 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -41,9 +41,9 @@ class Arduino_HTTP_Client : public IHTTP_Client { int get(const char *url_path) override; #if THINGSBOARD_ENABLE_STL - String get_response_body() override; -#else std::string get_response_body() override; +#else + String get_response_body() override; #endif // THINGSBOARD_ENABLE_STL private: diff --git a/src/IHTTP_Client.h b/src/IHTTP_Client.h index 78dd9485..6f4158bb 100644 --- a/src/IHTTP_Client.h +++ b/src/IHTTP_Client.h @@ -9,9 +9,9 @@ // Library include. #if THINGSBOARD_ENABLE_STL -#include -#else #include +#else +#include #endif // THINGSBOARD_ENABLE_STL @@ -62,9 +62,9 @@ class IHTTP_Client { /// should be called after calling get_response_status_code() and ensuring the request was successful /// @return Response body of a request #if THINGSBOARD_ENABLE_STL - virtual String get_response_body() = 0; -#else virtual std::string get_response_body() = 0; +#else + virtual String get_response_body() = 0; #endif // THINGSBOARD_ENABLE_STL }; From 8d1d9edf8c32f8f082f77b74476bb8a6477d59fe Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:02:14 +0200 Subject: [PATCH 19/80] Adjust doc to actually fetched libraries --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2f66154..5c3d7c85 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,12 @@ ThingsBoard SDK can be installed directly from the [Arduino Library manager](htt Following dependencies are installed automatically or must be installed, too: **Installed automatically:** - - [MQTT PubSub Client](https://github.com/thingsboard/pubsubclient) — for interacting with `MQTT`. - [ArduinoJSON](https://github.com/bblanchon/ArduinoJson) — for dealing with `JSON` files. - - [Arduino Http Client](https://github.com/arduino-libraries/ArduinoHttpClient) — for interacting with ThingsBoard using `HTTP/S`. + **Needs to be installed manually:** + - [MQTT PubSub Client](https://github.com/thingsboard/pubsubclient) — for interacting with `MQTT`, when using the `Arduino_MQTT_Client` instance as an argument to `ThingsBoard`. + - [Arduino Http Client](https://github.com/arduino-libraries/ArduinoHttpClient) — for interacting with `HTTP/S` when using the `Arduino_HTTP_Client` instance as an argument to `ThingsBoardHttp`. - [MbedTLS Library](https://github.com/Seeed-Studio/Seeed_Arduino_mbedtls) — needed to create hashes for the OTA update (`ESP8266` only, already included in `ESP32` base firmware). - [WiFiEsp Client](https://github.com/bportaluri/WiFiEsp) — needed when using a `Arduino Uno` in combination with a `ESP8266`. From 0c5c872c77be9edbbc2bd7fe58edb9a31440f42b Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:34:36 +0200 Subject: [PATCH 20/80] Add espidf to possible frameworks --- library.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.json b/library.json index 0b1e9b1c..2cad002e 100644 --- a/library.json +++ b/library.json @@ -12,6 +12,6 @@ }, "version": "0.12.0", "examples": "examples/*/*.ino", - "frameworks": "arduino", + "frameworks": ["arduino", "espidf"], "license": "MIT" } From 4e82b8968dbf04a1840552f0337000f2b2a27f1d Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:47:56 +0200 Subject: [PATCH 21/80] Add constant definition for strncmp_P --- src/Constants.h | 3 +++ src/ThingsBoardDefaultLogger.cpp | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Constants.h b/src/Constants.h index 77ed60ef..74572197 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -34,6 +34,9 @@ class ThingsBoardDefaultLogger; #ifndef vsnprintf_P #define vsnprintf_P vsnprintf #endif // vsnprintf_P +#ifndef strncmp_P +#define strncmp_P strncmp +#endif // strncmp_P #endif // THINGSBOARD_ENABLE_PROGMEM diff --git a/src/ThingsBoardDefaultLogger.cpp b/src/ThingsBoardDefaultLogger.cpp index 3a1c16ae..b8abc3ae 100644 --- a/src/ThingsBoardDefaultLogger.cpp +++ b/src/ThingsBoardDefaultLogger.cpp @@ -8,13 +8,12 @@ #if THINGSBOARD_ENABLE_PROGMEM #include #endif // THINGSBOARD_ENABLE_PROGMEM -#include +#include void ThingsBoardDefaultLogger::log(const char *msg) { #if THINGSBOARD_ENABLE_PROGMEM - Serial.print(F("[TB] ")); + printf(F("[TB] %s \r\n"), msg); #else - Serial.print("[TB] "); + printf("[TB] %s \r\n", msg); #endif // THINGSBOARD_ENABLE_PROGMEM - Serial.println(msg); } From 385be246f331d5edaa16871157bce20b5884fbdb Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:56:09 +0200 Subject: [PATCH 22/80] Fix invalid printf call --- src/ThingsBoardDefaultLogger.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ThingsBoardDefaultLogger.cpp b/src/ThingsBoardDefaultLogger.cpp index b8abc3ae..8d3dee75 100644 --- a/src/ThingsBoardDefaultLogger.cpp +++ b/src/ThingsBoardDefaultLogger.cpp @@ -10,10 +10,15 @@ #endif // THINGSBOARD_ENABLE_PROGMEM #include -void ThingsBoardDefaultLogger::log(const char *msg) { + +// Log messages. #if THINGSBOARD_ENABLE_PROGMEM - printf(F("[TB] %s \r\n"), msg); +constexpr char LOG_MESSAGE_FORMAT[] PROGMEM = "[TB] %s\n"; #else - printf("[TB] %s \r\n", msg); +constexpr char LOG_MESSAGE_FORMAT[] = "[TB] %s\n"; #endif // THINGSBOARD_ENABLE_PROGMEM + + +void ThingsBoardDefaultLogger::log(const char *msg) { + printf(LOG_MESSAGE_FORMAT, msg); } From 72f4da47f13d9e0e2cbbaabf9abdbc554bda6823 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sat, 9 Sep 2023 08:26:40 +0200 Subject: [PATCH 23/80] Ensure implementations only compile with arduino --- src/Arduino_HTTP_Client.cpp | 4 ++++ src/Arduino_HTTP_Client.h | 4 ++++ src/Arduino_MQTT_Client.cpp | 4 ++++ src/Arduino_MQTT_Client.h | 4 ++++ src/ESP32_Updater.cpp | 4 ++-- src/ESP32_Updater.h | 4 ++-- src/ESP6266_Updater.cpp | 4 ++-- src/ESP8266_Updater.h | 4 ++-- 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Arduino_HTTP_Client.cpp b/src/Arduino_HTTP_Client.cpp index f9c05a99..2ade787a 100644 --- a/src/Arduino_HTTP_Client.cpp +++ b/src/Arduino_HTTP_Client.cpp @@ -1,6 +1,8 @@ // Header include. #include "Arduino_HTTP_Client.h" +#ifdef ARDUINO + Arduino_HTTP_Client::Arduino_HTTP_Client(Client& transport_client, const char *host, const uint16_t& port) : m_http_client(transport_client, host, port) { @@ -41,3 +43,5 @@ String Arduino_HTTP_Client::get_response_body() { return m_http_client.responseBody(); #endif // THINGSBOARD_ENABLE_STL } + +#endif // ARDUINO diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index b531e8df..59e043c7 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -7,6 +7,8 @@ #ifndef Arduino_HTTP_Client_h #define Arduino_HTTP_Client_h +#ifdef ARDUINO + // Library includes. #include @@ -50,4 +52,6 @@ class Arduino_HTTP_Client : public IHTTP_Client { HttpClient m_http_client; }; +#endif // ARDUINO + #endif // Arduino_HTTP_Client_h diff --git a/src/Arduino_MQTT_Client.cpp b/src/Arduino_MQTT_Client.cpp index 3bb74d66..898262ae 100644 --- a/src/Arduino_MQTT_Client.cpp +++ b/src/Arduino_MQTT_Client.cpp @@ -1,6 +1,8 @@ // Header include. #include "Arduino_MQTT_Client.h" +#ifdef ARDUINO + Arduino_MQTT_Client::Arduino_MQTT_Client() : m_mqtt_client() { @@ -79,4 +81,6 @@ size_t Arduino_MQTT_Client::write(const uint8_t *buffer, size_t size) { return m_mqtt_client.write(buffer, size); } +#endif // ARDUINO + #endif // THINGSBOARD_ENABLE_STREAM_UTILS diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index e2d996f0..e653e4d1 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -7,6 +7,8 @@ #ifndef Arduino_MQTT_Client_h #define Arduino_MQTT_Client_h +#ifdef ARDUINO + // Local includes. #include "IMQTT_Client.h" @@ -73,4 +75,6 @@ class Arduino_MQTT_Client : public IMQTT_Client { PubSubClient m_mqtt_client; }; +#endif // ARDUINO + #endif // Arduino_MQTT_Client_h diff --git a/src/ESP32_Updater.cpp b/src/ESP32_Updater.cpp index feed709d..95364f0b 100644 --- a/src/ESP32_Updater.cpp +++ b/src/ESP32_Updater.cpp @@ -3,7 +3,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP32 +#ifdef ESP32 && ARDUINO // Library include. #include @@ -24,6 +24,6 @@ bool ESP32_Updater::end() { return Update.end(); } -#endif // ESP32 +#endif // ESP32 && ARDUINO #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/ESP32_Updater.h b/src/ESP32_Updater.h index 8731a0aa..a9bb7405 100644 --- a/src/ESP32_Updater.h +++ b/src/ESP32_Updater.h @@ -12,7 +12,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP32 +#ifdef ESP32 && ARDUINO // Local include. #include "IUpdater.h" @@ -29,7 +29,7 @@ class ESP32_Updater : public IUpdater { bool end() override; }; -#endif // ESP32 +#endif // ESP32 && ARDUINO #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/ESP6266_Updater.cpp b/src/ESP6266_Updater.cpp index 254e6091..bff2a97e 100644 --- a/src/ESP6266_Updater.cpp +++ b/src/ESP6266_Updater.cpp @@ -3,7 +3,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP8266 +#ifdef ESP8266 && ARDUINO // Library include. #include @@ -24,6 +24,6 @@ bool ESP8266_Updater::end() { return Update.end(); } -#endif // ESP8266 +#endif // ESP8266 && ARDUINO #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/ESP8266_Updater.h b/src/ESP8266_Updater.h index e602af82..3e9f8a54 100644 --- a/src/ESP8266_Updater.h +++ b/src/ESP8266_Updater.h @@ -12,7 +12,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP8266 +#ifdef ESP8266 && ARDUINO // Local include. #include "IUpdater.h" @@ -29,7 +29,7 @@ class ESP8266_Updater : public IUpdater { bool end() override; }; -#endif // ESP8266 +#endif // ESP8266 && ARDUINO #endif // THINGSBOARD_ENABLE_OTA From 18338d3252d33eba9ba824050a6ebd68d5dd3bde Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sat, 9 Sep 2023 08:35:23 +0200 Subject: [PATCH 24/80] Fix typo and incorrect filename --- src/Callback_Watchdog.cpp | 5 ++++- src/ESP32_Updater.cpp | 4 ++-- src/ESP32_Updater.h | 4 ++-- src/{ESP6266_Updater.cpp => ESP8266_Updater.cpp} | 4 ++-- src/ESP8266_Updater.h | 4 ++-- 5 files changed, 12 insertions(+), 9 deletions(-) rename src/{ESP6266_Updater.cpp => ESP8266_Updater.cpp} (84%) diff --git a/src/Callback_Watchdog.cpp b/src/Callback_Watchdog.cpp index 4168adfd..daa61668 100644 --- a/src/Callback_Watchdog.cpp +++ b/src/Callback_Watchdog.cpp @@ -29,7 +29,10 @@ Callback_Watchdog::Callback_Watchdog(std::function callback) : #if THINGSBOARD_USE_ESP_TIMER const esp_timer_create_args_t oneshot_timer_args = { .callback = &oneshot_timer_callback, - .name = WATCHDOG_TIMER_NAME + .arg = nullptr, + .dispatch_method = esp_timer_dispatch_t::ESP_TIMER_TASK, + .name = WATCHDOG_TIMER_NAME, + .skip_unhandled_events = false, }; // Temporary handle is used, because it allows using a void* as the actual oneshot_timer, // allowing us to only include the esp_timer header in the defintion (.cpp) file, diff --git a/src/ESP32_Updater.cpp b/src/ESP32_Updater.cpp index 95364f0b..dffc8fc8 100644 --- a/src/ESP32_Updater.cpp +++ b/src/ESP32_Updater.cpp @@ -3,7 +3,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP32 && ARDUINO +#if defined(ESP32) && defined(ARDUINO) // Library include. #include @@ -24,6 +24,6 @@ bool ESP32_Updater::end() { return Update.end(); } -#endif // ESP32 && ARDUINO +#endif // defined(ESP32) && defined(ARDUINO) #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/ESP32_Updater.h b/src/ESP32_Updater.h index a9bb7405..02c5432b 100644 --- a/src/ESP32_Updater.h +++ b/src/ESP32_Updater.h @@ -12,7 +12,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP32 && ARDUINO +#if defined(ESP32) && defined(ARDUINO) // Local include. #include "IUpdater.h" @@ -29,7 +29,7 @@ class ESP32_Updater : public IUpdater { bool end() override; }; -#endif // ESP32 && ARDUINO +#endif // defined(ESP32) && defined(ARDUINO) #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/ESP6266_Updater.cpp b/src/ESP8266_Updater.cpp similarity index 84% rename from src/ESP6266_Updater.cpp rename to src/ESP8266_Updater.cpp index bff2a97e..471e9bfa 100644 --- a/src/ESP6266_Updater.cpp +++ b/src/ESP8266_Updater.cpp @@ -3,7 +3,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP8266 && ARDUINO +#if defined(ESP8266) && defined(ARDUINO) // Library include. #include @@ -24,6 +24,6 @@ bool ESP8266_Updater::end() { return Update.end(); } -#endif // ESP8266 && ARDUINO +#endif // defined(ESP8266) && defined(ARDUINO) #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/ESP8266_Updater.h b/src/ESP8266_Updater.h index 3e9f8a54..8913555a 100644 --- a/src/ESP8266_Updater.h +++ b/src/ESP8266_Updater.h @@ -12,7 +12,7 @@ #if THINGSBOARD_ENABLE_OTA -#ifdef ESP8266 && ARDUINO +#if defined(ESP8266) && defined(ARDUINO) // Local include. #include "IUpdater.h" @@ -29,7 +29,7 @@ class ESP8266_Updater : public IUpdater { bool end() override; }; -#endif // ESP8266 && ARDUINO +#endif // defined(ESP8266) && defined(ARDUINO) #endif // THINGSBOARD_ENABLE_OTA From d94782d75e7d716895758de59c4583640bbbb465 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 11:37:17 +0200 Subject: [PATCH 25/80] Move erase method into Helper --- README.md | 5 +++-- library.json | 5 ++--- library.properties | 2 +- src/Arduino_MQTT_Client.cpp | 4 ++-- src/Helper.h | 21 +++++++++++++++++++++ src/IMQTT_Client.h | 2 +- src/ThingsBoard.h | 27 ++++++++++++++------------- src/Vector.h | 3 ++- 8 files changed, 46 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 5c3d7c85..da04524e 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ ThingsBoard SDK can be installed directly from the [Arduino Library manager](htt Following dependencies are installed automatically or must be installed, too: **Installed automatically:** - - [ArduinoJSON](https://github.com/bblanchon/ArduinoJson) — for dealing with `JSON` files. - + - [ArduinoJSON](https://github.com/bblanchon/ArduinoJson) — needed for dealing with the `JSON` payload that is sent to and received by `ThingsBoard` **Needs to be installed manually:** - [MQTT PubSub Client](https://github.com/thingsboard/pubsubclient) — for interacting with `MQTT`, when using the `Arduino_MQTT_Client` instance as an argument to `ThingsBoard`. - [Arduino Http Client](https://github.com/arduino-libraries/ArduinoHttpClient) — for interacting with `HTTP/S` when using the `Arduino_HTTP_Client` instance as an argument to `ThingsBoardHttp`. - [MbedTLS Library](https://github.com/Seeed-Studio/Seeed_Arduino_mbedtls) — needed to create hashes for the OTA update (`ESP8266` only, already included in `ESP32` base firmware). - [WiFiEsp Client](https://github.com/bportaluri/WiFiEsp) — needed when using a `Arduino Uno` in combination with a `ESP8266`. + - [StreamUtils](https://github.com/bblanchon/StreamUtils) — needed if `#define THINGSBOARD_ENABLE_STREAM_UTILS 1` is set, it allows sending arbitrary amount of payload even if the buffer size is too small to hold that complete payload ## Supported ThingsBoard Features @@ -42,6 +42,7 @@ All possible features are implemented over `MQTT` - [Device attribute publish](https://thingsboard.io/docs/reference/mqtt-api/#publish-attribute-update-to-the-server) - [Server-side RPC](https://thingsboard.io/docs/reference/mqtt-api/#server-side-rpc) - [Client-side RPC](https://thingsboard.io/docs/reference/mqtt-api/#client-side-rpc) + - [Request attribute values](https://thingsboard.io/docs/reference/mqtt-api/#request-attribute-values-from-the-server) - [Attribute update subscription](https://thingsboard.io/docs/reference/mqtt-api/#subscribe-to-attribute-updates-from-the-server) - [Device provisioning](https://thingsboard.io/docs/reference/mqtt-api/#device-provisioning) - [Device claiming](https://thingsboard.io/docs/reference/mqtt-api/#claiming-devices) diff --git a/library.json b/library.json index 2cad002e..8fab3fc4 100644 --- a/library.json +++ b/library.json @@ -7,11 +7,10 @@ "url": "https://github.com/thingsboard/thingsboard-arduino-sdk" }, "dependencies": { - "bblanchon/ArduinoJson": "^6.21.2", - "bblanchon/StreamUtils" : "^1.7.3" + "bblanchon/ArduinoJson": "^6.21.2" }, "version": "0.12.0", "examples": "examples/*/*.ino", - "frameworks": ["arduino", "espidf"], + "frameworks": "*", "license": "MIT" } diff --git a/library.properties b/library.properties index 1d1c851e..9e44257f 100644 --- a/library.properties +++ b/library.properties @@ -8,4 +8,4 @@ category=Communication url=https://github.com/thingsboard/thingsboard-arduino-sdk architectures=* includes=ThingsBoard.h -depends=ArduinoJson, StreamUtils +depends=ArduinoJson diff --git a/src/Arduino_MQTT_Client.cpp b/src/Arduino_MQTT_Client.cpp index 898262ae..4a75bfc2 100644 --- a/src/Arduino_MQTT_Client.cpp +++ b/src/Arduino_MQTT_Client.cpp @@ -81,6 +81,6 @@ size_t Arduino_MQTT_Client::write(const uint8_t *buffer, size_t size) { return m_mqtt_client.write(buffer, size); } -#endif // ARDUINO - #endif // THINGSBOARD_ENABLE_STREAM_UTILS + +#endif // ARDUINO diff --git a/src/Helper.h b/src/Helper.h index 175322a9..103ab0f9 100644 --- a/src/Helper.h +++ b/src/Helper.h @@ -13,6 +13,9 @@ // Library include. #include #include +#ifdef THINGSBOARD_ENABLE_STL +#include +#endif // THINGSBOARD_ENABLE_STL /// @brief Static helper class that includes some uniliterally used functionalities in multiple places @@ -40,6 +43,24 @@ class Helper { inline static size_t Measure_Json(const TSource& source) { return JSON_STRING_SIZE(measureJson(source)); } + + /// @brief Removes the element with the given index using the given iterator, which allows to use data containers that do not have a random-access iterator. + /// The user is also cautioned that this function only erases the element, and that if the element is itself a pointer, + /// the pointed-to memory is not touched in any way. Managing the pointer is the user's responsibility. + /// See https://stackoverflow.com/questions/875103/how-do-i-erase-an-element-from-stdvector-by-index for more information + /// @tparam DataContainer Class which allows to pass any arbitrary data container that contains the cbegin() and erase() method + /// @param container Data container holding the elements we want to remove an element from + /// @param index Index we want to delete the element at + template + inline static void remove(DataContainer& container, const size_t& index) { +#if THINGSBOARD_ENABLE_STL + auto iterator = container.cbegin(); + std::advance(iterator, index); + container.erase(iterator); +#else + container.erase(index); +#endif // THINGSBOARD_ENABLE_STL + } }; #endif // Helper diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index a59ce4a5..c791750d 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -70,7 +70,7 @@ class IMQTT_Client { /// The latter is recommended if relevant data is sent or if the client receives and handles Remote Procedure Calls or Shared Attribute Update Callbacks from the server, /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. /// However if Over the Air udpates are enabled secure communication should definetly be enabled, because if that is not done a 3rd party might impersonate the server sending a malicious payload, - /// which is then flashed onto the device instead of the real firmware. Which depeding on the payload might even be able to destroy the device or it make it otherwise unusable. + /// which is then flashed onto the device instead of the real firmware. Which depeding on the payload might even be able to destroy the device or make it otherwise unusable. /// See https://stackoverflow.blog/2020/12/14/security-considerations-for-ota-software-updates-for-iot-gateway-devices/ for more information on the aforementioned security risk virtual void set_server(const char *domain, const uint16_t& port) = 0; diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index dd93c68d..6174d02e 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -306,7 +306,7 @@ template #else /// @brief Wrapper around any arbitrary MQTT Client implementing the IMQTT_Client interface, to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. /// BufferSize of the underlying data buffer as well as the maximum amount of data points that can ever be sent have to defined as template arguments. -/// Changing is only possible if a new instance of this class is created. If theese values should be changeable and dynamic instead. +/// Changing is only possible if a new instance of this class is created. If these values should be changeable and dynamic instead. /// Simply set THINGSBOARD_ENABLE_DYNAMIC to 1, before including ThingsBoard.h /// @tparam MaxFieldsAmt Maximum amount of key value pair that we will be able to sent to ThingsBoard in one call, default = 8 /// @tparam Logger Logging class that should be used to print messages generated by ThingsBoard, default = ThingsBoardDefaultLogger @@ -318,7 +318,16 @@ class ThingsBoardSized { /// @brief Constructs a ThingsBoardSized instance with the given network client that should be used to establish the connection to ThingsBoard /// @param client MQTT Client implementation that should be used to establish the connection to ThingsBoard /// @param bufferSize Maximum amount of data that can be either received or sent to ThingsBoard at once, if bigger packets are received they are discarded - /// and if we attempt to send data that is bigger, it will not be sent, can be changed later with the setBufferSize() method, default = Default_Payload + /// and if we attempt to send data that is bigger, it will not be sent, the internal value can be changed later at any time with the setBufferSize() method + /// alternatively setting THINGSBOARD_ENABLE_STREAM_UTILS to 1 allows to send arbitrary size payloads if that is done the internal buffer of the PubSubClient + /// can be theoretically set to only be as big as the biggest message we should every receive from ThingsBoard, + /// this will mean though that all messages are sent over the StreamUtils library as long as they are bigger than the internal buffer, + /// which needs more time than sending a message directly but has the advantage of requiring less memory. + /// So if that is a problem on the board it might be useful to enable the THINGSBOARD_ENABLE_STREAM_UTILS option + /// and decrease the internal buffer size of the mqtt client to what is needed to receive all MQTT messages, + /// that size can vary but if all ThingsBoard features are used a buffer size of 256 bytes should suffice for receiving most responses. + /// If the aforementioned feature is not enabled the buffer size might need to be much bigger though, + /// but in that case if a message was too big to be sent the user will be informed with a message to the Logger, default = Default_Payload /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack, default = Default_Max_Stack_Size /// @param bufferingSize Amount of bytes allocated to speed up serialization, default = Default_Buffering_Size inline ThingsBoardSized(IMQTT_Client& client, const uint16_t& bufferSize = Default_Payload, const uint32_t& maxStackSize = Default_Max_Stack_Size, const size_t& bufferingSize = Default_Buffering_Size) @@ -1494,12 +1503,9 @@ class ThingsBoardSized { // Getting non-existing field from JSON should automatically // set JSONVariant to null rpc_request.Call_Callback(data); + // Delete callback because the changes have been requested and the callback is no longer needed -#if THINGSBOARD_ENABLE_STL - m_rpc_request_callbacks.erase(std::next(m_rpc_request_callbacks.begin(), i)); -#else - m_rpc_request_callbacks.erase(i); -#endif // THINGSBOARD_ENABLE_STL + Helper::remove(m_rpc_request_callbacks, i); break; } @@ -1755,13 +1761,8 @@ class ThingsBoardSized { attribute_request.Call_Callback(data); delete_callback: -#if THINGSBOARD_ENABLE_STL - // Delete callback because the changes have been requested and the callback is no longer needed - m_attribute_request_callbacks.erase(std::next(m_attribute_request_callbacks.begin(), i)); -#else // Delete callback because the changes have been requested and the callback is no longer needed - m_attribute_request_callbacks.erase(i); -#endif // THINGSBOARD_ENABLE_STL + Helper::remove(m_attribute_request_callbacks, i); break; } diff --git a/src/Vector.h b/src/Vector.h index 3494fb4a..207d7e51 100644 --- a/src/Vector.h +++ b/src/Vector.h @@ -117,12 +117,13 @@ class Vector { /// @brief Removes the element at the given index, has to move all element one to the left if the index is not at the end of the array /// @param index Index the element should be removed at from the underlying data container inline void erase(const size_t& index) { + // Check if the given index is bigger or equal than the actual amount of elements if it is we can not erase that element because it does not exist if (index < m_size) { // Move all elements after the index one position to the left for (size_t i = index; i < m_size - 1; i++) { m_elements[i] = m_elements[i + 1]; } - // Decrease the size of the vector + // Decrease the size of the vector to remove the last element, because either it was moved one index to the left or was the element we wanted to delete m_size--; } } From f988080eb89e27778a2f9b8f3ca1835ec9dafeb2 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 11:41:45 +0200 Subject: [PATCH 26/80] Fix include for Logger --- src/ThingsBoardDefaultLogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ThingsBoardDefaultLogger.cpp b/src/ThingsBoardDefaultLogger.cpp index 8d3dee75..c21c7bad 100644 --- a/src/ThingsBoardDefaultLogger.cpp +++ b/src/ThingsBoardDefaultLogger.cpp @@ -8,7 +8,7 @@ #if THINGSBOARD_ENABLE_PROGMEM #include #endif // THINGSBOARD_ENABLE_PROGMEM -#include +#include // Log messages. From 3ff62dbb1b565173198d03308ef9f4d7640f308c Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 18:58:53 +0200 Subject: [PATCH 27/80] Adjusting atoi comments --- src/ThingsBoard.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 2e415774..00fabc8c 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -1508,7 +1508,9 @@ class ThingsBoardSized { /// @param topic Previously subscribed topic, we got the response over /// @param data Payload sent by the server over our given topic, that contains our key value pairs inline void process_rpc_request_message(char *topic, const JsonObjectConst& data) { - // Remove the not needed part of the topic + // Remove the not needed part of the received topic string, which is everything before the response id, + // therefore we remove the section before that which is the topic + an additional "/" character, that seperates the topic from the response id. + // Meaning the index we want to get the substring from is the length of the topic + 1 for the additonal "/" character const size_t index = strlen(RPC_RESPONSE_TOPIC) + 1U; #if THINGSBOARD_ENABLE_STL std::string response = topic; @@ -1518,7 +1520,7 @@ class ThingsBoardSized { response = response.substring(index); #endif // THINGSBOARD_ENABLE_STL - // Convert the remaining text to an integer + // Convert the remaining text after the topic to an integer, because it should now contain only the response id const uint32_t responseId = atoi(response.c_str()); for (size_t i = 0; i < m_rpc_request_callbacks.size(); i++) { @@ -1598,7 +1600,9 @@ class ThingsBoardSized { return; } - // Remove the not needed part of the topic + // Remove the not needed part of the received topic string, which is everything before the request id, + // therefore we remove the section before that which is the topic + an additional "/" character, that seperates the topic from the request id. + // Meaning the index we want to get the substring from is the length of the topic + 1 for the additonal "/" character const size_t index = strlen(RPC_REQUEST_TOPIC) + 1U; #if THINGSBOARD_ENABLE_STL std::string request = topic; @@ -1608,7 +1612,7 @@ class ThingsBoardSized { request = request.substring(index); #endif // THINGSBOARD_ENABLE_STL - // Convert the remaining text to an integer + // Convert the remaining text after the topic to an integer, because it should now contain only the request id const uint32_t request_id = atoi(request.c_str()); char responseTopic[Helper::detectSize(RPC_SEND_RESPONSE_TOPIC, request_id)]; @@ -1626,7 +1630,9 @@ class ThingsBoardSized { /// @param payload Payload that was sent over the cloud and received over the given topic /// @param length Total length of the received payload inline void process_firmware_response(char *topic, uint8_t *payload, const unsigned int& length) { - // Remove the not needed part of the topic + // Remove the not needed part of the received topic string, which is everything before the request id, + // therefore we remove the section before that which is the topic + an additional "/" character, that seperates the topic from the request id. + // Meaning the index we want to get the substring from is the length of the topic + 1 for the additonal "/" character const size_t index = strlen(FIRMWARE_RESPONSE_TOPIC) + 1U; #if THINGSBOARD_ENABLE_STL std::string request = topic; @@ -1636,7 +1642,7 @@ class ThingsBoardSized { request = request.substring(index); #endif // THINGSBOARD_ENABLE_STL - // Convert the remaining text to an integer + // Convert the remaining text after the topic to an integer, because it should now contain only the request id const uint32_t request_id = atoi(request.c_str()); // Check if the remaining stack size of the current task would overflow the stack, @@ -1749,7 +1755,9 @@ class ThingsBoardSized { /// @param topic Previously subscribed topic, we got the response over /// @param data Payload sent by the server over our given topic, that contains our key value pairs inline void process_attribute_request_message(char *topic, JsonObjectConst& data) { - // Remove the not needed part of the topic + // Remove the not needed part of the received topic string, which is everything before the response id, + // therefore we remove the section before that which is the topic + an additional "/" character, that seperates the topic from the response id. + // Meaning the index we want to get the substring from is the length of the topic + 1 for the additonal "/" character const size_t index = strlen(ATTRIBUTE_RESPONSE_TOPIC) + 1U; #if THINGSBOARD_ENABLE_STL std::string response = topic; @@ -1759,7 +1767,7 @@ class ThingsBoardSized { response = response.substring(index); #endif // THINGSBOARD_ENABLE_STL - // Convert the remaining text to an integer + // Convert the remaining text after the topic to an integer, because it should now contain only the response id const uint32_t response_id = atoi(response.c_str()); #if THINGSBOARD_ENABLE_DEBUG From 1140660b7b7fc4422c71a1ad5b3ca451b7fee2a9 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 19:24:19 +0200 Subject: [PATCH 28/80] Remove usage of uint32_t --- README.md | 4 +-- src/Arduino_MQTT_Client.cpp | 4 +-- src/Arduino_MQTT_Client.h | 4 +-- src/Attribute_Request_Callback.cpp | 4 +-- src/Attribute_Request_Callback.h | 6 ++-- src/Helper.cpp | 6 ++-- src/Helper.h | 2 +- src/IMQTT_Client.h | 8 ++--- src/OTA_Handler.h | 16 ++++----- src/OTA_Update_Callback.h | 2 +- src/RPC_Request_Callback.cpp | 4 +-- src/RPC_Request_Callback.h | 6 ++-- src/ThingsBoard.h | 56 +++++++++++++++--------------- src/ThingsBoardHttp.h | 18 +++++----- 14 files changed, 70 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index da04524e..6696c430 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ class Custom_MQTT_Client : public IMQTT_Client { return true; } - bool publish(const char *topic, const uint8_t *payload, const uint32_t& length) override { + bool publish(const char *topic, const uint8_t *payload, const size_t& length) override { return true; } @@ -297,7 +297,7 @@ class Custom_MQTT_Client : public IMQTT_Client { #if THINGSBOARD_ENABLE_STREAM_UTILS - bool begin_publish(const char *topic, const uint32_t& length) override { + bool begin_publish(const char *topic, const size_t& length) override { return true; } diff --git a/src/Arduino_MQTT_Client.cpp b/src/Arduino_MQTT_Client.cpp index 4a75bfc2..40ab5dd9 100644 --- a/src/Arduino_MQTT_Client.cpp +++ b/src/Arduino_MQTT_Client.cpp @@ -47,7 +47,7 @@ bool Arduino_MQTT_Client::loop() { return m_mqtt_client.loop(); } -bool Arduino_MQTT_Client::publish(const char *topic, const uint8_t *payload, const uint32_t& length) { +bool Arduino_MQTT_Client::publish(const char *topic, const uint8_t *payload, const size_t& length) { return m_mqtt_client.publish(topic, payload, length, false); } @@ -65,7 +65,7 @@ bool Arduino_MQTT_Client::connected() { #if THINGSBOARD_ENABLE_STREAM_UTILS -bool Arduino_MQTT_Client::begin_publish(const char *topic, const uint32_t& length) { +bool Arduino_MQTT_Client::begin_publish(const char *topic, const size_t& length) { return m_mqtt_client.beginPublish(topic, length, false); } diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index e653e4d1..0ecc61bc 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -47,7 +47,7 @@ class Arduino_MQTT_Client : public IMQTT_Client { bool loop() override; - bool publish(const char *topic, const uint8_t *payload, const uint32_t& length) override; + bool publish(const char *topic, const uint8_t *payload, const size_t& length) override; bool subscribe(const char *topic) override; @@ -57,7 +57,7 @@ class Arduino_MQTT_Client : public IMQTT_Client { #if THINGSBOARD_ENABLE_STREAM_UTILS - bool begin_publish(const char *topic, const uint32_t& length) override; + bool begin_publish(const char *topic, const size_t& length) override; bool end_publish() override; diff --git a/src/Attribute_Request_Callback.cpp b/src/Attribute_Request_Callback.cpp index ace1f0b9..d1e202c2 100644 --- a/src/Attribute_Request_Callback.cpp +++ b/src/Attribute_Request_Callback.cpp @@ -23,11 +23,11 @@ Attribute_Request_Callback::Attribute_Request_Callback(const char *attributes, f #endif // !THINGSBOARD_ENABLE_STL -const uint32_t& Attribute_Request_Callback::Get_Request_ID() const { +const size_t& Attribute_Request_Callback::Get_Request_ID() const { return m_request_id; } -void Attribute_Request_Callback::Set_Request_ID(const uint32_t &request_id) { +void Attribute_Request_Callback::Set_Request_ID(const size_t &request_id) { m_request_id = request_id; } diff --git a/src/Attribute_Request_Callback.h b/src/Attribute_Request_Callback.h index 599f08bc..3850733f 100644 --- a/src/Attribute_Request_Callback.h +++ b/src/Attribute_Request_Callback.h @@ -69,13 +69,13 @@ class Attribute_Request_Callback : public Callback /// and will be later used to verifiy which Attribute_Request_Callback /// is connected to which received client-side or shared attributes /// @return Unique identifier connected to the requested client-side or shared attributes - const uint32_t& Get_Request_ID() const; + const size_t& Get_Request_ID() const; /// @brief Sets the unique request identifier that is connected to the original request, /// and will be later used to verifiy which Attribute_Request_Callback /// is connected to which received client-side or shared attributes /// @param request_id Unqiue identifier of the request for client-side or shared attributes - void Set_Request_ID(const uint32_t &request_id); + void Set_Request_ID(const size_t &request_id); /// @brief Gets the response key of the key-value pair, /// that we expect the client-side or shared attribute payload json data to be contained in @@ -131,7 +131,7 @@ class Attribute_Request_Callback : public Callback #else const char *m_attributes; // Attribute we want to request #endif // THINGSBOARD_ENABLE_STL - uint32_t m_request_id; // Id the request was called with + size_t m_request_id; // Id the request was called with const char *m_attribute_key; // Attribute key that we wil receive the response on ("client" or "shared") }; diff --git a/src/Helper.cpp b/src/Helper.cpp index 041af3d6..8096578a 100644 --- a/src/Helper.cpp +++ b/src/Helper.cpp @@ -22,12 +22,12 @@ uint8_t Helper::detectSize(const char *msg, ...) { return result; } -uint32_t Helper::getOccurences(const char *str, char symbol) { - uint32_t count = 0; +size_t Helper::getOccurences(const char *str, char symbol) { + size_t count = 0; if (str == nullptr) { return count; } - for (uint32_t i = 0; i < strlen(str); i++) { + for (size_t i = 0; i < strlen(str); i++) { if (str[i] != symbol) { continue; } diff --git a/src/Helper.h b/src/Helper.h index 103ab0f9..f56d14a1 100644 --- a/src/Helper.h +++ b/src/Helper.h @@ -32,7 +32,7 @@ class Helper { /// @param str String that we want to check the symbol in /// @param symbol Symbols we want to search for /// @return Amount of occurences of the given symbol - static uint32_t getOccurences(const char *str, char symbol); + static size_t getOccurences(const char *str, char symbol); /// @brief Calculates the total size of the string the serializeJson method would produce including the null end terminator. /// See https://arduinojson.org/v6/api/json/measurejson/ for more information on the underlying method used diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index c791750d..a776a339 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -39,9 +39,9 @@ class IMQTT_Client { public: /// @brief Callback signature #if THINGSBOARD_ENABLE_STL - using function = std::function; + using function = std::function; #else - using function = void (*)(char *topic, uint8_t *payload, unsigned int length); + using function = void (*)(char *topic, uint8_t *payload, size_t length); #endif // THINGSBOARD_ENABLE_STL /// @brief Sets the callback that is called, if any message is received by the MQTT broker, including the topic string that the message was received over, @@ -94,7 +94,7 @@ class IMQTT_Client { /// @param payload Payload containg the json data that should be sent /// @param length Length of the payload in bytes /// @return Whether publishing the payload on the given topic was successful or not - virtual bool publish(const char *topic, const uint8_t *payload, const uint32_t& length) = 0; + virtual bool publish(const char *topic, const uint8_t *payload, const size_t& length) = 0; /// @brief Subscribes to MQTT message on the given topic, which will cause an internal callback to be called for each message received on that topic from the server, /// it should then, call the previously configured callback with set_callback() with the received data @@ -122,7 +122,7 @@ class IMQTT_Client { /// @param topic Topic that the message is sent over, where different MQTT topics expect a different kind of payload /// @param length Length of the payload in bytes /// @return Whether starting to publish on the given topic was successful or not - virtual bool begin_publish(const char *topic, const uint32_t& length) = 0; + virtual bool begin_publish(const char *topic, const size_t& length) = 0; /// @brief Finishes any publish message started with begin_publish() /// @return Whether the complete packet was sent successfully or not diff --git a/src/OTA_Handler.h b/src/OTA_Handler.h index c8f4459a..8b818b9f 100644 --- a/src/OTA_Handler.h +++ b/src/OTA_Handler.h @@ -49,7 +49,7 @@ constexpr char ERROR_UPDATE_WRITE[] PROGMEM = "Only wrote (%u) bytes of binary d constexpr char UPDATING_HASH_FAILED[] PROGMEM = "Updating hash failed"; constexpr char ERROR_UPDATE_END[] PROGMEM = "Error (%u) during flash updater not all bytes written"; constexpr char CHKS_VER_FAILED[] PROGMEM = "Checksum verification failed"; -constexpr char FW_CHUNK[] PROGMEM = "Receive chunk (%i), with size (%u) bytes"; +constexpr char FW_CHUNK[] PROGMEM = "Receive chunk (%u), with size (%u) bytes"; constexpr char HASH_ACTUAL[] PROGMEM = "(%s) actual checksum: (%s)"; constexpr char HASH_EXPECTED[] PROGMEM = "(%s) expected checksum: (%s)"; constexpr char CHKS_VER_SUCCESS[] PROGMEM = "Checksum is the same as expected"; @@ -63,7 +63,7 @@ constexpr char ERROR_UPDATE_WRITE[] = "Only wrote (%u) bytes of binary data to f constexpr char UPDATING_HASH_FAILED[] = "Updating hash failed"; 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 FW_CHUNK[] = "Receive chunk (%u), with size (%u) bytes"; constexpr char HASH_ACTUAL[] = "(%s) actual checksum: (%s)"; constexpr char HASH_EXPECTED[] = "(%s) expected checksum: (%s)"; constexpr char CHKS_VER_SUCCESS[] = "Checksum is the same as expected"; @@ -81,7 +81,7 @@ class OTA_Handler { /// @param publish_callback Callback that is used to request the firmware chunk of the firmware binary with the given chunk number /// @param send_fw_state_callback Callback that is used to send information about the current state of the over the air update /// @param finish_callback Callback that is called once the update has been finished and the user has been informed of the failure or success - inline OTA_Handler(std::function publish_callback, std::function send_fw_state_callback, std::function finish_callback) + inline OTA_Handler(std::function publish_callback, std::function send_fw_state_callback, std::function finish_callback) : m_fw_callback(nullptr) , m_publish_callback(publish_callback) , m_send_fw_state_callback(send_fw_state_callback) @@ -105,7 +105,7 @@ class OTA_Handler { /// @param fw_algorithm String of the algorithm used to hash the firmware binary /// @param fw_checksum Checksum of the complete firmware binary, should be the same as the actually written data in the end /// @param fw_checksum_algorithm Algorithm used to hash the firmware binary - inline void Start_Firmware_Update(const OTA_Update_Callback *fw_callback, const uint32_t& fw_size, const std::string& fw_algorithm, const std::string& fw_checksum, const mbedtls_md_type_t& fw_checksum_algorithm) { + inline void Start_Firmware_Update(const OTA_Update_Callback *fw_callback, const size_t& fw_size, const std::string& fw_algorithm, const std::string& fw_checksum, const mbedtls_md_type_t& fw_checksum_algorithm) { m_fw_callback = fw_callback; m_fw_size = fw_size; m_total_chunks = (m_fw_size / m_fw_callback->Get_Chunk_Size()) + 1U; @@ -137,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 unsigned int& total_bytes) { + inline void Process_Firmware_Packet(const size_t& current_chunk, uint8_t *payload, const size_t& total_bytes) { (void)m_send_fw_state_callback(FW_STATE_DOWNLOADING, nullptr); if (current_chunk != m_requested_chunks) { @@ -195,7 +195,7 @@ class OTA_Handler { private: const OTA_Update_Callback *m_fw_callback; - std::function m_publish_callback; + std::function m_publish_callback; std::function m_send_fw_state_callback; std::function m_finish_callback; // Allows for a binary size of up to theoretically 4 GB. @@ -205,8 +205,8 @@ class OTA_Handler { 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; + size_t m_total_chunks; + size_t m_requested_chunks; uint8_t m_retries; Callback_Watchdog m_watchdog; diff --git a/src/OTA_Update_Callback.h b/src/OTA_Update_Callback.h index 4f48eddd..26f409a1 100644 --- a/src/OTA_Update_Callback.h +++ b/src/OTA_Update_Callback.h @@ -47,7 +47,7 @@ class OTA_Update_Callback : public Callback { public: /// @brief OTA firmware update callback signature using returnType = void; - using progressArgumentType = const uint32_t&; + using progressArgumentType = const size_t&; using progressFn = std::function; /// @brief Constructs empty callback, will result in never being called diff --git a/src/RPC_Request_Callback.cpp b/src/RPC_Request_Callback.cpp index d37ea356..0787182f 100644 --- a/src/RPC_Request_Callback.cpp +++ b/src/RPC_Request_Callback.cpp @@ -31,11 +31,11 @@ RPC_Request_Callback::RPC_Request_Callback(const char *methodName, const JsonArr // Nothing to do } -const uint32_t& RPC_Request_Callback::Get_Request_ID() const { +const size_t& RPC_Request_Callback::Get_Request_ID() const { return m_request_id; } -void RPC_Request_Callback::Set_Request_ID(const uint32_t &request_id) { +void RPC_Request_Callback::Set_Request_ID(const size_t &request_id) { m_request_id = request_id; } diff --git a/src/RPC_Request_Callback.h b/src/RPC_Request_Callback.h index 1b245a2c..9fc68ffb 100644 --- a/src/RPC_Request_Callback.h +++ b/src/RPC_Request_Callback.h @@ -38,13 +38,13 @@ class RPC_Request_Callback : public Callback { /// and will be later used to verifiy which RPC_Request_Callback /// is connected to which received RPC response /// @return Unique identifier connected to the request for client side rpc - const uint32_t& Get_Request_ID() const; + const size_t& Get_Request_ID() const; /// @brief Sets the unique request identifier that is connected to the original request, /// and will be later used to verifiy which RPC_Request_Callback /// is connected to which received RPC response /// @param request_id Unique identifier connected to the request for client side rpc - void Set_Request_ID(const uint32_t &request_id); + void Set_Request_ID(const size_t &request_id); /// @brief Gets the poiner to the underlying name /// @return Pointer to the passed methodName @@ -65,7 +65,7 @@ class RPC_Request_Callback : public Callback { private: const char *m_methodName; // Method name const JsonArray *m_parameters; // Parameter json - uint32_t m_request_id; // Id the request was called with + size_t m_request_id; // Id the request was called with }; #endif // RPC_Request_Callback_h diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 00fabc8c..1aa35efd 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -330,7 +330,7 @@ class ThingsBoardSized { /// but in that case if a message was too big to be sent the user will be informed with a message to the Logger, default = Default_Payload /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack, default = Default_Max_Stack_Size /// @param bufferingSize Amount of bytes allocated to speed up serialization, default = Default_Buffering_Size - inline ThingsBoardSized(IMQTT_Client& client, const uint16_t& bufferSize = Default_Payload, const uint32_t& maxStackSize = Default_Max_Stack_Size, const size_t& bufferingSize = Default_Buffering_Size) + inline ThingsBoardSized(IMQTT_Client& client, const uint16_t& bufferSize = Default_Payload, const size_t& maxStackSize = Default_Max_Stack_Size, const size_t& bufferingSize = Default_Buffering_Size) : m_client(client) , m_max_stack(maxStackSize) , m_buffering_size(bufferingSize) @@ -381,7 +381,7 @@ class ThingsBoardSized { /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before allocating on the heap instead /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack - inline void setMaximumStackSize(const uint32_t& maxStackSize) { + inline void setMaximumStackSize(const size_t& maxStackSize) { m_max_stack = maxStackSize; } @@ -445,7 +445,7 @@ class ThingsBoardSized { /// @param jsonSize Size of the data inside the source /// @return Whether sending the data was successful or not template - inline bool Send_Json(const char* topic, const TSource& source, const uint32_t& jsonSize) { + inline bool Send_Json(const char* topic, const TSource& source, const size_t& jsonSize) { // Check if allocating needed memory failed when trying to create the JsonObject, // if it did the method will return true. See https://arduinojson.org/v6/api/jsonvariant/isnull/ for more information. if (source.isNull()) { @@ -453,7 +453,7 @@ class ThingsBoardSized { return false; } #if !THINGSBOARD_ENABLE_DYNAMIC - const uint32_t amount = source.size(); + const size_t amount = source.size(); if (MaxFieldsAmt < amount) { char message[Helper::detectSize(TOO_MANY_JSON_FIELDS, amount, MaxFieldsAmt)]; snprintf_P(message, sizeof(message), TOO_MANY_JSON_FIELDS, amount, MaxFieldsAmt); @@ -541,7 +541,7 @@ class ThingsBoardSized { /// pass nullptr or an empty string if the user should be able to claim the device without any password /// @param durationMs Total time in milliseconds the user has to claim their device as their own /// @return Whether sending the claiming request was successful or not - inline bool Claim_Request(const char *secretKey, const uint32_t& durationMs) { + inline bool Claim_Request(const char *secretKey, const size_t& durationMs) { StaticJsonDocument requestBuffer; const JsonObject respObj = requestBuffer.to(); @@ -656,7 +656,7 @@ class ThingsBoardSized { /// @param jsonSize Size of the data inside the source /// @return Whether sending the data was successful or not template - inline bool sendTelemetryJson(const TSource& source, const uint32_t& jsonSize) { + inline bool sendTelemetryJson(const TSource& source, const size_t& jsonSize) { return Send_Json(TELEMETRY_TOPIC, source, jsonSize); } @@ -694,7 +694,7 @@ class ThingsBoardSized { /// @param jsonSize Size of the data inside the source /// @return Whether sending the data was successful or not template - inline bool sendAttributeJSON(const TSource& source, const uint32_t& jsonSize) { + inline bool sendAttributeJSON(const TSource& source, const size_t& jsonSize) { return Send_Json(ATTRIBUTE_TOPIC, source, jsonSize); } @@ -722,7 +722,7 @@ class ThingsBoardSized { template inline bool RPC_Subscribe(const InputIterator& first_itr, const InputIterator& last_itr) { #if !THINGSBOARD_ENABLE_DYNAMIC - const uint32_t size = std::distance(first_itr, last_itr); + const size_t size = std::distance(first_itr, last_itr); if (m_rpc_callbacks.size() + size > m_rpc_callbacks.capacity()) { Logger::log(MAX_RPC_EXCEEDED); return false; @@ -825,7 +825,7 @@ class ThingsBoardSized { // String are const char* and therefore stored as a pointer --> zero copy, meaning the size for the strings is 0 bytes, // Data structure size depends on the amount of key value pairs passed + the default methodName and params key needed for the request. // See https://arduinojson.org/v6/assistant/ for more information on the needed size for the JsonDocument - const uint32_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(parameters != nullptr ? parameters->size() + 2U : 2U); + const size_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(parameters != nullptr ? parameters->size() + 2U : 2U); TBJsonDocument requestBuffer(dataStructureMemoryUsage); #else // Ensure to have enough size for the infinite amount of possible parameters that could be sent to the cloud, @@ -952,7 +952,7 @@ class ThingsBoardSized { template inline bool Shared_Attributes_Subscribe(const InputIterator& first_itr, const InputIterator& last_itr) { #if !THINGSBOARD_ENABLE_DYNAMIC - const uint32_t size = std::distance(first_itr, last_itr); + const size_t size = std::distance(first_itr, last_itr); if (m_shared_attribute_update_callbacks.size() + size > m_shared_attribute_update_callbacks.capacity()) { Logger::log(MAX_SHARED_ATT_UPDATE_EXCEEDED); return false; @@ -1062,7 +1062,7 @@ class ThingsBoardSized { /// @param jsonSize Size of the data inside the source /// @return Whether sending the data was successful or not template - inline bool Serialize_Json(const char* topic, const TSource& source, const uint32_t& jsonSize) { + inline bool Serialize_Json(const char* topic, const TSource& source, const size_t& jsonSize) { if (!m_client.begin_publish(topic, jsonSize)) { Logger::log(UNABLE_TO_SERIALIZE_JSON); return false; @@ -1084,7 +1084,7 @@ class ThingsBoardSized { /// @brief Publishes a request via MQTT to request the given firmware chunk /// @param request_chunck Chunk index that should be requested from the server /// @return Whether publishing the message was successful or not - inline bool Publish_Chunk_Request(const uint32_t& request_chunck) { + inline bool Publish_Chunk_Request(const size_t& request_chunck) { // Calculate the number of chuncks we need to request, // in order to download the complete firmware binary const uint16_t& chunk_size = m_fw_callback->Get_Chunk_Size(); @@ -1105,7 +1105,7 @@ class ThingsBoardSized { /// @brief Returns the maximum amount of bytes that we want to allocate on the stack, before the memory is allocated on the heap instead /// @return Maximum amount of bytes we want to allocate on the stack - inline const uint32_t& getMaximumStackSize() const { + inline const size_t& getMaximumStackSize() const { return m_max_stack; } @@ -1160,7 +1160,7 @@ class ThingsBoardSized { // String are const char* and therefore stored as a pointer --> zero copy, meaning the size for the strings is 0 bytes, // Data structure size depends on the amount of key value pairs passed + the default clientKeys or sharedKeys // See https://arduinojson.org/v6/assistant/ for more information on the needed size for the JsonDocument - constexpr uint32_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(1U); + constexpr size_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(1U); StaticJsonDocument requestBuffer; // The .template variant of createing the JsonVariant has to be used, // because we are passing a template to the StaticJsonDocument template list @@ -1521,18 +1521,18 @@ class ThingsBoardSized { #endif // THINGSBOARD_ENABLE_STL // Convert the remaining text after the topic to an integer, because it should now contain only the response id - const uint32_t responseId = atoi(response.c_str()); + const size_t response_id = atoi(response.c_str()); for (size_t i = 0; i < m_rpc_request_callbacks.size(); i++) { const RPC_Request_Callback& rpc_request = m_rpc_request_callbacks.at(i); - if (rpc_request.Get_Request_ID() != responseId) { + if (rpc_request.Get_Request_ID() != response_id) { continue; } #if THINGSBOARD_ENABLE_DEBUG - char message[Helper::detectSize(CALLING_REQUEST_CB, responseId)]; - snprintf_P(message, sizeof(message), CALLING_REQUEST_CB, responseId); + char message[Helper::detectSize(CALLING_REQUEST_CB, response_id)]; + snprintf_P(message, sizeof(message), CALLING_REQUEST_CB, response_id); Logger::log(message); #endif // THINGSBOARD_ENABLE_DEBUG @@ -1613,7 +1613,7 @@ class ThingsBoardSized { #endif // THINGSBOARD_ENABLE_STL // Convert the remaining text after the topic to an integer, because it should now contain only the request id - const uint32_t request_id = atoi(request.c_str()); + const size_t request_id = atoi(request.c_str()); char responseTopic[Helper::detectSize(RPC_SEND_RESPONSE_TOPIC, request_id)]; snprintf_P(responseTopic, sizeof(responseTopic), RPC_SEND_RESPONSE_TOPIC, request_id); @@ -1629,7 +1629,7 @@ class ThingsBoardSized { /// @param topic Previously subscribed topic, we got the response over /// @param payload Payload that was sent over the cloud and received over the given topic /// @param length Total length of the received payload - inline void process_firmware_response(char *topic, uint8_t *payload, const unsigned int& length) { + inline void process_firmware_response(char *topic, uint8_t *payload, const size_t& length) { // Remove the not needed part of the received topic string, which is everything before the request id, // therefore we remove the section before that which is the topic + an additional "/" character, that seperates the topic from the request id. // Meaning the index we want to get the substring from is the length of the topic + 1 for the additonal "/" character @@ -1643,7 +1643,7 @@ class ThingsBoardSized { #endif // THINGSBOARD_ENABLE_STL // Convert the remaining text after the topic to an integer, because it should now contain only the request id - const uint32_t request_id = atoi(request.c_str()); + const size_t request_id = atoi(request.c_str()); // Check if the remaining stack size of the current task would overflow the stack, // if it would allocate the memory on the heap instead to ensure no stack overflow occurs. @@ -1768,7 +1768,7 @@ class ThingsBoardSized { #endif // THINGSBOARD_ENABLE_STL // Convert the remaining text after the topic to an integer, because it should now contain only the response id - const uint32_t response_id = atoi(response.c_str()); + const size_t response_id = atoi(response.c_str()); #if THINGSBOARD_ENABLE_DEBUG char message[Helper::detectSize(CALLING_REQUEST_CB, response_id)]; @@ -1837,7 +1837,7 @@ class ThingsBoardSized { // String are const char* and therefore stored as a pointer --> zero copy, meaning the size for the strings is 0 bytes, // Data structure size depends on the amount of key value pairs passed. // See https://arduinojson.org/v6/assistant/ for more information on the needed size for the JsonDocument - const uint32_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(data_count); + const size_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(data_count); TBJsonDocument jsonBuffer(dataStructureMemoryUsage); #else StaticJsonDocument jsonBuffer; @@ -1856,7 +1856,7 @@ class ThingsBoardSized { } IMQTT_Client& m_client; // MQTT client instance. - uint32_t m_max_stack; // Maximum stack size we allocate at once. + size_t m_max_stack; // Maximum stack size we allocate at once. size_t m_buffering_size; // Buffering size used to serialize directly into client. #if THINGSBOARD_ENABLE_STL @@ -1878,7 +1878,7 @@ class ThingsBoardSized { #endif Provision_Callback m_provision_callback; // Provision response callback - uint32_t m_request_id; // Allows nearly 4.3 million requests before wrapping back to 0 + size_t m_request_id; // Allows nearly 4.3 million requests before wrapping back to 0 #if THINGSBOARD_ENABLE_OTA const OTA_Update_Callback *m_fw_callback; @@ -1895,7 +1895,7 @@ class ThingsBoardSized { /// @param topic Previously subscribed topic, we got the response over /// @param payload Payload that was sent over the cloud and received over the given topic /// @param length Total length of the received payload - inline void onMQTTMessage(char *topic, uint8_t *payload, unsigned int length) { + inline void onMQTTMessage(char *topic, uint8_t *payload, size_t length) { #if THINGSBOARD_ENABLE_DEBUG char message[JSON_STRING_SIZE(strlen(RECEIVE_MESSAGE)) + JSON_STRING_SIZE(strlen(topic))]; snprintf_P(message, sizeof(message), RECEIVE_MESSAGE, topic); @@ -1906,7 +1906,7 @@ class ThingsBoardSized { // Buffer that we deserialize is writeable and not read only --> zero copy, meaning the size for the data is 0 bytes, // Data structure size depends on the amount of key value pairs received. // See https://arduinojson.org/v6/assistant/ for more information on the needed size for the JsonDocument - const uint32_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(Helper::getOccurences(reinterpret_cast(payload), COLON)); + const size_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(Helper::getOccurences(reinterpret_cast(payload), COLON)); TBJsonDocument jsonBuffer(dataStructureMemoryUsage); #else StaticJsonDocument jsonBuffer; @@ -1951,7 +1951,7 @@ class ThingsBoardSized { // To be able to forward event to an instance, rather than to a function, this pointer exists. static ThingsBoardSized *m_subscribedInstance; - static void onStaticMQTTMessage(char *topic, uint8_t *payload, unsigned int length) { + static void onStaticMQTTMessage(char *topic, uint8_t *payload, size_t length) { if (m_subscribedInstance == nullptr) { return; } diff --git a/src/ThingsBoardHttp.h b/src/ThingsBoardHttp.h index 27d8376b..c0ff8170 100644 --- a/src/ThingsBoardHttp.h +++ b/src/ThingsBoardHttp.h @@ -75,7 +75,7 @@ class ThingsBoardHttpSized { /// @param keepAlive Attempts to keep the establishes TCP connection alive to make sending data faster /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack, default = Default_Max_Stack_Size inline ThingsBoardHttpSized(IHTTP_Client& client, const char *access_token, - const char *host, const uint16_t& port = 80U, const bool& keepAlive = true, const uint32_t& maxStackSize = Default_Max_Stack_Size) + const char *host, const uint16_t& port = 80U, const bool& keepAlive = true, const size_t& maxStackSize = Default_Max_Stack_Size) : m_client(client) , m_max_stack(maxStackSize) , m_host(host) @@ -88,7 +88,7 @@ class ThingsBoardHttpSized { /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before allocating on the heap instead /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack - inline void setMaximumStackSize(const uint32_t& maxStackSize) { + inline void setMaximumStackSize(const size_t& maxStackSize) { m_max_stack = maxStackSize; } @@ -99,7 +99,7 @@ class ThingsBoardHttpSized { /// @param jsonSize Size of the data inside the source /// @return Whether sending the data was successful or not template - inline bool Send_Json(const char* topic, const TSource& source, const uint32_t& jsonSize) { + inline bool Send_Json(const char* topic, const TSource& source, const size_t& jsonSize) { // Check if allocating needed memory failed when trying to create the JsonObject, // if it did the method will return true. See https://arduinojson.org/v6/api/jsonvariant/isnull/ for more information. if (source.isNull()) { @@ -107,7 +107,7 @@ class ThingsBoardHttpSized { return false; } #if !THINGSBOARD_ENABLE_DYNAMIC - const uint32_t amount = source.size(); + const size_t amount = source.size(); if (MaxFieldsAmt < amount) { char message[Helper::detectSize(TOO_MANY_JSON_FIELDS, amount, MaxFieldsAmt)]; snprintf_P(message, sizeof(message), TOO_MANY_JSON_FIELDS, amount, MaxFieldsAmt); @@ -190,7 +190,7 @@ class ThingsBoardHttpSized { /// @param jsonSize Size of the data inside the source /// @return Whether sending the data was successful or not template - inline bool sendTelemetryJson(const TSource& source, const uint32_t& jsonSize) { + inline bool sendTelemetryJson(const TSource& source, const size_t& jsonSize) { return Send_Json(HTTP_TELEMETRY_TOPIC, source, jsonSize); } @@ -249,7 +249,7 @@ class ThingsBoardHttpSized { /// @param jsonSize Size of the data inside the source /// @return Whether sending the data was successful or not template - inline bool sendAttributeJSON(const TSource& source, const uint32_t& jsonSize) { + inline bool sendAttributeJSON(const TSource& source, const size_t& jsonSize) { return Send_Json(HTTP_ATTRIBUTES_TOPIC, source, jsonSize); } @@ -257,7 +257,7 @@ class ThingsBoardHttpSized { /// @brief Returns the maximum amount of bytes that we want to allocate on the stack, before the memory is allocated on the heap instead /// @return Maximum amount of bytes we want to allocate on the stack - inline const uint32_t& getMaximumStackSize() const { + inline const size_t& getMaximumStackSize() const { return m_max_stack; } @@ -328,7 +328,7 @@ class ThingsBoardHttpSized { // String are const char* and therefore stored as a pointer --> zero copy, meaning the size for the strings is 0 bytes, // Data structure size depends on the amount of key value pairs passed. // See https://arduinojson.org/v6/assistant/ for more information on the needed size for the JsonDocument - const uint32_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(data_count); + const size_t dataStructureMemoryUsage = JSON_OBJECT_SIZE(data_count); TBJsonDocument jsonBuffer(dataStructureMemoryUsage); #else StaticJsonDocument jsonBuffer; @@ -375,7 +375,7 @@ class ThingsBoardHttpSized { } IHTTP_Client& m_client; // HttpClient instance - uint32_t m_max_stack; // Maximum stack size we allocate at once on the stack. + size_t m_max_stack; // Maximum stack size we allocate at once on the stack. const char *m_host; // Host address we connect too const uint16_t m_port; // Port we connect over const char *m_token; // Access token used to connect with From 0d137cf918c2a97ff5b39a46f94620bfbd5d75dc Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 19:42:37 +0200 Subject: [PATCH 29/80] Fix breaking issues with newer version of mbedtls --- src/HashGenerator.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/HashGenerator.cpp b/src/HashGenerator.cpp index cb974a5b..b41a2953 100644 --- a/src/HashGenerator.cpp +++ b/src/HashGenerator.cpp @@ -19,7 +19,12 @@ HashGenerator::~HashGenerator(void) { } void HashGenerator::start(const mbedtls_md_type_t& type) { + // MBEDTLS Version 3 is a major breaking changes were accessing the internal structures requires the MBEDTLS_PRIVATE macro +#if MBEDTLS_VERSION_MAJOR < 3 if (m_ctx.hmac_ctx != nullptr && m_ctx.md_ctx != nullptr && m_ctx.md_info != nullptr) { +#else + if (m_ctx.MBEDTLS_PRIVATE(hmac_ctx) != nullptr && m_ctx.MBEDTLS_PRIVATE(md_ctx) != nullptr && m_ctx.MBEDTLS_PRIVATE(md_info) != nullptr) { +#endif mbedtls_md_free(&m_ctx); } // Initialize the context @@ -41,7 +46,12 @@ std::string HashGenerator::get_hash_string() { // Convert the hash value to a string std::stringstream ss; + // MBEDTLS Version 3 is a major breaking changes were accessing the internal structures requires the MBEDTLS_PRIVATE macro +#if MBEDTLS_VERSION_MAJOR < 3 for (size_t i = 0; i < mbedtls_md_get_size(m_ctx.md_info); i++) +#else + for (size_t i = 0; i < mbedtls_md_get_size(m_ctx.MBEDTLS_PRIVATE(md_info)); i++) +#endif ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(hash[i]); return ss.str(); } From eb655c2b8e3926d0345da82c5530d323897fd38d Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 19:47:00 +0200 Subject: [PATCH 30/80] Fix invalid callback signature --- src/IMQTT_Client.h | 4 ++-- src/ThingsBoard.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index a776a339..e99708a2 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -39,9 +39,9 @@ class IMQTT_Client { public: /// @brief Callback signature #if THINGSBOARD_ENABLE_STL - using function = std::function; + using function = std::function; #else - using function = void (*)(char *topic, uint8_t *payload, size_t length); + using function = void (*)(char *topic, uint8_t *payload, unsigned int length); #endif // THINGSBOARD_ENABLE_STL /// @brief Sets the callback that is called, if any message is received by the MQTT broker, including the topic string that the message was received over, diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 1aa35efd..96f47a3b 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -1895,7 +1895,7 @@ class ThingsBoardSized { /// @param topic Previously subscribed topic, we got the response over /// @param payload Payload that was sent over the cloud and received over the given topic /// @param length Total length of the received payload - inline void onMQTTMessage(char *topic, uint8_t *payload, size_t length) { + inline void onMQTTMessage(char *topic, uint8_t *payload, unsigned int length) { #if THINGSBOARD_ENABLE_DEBUG char message[JSON_STRING_SIZE(strlen(RECEIVE_MESSAGE)) + JSON_STRING_SIZE(strlen(topic))]; snprintf_P(message, sizeof(message), RECEIVE_MESSAGE, topic); @@ -1951,7 +1951,7 @@ class ThingsBoardSized { // To be able to forward event to an instance, rather than to a function, this pointer exists. static ThingsBoardSized *m_subscribedInstance; - static void onStaticMQTTMessage(char *topic, uint8_t *payload, size_t length) { + static void onStaticMQTTMessage(char *topic, uint8_t *payload, unsigned int length) { if (m_subscribedInstance == nullptr) { return; } From e8b63f090462ddaac5dded628fc88458bda3802d Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 19:52:23 +0200 Subject: [PATCH 31/80] Fix override issue with missing include --- src/IMQTT_Client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index e99708a2..715af0da 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -17,7 +17,7 @@ #if THINGSBOARD_ENABLE_STREAM_UTILS #include #endif // THINGSBOARD_ENABLE_STREAM_UTILS -#include +#include /// @brief MQTT Client interface that contains the method that a class that can be used to send and receive data over an MQTT connection should implement. From 8574dca864534c3b0de57d3bbcae35748f0479d4 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 19:54:42 +0200 Subject: [PATCH 32/80] Readd include --- src/IMQTT_Client.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index 715af0da..d9ae1a6b 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -17,6 +17,7 @@ #if THINGSBOARD_ENABLE_STREAM_UTILS #include #endif // THINGSBOARD_ENABLE_STREAM_UTILS +#include #include From f742c12dc76839caa85a58dd7362204a9276f540 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 10 Sep 2023 20:55:33 +0200 Subject: [PATCH 33/80] Fix typo in comment --- src/Arduino_HTTP_Client.h | 2 +- src/Arduino_MQTT_Client.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index 59e043c7..c88b52ee 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -16,7 +16,7 @@ #include "IHTTP_Client.h" -/// @brief HTTP Client interface imnplementation that uses the ArduinoHttpClient (https://github.com/arduino-libraries/ArduinoHttpClient), +/// @brief HTTP Client interface implementation that uses the ArduinoHttpClient (https://github.com/arduino-libraries/ArduinoHttpClient), /// under the hood to establish and communicate over a HTTP connection class Arduino_HTTP_Client : public IHTTP_Client { public: diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index 0ecc61bc..46a02137 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -16,7 +16,7 @@ #include -/// @brief MQTT Client interface imnplementation that uses the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), +/// @brief MQTT Client interface implementation that uses the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), /// under the hood to establish and communicate over a MQTT connection. The fork includes fixes to solve issues with using std::function callbacks for non ESP boards class Arduino_MQTT_Client : public IMQTT_Client { public: From 812dd39d51e4f076665dd1e460398b62927d9801 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:46:58 +0200 Subject: [PATCH 34/80] Add mqtt client implementation for esp idf --- src/Configuration.h | 16 +++ src/Espressif_MQTT_Client.cpp | 183 ++++++++++++++++++++++++++++++++++ src/Espressif_MQTT_Client.h | 80 +++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 src/Espressif_MQTT_Client.cpp create mode 100644 src/Espressif_MQTT_Client.h diff --git a/src/Configuration.h b/src/Configuration.h index 522dc965..1302cdec 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -60,6 +60,22 @@ # define THINGSBOARD_USE_ESP_TIMER 0 # endif +// Use the mqtt_client header internally for handling the sending and receiving of MQTT data, as long as the header exists, +// to allow users that do have the needed component to use the Espressif_MQTT_Client instead of only the Arduino_HTTP_Client +# ifdef __has_include +# if __has_include() +# ifndef THINGSBOARD_USE_ESP_MQTT +# define THINGSBOARD_USE_ESP_MQTT 1 +# endif +# else +# ifndef THINGSBOARD_USE_ESP_MQTT +# define THINGSBOARD_USE_ESP_MQTT 0 +# endif +# endif +# else +# define THINGSBOARD_USE_ESP_MQTT 0 +# endif + // Use the mbed_tls header internally for handling the creation of hashes from binary data, as long as the header exists, // because if it is already included we do not need to rely on and incude external lbiraries like Seeed_mbedtls.h, which implements the same features. # ifdef __has_include diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp new file mode 100644 index 00000000..271f1dc3 --- /dev/null +++ b/src/Espressif_MQTT_Client.cpp @@ -0,0 +1,183 @@ +// Header include. +#include "Espressif_MQTT_Client.h" + +#ifdef THINGSBOARD_USE_ESP_MQTT + +// Library include +#include + +constexpr char TAG[] = "MQTT"; +constexpr int MQTT_FAILURE_CODE = -1; + +Espressif_MQTT_Client *Espressif_MQTT_Client::m_instance = nullptr; + +Espressif_MQTT_Client::Espressif_MQTT_Client() : + m_received_data_callback(nullptr), + m_connected(false), + m_mqtt_configuration(), + m_mqtt_client() +{ + m_instance = this; +} + +void Espressif_MQTT_Client::set_server_certificate(const char *server_certificate_pem) { + // Because PEM format is expected for the server certificate we do not need to set the certificate_len, + // because the PEM format expects a null-terminated string. + m_mqtt_configuration.broker.verification.certificate = server_certificate_pem; +} + +void Espressif_MQTT_Client::set_callback(function cb) { + m_received_data_callback = cb; +} + +bool Espressif_MQTT_Client::set_buffer_size(const uint16_t& buffer_size) { + m_mqtt_configuration.buffer.size = buffer_size; + return true; +} + +uint16_t Espressif_MQTT_Client::get_buffer_size() { + return m_mqtt_configuration.buffer.size; +} + +void Espressif_MQTT_Client::set_server(const char *domain, const uint16_t& port) { + m_mqtt_configuration.broker.address.hostname = domain; + m_mqtt_configuration.broker.address.port = port; + // Decide transport depending on if a certificate was passed, because the set_server() method is called in the connect method meaning if the certificate has not been set yet, + // it is to late as we attempt to establish the connection in the connect() method which is called directly after this one. + const bool transport_over_sll = m_mqtt_configuration.broker.verification.certificate != nullptr; + const esp_mqtt_transport_t transport = (transport_over_sll ? esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_SSL : esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_TCP); + m_mqtt_configuration.broker.address.transport = transport; +} + +bool Espressif_MQTT_Client::connect(const char *client_id, const char *user_name, const char *password) { + m_mqtt_configuration.credentials.client_id = client_id; + m_mqtt_configuration.credentials.username = user_name; + m_mqtt_configuration.credentials.authentication.password = password; + + // The client is first initalized once the connect has actually been called, this is done because the passed setting are required for the client inizialitation structure, + // additionally before we attempt to connect with the client we have to ensure it is configued by then. + m_mqtt_client = esp_mqtt_client_init(&m_mqtt_configuration); + + // The last argument may be used to pass data to the event handler, in this example mqtt_event_handler. + ESP_ERROR_CHECK(esp_mqtt_client_register_event(m_mqtt_client, esp_mqtt_event_id_t::MQTT_EVENT_ANY, Espressif_MQTT_Client::static_mqtt_event_handler, nullptr)); + + ESP_ERROR_CHECK(esp_mqtt_client_start(m_mqtt_client)); + return true; +} + +void Espressif_MQTT_Client::disconnect() { + ESP_ERROR_CHECK(esp_mqtt_client_disconnect(m_mqtt_client)); +} + +bool Espressif_MQTT_Client::loop() { + // Unused because the esp mqtt client uses its own task to handle receiving and sending of data, therefore we do not need to do anything in the loop method. + // Because the loop method is meant for clients that do not have their own process method but instead rely on the upper level code calling a loop method to provide processsing time. + return m_connected; +} + +bool Espressif_MQTT_Client::publish(const char *topic, const uint8_t *payload, const size_t& length) { + // The blocking version esp_mqtt_client_publish() it is sent directly from the users task context. + // This way is used to send messages to the cloud, because like that no internal buffer has to be used to store the message until it should be sent, + // because all messages are sent with QoS level 0. If this is not wanted esp_mqtt_client_enqueue() could be used with store = true, + // to ensure the sending is done in the mqtt event context instead of the users task context. + // Allows to use the publish method without having to worry about any CPU overhead, so it can even be used in callbacks or high priority tasks, without starving other tasks. + const int message_id = esp_mqtt_client_publish(m_mqtt_client, topic, reinterpret_cast(payload), length, 0U, 0U); + return message_id != MQTT_FAILURE_CODE; +} + +bool Espressif_MQTT_Client::subscribe(const char *topic) { + const int message_id = esp_mqtt_client_subscribe(m_mqtt_client, topic, 0U); + return message_id != MQTT_FAILURE_CODE; +} + +bool Espressif_MQTT_Client::unsubscribe(const char *topic) { + const int message_id = esp_mqtt_client_unsubscribe(m_mqtt_client, topic); + return message_id != MQTT_FAILURE_CODE; +} + +bool Espressif_MQTT_Client::connected() { + return m_connected; +} + +void Espressif_MQTT_Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data) { + + ESP_LOGI(TAG, "Event dispatched from event loop base=%s, event_id=%i", base, static_cast::type>(event_id)); + esp_mqtt_event_handle_t event = static_cast(event_data); + esp_mqtt_client_handle_t client = event->client; + int msg_id; + + switch (event_id) { + case esp_mqtt_event_id_t::MQTT_EVENT_CONNECTED: + m_connected = true; + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + ESP_LOGI(TAG, " The client has successfully established a connection to the broker. The client is now ready to send and receive data"); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case esp_mqtt_event_id_t::MQTT_EVENT_DISCONNECTED: + m_connected = false; + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + ESP_LOGI(TAG, "The client has aborted the connection due to being unable to read or write data, e.g., because the server is unavailable"); + break; + case esp_mqtt_event_id_t::MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + ESP_LOGI(TAG, "The broker has acknowledged the client’s subscribe request. The event data contains the message ID of the subscribe message"); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case esp_mqtt_event_id_t::MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + ESP_LOGI(TAG, "The broker has acknowledged the client’s unsubscribe request. The event data contains the message ID of the unsubscribe message"); + break; + case esp_mqtt_event_id_t::MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + ESP_LOGI(TAG, "The broker has acknowledged the client’s publish message. This is only posted for QoS level 1 and 2, as level 0 does not use acknowledgements. The event data contains the message ID of the publish message."); + break; + case esp_mqtt_event_id_t::MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + ESP_LOGI(TAG, "The client has received a publish message. The event data contains: message ID, name of the topic it was published to, received data and its length. For data that exceeds the internal buffer, multiple MQTT_EVENT_DATA events are posted and current_data_offset and total_data_len from event data updated to keep track of the fragmented message"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + if (m_received_data_callback != nullptr) { + m_received_data_callback(event->topic, reinterpret_cast(event->data), event->data_len); + } + break; + case esp_mqtt_event_id_t::MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + ESP_LOGI(TAG, "The client has encountered an error. The field error_handle in the event data contains error_type that can be used to identify the error. The type of error determines which parts of the error_handle struct is filled"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { + ESP_LOGI(TAG, "Last error code reported from esp-tls: 0x%x", event->error_handle->esp_tls_last_esp_err); + ESP_LOGI(TAG, "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err); + ESP_LOGI(TAG, "Last captured errno : %d (%s)", event->error_handle->esp_transport_sock_errno, + strerror(event->error_handle->esp_transport_sock_errno)); + } else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) { + ESP_LOGI(TAG, "Connection refused error: 0x%x", event->error_handle->connect_return_code); + } else { + ESP_LOGW(TAG, "Unknown error type: 0x%x", event->error_handle->error_type); + } + break; + case esp_mqtt_event_id_t::MQTT_EVENT_BEFORE_CONNECT: + ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT"); + ESP_LOGI(TAG, "The client is initialized and about to start connecting to the broker"); + break; + default: + ESP_LOGI(TAG, "Other MQTT event id:%i", static_cast::type>(event_id)); + break; + } +} + +void Espressif_MQTT_Client::static_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { + if (m_instance == nullptr) { + return; + } + + m_instance->mqtt_event_handler(handler_args, base, static_cast(event_id), event_data); +} + +#endif // THINGSBOARD_USE_ESP_MQTT diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h new file mode 100644 index 00000000..ae7a95bb --- /dev/null +++ b/src/Espressif_MQTT_Client.h @@ -0,0 +1,80 @@ +/* + Espressif_MQTT_Client.h - Library API for sending data to the ThingsBoard + Based on PubSub MQTT library. + Created by Olender M. Oct 2018. + Released into the public domain. +*/ +#ifndef Espressif_MQTT_Client_h +#define Espressif_MQTT_Client_h + +// Local include. +#include "Configuration.h" + +#ifdef THINGSBOARD_USE_ESP_MQTT + +// Local includes. +#include "IMQTT_Client.h" + +// Library includes. +#include + +/// @brief MQTT Client interface implementation that uses the offical ESP MQTT client from Espressif (https://github.com/espressif/esp-mqtt), +/// under the hood to establish and communicate over a MQTT connection. +/// Documentation about the specific use and caviates of the ESP MQTT client can be found here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html. +class Espressif_MQTT_Client : public IMQTT_Client { + public: + /// @brief Constructs a IMQTT_Client implementation which creates and empty esp_mqtt_client_config_t, which then has to be configured with the other methods in the class. + Espressif_MQTT_Client(); + + /// @brief Configured the server certificate, which allows to connected to the MQTT broker over a secure TLS / SSL conenction instead of the default unencrypted channel. + /// The first option is recommended if relevant data is sent or if the client receives and handles Remote Procedure Calls or Shared Attribute Update Callbacks from the server, + /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. + /// However if Over the Air udpates are enabled secure communication should definetly be enabled, because if that is not done a 3rd party might impersonate the server sending a malicious payload, + /// which is then flashed onto the device instead of the real firmware. Which depeding on the payload might even be able to destroy the device or make it otherwise unusable. + /// See https://stackoverflow.blog/2020/12/14/security-considerations-for-ota-software-updates-for-iot-gateway-devices/ for more information on the aforementioned security risk + /// @param server_certificate_pem Null-terminated string containg the root certificate in PEM format, of the server we are attempting to send to and receive MQTT data from + void set_server_certificate(const char *server_certificate_pem); + + void set_callback(function cb) override; + + bool set_buffer_size(const uint16_t& buffer_size) override; + + uint16_t get_buffer_size() override; + + void set_server(const char *domain, const uint16_t& port) override; + + bool connect(const char *client_id, const char *user_name, const char *password) override; + + void disconnect() override; + + bool loop() override; + + bool publish(const char *topic, const uint8_t *payload, const size_t& length) override; + + bool subscribe(const char *topic) override; + + bool unsubscribe(const char *topic) override; + + bool connected() override; + + /// @brief Event handler registered to receive MQTT events. Is called by the MQTT client event loop, whenever a new event occurs. + /// @param handler_args User data registered to the event + /// @param base Event base for the handler + /// @param event_id The id for the received event + /// @param event_data The data for the event, esp_mqtt_event_handle_t + void mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data); + +private: + function m_received_data_callback; + bool m_connected; + esp_mqtt_client_config_t m_mqtt_configuration; + esp_mqtt_client_handle_t m_mqtt_client; + + static Espressif_MQTT_Client *m_instance; + + static void static_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); +}; + +#endif // THINGSBOARD_USE_ESP_MQTT + +#endif // Espressif_MQTT_Client_h From d6750a9641eba3ab483c905405622cc004f3a987 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 10:07:56 +0200 Subject: [PATCH 35/80] Replace #ifdef with #if --- src/Configuration.h | 2 +- src/Espressif_MQTT_Client.cpp | 2 +- src/Espressif_MQTT_Client.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Configuration.h b/src/Configuration.h index 1302cdec..60e0fede 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -63,7 +63,7 @@ // Use the mqtt_client header internally for handling the sending and receiving of MQTT data, as long as the header exists, // to allow users that do have the needed component to use the Espressif_MQTT_Client instead of only the Arduino_HTTP_Client # ifdef __has_include -# if __has_include() +# if __has_include() && __has_include() # ifndef THINGSBOARD_USE_ESP_MQTT # define THINGSBOARD_USE_ESP_MQTT 1 # endif diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index 271f1dc3..c80c9657 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -1,7 +1,7 @@ // Header include. #include "Espressif_MQTT_Client.h" -#ifdef THINGSBOARD_USE_ESP_MQTT +#if THINGSBOARD_USE_ESP_MQTT // Library include #include diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h index ae7a95bb..c7dc87b1 100644 --- a/src/Espressif_MQTT_Client.h +++ b/src/Espressif_MQTT_Client.h @@ -10,7 +10,7 @@ // Local include. #include "Configuration.h" -#ifdef THINGSBOARD_USE_ESP_MQTT +#if THINGSBOARD_USE_ESP_MQTT // Local includes. #include "IMQTT_Client.h" From 0d8f924aeb4a035fc87a32ba5d01c791613c844b Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:49:41 +0200 Subject: [PATCH 36/80] Add espressif example --- .../0000-arduino_send_telemetry/README.md | 4 + examples/0001-arduino_send_batch/README.md | 4 + examples/0002-arduino_rpc/README.md | 4 + .../0003-esp8266_esp32_send_data/README.md | 4 + .../README.md | 4 + .../README.md | 4 + .../README.md | 4 + .../0007-esp8266_esp32_claim_device/README.md | 4 + .../README.md | 4 + .../README.md | 4 + examples/0010-esp8266_esp32_rpc/README.md | 4 + .../README.md | 4 + .../README.md | 4 + .../0013-esp8266_esp32_request_rpc/README.md | 4 + .../0014-espressif_esp32_send_data.ino | 167 ++++++++++++++++++ .../0014-espressif_esp32_send_data/README.md | 20 +++ src/Espressif_MQTT_Client.cpp | 131 +++++++------- src/Espressif_MQTT_Client.h | 25 ++- 18 files changed, 331 insertions(+), 68 deletions(-) create mode 100644 examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.ino create mode 100644 examples/0014-espressif_esp32_send_data/README.md diff --git a/examples/0000-arduino_send_telemetry/README.md b/examples/0000-arduino_send_telemetry/README.md index 7d118bc3..c642a914 100644 --- a/examples/0000-arduino_send_telemetry/README.md +++ b/examples/0000-arduino_send_telemetry/README.md @@ -6,6 +6,10 @@ | Arduino Uno | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Telemetry](https://thingsboard.io/docs/user-guide/telemetry/) diff --git a/examples/0001-arduino_send_batch/README.md b/examples/0001-arduino_send_batch/README.md index 3186dc04..f37cd89a 100644 --- a/examples/0001-arduino_send_batch/README.md +++ b/examples/0001-arduino_send_batch/README.md @@ -6,6 +6,10 @@ | Arduino Uno | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Telemetry](https://thingsboard.io/docs/user-guide/telemetry/) [Attributes](https://thingsboard.io/docs/user-guide/attributes/) diff --git a/examples/0002-arduino_rpc/README.md b/examples/0002-arduino_rpc/README.md index 4297ced9..e2ff39b5 100644 --- a/examples/0002-arduino_rpc/README.md +++ b/examples/0002-arduino_rpc/README.md @@ -6,6 +6,10 @@ | Arduino Uno | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Server-side Remote Procedure Call](https://thingsboard.io/docs/user-guide/rpc/#server-side-rpc) diff --git a/examples/0003-esp8266_esp32_send_data/README.md b/examples/0003-esp8266_esp32_send_data/README.md index 377043b3..37ea9864 100644 --- a/examples/0003-esp8266_esp32_send_data/README.md +++ b/examples/0003-esp8266_esp32_send_data/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Telemetry](https://thingsboard.io/docs/user-guide/telemetry/) [Attributes](https://thingsboard.io/docs/user-guide/attributes/) diff --git a/examples/0004-arduino-sim900_send_telemetry/README.md b/examples/0004-arduino-sim900_send_telemetry/README.md index 72f9f172..b9d6d13f 100644 --- a/examples/0004-arduino-sim900_send_telemetry/README.md +++ b/examples/0004-arduino-sim900_send_telemetry/README.md @@ -6,6 +6,10 @@ | Arduino Uno | | GSM Modem (SIM9000) | +## Framework + +Arduino + ## ThingsBoard API [Telemetry](https://thingsboard.io/docs/user-guide/telemetry/) diff --git a/examples/0005-arduino-sim900_send_telemetry_http/README.md b/examples/0005-arduino-sim900_send_telemetry_http/README.md index 329a523e..ad87486d 100644 --- a/examples/0005-arduino-sim900_send_telemetry_http/README.md +++ b/examples/0005-arduino-sim900_send_telemetry_http/README.md @@ -6,6 +6,10 @@ | Arduino Uno | | GSM Modem (SIM9000) | +## Framework + +Arduino + ## ThingsBoard API [Telemetry](https://thingsboard.io/docs/user-guide/telemetry/) diff --git a/examples/0006-esp8266_esp32_process_shared_attribute_update/README.md b/examples/0006-esp8266_esp32_process_shared_attribute_update/README.md index 77164841..cc5469d7 100644 --- a/examples/0006-esp8266_esp32_process_shared_attribute_update/README.md +++ b/examples/0006-esp8266_esp32_process_shared_attribute_update/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Shared Attributes](https://thingsboard.io/docs/user-guide/attributes/#shared-attributes) diff --git a/examples/0007-esp8266_esp32_claim_device/README.md b/examples/0007-esp8266_esp32_claim_device/README.md index 4ea7a2a9..fc37f6f8 100644 --- a/examples/0007-esp8266_esp32_claim_device/README.md +++ b/examples/0007-esp8266_esp32_claim_device/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Claiming devices](https://thingsboard.io/docs/user-guide/claiming-devices/) diff --git a/examples/0008-esp8266_esp32_provision_device/README.md b/examples/0008-esp8266_esp32_provision_device/README.md index 924caa3f..b25ef156 100644 --- a/examples/0008-esp8266_esp32_provision_device/README.md +++ b/examples/0008-esp8266_esp32_provision_device/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Provisioning devices](https://thingsboard.io/docs/user-guide/device-provisioning/) diff --git a/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md b/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md index 0266e00a..f8c1872c 100644 --- a/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md +++ b/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [OTA Firmware updates](https://thingsboard.io/docs/user-guide/ota-updates/) [Shared Attributes](https://thingsboard.io/docs/user-guide/attributes/#shared-attributes) diff --git a/examples/0010-esp8266_esp32_rpc/README.md b/examples/0010-esp8266_esp32_rpc/README.md index 52660b20..f21c63f8 100644 --- a/examples/0010-esp8266_esp32_rpc/README.md +++ b/examples/0010-esp8266_esp32_rpc/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Server-side Remote Procedure Call](https://thingsboard.io/docs/user-guide/rpc/#server-side-rpc) diff --git a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md index 2cc59fed..d8211638 100644 --- a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md +++ b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [OTA Firmware updates](https://thingsboard.io/docs/user-guide/ota-updates/) [Shared Attributes](https://thingsboard.io/docs/user-guide/attributes/#shared-attributes) diff --git a/examples/0012-esp8266_esp32_request_shared_attribute/README.md b/examples/0012-esp8266_esp32_request_shared_attribute/README.md index d99b049d..6de66a98 100644 --- a/examples/0012-esp8266_esp32_request_shared_attribute/README.md +++ b/examples/0012-esp8266_esp32_request_shared_attribute/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Shared Attributes](https://thingsboard.io/docs/user-guide/attributes/#shared-attributes) [Client-Side Attributes](https://thingsboard.io/docs/user-guide/attributes/#client-side-attributes) diff --git a/examples/0013-esp8266_esp32_request_rpc/README.md b/examples/0013-esp8266_esp32_request_rpc/README.md index 58bd2b53..17283540 100644 --- a/examples/0013-esp8266_esp32_request_rpc/README.md +++ b/examples/0013-esp8266_esp32_request_rpc/README.md @@ -6,6 +6,10 @@ | ESP32 | | ESP8266 | +## Framework + +Arduino + ## ThingsBoard API [Client-side Remote Procedure Call](https://thingsboard.io/docs/user-guide/rpc/#client-side-rpc) diff --git a/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.ino b/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.ino new file mode 100644 index 00000000..f4874802 --- /dev/null +++ b/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.ino @@ -0,0 +1,167 @@ +#include +#include +#include +#include + + +// Whether the given script is using encryption or not, +// generally recommended as it increases security (communication with the server is not in clear text anymore), +// it does come with an overhead tough as having an encrypted session requires a lot of memory, +// which might not be avaialable on lower end devices. +#define ENCRYPTED false + + +#include +#include + + +// Examples using arduino used PROGMEM to save constants into flash memory, +// this is not needed when using Espressif IDF because per default +// all read only variables will be saved into DROM (flash memory). +// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/memory-types.html#drom-data-stored-in-flash +// for more information about the aforementioned feature +constexpr char WIFI_SSID[] = "YOUR_WIFI_SSID"; +constexpr char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; + +// See https://thingsboard.io/docs/getting-started-guides/helloworld/ +// to understand how to obtain an access token +constexpr char TOKEN[] = "YOUR_DEVICE_ACCESS_TOKEN"; + +// Thingsboard we want to establish a connection too +constexpr char THINGSBOARD_SERVER[] = "demo.thingsboard.io"; + +// MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port, +// whereas 8883 would be the default encrypted SSL MQTT port +#if ENCRYPTED +constexpr uint16_t THINGSBOARD_PORT = 8883U; +#else +constexpr uint16_t THINGSBOARD_PORT = 1883U; +#endif + +// Maximum size packets will ever be sent or received by the underlying MQTT client, +// if the size is to small messages might not be sent or received messages will be discarded +constexpr uint16_t MAX_MESSAGE_SIZE = 128U; + +#if ENCRYPTED +// See https://comodosslstore.com/resources/what-is-a-root-ca-certificate-and-how-do-i-download-it/ +// on how to get the root certificate of the server we want to communicate with, +// this is needed to establish a secure connection and changes depending on the website. +constexpr char ROOT_CERT[] = R"(-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)"; +#endif + +constexpr char TEMPERATURE_KEY[] = "temperature"; +constexpr char HUMIDITY_KEY[] = "humidity"; + + +// Initalize the Mqtt client instance +Espressif_MQTT_Client mqttClient; +// Initialize ThingsBoard instance with the maximum needed buffer size +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); + +// Status for successfully connecting to the given WiFi +bool wifi_connected = false; + + +/// @brief Callback method that is called if we got an ip address from the connected WiFi meaning we successfully established a connection +/// @param event_handler_arg User data registered to the event +/// @param event_base Event base for the handler +/// @param event_id The id for the received event +/// @param event_data The data for the event, esp_event_handler_t +void on_got_ip(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + wifi_connected = true; +} + +/// @brief Initalizes WiFi connection, +// will endlessly delay until a connection has been successfully established +void InitWiFi() { + const wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config)); + + esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_WIFI_STA(); + esp_netif_t *netif = esp_netif_new(&netif_config); + assert(netif); + + ESP_ERROR_CHECK(esp_netif_attach_wifi_station(netif)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ip_event_t::IP_EVENT_STA_GOT_IP, &on_got_ip, NULL)); + ESP_ERROR_CHECK(esp_wifi_set_default_wifi_sta_handlers()); + ESP_ERROR_CHECK(esp_wifi_set_storage(wifi_storage_t::WIFI_STORAGE_RAM)); + + wifi_config_t wifi_config; + memset(&wifi_config, 0, sizeof(wifi_config)); + strncpy(reinterpret_cast(wifi_config.sta.ssid), WIFI_SSID, strlen(WIFI_SSID) + 1); + strncpy(reinterpret_cast(wifi_config.sta.password), WIFI_PASSWORD, strlen(WIFI_PASSWORD) + 1); + + ESP_LOGI("MAIN", "Connecting to %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_mode(wifi_mode_t::WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(wifi_interface_t::WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); +} + +extern "C" void app_main() { + ESP_LOGI("MAIN", "[APP] Startup.."); + ESP_LOGI("MAIN", "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); + ESP_LOGI("MAIN", "[APP] IDF version: %s", esp_get_idf_version()); + + esp_log_level_set("*", ESP_LOG_INFO); + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + InitWiFi(); + +#if ENCRYPTED + mqttClient.set_server_certificate(ROOT_CERT); +#endif // ENCRYPTED + + for (;;) { + // Wait until we connected to WiFi + if (!wifi_connected) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + continue; + } + + if (!tb.connected()) { + tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT); + } + + tb.sendTelemetryData(TEMPERATURE_KEY, esp_random()); + tb.sendTelemetryData(HUMIDITY_KEY, esp_random()); + + tb.loop(); + + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} diff --git a/examples/0014-espressif_esp32_send_data/README.md b/examples/0014-espressif_esp32_send_data/README.md new file mode 100644 index 00000000..16cd5b43 --- /dev/null +++ b/examples/0014-espressif_esp32_send_data/README.md @@ -0,0 +1,20 @@ +# Sending telemetry / attribute data + +## Devices +| Supported Devices | +|-------------------| +| ESP32 | + +## Framework + +Espressif IDF + +## ThingsBoard API +[Telemetry](https://thingsboard.io/docs/user-guide/telemetry/) +[Attributes](https://thingsboard.io/docs/user-guide/attributes/) + +## Feature +Allows uploading telemetry values to the cloud, as well as attributes. +Telemetry values keep track of their previous values meaning we can draw graphs with them. +Meant for values which change over time and where a history might be useful (temperature, humidity, ...) +Whereas attributes are meant for data, which does not require a history and is more seldom updated (version, settings, ...) diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index c80c9657..94bcb995 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -3,11 +3,7 @@ #if THINGSBOARD_USE_ESP_MQTT -// Library include -#include - -constexpr char TAG[] = "MQTT"; -constexpr int MQTT_FAILURE_CODE = -1; +constexpr int MQTT_FAILURE_MESSAGE_ID = -1; Espressif_MQTT_Client *Espressif_MQTT_Client::m_instance = nullptr; @@ -15,15 +11,20 @@ Espressif_MQTT_Client::Espressif_MQTT_Client() : m_received_data_callback(nullptr), m_connected(false), m_mqtt_configuration(), - m_mqtt_client() + m_mqtt_client(nullptr) { m_instance = this; } void Espressif_MQTT_Client::set_server_certificate(const char *server_certificate_pem) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.cert_pem = server_certificate_pem; +#else // Because PEM format is expected for the server certificate we do not need to set the certificate_len, // because the PEM format expects a null-terminated string. m_mqtt_configuration.broker.verification.certificate = server_certificate_pem; +#endif // ESP_IDF_VERSION_MAJOR < 5 } void Espressif_MQTT_Client::set_callback(function cb) { @@ -31,42 +32,80 @@ void Espressif_MQTT_Client::set_callback(function cb) { } bool Espressif_MQTT_Client::set_buffer_size(const uint16_t& buffer_size) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.buffer_size = buffer_size; +#else m_mqtt_configuration.buffer.size = buffer_size; - return true; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); } uint16_t Espressif_MQTT_Client::get_buffer_size() { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + return m_mqtt_configuration.buffer_size; +#else return m_mqtt_configuration.buffer.size; +#endif // ESP_IDF_VERSION_MAJOR < 5 } void Espressif_MQTT_Client::set_server(const char *domain, const uint16_t& port) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.host = domain; + m_mqtt_configuration.port = port; + // Decide transport depending on if a certificate was passed, because the set_server() method is called in the connect method meaning if the certificate has not been set yet, + // it is to late as we attempt to establish the connection in the connect() method which is called directly after this one. + const bool transport_over_sll = m_mqtt_configuration.cert_pem != nullptr; +#else m_mqtt_configuration.broker.address.hostname = domain; m_mqtt_configuration.broker.address.port = port; // Decide transport depending on if a certificate was passed, because the set_server() method is called in the connect method meaning if the certificate has not been set yet, // it is to late as we attempt to establish the connection in the connect() method which is called directly after this one. const bool transport_over_sll = m_mqtt_configuration.broker.verification.certificate != nullptr; +#endif // ESP_IDF_VERSION_MAJOR < 5 + const esp_mqtt_transport_t transport = (transport_over_sll ? esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_SSL : esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_TCP); + + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.transport = transport; +#else m_mqtt_configuration.broker.address.transport = transport; +#endif // ESP_IDF_VERSION_MAJOR < 5 } bool Espressif_MQTT_Client::connect(const char *client_id, const char *user_name, const char *password) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.client_id = client_id; + m_mqtt_configuration.username = user_name; + m_mqtt_configuration.password = password; +#else m_mqtt_configuration.credentials.client_id = client_id; m_mqtt_configuration.credentials.username = user_name; m_mqtt_configuration.credentials.authentication.password = password; +#endif // ESP_IDF_VERSION_MAJOR < 5 // The client is first initalized once the connect has actually been called, this is done because the passed setting are required for the client inizialitation structure, // additionally before we attempt to connect with the client we have to ensure it is configued by then. m_mqtt_client = esp_mqtt_client_init(&m_mqtt_configuration); - // The last argument may be used to pass data to the event handler, in this example mqtt_event_handler. - ESP_ERROR_CHECK(esp_mqtt_client_register_event(m_mqtt_client, esp_mqtt_event_id_t::MQTT_EVENT_ANY, Espressif_MQTT_Client::static_mqtt_event_handler, nullptr)); + // The last argument may be used to pass data to the event handler, here that would be the static_mqtt_event_handler. But for our use case this is not needed, + // because the static_mqtt_event_handler calls a private method on this class again anyway, meaning we already have access to all private member variables that are required + esp_err_t error = esp_mqtt_client_register_event(m_mqtt_client, esp_mqtt_event_id_t::MQTT_EVENT_ANY, Espressif_MQTT_Client::static_mqtt_event_handler, nullptr); + + if (error != ESP_OK) { + return false; + } - ESP_ERROR_CHECK(esp_mqtt_client_start(m_mqtt_client)); - return true; + error = esp_mqtt_client_start(m_mqtt_client); + return error == ESP_OK; } void Espressif_MQTT_Client::disconnect() { - ESP_ERROR_CHECK(esp_mqtt_client_disconnect(m_mqtt_client)); + (void)esp_mqtt_client_disconnect(m_mqtt_client); } bool Espressif_MQTT_Client::loop() { @@ -82,92 +121,66 @@ bool Espressif_MQTT_Client::publish(const char *topic, const uint8_t *payload, c // to ensure the sending is done in the mqtt event context instead of the users task context. // Allows to use the publish method without having to worry about any CPU overhead, so it can even be used in callbacks or high priority tasks, without starving other tasks. const int message_id = esp_mqtt_client_publish(m_mqtt_client, topic, reinterpret_cast(payload), length, 0U, 0U); - return message_id != MQTT_FAILURE_CODE; + return message_id != MQTT_FAILURE_MESSAGE_ID; } bool Espressif_MQTT_Client::subscribe(const char *topic) { const int message_id = esp_mqtt_client_subscribe(m_mqtt_client, topic, 0U); - return message_id != MQTT_FAILURE_CODE; + return message_id != MQTT_FAILURE_MESSAGE_ID; } bool Espressif_MQTT_Client::unsubscribe(const char *topic) { const int message_id = esp_mqtt_client_unsubscribe(m_mqtt_client, topic); - return message_id != MQTT_FAILURE_CODE; + return message_id != MQTT_FAILURE_MESSAGE_ID; } bool Espressif_MQTT_Client::connected() { return m_connected; } -void Espressif_MQTT_Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data) { +bool Espressif_MQTT_Client::update_configuration() { + // Check if the client has been initalized, because if it did not the value should still be nullptr + // and updating the config makes no sense because the changed settings will be applied anyway when the client is first intialized + if (m_mqtt_client == nullptr) { + return true; + } + + const esp_err_t error = esp_mqtt_set_config(m_mqtt_client, &m_mqtt_configuration); + return error == ESP_OK; +} - ESP_LOGI(TAG, "Event dispatched from event loop base=%s, event_id=%i", base, static_cast::type>(event_id)); - esp_mqtt_event_handle_t event = static_cast(event_data); - esp_mqtt_client_handle_t client = event->client; - int msg_id; +void Espressif_MQTT_Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data) { + const esp_mqtt_event_handle_t event = static_cast(event_data); switch (event_id) { case esp_mqtt_event_id_t::MQTT_EVENT_CONNECTED: m_connected = true; - ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); - ESP_LOGI(TAG, " The client has successfully established a connection to the broker. The client is now ready to send and receive data"); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); - ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); - ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); - ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; case esp_mqtt_event_id_t::MQTT_EVENT_DISCONNECTED: m_connected = false; - ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); - ESP_LOGI(TAG, "The client has aborted the connection due to being unable to read or write data, e.g., because the server is unavailable"); break; case esp_mqtt_event_id_t::MQTT_EVENT_SUBSCRIBED: - ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); - ESP_LOGI(TAG, "The broker has acknowledged the client’s subscribe request. The event data contains the message ID of the subscribe message"); - msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); - ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + // Nothing to do break; case esp_mqtt_event_id_t::MQTT_EVENT_UNSUBSCRIBED: - ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); - ESP_LOGI(TAG, "The broker has acknowledged the client’s unsubscribe request. The event data contains the message ID of the unsubscribe message"); + // Nothing to do break; case esp_mqtt_event_id_t::MQTT_EVENT_PUBLISHED: - ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); - ESP_LOGI(TAG, "The broker has acknowledged the client’s publish message. This is only posted for QoS level 1 and 2, as level 0 does not use acknowledgements. The event data contains the message ID of the publish message."); + // Nothing to do break; case esp_mqtt_event_id_t::MQTT_EVENT_DATA: - ESP_LOGI(TAG, "MQTT_EVENT_DATA"); - ESP_LOGI(TAG, "The client has received a publish message. The event data contains: message ID, name of the topic it was published to, received data and its length. For data that exceeds the internal buffer, multiple MQTT_EVENT_DATA events are posted and current_data_offset and total_data_len from event data updated to keep track of the fragmented message"); - printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); - printf("DATA=%.*s\r\n", event->data_len, event->data); if (m_received_data_callback != nullptr) { m_received_data_callback(event->topic, reinterpret_cast(event->data), event->data_len); } break; case esp_mqtt_event_id_t::MQTT_EVENT_ERROR: - ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); - ESP_LOGI(TAG, "The client has encountered an error. The field error_handle in the event data contains error_type that can be used to identify the error. The type of error determines which parts of the error_handle struct is filled"); - if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { - ESP_LOGI(TAG, "Last error code reported from esp-tls: 0x%x", event->error_handle->esp_tls_last_esp_err); - ESP_LOGI(TAG, "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err); - ESP_LOGI(TAG, "Last captured errno : %d (%s)", event->error_handle->esp_transport_sock_errno, - strerror(event->error_handle->esp_transport_sock_errno)); - } else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) { - ESP_LOGI(TAG, "Connection refused error: 0x%x", event->error_handle->connect_return_code); - } else { - ESP_LOGW(TAG, "Unknown error type: 0x%x", event->error_handle->error_type); - } + // Nothing to do break; case esp_mqtt_event_id_t::MQTT_EVENT_BEFORE_CONNECT: - ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT"); - ESP_LOGI(TAG, "The client is initialized and about to start connecting to the broker"); + // Nothing to do break; default: - ESP_LOGI(TAG, "Other MQTT event id:%i", static_cast::type>(event_id)); + // Nothing to do break; } } diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h index c7dc87b1..9ee98231 100644 --- a/src/Espressif_MQTT_Client.h +++ b/src/Espressif_MQTT_Client.h @@ -19,7 +19,8 @@ #include /// @brief MQTT Client interface implementation that uses the offical ESP MQTT client from Espressif (https://github.com/espressif/esp-mqtt), -/// under the hood to establish and communicate over a MQTT connection. +/// under the hood to establish and communicate over a MQTT connection. This component works with both Espressif IDF v4.X and v5.X, meaning it is version idependent, this is the case +/// because depending on the used version the implementation automatically adjusts to still initalize the client correctly. /// Documentation about the specific use and caviates of the ESP MQTT client can be found here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html. class Espressif_MQTT_Client : public IMQTT_Client { public: @@ -27,7 +28,8 @@ class Espressif_MQTT_Client : public IMQTT_Client { Espressif_MQTT_Client(); /// @brief Configured the server certificate, which allows to connected to the MQTT broker over a secure TLS / SSL conenction instead of the default unencrypted channel. - /// The first option is recommended if relevant data is sent or if the client receives and handles Remote Procedure Calls or Shared Attribute Update Callbacks from the server, + /// Has to be called before initally calling connect() on the client to ensure the certificate is set before the connection is established, if that is not done the connection will not be encrypted. + /// Encryption is recommended if relevant data is sent or if the client receives and handles Remote Procedure Calls or Shared Attribute Update Callbacks from the server, /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. /// However if Over the Air udpates are enabled secure communication should definetly be enabled, because if that is not done a 3rd party might impersonate the server sending a malicious payload, /// which is then flashed onto the device instead of the real firmware. Which depeding on the payload might even be able to destroy the device or make it otherwise unusable. @@ -57,13 +59,6 @@ class Espressif_MQTT_Client : public IMQTT_Client { bool connected() override; - /// @brief Event handler registered to receive MQTT events. Is called by the MQTT client event loop, whenever a new event occurs. - /// @param handler_args User data registered to the event - /// @param base Event base for the handler - /// @param event_id The id for the received event - /// @param event_data The data for the event, esp_mqtt_event_handle_t - void mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data); - private: function m_received_data_callback; bool m_connected; @@ -72,6 +67,18 @@ class Espressif_MQTT_Client : public IMQTT_Client { static Espressif_MQTT_Client *m_instance; + /// @brief Is internalyl used to allow changes to the underlying configuration of the esp_mqtt_client_handle_t after it has connected, + /// to for example increase the buffer size or increase the timeouts or stack size + /// @return Whether updating the configuration with the changed settings was successfull or not. + bool update_configuration(); + + /// @brief Event handler registered to receive MQTT events. Is called by the MQTT client event loop, whenever a new event occurs + /// @param handler_args User data registered to the event + /// @param base Event base for the handler + /// @param event_id The id for the received event + /// @param event_data The data for the event, esp_mqtt_event_handle_t + void mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data); + static void static_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); }; From 909474be55afc56964d84aea78c48adcb757d6ea Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:29:09 +0200 Subject: [PATCH 37/80] Add documentation on supported frameworks --- README.md | 10 +++++++++- src/Configuration.h | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6696c430..a9dc216a 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,19 @@ Remaining features have to be implemented by hand with the `sendGetRequest` or ` Example implementations for all base features, mentioned above, can be found in the `examples` folder. See the according `README.md`, to see which boards are supported and which functionality the example shows. +## Supported Frameworks + +`ThingsBoardArduinoSDK` does not directly depend on any specific `MQTT Client` or `HTTP Client` implementation, instead any implementation of the `IMQTT_Client` or `IHTTP Client` can be used. Because there are no further dependencies on `Arduino`, besides the client that communicates it allows us to use this library with `Arduino`, when using the `Arduino_MQTT_Client` or with `Espressif IDF` when using the `Espressif_MQTT_Client`. + +Example usage for `Espressif` can be found in the `examples/0014-espressif_esp32_send_data` folder, all other code portions can be implemented the same way only initialization of the needed dependencies is slightly different. Meaning internal call to `ThingsBoard` works the same on both `Espressif` and `Arduino`. + +This is also the case, because the only always used dependency that is remaining, is [`ArduinoJson`](https://arduinojson.org/), which despite its name does not require any `Arduino` component. + ## Troubleshooting ### No PROGMEM support causing crashes -If the device is crashing with an `Exception` especially `Exception (3)`, more specifically `LoadStoreError` or `LoadStoreErrorCause` this might be because, all constant variables are per default in flash memory to decrease the memory footprint of the library, if the libraries used or the board itself don't support `PROGMEM`. This can cause crashes to mitigate that simply add a `#define THINGSBOARD_ENABLE_PROGMEM 0` before including the ThingsBoard header file. +If the device is crashing with an `Exception` especially `Exception (3)`, more specifically `LoadStoreError` or `LoadStoreErrorCause` this might be because, all constant variables are per default in flash memory to decrease the memory footprint of the library, if the libraries used or the board itself don't support `PROGMEM`. This can cause crashes to mitigate that simply add a `#define THINGSBOARD_ENABLE_PROGMEM 0` before including the `ThingsBoard` header file. ```c++ // If not set otherwise the value is 1 per default if the pgmspace include exists, diff --git a/src/Configuration.h b/src/Configuration.h index 60e0fede..dc22870c 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -61,9 +61,9 @@ # endif // Use the mqtt_client header internally for handling the sending and receiving of MQTT data, as long as the header exists, -// to allow users that do have the needed component to use the Espressif_MQTT_Client instead of only the Arduino_HTTP_Client +// to allow users that do have the needed component to use the Espressif_MQTT_Client instead of only the Arduino_MQTT_Client # ifdef __has_include -# if __has_include() && __has_include() +# if __has_include() # ifndef THINGSBOARD_USE_ESP_MQTT # define THINGSBOARD_USE_ESP_MQTT 1 # endif From a8c2b1418899837604ef3506cc39ecdb6ec91963 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 21:20:49 +0200 Subject: [PATCH 38/80] Replace typo ifdef with if --- src/Helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Helper.h b/src/Helper.h index f56d14a1..ba0aae97 100644 --- a/src/Helper.h +++ b/src/Helper.h @@ -13,7 +13,7 @@ // Library include. #include #include -#ifdef THINGSBOARD_ENABLE_STL +#if THINGSBOARD_ENABLE_STL #include #endif // THINGSBOARD_ENABLE_STL From 73dc106a0a9ab73e1f135c728100b8a9e225d4da Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:09:28 +0200 Subject: [PATCH 39/80] Make project buildable with esp-idf --- CMakeLists.txt | 44 +++++++++++++++++++ Kconfig | 15 +++++++ ...ino => 0014-espressif_esp32_send_data.cpp} | 1 + idf_component.yml | 7 +++ src/Configuration.h | 22 ++++++++-- 5 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 Kconfig rename examples/0014-espressif_esp32_send_data/{0014-espressif_esp32_send_data.ino => 0014-espressif_esp32_send_data.cpp} (99%) create mode 100644 idf_component.yml diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..42fd3b3c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.12) + +set(srcs + src/Arduino_HTTP_Client.cpp + src/Arduino_MQTT_Client.cpp + src/Attribute_Request_Callback.cpp + src/Callback_Watchdog.cpp + src/ESP32_Updater.cpp + src/ESP32_Updater.cpp + src/Espressif_MQTT_Client.cpp + src/HashGenerator.cpp + src/Helper.cpp + src/OTA_Update_Callback.cpp + src/Provision_Callback.cpp + src/RPC_Callback.cpp + src/RPC_Request_Callback.cpp + src/RPC_Response.cpp + src/Shared_Attribute_Callback.cpp + src/Telemetry.cpp + src/ThingsBoardDefaultLogger.cpp +) + +set(dependencies + mqtt + mbedtls +) + +set(private_dependencies + esp_timer +) + +if(ESP_PLATFORM) + # Build ThingsBoard Arduino SDK as an ESP-IDF component + idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS "." + PRIV_INCLUDE_DIRS "." + REQUIRES ${dependencies} + PRIV_REQUIRES ${private_dependencies} + ) + return() +endif() + +project(ThingsBoardArduinoSDK VERSION 0.12.0) diff --git a/Kconfig b/Kconfig new file mode 100644 index 00000000..08b49119 --- /dev/null +++ b/Kconfig @@ -0,0 +1,15 @@ +menu "ThingsBoard Arduino SDK Configurations" + + config THINGSBOARD_ENABLE_DYNAMIC + bool "Removes the need to set MaxFieldAmts in template class, but saves json into DynamicJsonDocument, requiring heap instead of stack memory" + default n + help + If this is disabled the library will use StaticJsonDocument to receive messages from the MQTT broker instead + + config THINGSBOARD_ENABLE_DEBUG + bool "Enables debug messages in the ThingsBoard client" + default n + help + If this is enabled the library uses more global constant variables containg messages that are printed + +endmenu diff --git a/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.ino b/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.cpp similarity index 99% rename from examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.ino rename to examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.cpp index f4874802..c8803b5e 100644 --- a/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.ino +++ b/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.cpp @@ -2,6 +2,7 @@ #include #include #include +#include // Whether the given script is using encryption or not, diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 00000000..90ed7e80 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,7 @@ +version: "0.12.0" +description: Provides access to ThingsBoard platform over the MQTT protocol or alternatively over HTTP/S. +url: https://github.com/thingsboard/thingsboard-arduino-sdk +dependencies: + bblanchon/arduinojson: "*" + idf: + version: "*" diff --git a/src/Configuration.h b/src/Configuration.h index dc22870c..7b7867a3 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -7,6 +7,10 @@ #ifndef Configuration_h #define Configuration_h +// Library includes. +#include + + // Enabled the usage of int64_t and double values with ArduinoJson. Making the JsonVariant store double and int64_t instead of float and int32_t. // See https://arduinojson.org/v6/api/config/use_long_long/ for more information #define ARDUINOJSON_USE_LONG_LONG 1 @@ -110,15 +114,27 @@ // Enables the ThingsBoard class to be fully dynamic instead of requiring template arguments to statically allocate memory. // If enabled the program might be slightly slower and all the memory will be placed onto the heap instead of the stack. // See https://arduinojson.org/v6/api/dynamicjsondocument/ for the main difference in the underlying code. +// Can also optionally be configured via the ESP-IDF menuconfig, if that is the done the value is set to the value entered in the menuconfig, +// if the value is manually overriden tough with a #define before including ThingsBoard then the hardcoded value takes precendence. # ifndef THINGSBOARD_ENABLE_DYNAMIC -# define THINGSBOARD_ENABLE_DYNAMIC 0 +# ifndef CONFIG_THINGSBOARD_ENABLE_DYNAMIC +# define THINGSBOARD_ENABLE_DYNAMIC 0 +# else +# define THINGSBOARD_ENABLE_DYNAMIC CONFIG_THINGSBOARD_ENABLE_DYNAMIC +# endif # endif // Enables the ThingsBoard class to print all received and sent messages and their topic, from and to the server, // additionally some more debug messages will be printed. Requires more flash memory, and more Serial calls requiring more performance. // Recommended to disable when building for release. +// Can also optionally be configured via the ESP-IDF menuconfig, if that is the done the value is set to the value entered in the menuconfig, +// if the value is manually overriden tough with a #define before including ThingsBoard then the hardcoded value takes precendence. # ifndef THINGSBOARD_ENABLE_DEBUG -# define THINGSBOARD_ENABLE_DEBUG 0 +# ifndef CONFIG_THINGSBOARD_ENABLE_DEBUG +# define THINGSBOARD_ENABLE_DEBUG 0 +# else +# define THINGSBOARD_ENABLE_DEBUG CONFIG_THINGSBOARD_ENABLE_DEBUG +# endif # endif // Enables the usage of an additonal library as a fallback, to directly serialize a json message that is sent to the cloud, @@ -132,7 +148,7 @@ // Enables the ThingsBoard class to save the allocated memory of the DynamicJsonDocument into psram instead of onto the sram. // Enabled by default if THINGSBOARD_ENABLE_DYNAMIC has been set and the esp_heap_caps header exists, because it requries DynamicJsonDocument to work. // If enabled the program might be slightly slower, but all the memory will be placed onto psram instead of sram, meaning the sram can be allocated for other things. -// See https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/ and https://arduinojson.org/v6/api/basicjsondocument/ for for the main difference in the underlying code. +// See https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/ and https://arduinojson.org/v6/api/basicjsondocument/ for the main difference in the underlying code. # ifdef __has_include # if THINGSBOARD_ENABLE_DYNAMIC && __has_include() # ifndef THINGSBOARD_ENABLE_PSRAM From c35c5b76b9c35565f10d562b1ef36a107a768771 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:15:03 +0200 Subject: [PATCH 40/80] Only include sdkconfig if it exists. --- src/Configuration.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Configuration.h b/src/Configuration.h index 7b7867a3..4391ff02 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -7,8 +7,12 @@ #ifndef Configuration_h #define Configuration_h -// Library includes. -#include +// Include sdkconfig file it it exists to allow overwriting of some defines with the configuration entered in the ESP IDF menuconfig +# ifdef __has_include +# if __has_include() +# include +# endif +# endif // Enabled the usage of int64_t and double values with ArduinoJson. Making the JsonVariant store double and int64_t instead of float and int32_t. From 3bcf399a73531d91954eb6c1e99cee92431e8a7c Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:39:51 +0200 Subject: [PATCH 41/80] Fix build issue --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42fd3b3c..6a4fe640 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,8 +33,8 @@ if(ESP_PLATFORM) # Build ThingsBoard Arduino SDK as an ESP-IDF component idf_component_register( SRCS ${srcs} - INCLUDE_DIRS "." - PRIV_INCLUDE_DIRS "." + INCLUDE_DIRS "src" + PRIV_INCLUDE_DIRS "src" REQUIRES ${dependencies} PRIV_REQUIRES ${private_dependencies} ) From b59d38b9e4d84ed22c626b87c73f5a83bebc4a33 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:45:32 +0200 Subject: [PATCH 42/80] Add version identifier to idf_component registry --- idf_component.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idf_component.yml b/idf_component.yml index 90ed7e80..ef10846a 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -2,6 +2,6 @@ version: "0.12.0" description: Provides access to ThingsBoard platform over the MQTT protocol or alternatively over HTTP/S. url: https://github.com/thingsboard/thingsboard-arduino-sdk dependencies: - bblanchon/arduinojson: "*" + bblanchon/arduinojson: "^6.21.2" idf: version: "*" From fda6d49b3591d77d5e09f9d2ee0b9648844bf04c Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:33:35 +0200 Subject: [PATCH 43/80] Add information on Installation --- README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a9dc216a..30864b72 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,59 @@ This library provides access to ThingsBoard platform over the `MQTT` protocol or The SDK comes with a number of example sketches. See **Files --> Examples --> ThingsBoard** within the Arduino application. Please review the complete guide for `ESP32` Pico Kit `GPIO` control and `DHT22` sensor monitoring available [here](https://thingsboard.io/docs/samples/esp32/gpio-control-pico-kit-dht22-sensor/). +## Supported Frameworks + +`ThingsBoardArduinoSDK` does not directly depend on any specific `MQTT Client` or `HTTP Client` implementation, instead any implementation of the `IMQTT_Client` or `IHTTP Client` can be used. Because there are no further dependencies on `Arduino`, besides the client that communicates it allows us to use this library with `Arduino`, when using the `Arduino_MQTT_Client` or with `Espressif IDF` when using the `Espressif_MQTT_Client`. + +Example usage for `Espressif` can be found in the `examples/0014-espressif_esp32_send_data` folder, all other code portions can be implemented the same way only initialization of the needed dependencies is slightly different. Meaning internal call to `ThingsBoard` works the same on both `Espressif` and `Arduino`. + +This is also the case, because the only always used dependency that is remaining, is [`ArduinoJson`](https://arduinojson.org/), which despite its name does not require any `Arduino` component. + ## Installation -ThingsBoard SDK can be installed directly from the [Arduino Library manager](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries) or [PlattformIO](https://registry.platformio.org/). +This project can be built with either [PlatformIO](https://platformio.org/), [`ESP IDF Extension`](https://www.espressif.com/) or [Arduino IDE](https://www.arduino.cc/en/software). + +The project can be found in the [PlatformIO Registry](https://registry.platformio.org/libraries/thingsboard/ThingsBoard), [ESP Component registry](https://components.espressif.com/components/thingsboard/thingsboard) or the [Arduino libraries](https://www.arduino.cc/reference/en/libraries/thingsboard/). + +A description on how to include the library in you project can be found below for each of the aforementioned possible methods of integrating the project. + +#### PlatformIO + +To add an external library, the most important portion is the [`lib_deps`](https://docs.platformio.org/en/latest/projectconf/sections/env/options/library/lib_deps.html) specification, simply add `thingsboard/ThingsBoard`. +There are multiple ways to define the version that should be fetched, but the most basic is simply getting the last released version, with the aforementioned line, to learn more see [Package Specifications](https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html#package-specifications). + +``` +lib_deps= + thingsboard/ThingsBoard +``` + +#### ESP IDF Extension + +To add an external library, what needs to be done differs between versions. If an [ESP-IDF](https://github.com/espressif/esp-idf) version after and including `v3.2.0` is used +then the project can simply be added over the [Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +To do that we can simply call `idf.py add-dependency `, with the name of the dependency as an argument. Similar to `PlatformIO` there are a multiple way to define the version that should be fetched, but the method below is the most basic to simply get the last released version, to learn more see [Using Component Manager with a Project](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html#using-with-a-project). + +``` +idf.py add-dependency "thingsboard/ThingsBoard" +``` + +If an [ESP-IDF](https://github.com/espressif/esp-idf) version prior to `v3.2.0` is used then the component has to be added as a `git submodule`. +Meaning the repository has to first be a `git` project, if that is not the case already simply install `git` and call `git init` in the folder containing your project. + +Similar to the other call there are a multiple way to define the version that should be fetched, but the method below is the most basic to simply get the last released version, to learn more see [Git Submodule Help page](https://git-scm.com/docs/git-submodule). + +``` +git submodule add https://github.com/thingsboard/thingsboard-arduino-sdk.git components/ThingsBoard +``` + +#### Arduino IDE + +To add an external library, we simply have to open `Tools` -> `Manage Libraries` and then search for `ThingsBoard` then press the `install` button for the wanted version. See [how to install library on Arduino IDE](https://arduinogetstarted.com/faq/how-to-install-library-on-arduino-ide) for more detailed information and some troubleshooting if the aforementioned method does not work. + + +## Dependencies + Following dependencies are installed automatically or must be installed, too: **Installed automatically:** @@ -48,7 +98,7 @@ All possible features are implemented over `MQTT` - [Device claiming](https://thingsboard.io/docs/reference/mqtt-api/#claiming-devices) - [Firmware OTA update](https://thingsboard.io/docs/reference/mqtt-api/#firmware-api) -Over `HTTP\S`: +### Over `HTTP\S`: Remaining features have to be implemented by hand with the `sendGetRequest` or `sendPostRequest` method, see the [ThingsBoard Documentation](https://thingsboard.io/docs/reference/http-api) on how these features could be implemented. @@ -57,16 +107,10 @@ Remaining features have to be implemented by hand with the `sendGetRequest` or ` Example implementations for all base features, mentioned above, can be found in the `examples` folder. See the according `README.md`, to see which boards are supported and which functionality the example shows. -## Supported Frameworks - -`ThingsBoardArduinoSDK` does not directly depend on any specific `MQTT Client` or `HTTP Client` implementation, instead any implementation of the `IMQTT_Client` or `IHTTP Client` can be used. Because there are no further dependencies on `Arduino`, besides the client that communicates it allows us to use this library with `Arduino`, when using the `Arduino_MQTT_Client` or with `Espressif IDF` when using the `Espressif_MQTT_Client`. - -Example usage for `Espressif` can be found in the `examples/0014-espressif_esp32_send_data` folder, all other code portions can be implemented the same way only initialization of the needed dependencies is slightly different. Meaning internal call to `ThingsBoard` works the same on both `Espressif` and `Arduino`. - -This is also the case, because the only always used dependency that is remaining, is [`ArduinoJson`](https://arduinojson.org/), which despite its name does not require any `Arduino` component. - ## Troubleshooting +This troubleshooting guide contains common issues that are well known and can occur if the library is used wrongly. Ensure to read this section before creating a new `GitHub Issue`. + ### No PROGMEM support causing crashes If the device is crashing with an `Exception` especially `Exception (3)`, more specifically `LoadStoreError` or `LoadStoreErrorCause` this might be because, all constant variables are per default in flash memory to decrease the memory footprint of the library, if the libraries used or the board itself don't support `PROGMEM`. This can cause crashes to mitigate that simply add a `#define THINGSBOARD_ENABLE_PROGMEM 0` before including the `ThingsBoard` header file. From e91d9dea3fe2186512880b9371c5a76ad5fa23fb Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:50:29 +0200 Subject: [PATCH 44/80] Add IUpdater implementation for ESP-IDF --- CMakeLists.txt | 5 +- .../0009-esp8266_esp32_process_OTA_MQTT.ino | 10 +- .../0011-esp8266_esp32_subscribe_OTA_MQTT.ino | 10 +- ..._Updater.cpp => Arduino_ESP32_Updater.cpp} | 10 +- ...SP32_Updater.h => Arduino_ESP32_Updater.h} | 10 +- ...pdater.cpp => Arduino_ESP8266_Updater.cpp} | 10 +- ...66_Updater.h => Arduino_ESP8266_Updater.h} | 10 +- src/Callback_Watchdog.cpp | 2 +- src/Configuration.h | 18 +- src/Espressif_MQTT_Client.cpp | 71 +++++- src/Espressif_MQTT_Client.h | 43 +++- src/Espressif_Updater.cpp | 228 ++++++++++++++++++ src/Espressif_Updater.h | 68 ++++++ 13 files changed, 456 insertions(+), 39 deletions(-) rename src/{ESP32_Updater.cpp => Arduino_ESP32_Updater.cpp} (57%) rename src/{ESP32_Updater.h => Arduino_ESP32_Updater.h} (70%) rename src/{ESP8266_Updater.cpp => Arduino_ESP8266_Updater.cpp} (57%) rename src/{ESP8266_Updater.h => Arduino_ESP8266_Updater.h} (70%) create mode 100644 src/Espressif_Updater.cpp create mode 100644 src/Espressif_Updater.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a4fe640..cab830f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,9 @@ set(srcs src/Arduino_MQTT_Client.cpp src/Attribute_Request_Callback.cpp src/Callback_Watchdog.cpp - src/ESP32_Updater.cpp - src/ESP32_Updater.cpp + src/Arduino_ESP32_Updater.cpp + src/Arduino_ESP8266_Updater.cpp + src/Espressif_Updater.cpp src/Espressif_MQTT_Client.cpp src/HashGenerator.cpp src/Helper.cpp diff --git a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino index ff866efb..c963557c 100644 --- a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino +++ b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino @@ -14,10 +14,10 @@ #include #ifdef ESP8266 -#include +#include #else #ifdef ESP32 -#include +#include #endif // ESP32 #endif // ESP8266 @@ -272,15 +272,15 @@ void updatedCallback(const bool& success) { /// @brief Progress callback that will be called every time we downloaded a new chunk successfully /// @param currentChunk /// @param totalChuncks -void progressCallback(const uint32_t& currentChunk, const uint32_t& totalChuncks) { +void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { Serial.printf("Progress %.2f%%\n", static_cast(currentChunk * 100U) / totalChuncks); } #ifdef ESP8266 -ESP8266_Updater updater; +Arduino_ESP8266_Updater updater; #else #ifdef ESP32 -ESP32_Updater updater; +Arduino_ESP32_Updater updater; #endif // ESP32 #endif // ESP8266 diff --git a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino index 4494a9ab..ed5a9785 100644 --- a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino +++ b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino @@ -14,10 +14,10 @@ #include #ifdef ESP8266 -#include +#include #else #ifdef ESP32 -#include +#include #endif // ESP32 #endif // ESP8266 @@ -272,15 +272,15 @@ void updatedCallback(const bool& success) { /// @brief Progress callback that will be called every time we downloaded a new chunk successfully /// @param currentChunk /// @param totalChuncks -void progressCallback(const uint32_t& currentChunk, const uint32_t& totalChuncks) { +void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { Serial.printf("Progress %.2f%%\n", static_cast(currentChunk * 100U) / totalChuncks); } #ifdef ESP8266 -ESP8266_Updater updater; +Arduino_ESP8266_Updater updater; #else #ifdef ESP32 -ESP32_Updater updater; +Arduino_ESP32_Updater updater; #endif // ESP32 #endif // ESP8266 diff --git a/src/ESP32_Updater.cpp b/src/Arduino_ESP32_Updater.cpp similarity index 57% rename from src/ESP32_Updater.cpp rename to src/Arduino_ESP32_Updater.cpp index dffc8fc8..1c20c681 100644 --- a/src/ESP32_Updater.cpp +++ b/src/Arduino_ESP32_Updater.cpp @@ -1,5 +1,5 @@ // Header include. -#include "ESP32_Updater.h" +#include "Arduino_ESP32_Updater.h" #if THINGSBOARD_ENABLE_OTA @@ -8,19 +8,19 @@ // Library include. #include -bool ESP32_Updater::begin(const size_t& firmware_size) { +bool Arduino_ESP32_Updater::begin(const size_t& firmware_size) { return Update.begin(firmware_size); } -size_t ESP32_Updater::write(uint8_t* payload, const size_t& total_bytes) { +size_t Arduino_ESP32_Updater::write(uint8_t* payload, const size_t& total_bytes) { return Update.write(payload, total_bytes); } -void ESP32_Updater::reset() { +void Arduino_ESP32_Updater::reset() { Update.abort(); } -bool ESP32_Updater::end() { +bool Arduino_ESP32_Updater::end() { return Update.end(); } diff --git a/src/ESP32_Updater.h b/src/Arduino_ESP32_Updater.h similarity index 70% rename from src/ESP32_Updater.h rename to src/Arduino_ESP32_Updater.h index 02c5432b..9715fb91 100644 --- a/src/ESP32_Updater.h +++ b/src/Arduino_ESP32_Updater.h @@ -1,11 +1,11 @@ /* - ESP32_Updater.h - Library API for sending data to the ThingsBoard + Arduino_ESP32_Updater.h - Library API for sending data to the ThingsBoard Based on PubSub MQTT library. Created by Olender M. Oct 2018. Released into the public domain. */ -#ifndef ESP32_Updater_h -#define ESP32_Updater_h +#ifndef Arduino_ESP32_Updater_h +#define Arduino_ESP32_Updater_h // Local include. #include "Configuration.h" @@ -18,7 +18,7 @@ #include "IUpdater.h" -class ESP32_Updater : public IUpdater { +class Arduino_ESP32_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; @@ -33,4 +33,4 @@ class ESP32_Updater : public IUpdater { #endif // THINGSBOARD_ENABLE_OTA -#endif // ESP32_Updater_h +#endif // Arduino_ESP32_Updater_h diff --git a/src/ESP8266_Updater.cpp b/src/Arduino_ESP8266_Updater.cpp similarity index 57% rename from src/ESP8266_Updater.cpp rename to src/Arduino_ESP8266_Updater.cpp index 471e9bfa..5f346394 100644 --- a/src/ESP8266_Updater.cpp +++ b/src/Arduino_ESP8266_Updater.cpp @@ -1,5 +1,5 @@ // Header include. -#include "ESP8266_Updater.h" +#include "Arduino_ESP8266_Updater.h" #if THINGSBOARD_ENABLE_OTA @@ -8,19 +8,19 @@ // Library include. #include -bool ESP8266_Updater::begin(const size_t& firmware_size) { +bool Arduino_ESP8266_Updater::begin(const size_t& firmware_size) { return Update.begin(firmware_size); } -size_t ESP8266_Updater::write(uint8_t* payload, const size_t& total_bytes) { +size_t Arduino_ESP8266_Updater::write(uint8_t* payload, const size_t& total_bytes) { return Update.write(payload, total_bytes); } -void ESP8266_Updater::reset() { +void Arduino_ESP8266_Updater::reset() { // Nothing to do } -bool ESP8266_Updater::end() { +bool Arduino_ESP8266_Updater::end() { return Update.end(); } diff --git a/src/ESP8266_Updater.h b/src/Arduino_ESP8266_Updater.h similarity index 70% rename from src/ESP8266_Updater.h rename to src/Arduino_ESP8266_Updater.h index 8913555a..65fc77f9 100644 --- a/src/ESP8266_Updater.h +++ b/src/Arduino_ESP8266_Updater.h @@ -1,11 +1,11 @@ /* - ESP8266_Updater.h - Library API for sending data to the ThingsBoard + Arduino_ESP8266_Updater.h - Library API for sending data to the ThingsBoard Based on PubSub MQTT library. Created by Olender M. Oct 2018. Released into the public domain. */ -#ifndef ESP8266_Updater_h -#define ESP8266_Updater_h +#ifndef Arduino_ESP8266_Updater_h +#define Arduino_ESP8266_Updater_h // Local include. #include "Configuration.h" @@ -18,7 +18,7 @@ #include "IUpdater.h" -class ESP8266_Updater : public IUpdater { +class Arduino_ESP8266_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; @@ -33,4 +33,4 @@ class ESP8266_Updater : public IUpdater { #endif // THINGSBOARD_ENABLE_OTA -#endif // ESP8266_Updater_h +#endif // Arduino_ESP8266_Updater_h diff --git a/src/Callback_Watchdog.cpp b/src/Callback_Watchdog.cpp index daa61668..afe4f5fa 100644 --- a/src/Callback_Watchdog.cpp +++ b/src/Callback_Watchdog.cpp @@ -32,7 +32,7 @@ Callback_Watchdog::Callback_Watchdog(std::function callback) : .arg = nullptr, .dispatch_method = esp_timer_dispatch_t::ESP_TIMER_TASK, .name = WATCHDOG_TIMER_NAME, - .skip_unhandled_events = false, + .skip_unhandled_events = false }; // Temporary handle is used, because it allows using a void* as the actual oneshot_timer, // allowing us to only include the esp_timer header in the defintion (.cpp) file, diff --git a/src/Configuration.h b/src/Configuration.h index 4391ff02..f1866ad8 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -69,7 +69,7 @@ # endif // Use the mqtt_client header internally for handling the sending and receiving of MQTT data, as long as the header exists, -// to allow users that do have the needed component to use the Espressif_MQTT_Client instead of only the Arduino_MQTT_Client +// to allow users that do have the needed component to use the Espressif_MQTT_Client instead of only the Arduino_MQTT_Client. # ifdef __has_include # if __has_include() # ifndef THINGSBOARD_USE_ESP_MQTT @@ -100,6 +100,22 @@ # define THINGSBOARD_USE_MBED_TLS 0 # endif +// Use the espressif partitions header internally for handling the writing of ota update data, as long as the header exists, +// to allow users that do have the needed component to use the Espressif_Updater instead of only the Arduino_ESP32_Updater. +# ifdef __has_include +# if __has_include() && __has_include() +# ifndef THINGSBOARD_USE_ESP_PARTITION +# define THINGSBOARD_USE_ESP_PARTITION 1 +# endif +# else +# ifndef THINGSBOARD_USE_ESP_PARTITION +# define THINGSBOARD_USE_ESP_PARTITION 0 +# endif +# endif +# else +# define THINGSBOARD_USE_ESP_PARTITION 0 +# endif + // Enable the usage of the PROGMEM header for constants variables (variables are placed into flash memory instead of sram). # ifdef __has_include # if __has_include() diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index 94bcb995..d640494d 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -16,15 +16,78 @@ Espressif_MQTT_Client::Espressif_MQTT_Client() : m_instance = this; } -void Espressif_MQTT_Client::set_server_certificate(const char *server_certificate_pem) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +bool Espressif_MQTT_Client::set_server_certificate(const char *server_certificate_pem) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely. + // Because PEM format is expected for the server certificate we do not need to set the certificate_len, + // because the PEM format expects a null-terminated string. #if ESP_IDF_VERSION_MAJOR < 5 m_mqtt_configuration.cert_pem = server_certificate_pem; #else - // Because PEM format is expected for the server certificate we do not need to set the certificate_len, - // because the PEM format expects a null-terminated string. m_mqtt_configuration.broker.verification.certificate = server_certificate_pem; #endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} + +bool Espressif_MQTT_Client::set_keep_alive_timeout(const uint16_t& keep_alive_timeout_seconds) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.keepalive = keep_alive_timeout_seconds; +#else + m_mqtt_configuration.session.keepalive = keep_alive_timeout_seconds; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} + +bool Espressif_MQTT_Client::set_disable_keep_alive(const bool& disable_keep_alive) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.disable_keepalive = disable_keep_alive; +#else + m_mqtt_configuration.session.disable_keepalive = disable_keep_alive; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} + +bool Espressif_MQTT_Client::set_disable_auto_reconnect(const bool& disable_auto_reconnect) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.disable_auto_reconnect = disable_auto_reconnect; +#else + m_mqtt_configuration.network.disable_auto_reconnect = disable_auto_reconnect; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} + +bool Espressif_MQTT_Client::set_mqtt_task_configuration(const uint8_t& priority, const uint16_t& stack_size) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.task_prio = priority; + m_mqtt_configuration.task_stack = stack_size; +#else + m_mqtt_configuration.task.priority = priority; + m_mqtt_configuration.task.stack_size = stack_size; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} + +bool Espressif_MQTT_Client::set_reconnect_timeout(const uint16_t& reconnect_timeout_milliseconds) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.reconnect_timeout_ms = reconnect_timeout_milliseconds; +#else + m_mqtt_configuration.network.reconnect_timeout_ms = reconnect_timeout_milliseconds; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} + +bool Espressif_MQTT_Client::set_network_timeout(const uint16_t& network_timeout_milliseconds) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.network_timeout_ms = network_timeout_milliseconds; +#else + m_mqtt_configuration.network.timeout_ms = network_timeout_milliseconds; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); } void Espressif_MQTT_Client::set_callback(function cb) { diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h index 9ee98231..1136436c 100644 --- a/src/Espressif_MQTT_Client.h +++ b/src/Espressif_MQTT_Client.h @@ -35,7 +35,48 @@ class Espressif_MQTT_Client : public IMQTT_Client { /// which is then flashed onto the device instead of the real firmware. Which depeding on the payload might even be able to destroy the device or make it otherwise unusable. /// See https://stackoverflow.blog/2020/12/14/security-considerations-for-ota-software-updates-for-iot-gateway-devices/ for more information on the aforementioned security risk /// @param server_certificate_pem Null-terminated string containg the root certificate in PEM format, of the server we are attempting to send to and receive MQTT data from - void set_server_certificate(const char *server_certificate_pem); + /// @return Whether chaing the internal server certificate was successful or not, ensure to disconnect and reconnect to actually apply the change. + bool set_server_certificate(const char *server_certificate_pem); + + /// @brief Sets the keep alive timeout in seconds, if the value is 0 then the default of 120 seconds is used instead to disable the keep alive mechanism use set_disable_keep_alive() instead. + /// The default timeout value ThingsBoard expectes to receive any message including a keep alive to not show the device as inactive can be found here https://thingsboard.io/docs/user-guide/install/config/#mqtt-server-parameters + /// under the transport.sessions.inactivity_timeout section and is 300 seconds. Meaning a value bigger than 300 seconds with the default config defeats the purpose of the keep alive alltogether + /// @param keep_alive_timeout_seconds Timeout until we send another PINGREQ control packets to the broker to establish that we are still connected + /// @return Whether changing the internal keep alive timeout was successful or not + bool set_keep_alive_timeout(const uint16_t& keep_alive_timeout_seconds); + + /// @brief Whether to disable or enable the keep alive mechanism, meaning we do not send any PINGREQ control packets to the broker anymore, meaning if we do not send other packet the device will be marked as inactive. + /// The default value is false meaning the keep alive mechanism is enabled. + /// The default timeout value ThingsBoard expectes to receive any message including a keep alive to not show the device as inactive can be found here https://thingsboard.io/docs/user-guide/install/config/#mqtt-server-parameters + /// under the transport.sessions.inactivity_timeout section and is 300 seconds. Meaning if we disable the keep alive mechanism and do not send a packet atleast every 300 seconds with the default config then the device will change states between active and inactive + /// @param disable_keep_alive Whether to enable or disable the internal keep alive mechanism, which sends PINGREQ control packets every keep alive timeout seconds, configurable in set_keep_alive_timeout() + /// @return Whether enabling or disabling the internal keep alive mechanism was successful or not + bool set_disable_keep_alive(const bool& disable_keep_alive); + + /// @brief Wheter to disable or enable that the MQTT client will reconnect to the server automatically if it errors or disconnects. The default is false meaning we will automatially reconnect + /// @param disable_auto_reconnect Whether to automatically reconnect if the the client errors or disconnects + /// @return Whether enabling or disabling the internal auto reconnect mechanism was successful or not + bool set_disable_auto_reconnect(const bool& disable_auto_reconnect); + + /// @brief Sets the priority of the MQTT task running in the background and handling the receiving and sending of any outstanding MQTT messages to or from the broker. + /// The default value for the priority is 5 and can also be changed in the ESp IDF menuconfig and the default value for the stack size + /// @param priority Task priority with which the MQTT task should run, higher priority means it takes more precedence over other tasks, making it more important, default value is 5 and can be changed in the ESP IDF menuconfig + /// @param stack_size Task stack size meaning how much stack size the MQTT task can use before the device crashes with a StackOverflow, default value is 6144 bytes, can be changed in the ESP IDF menuconfig + /// @return Whether changing the internal MQTT task configurations were successfull or not + bool set_mqtt_task_configuration(const uint8_t& priority, const uint16_t& stack_size); + + /// @brief Sets the amount of time in milliseconds that we wait before we automatically reconnect to the MQTT broker if the connection has been lost. The default value is 10 seconds. + /// Will be ignored if set_disable_auto_reconnect() has been set to true, because we will not reconnect automatically anymore, instead that would have to be done by the user + /// @param reconnect_timeout_milliseconds Time in milliseconds until we automatically reconnect to the MQTT broker + /// @return Whether changing the internal reconnect timeout was successfull or not + bool set_reconnect_timeout(const uint16_t& reconnect_timeout_milliseconds); + + /// @brief Sets the amount of time in millseconds that we wait until we expect a netowrk operation on the MQTT client to have successfully finished. The defalt value is 10 seconds. + /// If that is not the case then the network operation will be aborted, might be useful to increase for devices that perform other high priority tasks and do not have enough CPU resources, + /// to send bigger messages to the MQTT broker in time, which can cause disconnects and the sent message being discarded + /// @param network_timeout_milliseconds Time in milliseconds that we wait until we abort the network operation if it has not completed yet + /// @return Whether changing the internal network timeout was successfull or not + bool set_network_timeout(const uint16_t& network_timeout_milliseconds); void set_callback(function cb) override; diff --git a/src/Espressif_Updater.cpp b/src/Espressif_Updater.cpp new file mode 100644 index 00000000..780283d0 --- /dev/null +++ b/src/Espressif_Updater.cpp @@ -0,0 +1,228 @@ +// Header include. +#include "Espressif_Updater.h" + +#if THINGSBOARD_ENABLE_OTA + +#if THINGSBOARD_USE_ESP_PARTITION + +// Library include. +#include +#include +#include + + +constexpr size_t SPI_FLASH_ALIGNMENT = SPI_FLASH_SEC_SIZE; +constexpr size_t SPI_FLASH_BLOCK_SIZE = SPI_FLASH_ALIGNMENT * SPI_FLASH_HEADER_SIZE; + + +Espressif_Updater::Espressif_Updater() : + m_data_buffer(nullptr), + m_start_data_buffer(), + m_written_bytes_amount(0U), + m_total_bytes_amount(0U), + m_buffer_length(0U), + m_ota_partition(nullptr) +{ + // Nothing to do +} + +bool Espressif_Updater::begin(const size_t& firmware_size) { + if (m_total_bytes_amount) { + return false; + } + + reset(); + + const esp_partition_t *ota_parition = esp_ota_get_next_update_partition(nullptr); + + if (ota_parition == nullptr) { + return false; + } + + if (firmware_size > ota_parition->size) { + return false; + } + + m_total_bytes_amount = firmware_size; + m_ota_partition = ota_parition; + return true; +} + +size_t Espressif_Updater::write(uint8_t* payload, const size_t& total_bytes) { + if (!started_update()) { + return 0U; + } + + if (total_bytes > remaining_bytes()) { + reset(); + return 0U; + } + + size_t bytes_to_write = total_bytes; + + while ((m_buffer_length + bytes_to_write) > SPI_FLASH_ALIGNMENT) { + size_t buffer_size = SPI_FLASH_ALIGNMENT - m_buffer_length; + memcpy(m_data_buffer + m_buffer_length, payload + (total_bytes - bytes_to_write), buffer_size); + m_buffer_length += buffer_size; + if (!write_buffer()) { + return total_bytes - bytes_to_write; + } + bytes_to_write -= buffer_size; + } + + memcpy(m_data_buffer + m_buffer_length, payload + (total_bytes - bytes_to_write), bytes_to_write); + m_buffer_length += bytes_to_write; + + if (m_buffer_length == remaining_bytes()) { + if (!write_buffer()) { + return total_bytes - bytes_to_write; + } + } + return total_bytes; +} + +void Espressif_Updater::reset() { + delete[] m_data_buffer; + m_written_bytes_amount = 0U; + m_total_bytes_amount = 0U; + m_buffer_length = 0U; + m_ota_partition = nullptr; +} + +bool Espressif_Updater::end() { + if (m_total_bytes_amount == 0) { + return false; + } + + if (!is_finished()) { + reset(); + return false; + } + + return verify_end(); +} + +bool Espressif_Updater::write_buffer() { + // First x amount of bytes of the firmware, which are initally not written to ensure a not completly written firmware is not bootable + size_t skip_bytes_amount = 0U; + + if (m_written_bytes_amount == 0U) { + // Check wheter the magic byte has been set or not + if (m_data_buffer[0] != ESP_IMAGE_HEADER_MAGIC) { + reset(); + return false; + } + + // Stash the first 16 bytes of data and set the offset so they are + // not written at this point so that partially written firmware + // will not be bootable + skip_bytes_amount = SPI_FLASH_HEADER_SIZE; + memcpy(m_start_data_buffer, m_data_buffer, SPI_FLASH_HEADER_SIZE); + } + + const esp_partition_t *partition = static_cast(m_ota_partition); + + size_t offset = partition->address + m_written_bytes_amount; + bool block_erase = (m_total_bytes_amount - m_written_bytes_amount >= SPI_FLASH_BLOCK_SIZE) && (offset % SPI_FLASH_BLOCK_SIZE == 0); // if it's the block boundary, than erase the whole block from here + bool part_head_sectors = partition->address % SPI_FLASH_BLOCK_SIZE && offset < (partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition heading block + bool part_tail_sectors = offset >= (partition->address + m_total_bytes_amount) / SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition tailing block + if (block_erase || part_head_sectors || part_tail_sectors) { + const esp_err_t error = esp_partition_erase_range(partition, m_written_bytes_amount, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_ALIGNMENT); + if (error != ESP_OK) { + reset(); + return false; + } + } + + // try to skip empty blocks on unecrypted partitions + size_t skip_block_amount = skip_bytes_amount / sizeof(uint32_t); + if ((partition->encrypted || check_if_block_is_empty(m_data_buffer + skip_block_amount, m_buffer_length - skip_bytes_amount)) && esp_partition_write(partition, m_written_bytes_amount + skip_bytes_amount, reinterpret_cast(m_data_buffer) + skip_block_amount, m_buffer_length - skip_bytes_amount) != ESP_OK) { + reset(); + return false; + } + + // Restore magic byte + if (m_written_bytes_amount == 0U) { + m_data_buffer[0] = ESP_IMAGE_HEADER_MAGIC; + } + + m_written_bytes_amount += m_buffer_length; + m_buffer_length = 0; + return true; +} + +bool Espressif_Updater::verify_end() { + if (!write_start_data() || !is_partition_bootable()) { + reset(); + return false; + } + + if (esp_ota_set_boot_partition(static_cast(m_ota_partition))) { + reset(); + return false; + } + + reset(); + return true; +} + +bool Espressif_Updater::started_update() const { + return m_total_bytes_amount != 0U; +} + +size_t Espressif_Updater::remaining_bytes() const { + return m_total_bytes_amount - m_written_bytes_amount; +} + +bool Espressif_Updater::is_partition_bootable() const { + if (m_ota_partition == nullptr) { + return false; + } + + uint8_t buf[SPI_FLASH_HEADER_SIZE]; + const esp_err_t error = esp_partition_read(static_cast(m_ota_partition), 0, (uint32_t*)buf, SPI_FLASH_HEADER_SIZE); + if (error != ESP_OK) { + return false; + } + + if (buf[0] != ESP_IMAGE_HEADER_MAGIC) { + return false; + } + return true; +} + +bool Espressif_Updater::write_start_data() const { + if (m_ota_partition == nullptr) { + return false; + } + + const esp_err_t error = esp_partition_write(static_cast(m_ota_partition), 0, reinterpret_cast(m_start_data_buffer), SPI_FLASH_HEADER_SIZE); + return error == ESP_OK; +} + +bool Espressif_Updater::is_finished() const { + return m_total_bytes_amount == m_written_bytes_amount; +} + +bool Espressif_Updater::check_if_block_is_empty(const uint8_t* payload, const size_t& total_bytes) const { + // check 32-bit aligned blocks only + if (!total_bytes || total_bytes % sizeof(uint32_t)) { + return true; + } + + size_t dwl = total_bytes / sizeof(uint32_t); + + do { + // for SPI NOR flash empty blocks are all one's, i.e. filled with 0xff byte + if (*(uint32_t*)payload ^ 0xffffffff) { + return true; + } + + payload += sizeof(uint32_t); + } while (--dwl); + return false; +} + +#endif // THINGSBOARD_USE_ESP_PARTITION + +#endif // THINGSBOARD_ENABLE_OTA diff --git a/src/Espressif_Updater.h b/src/Espressif_Updater.h new file mode 100644 index 00000000..686d99d9 --- /dev/null +++ b/src/Espressif_Updater.h @@ -0,0 +1,68 @@ +/* + Espressif_Updater.h - Library API for sending data to the ThingsBoard + Based on PubSub MQTT library. + Created by Olender M. Oct 2018. + Released into the public domain. +*/ +#ifndef Espressif_Updater_h +#define Espressif_Updater_h + +// Local include. +#include "Configuration.h" + +#if THINGSBOARD_ENABLE_OTA + +#if THINGSBOARD_USE_ESP_PARTITION + +// Local include. +#include "IUpdater.h" + + +constexpr size_t SPI_FLASH_HEADER_SIZE = 16U; + + +class Espressif_Updater : public IUpdater { + public: + Espressif_Updater(); + + bool begin(const size_t& firmware_size) override; + + size_t write(uint8_t* payload, const size_t& total_bytes) override; + + void reset() override; + + bool end() override; + + private: + uint8_t *m_data_buffer; + // First 16 bytes are seperated so a partially written firmware is not bootable, + // is written back at the correct place in flash memory at the successfull end of the update. + uint8_t m_start_data_buffer[SPI_FLASH_HEADER_SIZE]; + size_t m_written_bytes_amount; + size_t m_total_bytes_amount; + size_t m_buffer_length; + const void *m_ota_partition; + + bool write_buffer(); + + bool verify_end(); + + bool started_update() const; + + size_t remaining_bytes() const; + + bool is_partition_bootable() const; + + bool write_start_data() const; + + bool is_finished() const; + + bool check_if_block_is_empty(const uint8_t* payload, const size_t& total_bytes) const; + +}; + +#endif // THINGSBOARD_USE_ESP_PARTITION + +#endif // THINGSBOARD_ENABLE_OTA + +#endif // Espressif_Updater_h From 3d6ddd49a0d873b197dcc0222ec0aa661e3a2681 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:58:05 +0200 Subject: [PATCH 45/80] Added missing includes for Espressif Updater --- src/Configuration.h | 2 +- src/Espressif_Updater.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Configuration.h b/src/Configuration.h index f1866ad8..0396a65f 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -103,7 +103,7 @@ // Use the espressif partitions header internally for handling the writing of ota update data, as long as the header exists, // to allow users that do have the needed component to use the Espressif_Updater instead of only the Arduino_ESP32_Updater. # ifdef __has_include -# if __has_include() && __has_include() +# if __has_include() && (__has_include() || __has_include()) && __has_include() # ifndef THINGSBOARD_USE_ESP_PARTITION # define THINGSBOARD_USE_ESP_PARTITION 1 # endif diff --git a/src/Espressif_Updater.cpp b/src/Espressif_Updater.cpp index 780283d0..745fbccd 100644 --- a/src/Espressif_Updater.cpp +++ b/src/Espressif_Updater.cpp @@ -7,7 +7,14 @@ // Library include. #include +#include +// ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the old esp_spi_flash.h header has been deprecated and spi_flash_mmap should be used instead. +#if ESP_IDF_VERSION_MAJOR < 5 #include +#else +#include +#endif // ESP_IDF_VERSION_MAJOR < 5 +#include #include From b01685f0670a12b89b4d3e2bafb3b8af6f9d5fd8 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:08:47 +0200 Subject: [PATCH 46/80] Directly pass ota payload to method --- src/ThingsBoard.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 96f47a3b..65c05743 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -120,7 +120,7 @@ constexpr char RPC_EMPTY_PARAMS_VALUE[] = "{}"; // Log messages. #if THINGSBOARD_ENABLE_PROGMEM -constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] PROGMEM = "Unable to de-serialize received json data with error (%s)"; +constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] PROGMEM = "Unable to de-serialize received json data with error (DeserializationError::%s)"; constexpr char INVALID_BUFFER_SIZE[] PROGMEM = "Buffer size (%u) to small for the given payloads size (%u), increase with setBufferSize accordingly or set THINGSBOARD_ENABLE_STREAM_UTILS to 1 before including ThingsBoard"; constexpr char NUMBER_PRINTF[] PROGMEM = "%u"; #if !THINGSBOARD_ENABLE_DYNAMIC @@ -149,7 +149,7 @@ constexpr char SEND_MESSAGE[] PROGMEM = "Sending data to server over topic (%s) constexpr char SEND_SERIALIZED[] PROGMEM = "Hidden, because json data is bigger than buffer, therefore showing in console is skipped"; #endif // THINGSBOARD_ENABLE_DEBUG #else -constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] = "Unable to de-serialize received json data with error (%s)"; +constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] = "Unable to de-serialize received json data with error (DeserializationError::%s)"; constexpr char INVALID_BUFFER_SIZE[] = "Buffer size (%u) to small for the given payloads size (%u), increase with setBufferSize accordingly or set THINGSBOARD_ENABLE_STREAM_UTILS to 1 before including ThingsBoard"; constexpr char NUMBER_PRINTF[] = "%u"; #if !THINGSBOARD_ENABLE_DYNAMIC @@ -1902,6 +1902,15 @@ class ThingsBoardSized { Logger::log(message); #endif // THINGSBOARD_ENABLE_DEBUG +#if THINGSBOARD_ENABLE_OTA + // When receiving the ota binary payload we do not want to deserialize it into json, because it only contains + // firmware bytes that should be directly writtin into flash, therefore we can skip that step and directly process those bytes + if (strncmp_P(FIRMWARE_RESPONSE_TOPIC, topic, strlen(FIRMWARE_RESPONSE_TOPIC)) == 0) { + process_firmware_response(topic, payload, length); + return; + } +#endif // THINGSBOARD_ENABLE_OTA + #if THINGSBOARD_ENABLE_DYNAMIC // Buffer that we deserialize is writeable and not read only --> zero copy, meaning the size for the data is 0 bytes, // Data structure size depends on the amount of key value pairs received. @@ -1937,10 +1946,6 @@ class ThingsBoardSized { process_shared_attribute_update_message(topic, data); } else if (strncmp_P(PROV_RESPONSE_TOPIC, topic, strlen(PROV_RESPONSE_TOPIC)) == 0) { process_provisioning_response(topic, data); - } else if (strncmp_P(FIRMWARE_RESPONSE_TOPIC, topic, strlen(FIRMWARE_RESPONSE_TOPIC)) == 0) { -#if THINGSBOARD_ENABLE_OTA - process_firmware_response(topic, payload, length); -#endif // THINGSBOARD_ENABLE_OTA } } From dd1af64e21a4f686cdf2dbdb71dc489ba008af93 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 12 Sep 2023 19:16:42 +0200 Subject: [PATCH 47/80] Implement Espressif Updater with esp_ota_ops. --- src/Arduino_ESP32_Updater.h | 1 + src/Arduino_ESP8266_Updater.h | 1 + src/Configuration.h | 4 +- src/Espressif_MQTT_Client.cpp | 454 +++++++++++++++++++--------------- src/Espressif_Updater.cpp | 208 ++-------------- src/Espressif_Updater.h | 33 +-- src/ThingsBoard.h | 6 +- 7 files changed, 280 insertions(+), 427 deletions(-) diff --git a/src/Arduino_ESP32_Updater.h b/src/Arduino_ESP32_Updater.h index 9715fb91..ba8fec6f 100644 --- a/src/Arduino_ESP32_Updater.h +++ b/src/Arduino_ESP32_Updater.h @@ -18,6 +18,7 @@ #include "IUpdater.h" +/// @brief IUpdater implementation using the Arduino Update class in the background, all calls are simply forwarded directly class Arduino_ESP32_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; diff --git a/src/Arduino_ESP8266_Updater.h b/src/Arduino_ESP8266_Updater.h index 65fc77f9..718c61a8 100644 --- a/src/Arduino_ESP8266_Updater.h +++ b/src/Arduino_ESP8266_Updater.h @@ -18,6 +18,7 @@ #include "IUpdater.h" +/// @brief IUpdater implementation using the Arduino Updater class in the background, all calls are simply forwarded directly class Arduino_ESP8266_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; diff --git a/src/Configuration.h b/src/Configuration.h index 0396a65f..1d74718d 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -100,10 +100,10 @@ # define THINGSBOARD_USE_MBED_TLS 0 # endif -// Use the espressif partitions header internally for handling the writing of ota update data, as long as the header exists, +// Use the esp_ota_ops header internally for handling the writing of ota update data, as long as the header exists, // to allow users that do have the needed component to use the Espressif_Updater instead of only the Arduino_ESP32_Updater. # ifdef __has_include -# if __has_include() && (__has_include() || __has_include()) && __has_include() +# if __has_include() # ifndef THINGSBOARD_USE_ESP_PARTITION # define THINGSBOARD_USE_ESP_PARTITION 1 # endif diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index d640494d..fb44153d 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -1,259 +1,303 @@ // Header include. -#include "Espressif_MQTT_Client.h" +#include "Espressif_Updater.h" -#if THINGSBOARD_USE_ESP_MQTT +#if THINGSBOARD_ENABLE_OTA -constexpr int MQTT_FAILURE_MESSAGE_ID = -1; +#if THINGSBOARD_USE_ESP_PARTITION -Espressif_MQTT_Client *Espressif_MQTT_Client::m_instance = nullptr; - -Espressif_MQTT_Client::Espressif_MQTT_Client() : - m_received_data_callback(nullptr), - m_connected(false), - m_mqtt_configuration(), - m_mqtt_client(nullptr) -{ - m_instance = this; -} - -bool Espressif_MQTT_Client::set_server_certificate(const char *server_certificate_pem) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely. - // Because PEM format is expected for the server certificate we do not need to set the certificate_len, - // because the PEM format expects a null-terminated string. +// Library include. +#include +#include +#include +#include +// ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the old esp_spi_flash.h header has been deprecated and spi_flash_mmap should be used instead. #if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.cert_pem = server_certificate_pem; +#include #else - m_mqtt_configuration.broker.verification.certificate = server_certificate_pem; +#include #endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); -} +#include -bool Espressif_MQTT_Client::set_keep_alive_timeout(const uint16_t& keep_alive_timeout_seconds) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.keepalive = keep_alive_timeout_seconds; -#else - m_mqtt_configuration.session.keepalive = keep_alive_timeout_seconds; -#endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); -} -bool Espressif_MQTT_Client::set_disable_keep_alive(const bool& disable_keep_alive) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.disable_keepalive = disable_keep_alive; -#else - m_mqtt_configuration.session.disable_keepalive = disable_keep_alive; -#endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); -} +#include -bool Espressif_MQTT_Client::set_disable_auto_reconnect(const bool& disable_auto_reconnect) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.disable_auto_reconnect = disable_auto_reconnect; -#else - m_mqtt_configuration.network.disable_auto_reconnect = disable_auto_reconnect; -#endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); -} -bool Espressif_MQTT_Client::set_mqtt_task_configuration(const uint8_t& priority, const uint16_t& stack_size) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.task_prio = priority; - m_mqtt_configuration.task_stack = stack_size; -#else - m_mqtt_configuration.task.priority = priority; - m_mqtt_configuration.task.stack_size = stack_size; -#endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); -} +constexpr size_t SPI_FLASH_ALIGNMENT = SPI_FLASH_SEC_SIZE; +constexpr size_t SPI_FLASH_BLOCK_SIZE = SPI_FLASH_ALIGNMENT * SPI_FLASH_HEADER_SIZE; -bool Espressif_MQTT_Client::set_reconnect_timeout(const uint16_t& reconnect_timeout_milliseconds) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.reconnect_timeout_ms = reconnect_timeout_milliseconds; -#else - m_mqtt_configuration.network.reconnect_timeout_ms = reconnect_timeout_milliseconds; -#endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); -} -bool Espressif_MQTT_Client::set_network_timeout(const uint16_t& network_timeout_milliseconds) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.network_timeout_ms = network_timeout_milliseconds; -#else - m_mqtt_configuration.network.timeout_ms = network_timeout_milliseconds; -#endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); +Espressif_Updater::Espressif_Updater() : + m_data_buffer(nullptr), + m_start_data_buffer(), + m_written_bytes_amount(0U), + m_total_bytes_amount(0U), + m_buffer_length(0U), + m_ota_partition(nullptr) +{ + // Nothing to do } -void Espressif_MQTT_Client::set_callback(function cb) { - m_received_data_callback = cb; +bool Espressif_Updater::begin(const size_t& firmware_size) { + if (started_update() || firmware_size == 0U) { + return false; + } + + reset(); + + const esp_partition_t *ota_parition = esp_ota_get_next_update_partition(nullptr); + + if (ota_parition == nullptr) { + return false; + } + + if (firmware_size > ota_parition->size) { + return false; + } + + m_total_bytes_amount = firmware_size; + + if (firmware_size == OTA_SIZE_UNKNOWN) { + m_total_bytes_amount = ota_parition->size; + } + + // Buffer to write memory is created with the needed alignment size, + // writing less or not to 4KB aligned data to flash memory is not possible + m_data_buffer = new uint8_t[SPI_FLASH_ALIGNMENT]; + + if (m_data_buffer == nullptr){ + return false; + } + + m_ota_partition = ota_parition; + return true; } -bool Espressif_MQTT_Client::set_buffer_size(const uint16_t& buffer_size) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.buffer_size = buffer_size; -#else - m_mqtt_configuration.buffer.size = buffer_size; -#endif // ESP_IDF_VERSION_MAJOR < 5 - return update_configuration(); +size_t Espressif_Updater::write(uint8_t* payload, const size_t& total_bytes) { + if (!started_update()) { + return 0U; + } + + if (total_bytes > remaining_bytes()) { + reset(); + return 0U; + } + + size_t bytes_to_write = total_bytes; + + while ((m_buffer_length + bytes_to_write) > SPI_FLASH_ALIGNMENT) { + const size_t buffer_size = SPI_FLASH_ALIGNMENT - m_buffer_length; + std::copy(payload + (total_bytes - bytes_to_write), payload + buffer_size, m_data_buffer); + m_buffer_length += buffer_size; + if (!write_buffer()) { + return total_bytes - bytes_to_write; + } + bytes_to_write -= buffer_size; + } + + std::copy(payload + (total_bytes - bytes_to_write), payload + bytes_to_write, m_data_buffer); + m_buffer_length += bytes_to_write; + + if (m_buffer_length == remaining_bytes()) { + if (!write_buffer()) { + return total_bytes - bytes_to_write; + } + } + return total_bytes; } -uint16_t Espressif_MQTT_Client::get_buffer_size() { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - return m_mqtt_configuration.buffer_size; -#else - return m_mqtt_configuration.buffer.size; -#endif // ESP_IDF_VERSION_MAJOR < 5 +void Espressif_Updater::reset() { + delete[] m_data_buffer; + m_written_bytes_amount = 0U; + m_total_bytes_amount = 0U; + m_buffer_length = 0U; + m_ota_partition = nullptr; } -void Espressif_MQTT_Client::set_server(const char *domain, const uint16_t& port) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.host = domain; - m_mqtt_configuration.port = port; - // Decide transport depending on if a certificate was passed, because the set_server() method is called in the connect method meaning if the certificate has not been set yet, - // it is to late as we attempt to establish the connection in the connect() method which is called directly after this one. - const bool transport_over_sll = m_mqtt_configuration.cert_pem != nullptr; -#else - m_mqtt_configuration.broker.address.hostname = domain; - m_mqtt_configuration.broker.address.port = port; - // Decide transport depending on if a certificate was passed, because the set_server() method is called in the connect method meaning if the certificate has not been set yet, - // it is to late as we attempt to establish the connection in the connect() method which is called directly after this one. - const bool transport_over_sll = m_mqtt_configuration.broker.verification.certificate != nullptr; -#endif // ESP_IDF_VERSION_MAJOR < 5 +bool Espressif_Updater::end() { + if (m_total_bytes_amount == 0) { + return false; + } - const esp_mqtt_transport_t transport = (transport_over_sll ? esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_SSL : esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_TCP); + if (!is_finished()) { + reset(); + return false; + } - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.transport = transport; -#else - m_mqtt_configuration.broker.address.transport = transport; -#endif // ESP_IDF_VERSION_MAJOR < 5 + return verify_end(); } -bool Espressif_MQTT_Client::connect(const char *client_id, const char *user_name, const char *password) { - // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely -#if ESP_IDF_VERSION_MAJOR < 5 - m_mqtt_configuration.client_id = client_id; - m_mqtt_configuration.username = user_name; - m_mqtt_configuration.password = password; -#else - m_mqtt_configuration.credentials.client_id = client_id; - m_mqtt_configuration.credentials.username = user_name; - m_mqtt_configuration.credentials.authentication.password = password; -#endif // ESP_IDF_VERSION_MAJOR < 5 +bool Espressif_Updater::write_buffer() { + // First x amount of bytes of the firmware, which are initally not written to ensure a not completly written firmware is not bootable + size_t skip_bytes_amount = 0U; + + if (m_written_bytes_amount == 0U) { + // Check wheter the magic byte has been set or not + if (m_data_buffer[0] != ESP_IMAGE_HEADER_MAGIC) { + reset(); + return false; + } + + // Stash the first 16 bytes of data and set the offset so they are + // not written at this point so that partially written firmware + // will not be bootable + skip_bytes_amount = SPI_FLASH_HEADER_SIZE; + std::copy(m_data_buffer, m_data_buffer + skip_bytes_amount, m_start_data_buffer); + } + + const esp_partition_t *partition = static_cast(m_ota_partition); + + const size_t write_address = partition->address + m_written_bytes_amount; + // If it's the block boundary, than erase the whole block from here + const bool block_erase = (m_total_bytes_amount - m_written_bytes_amount >= SPI_FLASH_BLOCK_SIZE) && (write_address % SPI_FLASH_BLOCK_SIZE == 0); + // Sector belongs to unaligned partition heading block + const bool part_head_sectors = partition->address % SPI_FLASH_BLOCK_SIZE && write_address < (partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; + // Sector belongs to unaligned partition tailing block + const bool part_tail_sectors = write_address >= (partition->address + m_total_bytes_amount) / (SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE); + + if (block_erase || part_head_sectors || part_tail_sectors) { + const esp_err_t error = esp_partition_erase_range(partition, m_written_bytes_amount, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_ALIGNMENT); + if (error != ESP_OK) { + reset(); + return false; + } + } + + // Try to skip empty blocks on unecrypted partitions + const size_t skip_block_amount = skip_bytes_amount / sizeof(uint32_t); + const bool block_is_empty = (partition->encrypted || check_if_block_is_not_empty(m_data_buffer + skip_block_amount, m_buffer_length - skip_bytes_amount)); + if (block_is_empty) { + const esp_err_t error = esp_partition_write(partition, m_written_bytes_amount + skip_bytes_amount, reinterpret_cast(m_data_buffer) + skip_block_amount, m_buffer_length - skip_bytes_amount); + if (error != ESP_OK) { + reset(); + return false; + } + } - // The client is first initalized once the connect has actually been called, this is done because the passed setting are required for the client inizialitation structure, - // additionally before we attempt to connect with the client we have to ensure it is configued by then. - m_mqtt_client = esp_mqtt_client_init(&m_mqtt_configuration); + m_written_bytes_amount += m_buffer_length; + m_buffer_length = 0; + return true; - // The last argument may be used to pass data to the event handler, here that would be the static_mqtt_event_handler. But for our use case this is not needed, - // because the static_mqtt_event_handler calls a private method on this class again anyway, meaning we already have access to all private member variables that are required - esp_err_t error = esp_mqtt_client_register_event(m_mqtt_client, esp_mqtt_event_id_t::MQTT_EVENT_ANY, Espressif_MQTT_Client::static_mqtt_event_handler, nullptr); - if (error != ESP_OK) { + + + + //first bytes of new firmware + uint8_t skip = 0; + if(!_progress && _command == U_FLASH){ + //check magic + if(_buffer[0] != ESP_IMAGE_HEADER_MAGIC){ + _abort(UPDATE_ERROR_MAGIC_BYTE); + return false; + } + + //Stash the first 16 bytes of data and set the offset so they are + //not written at this point so that partially written firmware + //will not be bootable + skip = ENCRYPTED_BLOCK_SIZE; + _skipBuffer = (uint8_t*)malloc(skip); + if(!_skipBuffer){ + log_e("malloc failed"); return false; + } + memcpy(_skipBuffer, _buffer, skip); + } + if (!_progress && _progress_callback) { + _progress_callback(0, _size); + } + size_t offset = _partition->address + _progress; + bool block_erase = (_size - _progress >= SPI_FLASH_BLOCK_SIZE) && (offset % SPI_FLASH_BLOCK_SIZE == 0); // if it's the block boundary, than erase the whole block from here + bool part_head_sectors = _partition->address % SPI_FLASH_BLOCK_SIZE && offset < (_partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition heading block + bool part_tail_sectors = offset >= (_partition->address + _size) / (SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE); // sector belong to unaligned partition tailing block + if (block_erase || part_head_sectors || part_tail_sectors){ + if(!ESP.partitionEraseRange(_partition, _progress, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_SEC_SIZE)){ + _abort(UPDATE_ERROR_ERASE); + return false; + } } - error = esp_mqtt_client_start(m_mqtt_client); - return error == ESP_OK; -} + // try to skip empty blocks on unecrypted partitions + if ((_partition->encrypted || check_if_block_is_not_empty(_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) && !ESP.partitionWrite(_partition, _progress + skip, (uint32_t*)_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) { + reset(); + return false; + } -void Espressif_MQTT_Client::disconnect() { - (void)esp_mqtt_client_disconnect(m_mqtt_client); + m_written_bytes_amount += m_buffer_length; + m_buffer_length = 0; + return true; } -bool Espressif_MQTT_Client::loop() { - // Unused because the esp mqtt client uses its own task to handle receiving and sending of data, therefore we do not need to do anything in the loop method. - // Because the loop method is meant for clients that do not have their own process method but instead rely on the upper level code calling a loop method to provide processsing time. - return m_connected; -} +bool Espressif_Updater::verify_end() { + if (!write_start_data() || !is_partition_bootable()) { + reset(); + return false; + } + + if (esp_ota_set_boot_partition(static_cast(m_ota_partition))) { + reset(); + return false; + } -bool Espressif_MQTT_Client::publish(const char *topic, const uint8_t *payload, const size_t& length) { - // The blocking version esp_mqtt_client_publish() it is sent directly from the users task context. - // This way is used to send messages to the cloud, because like that no internal buffer has to be used to store the message until it should be sent, - // because all messages are sent with QoS level 0. If this is not wanted esp_mqtt_client_enqueue() could be used with store = true, - // to ensure the sending is done in the mqtt event context instead of the users task context. - // Allows to use the publish method without having to worry about any CPU overhead, so it can even be used in callbacks or high priority tasks, without starving other tasks. - const int message_id = esp_mqtt_client_publish(m_mqtt_client, topic, reinterpret_cast(payload), length, 0U, 0U); - return message_id != MQTT_FAILURE_MESSAGE_ID; + reset(); + return true; } -bool Espressif_MQTT_Client::subscribe(const char *topic) { - const int message_id = esp_mqtt_client_subscribe(m_mqtt_client, topic, 0U); - return message_id != MQTT_FAILURE_MESSAGE_ID; +bool Espressif_Updater::started_update() const { + return m_total_bytes_amount != 0U; } -bool Espressif_MQTT_Client::unsubscribe(const char *topic) { - const int message_id = esp_mqtt_client_unsubscribe(m_mqtt_client, topic); - return message_id != MQTT_FAILURE_MESSAGE_ID; +size_t Espressif_Updater::remaining_bytes() const { + return m_total_bytes_amount - m_written_bytes_amount; } -bool Espressif_MQTT_Client::connected() { - return m_connected; +bool Espressif_Updater::is_partition_bootable() const { + if (m_ota_partition == nullptr) { + return false; + } + + uint8_t buf[SPI_FLASH_HEADER_SIZE]; + const esp_err_t error = esp_partition_read(static_cast(m_ota_partition), 0U, (uint32_t*)buf, SPI_FLASH_HEADER_SIZE); + if (error != ESP_OK) { + return false; + } + + if (buf[0] != ESP_IMAGE_HEADER_MAGIC) { + return false; + } + return true; } -bool Espressif_MQTT_Client::update_configuration() { - // Check if the client has been initalized, because if it did not the value should still be nullptr - // and updating the config makes no sense because the changed settings will be applied anyway when the client is first intialized - if (m_mqtt_client == nullptr) { - return true; +bool Espressif_Updater::write_start_data() const { + if (m_ota_partition == nullptr) { + return false; } - const esp_err_t error = esp_mqtt_set_config(m_mqtt_client, &m_mqtt_configuration); + const esp_err_t error = esp_partition_write(static_cast(m_ota_partition), 0U, reinterpret_cast(m_start_data_buffer), SPI_FLASH_HEADER_SIZE); return error == ESP_OK; } -void Espressif_MQTT_Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data) { - const esp_mqtt_event_handle_t event = static_cast(event_data); - - switch (event_id) { - case esp_mqtt_event_id_t::MQTT_EVENT_CONNECTED: - m_connected = true; - break; - case esp_mqtt_event_id_t::MQTT_EVENT_DISCONNECTED: - m_connected = false; - break; - case esp_mqtt_event_id_t::MQTT_EVENT_SUBSCRIBED: - // Nothing to do - break; - case esp_mqtt_event_id_t::MQTT_EVENT_UNSUBSCRIBED: - // Nothing to do - break; - case esp_mqtt_event_id_t::MQTT_EVENT_PUBLISHED: - // Nothing to do - break; - case esp_mqtt_event_id_t::MQTT_EVENT_DATA: - if (m_received_data_callback != nullptr) { - m_received_data_callback(event->topic, reinterpret_cast(event->data), event->data_len); - } - break; - case esp_mqtt_event_id_t::MQTT_EVENT_ERROR: - // Nothing to do - break; - case esp_mqtt_event_id_t::MQTT_EVENT_BEFORE_CONNECT: - // Nothing to do - break; - default: - // Nothing to do - break; - } +bool Espressif_Updater::is_finished() const { + return m_total_bytes_amount == m_written_bytes_amount; } -void Espressif_MQTT_Client::static_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { - if (m_instance == nullptr) { - return; +bool Espressif_Updater::check_if_block_is_not_empty(const uint8_t* payload, const size_t& total_bytes) const { + // check 32-bit aligned blocks only + if (!total_bytes || total_bytes % sizeof(uint32_t)) { + return true; } - m_instance->mqtt_event_handler(handler_args, base, static_cast(event_id), event_data); + size_t dwl = total_bytes / sizeof(uint32_t); + + do { + // for SPI NOR flash empty blocks are all one's, i.e. filled with 0xff byte + if (*(uint32_t*)payload ^ 0xffffffff) { + return true; + } + + payload += sizeof(uint32_t); + } while (--dwl); + return false; } -#endif // THINGSBOARD_USE_ESP_MQTT +#endif // THINGSBOARD_USE_ESP_PARTITION + +#endif // THINGSBOARD_ENABLE_OTA diff --git a/src/Espressif_Updater.cpp b/src/Espressif_Updater.cpp index 745fbccd..34e522fd 100644 --- a/src/Espressif_Updater.cpp +++ b/src/Espressif_Updater.cpp @@ -7,229 +7,61 @@ // Library include. #include -#include -// ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the old esp_spi_flash.h header has been deprecated and spi_flash_mmap should be used instead. -#if ESP_IDF_VERSION_MAJOR < 5 -#include -#else -#include -#endif // ESP_IDF_VERSION_MAJOR < 5 -#include -#include - - -constexpr size_t SPI_FLASH_ALIGNMENT = SPI_FLASH_SEC_SIZE; -constexpr size_t SPI_FLASH_BLOCK_SIZE = SPI_FLASH_ALIGNMENT * SPI_FLASH_HEADER_SIZE; Espressif_Updater::Espressif_Updater() : - m_data_buffer(nullptr), - m_start_data_buffer(), - m_written_bytes_amount(0U), - m_total_bytes_amount(0U), - m_buffer_length(0U), - m_ota_partition(nullptr) + m_ota_handle(0U), + m_update_partition(nullptr) { // Nothing to do } bool Espressif_Updater::begin(const size_t& firmware_size) { - if (m_total_bytes_amount) { + const esp_partition_t *running = esp_ota_get_running_partition(); + const esp_partition_t *configured = esp_ota_get_boot_partition(); + + if (configured != running) { return false; } - reset(); + const esp_partition_t *update_partition = esp_ota_get_next_update_partition(nullptr); - const esp_partition_t *ota_parition = esp_ota_get_next_update_partition(nullptr); - - if (ota_parition == nullptr) { + if (update_partition == nullptr) { return false; } - if (firmware_size > ota_parition->size) { + esp_ota_handle_t ota_handle; + const esp_err_t error = esp_ota_begin(update_partition, firmware_size, &ota_handle); + + if (error != ESP_OK) { return false; } - m_total_bytes_amount = firmware_size; - m_ota_partition = ota_parition; + m_ota_handle = ota_handle; + m_update_partition = update_partition; return true; } size_t Espressif_Updater::write(uint8_t* payload, const size_t& total_bytes) { - if (!started_update()) { - return 0U; - } - - if (total_bytes > remaining_bytes()) { - reset(); - return 0U; - } - - size_t bytes_to_write = total_bytes; - - while ((m_buffer_length + bytes_to_write) > SPI_FLASH_ALIGNMENT) { - size_t buffer_size = SPI_FLASH_ALIGNMENT - m_buffer_length; - memcpy(m_data_buffer + m_buffer_length, payload + (total_bytes - bytes_to_write), buffer_size); - m_buffer_length += buffer_size; - if (!write_buffer()) { - return total_bytes - bytes_to_write; - } - bytes_to_write -= buffer_size; - } - - memcpy(m_data_buffer + m_buffer_length, payload + (total_bytes - bytes_to_write), bytes_to_write); - m_buffer_length += bytes_to_write; - - if (m_buffer_length == remaining_bytes()) { - if (!write_buffer()) { - return total_bytes - bytes_to_write; - } - } - return total_bytes; + const esp_err_t error = esp_ota_write(m_ota_handle, payload, total_bytes); + const size_t written_bytes = (error == ESP_OK) ? total_bytes : 0U; + return written_bytes; } void Espressif_Updater::reset() { - delete[] m_data_buffer; - m_written_bytes_amount = 0U; - m_total_bytes_amount = 0U; - m_buffer_length = 0U; - m_ota_partition = nullptr; + (void)esp_ota_abort(m_ota_handle); } bool Espressif_Updater::end() { - if (m_total_bytes_amount == 0) { - return false; - } - - if (!is_finished()) { - reset(); - return false; - } - - return verify_end(); -} - -bool Espressif_Updater::write_buffer() { - // First x amount of bytes of the firmware, which are initally not written to ensure a not completly written firmware is not bootable - size_t skip_bytes_amount = 0U; - - if (m_written_bytes_amount == 0U) { - // Check wheter the magic byte has been set or not - if (m_data_buffer[0] != ESP_IMAGE_HEADER_MAGIC) { - reset(); - return false; - } - - // Stash the first 16 bytes of data and set the offset so they are - // not written at this point so that partially written firmware - // will not be bootable - skip_bytes_amount = SPI_FLASH_HEADER_SIZE; - memcpy(m_start_data_buffer, m_data_buffer, SPI_FLASH_HEADER_SIZE); - } - - const esp_partition_t *partition = static_cast(m_ota_partition); - - size_t offset = partition->address + m_written_bytes_amount; - bool block_erase = (m_total_bytes_amount - m_written_bytes_amount >= SPI_FLASH_BLOCK_SIZE) && (offset % SPI_FLASH_BLOCK_SIZE == 0); // if it's the block boundary, than erase the whole block from here - bool part_head_sectors = partition->address % SPI_FLASH_BLOCK_SIZE && offset < (partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition heading block - bool part_tail_sectors = offset >= (partition->address + m_total_bytes_amount) / SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition tailing block - if (block_erase || part_head_sectors || part_tail_sectors) { - const esp_err_t error = esp_partition_erase_range(partition, m_written_bytes_amount, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_ALIGNMENT); - if (error != ESP_OK) { - reset(); - return false; - } - } - - // try to skip empty blocks on unecrypted partitions - size_t skip_block_amount = skip_bytes_amount / sizeof(uint32_t); - if ((partition->encrypted || check_if_block_is_empty(m_data_buffer + skip_block_amount, m_buffer_length - skip_bytes_amount)) && esp_partition_write(partition, m_written_bytes_amount + skip_bytes_amount, reinterpret_cast(m_data_buffer) + skip_block_amount, m_buffer_length - skip_bytes_amount) != ESP_OK) { - reset(); - return false; - } - - // Restore magic byte - if (m_written_bytes_amount == 0U) { - m_data_buffer[0] = ESP_IMAGE_HEADER_MAGIC; - } - - m_written_bytes_amount += m_buffer_length; - m_buffer_length = 0; - return true; -} - -bool Espressif_Updater::verify_end() { - if (!write_start_data() || !is_partition_bootable()) { - reset(); - return false; - } - - if (esp_ota_set_boot_partition(static_cast(m_ota_partition))) { - reset(); - return false; - } - - reset(); - return true; -} - -bool Espressif_Updater::started_update() const { - return m_total_bytes_amount != 0U; -} - -size_t Espressif_Updater::remaining_bytes() const { - return m_total_bytes_amount - m_written_bytes_amount; -} - -bool Espressif_Updater::is_partition_bootable() const { - if (m_ota_partition == nullptr) { - return false; - } - - uint8_t buf[SPI_FLASH_HEADER_SIZE]; - const esp_err_t error = esp_partition_read(static_cast(m_ota_partition), 0, (uint32_t*)buf, SPI_FLASH_HEADER_SIZE); + esp_err_t error = esp_ota_end(m_ota_handle); if (error != ESP_OK) { return false; } - if (buf[0] != ESP_IMAGE_HEADER_MAGIC) { - return false; - } - return true; -} - -bool Espressif_Updater::write_start_data() const { - if (m_ota_partition == nullptr) { - return false; - } - - const esp_err_t error = esp_partition_write(static_cast(m_ota_partition), 0, reinterpret_cast(m_start_data_buffer), SPI_FLASH_HEADER_SIZE); + error = esp_ota_set_boot_partition(static_cast(m_update_partition)); return error == ESP_OK; } -bool Espressif_Updater::is_finished() const { - return m_total_bytes_amount == m_written_bytes_amount; -} - -bool Espressif_Updater::check_if_block_is_empty(const uint8_t* payload, const size_t& total_bytes) const { - // check 32-bit aligned blocks only - if (!total_bytes || total_bytes % sizeof(uint32_t)) { - return true; - } - - size_t dwl = total_bytes / sizeof(uint32_t); - - do { - // for SPI NOR flash empty blocks are all one's, i.e. filled with 0xff byte - if (*(uint32_t*)payload ^ 0xffffffff) { - return true; - } - - payload += sizeof(uint32_t); - } while (--dwl); - return false; -} - #endif // THINGSBOARD_USE_ESP_PARTITION #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/Espressif_Updater.h b/src/Espressif_Updater.h index 686d99d9..c99a4b06 100644 --- a/src/Espressif_Updater.h +++ b/src/Espressif_Updater.h @@ -18,9 +18,7 @@ #include "IUpdater.h" -constexpr size_t SPI_FLASH_HEADER_SIZE = 16U; - - +/// @brief IUpdater implementation, which uses the Over the Air Update API of Espressif in the background, see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html for more information class Espressif_Updater : public IUpdater { public: Espressif_Updater(); @@ -32,33 +30,10 @@ class Espressif_Updater : public IUpdater { void reset() override; bool end() override; - - private: - uint8_t *m_data_buffer; - // First 16 bytes are seperated so a partially written firmware is not bootable, - // is written back at the correct place in flash memory at the successfull end of the update. - uint8_t m_start_data_buffer[SPI_FLASH_HEADER_SIZE]; - size_t m_written_bytes_amount; - size_t m_total_bytes_amount; - size_t m_buffer_length; - const void *m_ota_partition; - - bool write_buffer(); - - bool verify_end(); - - bool started_update() const; - - size_t remaining_bytes() const; - - bool is_partition_bootable() const; - - bool write_start_data() const; - - bool is_finished() const; - - bool check_if_block_is_empty(const uint8_t* payload, const size_t& total_bytes) const; + private: + uint32_t m_ota_handle; + const void *m_update_partition; }; #endif // THINGSBOARD_USE_ESP_PARTITION diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 65c05743..f482722a 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -351,15 +351,15 @@ class ThingsBoardSized { #endif // THINGSBOARD_ENABLE_OTA { setBufferSize(bufferSize); + // Initalize callback. #if THINGSBOARD_ENABLE_STL m_client.set_callback(std::bind(&ThingsBoardSized::onMQTTMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); #else m_client.set_callback(ThingsBoardSized::onStaticMQTTMessage); -#endif // THINGSBOARD_ENABLE_STL -#if !THINGSBOARD_ENABLE_STL m_subscribedInstance = this; -#endif // !THINGSBOARD_ENABLE_STL +#endif // THINGSBOARD_ENABLE_STL + #if !THINGSBOARD_ENABLE_DYNAMIC reserve_callback_size(MaxFieldsAmt); #endif // !THINGSBOARD_ENABLE_DYNAMIC From bb94fe2a5974ea1abddec46c96b9d6cccefa9aba Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:29:42 +0200 Subject: [PATCH 48/80] Integrate OTA updates for esp idf --- src/Callback_Watchdog.cpp | 47 ++-- src/Callback_Watchdog.h | 5 + src/Espressif_MQTT_Client.cpp | 454 +++++++++++++++------------------- src/Espressif_Updater.cpp | 3 + 4 files changed, 245 insertions(+), 264 deletions(-) diff --git a/src/Callback_Watchdog.cpp b/src/Callback_Watchdog.cpp index afe4f5fa..9045cc3c 100644 --- a/src/Callback_Watchdog.cpp +++ b/src/Callback_Watchdog.cpp @@ -4,7 +4,9 @@ #if THINGSBOARD_ENABLE_OTA // Library includes. +#if THINGSBOARD_USE_ESP_TIMER #include +#endif // THINGSBOARD_USE_ESP_TIMER #if THINGSBOARD_ENABLE_PROGMEM #include #endif // THINGSBOARD_ENABLE_PROGMEM @@ -26,21 +28,6 @@ Callback_Watchdog::Callback_Watchdog(std::function callback) : #endif // THINGSBOARD_USE_ESP_TIMER { m_instance = this; -#if THINGSBOARD_USE_ESP_TIMER - const esp_timer_create_args_t oneshot_timer_args = { - .callback = &oneshot_timer_callback, - .arg = nullptr, - .dispatch_method = esp_timer_dispatch_t::ESP_TIMER_TASK, - .name = WATCHDOG_TIMER_NAME, - .skip_unhandled_events = false - }; - // Temporary handle is used, because it allows using a void* as the actual oneshot_timer, - // allowing us to only include the esp_timer header in the defintion (.cpp) file, - // instead of also needing to declare it in the declaration (.h) header file - esp_timer_handle_t temp_handle; - (void)esp_timer_create(&oneshot_timer_args, &temp_handle); - m_oneshot_timer = temp_handle; -#endif // THINGSBOARD_USE_ESP_TIMER } Callback_Watchdog::~Callback_Watchdog() { @@ -48,6 +35,7 @@ Callback_Watchdog::~Callback_Watchdog() { // Timer only has to deleted at the end of the lifetime of this object, to ensure no memory leak occurs. // But besides that the same timer can simply be stopped and restarted without needing to delete and create the timer again everytime. (void)esp_timer_delete(static_cast(m_oneshot_timer)); + m_oneshot_timer = nullptr; #else m_oneshot_timer.detach(); #endif // THINGSBOARD_USE_ESP_TIMER @@ -56,6 +44,7 @@ Callback_Watchdog::~Callback_Watchdog() { void Callback_Watchdog::once(const uint64_t& timeout_microseconds) { #if THINGSBOARD_USE_ESP_TIMER + create_timer(); (void)esp_timer_start_once(static_cast(m_oneshot_timer), timeout_microseconds); #else const uint32_t timeout_millis = timeout_microseconds / 1000U; @@ -71,6 +60,34 @@ void Callback_Watchdog::detach() { #endif // THINGSBOARD_USE_ESP_TIMER } +#if THINGSBOARD_USE_ESP_TIMER +void Callback_Watchdog::create_timer() { + // Timer has already been created previously there is no need to create it again + if (m_oneshot_timer != nullptr) { + return; + } + + const esp_timer_create_args_t oneshot_timer_args = { + .callback = &oneshot_timer_callback, + .arg = nullptr, + .dispatch_method = esp_timer_dispatch_t::ESP_TIMER_TASK, + .name = WATCHDOG_TIMER_NAME, + .skip_unhandled_events = false + }; + + // Temporary handle is used, because it allows using a void* as the actual oneshot_timer, + // allowing us to only include the esp_timer header in the defintion (.cpp) file, + // instead of also needing to declare it in the declaration (.h) header file + esp_timer_handle_t temp_handle; + const esp_err_t error = esp_timer_create(&oneshot_timer_args, &temp_handle); + if (error != ESP_OK) { + return; + } + + m_oneshot_timer = temp_handle; +} +#endif // THINGSBOARD_USE_ESP_TIMER + #if THINGSBOARD_USE_ESP_TIMER void Callback_Watchdog::oneshot_timer_callback(void *arg) { #else diff --git a/src/Callback_Watchdog.h b/src/Callback_Watchdog.h index 45124b42..950e5536 100644 --- a/src/Callback_Watchdog.h +++ b/src/Callback_Watchdog.h @@ -49,6 +49,11 @@ class Callback_Watchdog { static Callback_Watchdog *m_instance; +#if THINGSBOARD_USE_ESP_TIMER + /// @brief Creates and initally configures the timer, has to be done once before either esp_timer_start_once or esp_timer_stop is called. + void create_timer(); +#endif // THINGSBOARD_USE_ESP_TIMER + /// @brief Static callback used to call the initally subscribed callback, if the internal watchdog has not been fed with detach() #if THINGSBOARD_USE_ESP_TIMER /// @param arg Possible argument passed to the timer callback diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index fb44153d..d640494d 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -1,303 +1,259 @@ // Header include. -#include "Espressif_Updater.h" +#include "Espressif_MQTT_Client.h" -#if THINGSBOARD_ENABLE_OTA +#if THINGSBOARD_USE_ESP_MQTT -#if THINGSBOARD_USE_ESP_PARTITION +constexpr int MQTT_FAILURE_MESSAGE_ID = -1; -// Library include. -#include -#include -#include -#include -// ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the old esp_spi_flash.h header has been deprecated and spi_flash_mmap should be used instead. -#if ESP_IDF_VERSION_MAJOR < 5 -#include -#else -#include -#endif // ESP_IDF_VERSION_MAJOR < 5 -#include - - -#include - - -constexpr size_t SPI_FLASH_ALIGNMENT = SPI_FLASH_SEC_SIZE; -constexpr size_t SPI_FLASH_BLOCK_SIZE = SPI_FLASH_ALIGNMENT * SPI_FLASH_HEADER_SIZE; +Espressif_MQTT_Client *Espressif_MQTT_Client::m_instance = nullptr; - -Espressif_Updater::Espressif_Updater() : - m_data_buffer(nullptr), - m_start_data_buffer(), - m_written_bytes_amount(0U), - m_total_bytes_amount(0U), - m_buffer_length(0U), - m_ota_partition(nullptr) +Espressif_MQTT_Client::Espressif_MQTT_Client() : + m_received_data_callback(nullptr), + m_connected(false), + m_mqtt_configuration(), + m_mqtt_client(nullptr) { - // Nothing to do + m_instance = this; } -bool Espressif_Updater::begin(const size_t& firmware_size) { - if (started_update() || firmware_size == 0U) { - return false; - } - - reset(); - - const esp_partition_t *ota_parition = esp_ota_get_next_update_partition(nullptr); - - if (ota_parition == nullptr) { - return false; - } - - if (firmware_size > ota_parition->size) { - return false; - } - - m_total_bytes_amount = firmware_size; - - if (firmware_size == OTA_SIZE_UNKNOWN) { - m_total_bytes_amount = ota_parition->size; - } - - // Buffer to write memory is created with the needed alignment size, - // writing less or not to 4KB aligned data to flash memory is not possible - m_data_buffer = new uint8_t[SPI_FLASH_ALIGNMENT]; - - if (m_data_buffer == nullptr){ - return false; - } - - m_ota_partition = ota_parition; - return true; +bool Espressif_MQTT_Client::set_server_certificate(const char *server_certificate_pem) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely. + // Because PEM format is expected for the server certificate we do not need to set the certificate_len, + // because the PEM format expects a null-terminated string. +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.cert_pem = server_certificate_pem; +#else + m_mqtt_configuration.broker.verification.certificate = server_certificate_pem; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); } -size_t Espressif_Updater::write(uint8_t* payload, const size_t& total_bytes) { - if (!started_update()) { - return 0U; - } - - if (total_bytes > remaining_bytes()) { - reset(); - return 0U; - } - - size_t bytes_to_write = total_bytes; - - while ((m_buffer_length + bytes_to_write) > SPI_FLASH_ALIGNMENT) { - const size_t buffer_size = SPI_FLASH_ALIGNMENT - m_buffer_length; - std::copy(payload + (total_bytes - bytes_to_write), payload + buffer_size, m_data_buffer); - m_buffer_length += buffer_size; - if (!write_buffer()) { - return total_bytes - bytes_to_write; - } - bytes_to_write -= buffer_size; - } - - std::copy(payload + (total_bytes - bytes_to_write), payload + bytes_to_write, m_data_buffer); - m_buffer_length += bytes_to_write; +bool Espressif_MQTT_Client::set_keep_alive_timeout(const uint16_t& keep_alive_timeout_seconds) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.keepalive = keep_alive_timeout_seconds; +#else + m_mqtt_configuration.session.keepalive = keep_alive_timeout_seconds; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} - if (m_buffer_length == remaining_bytes()) { - if (!write_buffer()) { - return total_bytes - bytes_to_write; - } - } - return total_bytes; +bool Espressif_MQTT_Client::set_disable_keep_alive(const bool& disable_keep_alive) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.disable_keepalive = disable_keep_alive; +#else + m_mqtt_configuration.session.disable_keepalive = disable_keep_alive; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); } -void Espressif_Updater::reset() { - delete[] m_data_buffer; - m_written_bytes_amount = 0U; - m_total_bytes_amount = 0U; - m_buffer_length = 0U; - m_ota_partition = nullptr; +bool Espressif_MQTT_Client::set_disable_auto_reconnect(const bool& disable_auto_reconnect) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.disable_auto_reconnect = disable_auto_reconnect; +#else + m_mqtt_configuration.network.disable_auto_reconnect = disable_auto_reconnect; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); } -bool Espressif_Updater::end() { - if (m_total_bytes_amount == 0) { - return false; - } +bool Espressif_MQTT_Client::set_mqtt_task_configuration(const uint8_t& priority, const uint16_t& stack_size) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.task_prio = priority; + m_mqtt_configuration.task_stack = stack_size; +#else + m_mqtt_configuration.task.priority = priority; + m_mqtt_configuration.task.stack_size = stack_size; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} - if (!is_finished()) { - reset(); - return false; - } +bool Espressif_MQTT_Client::set_reconnect_timeout(const uint16_t& reconnect_timeout_milliseconds) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.reconnect_timeout_ms = reconnect_timeout_milliseconds; +#else + m_mqtt_configuration.network.reconnect_timeout_ms = reconnect_timeout_milliseconds; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} - return verify_end(); +bool Espressif_MQTT_Client::set_network_timeout(const uint16_t& network_timeout_milliseconds) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.network_timeout_ms = network_timeout_milliseconds; +#else + m_mqtt_configuration.network.timeout_ms = network_timeout_milliseconds; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); } -bool Espressif_Updater::write_buffer() { - // First x amount of bytes of the firmware, which are initally not written to ensure a not completly written firmware is not bootable - size_t skip_bytes_amount = 0U; - - if (m_written_bytes_amount == 0U) { - // Check wheter the magic byte has been set or not - if (m_data_buffer[0] != ESP_IMAGE_HEADER_MAGIC) { - reset(); - return false; - } - - // Stash the first 16 bytes of data and set the offset so they are - // not written at this point so that partially written firmware - // will not be bootable - skip_bytes_amount = SPI_FLASH_HEADER_SIZE; - std::copy(m_data_buffer, m_data_buffer + skip_bytes_amount, m_start_data_buffer); - } - - const esp_partition_t *partition = static_cast(m_ota_partition); - - const size_t write_address = partition->address + m_written_bytes_amount; - // If it's the block boundary, than erase the whole block from here - const bool block_erase = (m_total_bytes_amount - m_written_bytes_amount >= SPI_FLASH_BLOCK_SIZE) && (write_address % SPI_FLASH_BLOCK_SIZE == 0); - // Sector belongs to unaligned partition heading block - const bool part_head_sectors = partition->address % SPI_FLASH_BLOCK_SIZE && write_address < (partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; - // Sector belongs to unaligned partition tailing block - const bool part_tail_sectors = write_address >= (partition->address + m_total_bytes_amount) / (SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE); - - if (block_erase || part_head_sectors || part_tail_sectors) { - const esp_err_t error = esp_partition_erase_range(partition, m_written_bytes_amount, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_ALIGNMENT); - if (error != ESP_OK) { - reset(); - return false; - } - } +void Espressif_MQTT_Client::set_callback(function cb) { + m_received_data_callback = cb; +} - // Try to skip empty blocks on unecrypted partitions - const size_t skip_block_amount = skip_bytes_amount / sizeof(uint32_t); - const bool block_is_empty = (partition->encrypted || check_if_block_is_not_empty(m_data_buffer + skip_block_amount, m_buffer_length - skip_bytes_amount)); - if (block_is_empty) { - const esp_err_t error = esp_partition_write(partition, m_written_bytes_amount + skip_bytes_amount, reinterpret_cast(m_data_buffer) + skip_block_amount, m_buffer_length - skip_bytes_amount); - if (error != ESP_OK) { - reset(); - return false; - } - } +bool Espressif_MQTT_Client::set_buffer_size(const uint16_t& buffer_size) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.buffer_size = buffer_size; +#else + m_mqtt_configuration.buffer.size = buffer_size; +#endif // ESP_IDF_VERSION_MAJOR < 5 + return update_configuration(); +} - m_written_bytes_amount += m_buffer_length; - m_buffer_length = 0; - return true; +uint16_t Espressif_MQTT_Client::get_buffer_size() { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + return m_mqtt_configuration.buffer_size; +#else + return m_mqtt_configuration.buffer.size; +#endif // ESP_IDF_VERSION_MAJOR < 5 +} +void Espressif_MQTT_Client::set_server(const char *domain, const uint16_t& port) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.host = domain; + m_mqtt_configuration.port = port; + // Decide transport depending on if a certificate was passed, because the set_server() method is called in the connect method meaning if the certificate has not been set yet, + // it is to late as we attempt to establish the connection in the connect() method which is called directly after this one. + const bool transport_over_sll = m_mqtt_configuration.cert_pem != nullptr; +#else + m_mqtt_configuration.broker.address.hostname = domain; + m_mqtt_configuration.broker.address.port = port; + // Decide transport depending on if a certificate was passed, because the set_server() method is called in the connect method meaning if the certificate has not been set yet, + // it is to late as we attempt to establish the connection in the connect() method which is called directly after this one. + const bool transport_over_sll = m_mqtt_configuration.broker.verification.certificate != nullptr; +#endif // ESP_IDF_VERSION_MAJOR < 5 + const esp_mqtt_transport_t transport = (transport_over_sll ? esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_SSL : esp_mqtt_transport_t::MQTT_TRANSPORT_OVER_TCP); + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.transport = transport; +#else + m_mqtt_configuration.broker.address.transport = transport; +#endif // ESP_IDF_VERSION_MAJOR < 5 +} +bool Espressif_MQTT_Client::connect(const char *client_id, const char *user_name, const char *password) { + // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely +#if ESP_IDF_VERSION_MAJOR < 5 + m_mqtt_configuration.client_id = client_id; + m_mqtt_configuration.username = user_name; + m_mqtt_configuration.password = password; +#else + m_mqtt_configuration.credentials.client_id = client_id; + m_mqtt_configuration.credentials.username = user_name; + m_mqtt_configuration.credentials.authentication.password = password; +#endif // ESP_IDF_VERSION_MAJOR < 5 - //first bytes of new firmware - uint8_t skip = 0; - if(!_progress && _command == U_FLASH){ - //check magic - if(_buffer[0] != ESP_IMAGE_HEADER_MAGIC){ - _abort(UPDATE_ERROR_MAGIC_BYTE); - return false; - } + // The client is first initalized once the connect has actually been called, this is done because the passed setting are required for the client inizialitation structure, + // additionally before we attempt to connect with the client we have to ensure it is configued by then. + m_mqtt_client = esp_mqtt_client_init(&m_mqtt_configuration); - //Stash the first 16 bytes of data and set the offset so they are - //not written at this point so that partially written firmware - //will not be bootable - skip = ENCRYPTED_BLOCK_SIZE; - _skipBuffer = (uint8_t*)malloc(skip); - if(!_skipBuffer){ - log_e("malloc failed"); - return false; - } - memcpy(_skipBuffer, _buffer, skip); - } - if (!_progress && _progress_callback) { - _progress_callback(0, _size); - } - size_t offset = _partition->address + _progress; - bool block_erase = (_size - _progress >= SPI_FLASH_BLOCK_SIZE) && (offset % SPI_FLASH_BLOCK_SIZE == 0); // if it's the block boundary, than erase the whole block from here - bool part_head_sectors = _partition->address % SPI_FLASH_BLOCK_SIZE && offset < (_partition->address / SPI_FLASH_BLOCK_SIZE + 1) * SPI_FLASH_BLOCK_SIZE; // sector belong to unaligned partition heading block - bool part_tail_sectors = offset >= (_partition->address + _size) / (SPI_FLASH_BLOCK_SIZE * SPI_FLASH_BLOCK_SIZE); // sector belong to unaligned partition tailing block - if (block_erase || part_head_sectors || part_tail_sectors){ - if(!ESP.partitionEraseRange(_partition, _progress, block_erase ? SPI_FLASH_BLOCK_SIZE : SPI_FLASH_SEC_SIZE)){ - _abort(UPDATE_ERROR_ERASE); - return false; - } - } + // The last argument may be used to pass data to the event handler, here that would be the static_mqtt_event_handler. But for our use case this is not needed, + // because the static_mqtt_event_handler calls a private method on this class again anyway, meaning we already have access to all private member variables that are required + esp_err_t error = esp_mqtt_client_register_event(m_mqtt_client, esp_mqtt_event_id_t::MQTT_EVENT_ANY, Espressif_MQTT_Client::static_mqtt_event_handler, nullptr); - // try to skip empty blocks on unecrypted partitions - if ((_partition->encrypted || check_if_block_is_not_empty(_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) && !ESP.partitionWrite(_partition, _progress + skip, (uint32_t*)_buffer + skip/sizeof(uint32_t), _bufferLen - skip)) { - reset(); + if (error != ESP_OK) { return false; } - m_written_bytes_amount += m_buffer_length; - m_buffer_length = 0; - return true; + error = esp_mqtt_client_start(m_mqtt_client); + return error == ESP_OK; } -bool Espressif_Updater::verify_end() { - if (!write_start_data() || !is_partition_bootable()) { - reset(); - return false; - } - - if (esp_ota_set_boot_partition(static_cast(m_ota_partition))) { - reset(); - return false; - } - - reset(); - return true; +void Espressif_MQTT_Client::disconnect() { + (void)esp_mqtt_client_disconnect(m_mqtt_client); } -bool Espressif_Updater::started_update() const { - return m_total_bytes_amount != 0U; +bool Espressif_MQTT_Client::loop() { + // Unused because the esp mqtt client uses its own task to handle receiving and sending of data, therefore we do not need to do anything in the loop method. + // Because the loop method is meant for clients that do not have their own process method but instead rely on the upper level code calling a loop method to provide processsing time. + return m_connected; } -size_t Espressif_Updater::remaining_bytes() const { - return m_total_bytes_amount - m_written_bytes_amount; +bool Espressif_MQTT_Client::publish(const char *topic, const uint8_t *payload, const size_t& length) { + // The blocking version esp_mqtt_client_publish() it is sent directly from the users task context. + // This way is used to send messages to the cloud, because like that no internal buffer has to be used to store the message until it should be sent, + // because all messages are sent with QoS level 0. If this is not wanted esp_mqtt_client_enqueue() could be used with store = true, + // to ensure the sending is done in the mqtt event context instead of the users task context. + // Allows to use the publish method without having to worry about any CPU overhead, so it can even be used in callbacks or high priority tasks, without starving other tasks. + const int message_id = esp_mqtt_client_publish(m_mqtt_client, topic, reinterpret_cast(payload), length, 0U, 0U); + return message_id != MQTT_FAILURE_MESSAGE_ID; } -bool Espressif_Updater::is_partition_bootable() const { - if (m_ota_partition == nullptr) { - return false; - } +bool Espressif_MQTT_Client::subscribe(const char *topic) { + const int message_id = esp_mqtt_client_subscribe(m_mqtt_client, topic, 0U); + return message_id != MQTT_FAILURE_MESSAGE_ID; +} - uint8_t buf[SPI_FLASH_HEADER_SIZE]; - const esp_err_t error = esp_partition_read(static_cast(m_ota_partition), 0U, (uint32_t*)buf, SPI_FLASH_HEADER_SIZE); - if (error != ESP_OK) { - return false; - } +bool Espressif_MQTT_Client::unsubscribe(const char *topic) { + const int message_id = esp_mqtt_client_unsubscribe(m_mqtt_client, topic); + return message_id != MQTT_FAILURE_MESSAGE_ID; +} - if (buf[0] != ESP_IMAGE_HEADER_MAGIC) { - return false; - } - return true; +bool Espressif_MQTT_Client::connected() { + return m_connected; } -bool Espressif_Updater::write_start_data() const { - if (m_ota_partition == nullptr) { - return false; +bool Espressif_MQTT_Client::update_configuration() { + // Check if the client has been initalized, because if it did not the value should still be nullptr + // and updating the config makes no sense because the changed settings will be applied anyway when the client is first intialized + if (m_mqtt_client == nullptr) { + return true; } - const esp_err_t error = esp_partition_write(static_cast(m_ota_partition), 0U, reinterpret_cast(m_start_data_buffer), SPI_FLASH_HEADER_SIZE); + const esp_err_t error = esp_mqtt_set_config(m_mqtt_client, &m_mqtt_configuration); return error == ESP_OK; } -bool Espressif_Updater::is_finished() const { - return m_total_bytes_amount == m_written_bytes_amount; +void Espressif_MQTT_Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, const esp_mqtt_event_id_t& event_id, void *event_data) { + const esp_mqtt_event_handle_t event = static_cast(event_data); + + switch (event_id) { + case esp_mqtt_event_id_t::MQTT_EVENT_CONNECTED: + m_connected = true; + break; + case esp_mqtt_event_id_t::MQTT_EVENT_DISCONNECTED: + m_connected = false; + break; + case esp_mqtt_event_id_t::MQTT_EVENT_SUBSCRIBED: + // Nothing to do + break; + case esp_mqtt_event_id_t::MQTT_EVENT_UNSUBSCRIBED: + // Nothing to do + break; + case esp_mqtt_event_id_t::MQTT_EVENT_PUBLISHED: + // Nothing to do + break; + case esp_mqtt_event_id_t::MQTT_EVENT_DATA: + if (m_received_data_callback != nullptr) { + m_received_data_callback(event->topic, reinterpret_cast(event->data), event->data_len); + } + break; + case esp_mqtt_event_id_t::MQTT_EVENT_ERROR: + // Nothing to do + break; + case esp_mqtt_event_id_t::MQTT_EVENT_BEFORE_CONNECT: + // Nothing to do + break; + default: + // Nothing to do + break; + } } -bool Espressif_Updater::check_if_block_is_not_empty(const uint8_t* payload, const size_t& total_bytes) const { - // check 32-bit aligned blocks only - if (!total_bytes || total_bytes % sizeof(uint32_t)) { - return true; +void Espressif_MQTT_Client::static_mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { + if (m_instance == nullptr) { + return; } - size_t dwl = total_bytes / sizeof(uint32_t); - - do { - // for SPI NOR flash empty blocks are all one's, i.e. filled with 0xff byte - if (*(uint32_t*)payload ^ 0xffffffff) { - return true; - } - - payload += sizeof(uint32_t); - } while (--dwl); - return false; + m_instance->mqtt_event_handler(handler_args, base, static_cast(event_id), event_data); } -#endif // THINGSBOARD_USE_ESP_PARTITION - -#endif // THINGSBOARD_ENABLE_OTA +#endif // THINGSBOARD_USE_ESP_MQTT diff --git a/src/Espressif_Updater.cpp b/src/Espressif_Updater.cpp index 34e522fd..7b534ae5 100644 --- a/src/Espressif_Updater.cpp +++ b/src/Espressif_Updater.cpp @@ -30,6 +30,9 @@ bool Espressif_Updater::begin(const size_t& firmware_size) { return false; } + // Temporary handle is used, because it allows using a void* as the actual ota_handle, + // allowing us to only include the esp_ota_ops header in the defintion (.cpp) file, + // instead of also needing to declare it in the declaration (.h) header file esp_ota_handle_t ota_handle; const esp_err_t error = esp_ota_begin(update_partition, firmware_size, &ota_handle); From 80deec1fb9441c01134048902d768bedae727a78 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:46:04 +0200 Subject: [PATCH 49/80] Add Espressif OTA example and add notes for currently open framework issues --- .../0009-esp8266_esp32_process_OTA_MQTT.ino | 13 +- .../README.md | 2 +- .../0011-esp8266_esp32_subscribe_OTA_MQTT.ino | 13 +- .../README.md | 2 +- .../0015-espressif_esp32_process_OTA_MQTT.ino | 218 ++++++++++++++++++ .../README.md | 24 ++ src/Espressif_MQTT_Client.cpp | 17 ++ 7 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino create mode 100644 examples/0015-espressif_esp32_process_OTA_MQTT/README.md diff --git a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino index c963557c..6bf018c5 100644 --- a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino +++ b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino @@ -17,7 +17,16 @@ #include #else #ifdef ESP32 -#include +// We do theoretically have an Arduino_ESP32_Updater component, which has dependencies on Arduino and used the UpdaterClass, +// but it makes not sense to use that component atleast currently, because it simply implements writing to partitions +// in a very suboptimal way, allocatng 4KB on the heap and even causing undefined behaviour and even memory leaks. +// See https://github.com/espressif/arduino-esp32/issues/7984#issuecomment-1717151822 for more information on the issues with the UpdaterClass. +// Therefore instead it is recommended to use the Epsressif_Updater which directly uses the headers, which are included in the UpdaterClass anyway, +// but because it directly use the OTA Update API see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html for more information, +// it is more efficient and does not have any of the aforementioned issues. +// Thanks to those issues it is highly recommended to use the Espressif_Updater as long as the issue has not been resolved on a Arduino level, additional the same is the case +// if an older version of Arduino is used that might not contain the possible fixes yet. +#include #endif // ESP32 #endif // ESP8266 @@ -280,7 +289,7 @@ void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { Arduino_ESP8266_Updater updater; #else #ifdef ESP32 -Arduino_ESP32_Updater updater; +Espressif_Updater updater; #endif // ESP32 #endif // ESP8266 diff --git a/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md b/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md index f8c1872c..ca94a197 100644 --- a/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md +++ b/examples/0009-esp8266_esp32_process_OTA_MQTT/README.md @@ -20,6 +20,6 @@ if the received shared attributes include the same title but a different version ## Remarks Custom Updater implementation can be used to flash any firmware device as long as it supports the STL base functionality. -Simply create an own `IUpdater` implementation similar to `ESP32_Updater` or `ESP8266_Updater`. +Simply create an own `IUpdater` implementation similar to `Arduino_ESP32_Updater` or `Arduino_ESP8266_Updater`. [Thinger IO Arduino library](https://github.com/thinger-io/Arduino-Library) can be used as inspiration on how to write the firmware to [`WiFiStorage for Arduino Nano`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerWiFiNINAOTA.h), [`File Storage for Arduino Nano`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerMbedOTA.h) or [`Second Stage Bootloader (SNB) for Arduino MKRNB`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerMKRNBOTA.h) diff --git a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino index ed5a9785..0fefe852 100644 --- a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino +++ b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino @@ -17,7 +17,16 @@ #include #else #ifdef ESP32 -#include +// We do theoretically have an Arduino_ESP32_Updater component, which has dependencies on Arduino and used the UpdaterClass, +// but it makes not sense to use that component atleast currently, because it simply implements writing to partitions +// in a very suboptimal way, allocatng 4KB on the heap and even causing undefined behaviour and even memory leaks. +// See https://github.com/espressif/arduino-esp32/issues/7984#issuecomment-1717151822 for more information on the issues with the UpdaterClass. +// Therefore instead it is recommended to use the Epsressif_Updater which directly uses the headers, which are included in the UpdaterClass anyway, +// but because it directly use the OTA Update API see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html for more information, +// it is more efficient and does not have any of the aforementioned issues. +// Thanks to those issues it is highly recommended to use the Espressif_Updater as long as the issue has not been resolved on a Arduino level, additional the same is the case +// if an older version of Arduino is used that might not contain the possible fixes yet. +#include #endif // ESP32 #endif // ESP8266 @@ -280,7 +289,7 @@ void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { Arduino_ESP8266_Updater updater; #else #ifdef ESP32 -Arduino_ESP32_Updater updater; +Espressif_Updater updater; #endif // ESP32 #endif // ESP8266 diff --git a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md index d8211638..b8f57130 100644 --- a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md +++ b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/README.md @@ -21,6 +21,6 @@ but a different version number then the one that was passed initially ## Remarks Custom Updater implementation can be used to flash any firmware device as long as it supports the STL base functionality. -Simply create an own `IUpdater` implementation similar to `ESP32_Updater` or `ESP8266_Updater`. +Simply create an own `IUpdater` implementation similar to `Arduino_ESP32_Updater` or `Arduino_ESP8266_Updater`. [Thinger IO Arduino library](https://github.com/thinger-io/Arduino-Library) can be used as inspiration on how to write the firmware to [`WiFiStorage for Arduino Nano`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerWiFiNINAOTA.h), [`File Storage for Arduino Nano`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerMbedOTA.h) or [`Second Stage Bootloader (SNB) for Arduino MKRNB`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerMKRNBOTA.h) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino b/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino new file mode 100644 index 00000000..3c7560de --- /dev/null +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino @@ -0,0 +1,218 @@ +#include +#include +#include +#include + + +// Whether the given script is using encryption or not, +// generally recommended as it increases security (communication with the server is not in clear text anymore), +// it does come with an overhead tough as having an encrypted session requires a lot of memory, +// which might not be avaialable on lower end devices. +#define ENCRYPTED false + + +#include +#include +#include + + +// Firmware title and version used to compare with remote version, to check if an update is needed. +// Title needs to be the same and version needs to be different --> downgrading is possible +constexpr char CURRENT_FIRMWARE_TITLE[] = "ESP32"; +constexpr char CURRENT_FIRMWARE_VERSION[] = "1.0.0"; + +// Maximum amount of retries we attempt to download each firmware chunck over MQTT +constexpr uint8_t FIRMWARE_FAILURE_RETRIES = 12U; +// Size of each firmware chunck downloaded over MQTT, +// increased packet size, might increase download speed +constexpr uint16_t FIRMWARE_PACKET_SIZE = 4096U; + +// Examples using arduino used PROGMEM to save constants into flash memory, +// this is not needed when using Espressif IDF because per default +// all read only variables will be saved into DROM (flash memory). +// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/memory-types.html#drom-data-stored-in-flash +// for more information about the aforementioned feature +constexpr char WIFI_SSID[] = "YOUR_WIFI_SSID"; +constexpr char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; + +// See https://thingsboard.io/docs/getting-started-guides/helloworld/ +// to understand how to obtain an access token +constexpr char TOKEN[] = "YOUR_DEVICE_ACCESS_TOKEN"; + +// Thingsboard we want to establish a connection too +constexpr char THINGSBOARD_SERVER[] = "demo.thingsboard.io"; + +// MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port, +// whereas 8883 would be the default encrypted SSL MQTT port +#if ENCRYPTED +constexpr uint16_t THINGSBOARD_PORT = 8883U; +#else +constexpr uint16_t THINGSBOARD_PORT = 1883U; +#endif + +// Maximum size packets will ever be sent or received by the underlying MQTT client, +// if the size is to small messages might not be sent or received messages will be discarded. +// The Espressif_MQTT_Client, currently has an issue with the underlying library used, where it is not possible +// to change the buffer size once the client has been initalized, meaning the buffer size can only be set before calling connect(), +// for the first time. Therefore when using the OTA update mechanism it is required to increase the buffer size to the size of the received firmware packets +// and a little bit more for the topic we received the message on. +// This has to be done at least until the issue https://github.com/espressif/esp-mqtt/issues/267 has been fixed in the esp-mqtt client, +// or if an older version of the esp-mqtt client is used that does not include the possible fixes to the aforementioned issue yet. +constexpr uint16_t MAX_MESSAGE_SIZE = FIRMWARE_PACKET_SIZE + 50U; + +#if ENCRYPTED +// See https://comodosslstore.com/resources/what-is-a-root-ca-certificate-and-how-do-i-download-it/ +// on how to get the root certificate of the server we want to communicate with, +// this is needed to establish a secure connection and changes depending on the website. +constexpr char ROOT_CERT[] = R"(-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)"; +#endif + + +// Initalize the Mqtt client instance +Espressif_MQTT_Client mqttClient; +// Initialize ThingsBoard instance with the maximum needed buffer size +ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); + +// Status for successfully connecting to the given WiFi +bool wifi_connected = false; +// Statuses for updating +bool currentFWSent = false; +bool updateRequestSent = false; + + +/// @brief Updated callback that will be called as soon as the firmware update finishes +/// @param success Either true (update successful) or false (update failed) +void updatedCallback(const bool& success) { + if (success) { + ESP_LOGI("MAIN", "Done, Reboot now"); + esp_restart(); + return; + } + ESP_LOGI("MAIN", "Downloading firmware failed"); +} + +/// @brief Progress callback that will be called every time we downloaded a new chunk successfully +/// @param currentChunk +/// @param totalChuncks +void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { + ESP_LOGI("MAIN", "Downwloading firmware progress %.2f%%", static_cast(currentChunk * 100U) / totalChuncks); +} + +Espressif_Updater updater; +const OTA_Update_Callback callback(&progressCallback, &updatedCallback, CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION, &updater, FIRMWARE_FAILURE_RETRIES, FIRMWARE_PACKET_SIZE); + + +/// @brief Callback method that is called if we got an ip address from the connected WiFi meaning we successfully established a connection +/// @param event_handler_arg User data registered to the event +/// @param event_base Event base for the handler +/// @param event_id The id for the received event +/// @param event_data The data for the event, esp_event_handler_t +void on_got_ip(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + wifi_connected = true; +} + +/// @brief Initalizes WiFi connection, +// will endlessly delay until a connection has been successfully established +void InitWiFi() { + const wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config)); + + esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_WIFI_STA(); + esp_netif_t *netif = esp_netif_new(&netif_config); + assert(netif); + + ESP_ERROR_CHECK(esp_netif_attach_wifi_station(netif)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ip_event_t::IP_EVENT_STA_GOT_IP, &on_got_ip, NULL)); + ESP_ERROR_CHECK(esp_wifi_set_default_wifi_sta_handlers()); + ESP_ERROR_CHECK(esp_wifi_set_storage(wifi_storage_t::WIFI_STORAGE_RAM)); + + wifi_config_t wifi_config; + memset(&wifi_config, 0, sizeof(wifi_config)); + strncpy(reinterpret_cast(wifi_config.sta.ssid), WIFI_SSID, strlen(WIFI_SSID) + 1); + strncpy(reinterpret_cast(wifi_config.sta.password), WIFI_PASSWORD, strlen(WIFI_PASSWORD) + 1); + + ESP_LOGI("MAIN", "Connecting to %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_mode(wifi_mode_t::WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(wifi_interface_t::WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); +} + +extern "C" void app_main() { + ESP_LOGI("MAIN", "[APP] Startup.."); + ESP_LOGI("MAIN", "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); + ESP_LOGI("MAIN", "[APP] IDF version: %s", esp_get_idf_version()); + + esp_log_level_set("*", ESP_LOG_INFO); + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + InitWiFi(); + +#if ENCRYPTED + mqttClient.set_server_certificate(ROOT_CERT); +#endif // ENCRYPTED + + for (;;) { + // Wait until we connected to WiFi + if (!wifi_connected) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + continue; + } + + if (!tb.connected()) { + tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT); + } + + if (!currentFWSent) { + // Firmware state send at the start of the firmware, to inform the cloud about the current firmware and that it was installed correctly, + // especially important when using OTA update, because the OTA update sends the last firmware state as UPDATING, meaning the device is restarting + // if the device restarted correctly and has the new given firmware title and version it should then send thoose to the cloud with the state UPDATED, + // to inform any end user that the device has successfully restarted and does actually contain the version it was flashed too + currentFWSent = tb.Firmware_Send_Info(CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION) && tb.Firmware_Send_State(FW_STATE_UPDATED); + } + + if (!updateRequestSent) { + // See https://thingsboard.io/docs/user-guide/ota-updates/ + // to understand how to create a new OTA pacakge and assign it to a device so it can download it. + updateRequestSent = tb.Start_Firmware_Update(callback); + } + + tb.loop(); + + vTaskDelay(10000 / portTICK_PERIOD_MS); + } +} diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/README.md b/examples/0015-espressif_esp32_process_OTA_MQTT/README.md new file mode 100644 index 00000000..1eb67bcf --- /dev/null +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/README.md @@ -0,0 +1,24 @@ +# Active OTA firmware update (immediate) + +## Devices +| Supported Devices | +|-------------------| +| ESP32 | + +## Framework + +Espressif IDF + +## ThingsBoard API +[OTA Firmware updates](https://thingsboard.io/docs/user-guide/ota-updates/) +[Shared Attributes](https://thingsboard.io/docs/user-guide/attributes/#shared-attributes) + +## Feature +Requests the current firmware shared attributes which then start an OTA firmware update immediately, +if the received shared attributes include the same title but a different version number then the one that was passed initially + +## Remarks +Custom Updater implementation can be used to flash any firmware device as long as it supports the STL base functionality. +Simply create an own `IUpdater` implementation similar to `Arduino_ESP32_Updater` or `Arduino_ESP8266_Updater`. + +[Thinger IO Arduino library](https://github.com/thinger-io/Arduino-Library) can be used as inspiration on how to write the firmware to [`WiFiStorage for Arduino Nano`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerWiFiNINAOTA.h), [`File Storage for Arduino Nano`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerMbedOTA.h) or [`Second Stage Bootloader (SNB) for Arduino MKRNB`](https://github.com/thinger-io/Arduino-Library/blob/master/src/ThingerMKRNBOTA.h) diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index d640494d..741a651e 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -101,6 +101,13 @@ bool Espressif_MQTT_Client::set_buffer_size(const uint16_t& buffer_size) { #else m_mqtt_configuration.buffer.size = buffer_size; #endif // ESP_IDF_VERSION_MAJOR < 5 + + // Calls esp_mqtt_set_config(), which should adjust the underlying mqtt client to the changed values. + // Which it does but not for the buffer_size, this results in the buffer size only being able to be changed when initally creating the mqtt client. + // If the mqtt client is reinitalized this causes disconnected and reconnects tough and the connection becomes unstable. + // Therefore this workaround can also not be used. Instead we expect the esp_mqtt_set_config(), to do what the name implies and therefore still call it + // and created an issue revolving around the aformentioned problem so it might get fixed in future version of the esp_mqtt client. + // See https://github.com/espressif/esp-mqtt/issues/267 for more information on the issue return update_configuration(); } @@ -150,6 +157,16 @@ bool Espressif_MQTT_Client::connect(const char *client_id, const char *user_name m_mqtt_configuration.credentials.username = user_name; m_mqtt_configuration.credentials.authentication.password = password; #endif // ESP_IDF_VERSION_MAJOR < 5 + // Update configuration is called to ensure that if we connected previously and call connect again with other credentials, + // then we also update the client_id, username and password we connect with. Especially important for the provisioning workflow to work correctly + update_configuration(); + + // Check wheter the client has been initalzed before already, it it has we do not want to reinitalize, + // but simply force reconnection with the client because it has lost that connection + if (m_mqtt_client != nullptr) { + const esp_err_t error = esp_mqtt_client_reconnect(m_mqtt_client); + return error == ESP_OK; + } // The client is first initalized once the connect has actually been called, this is done because the passed setting are required for the client inizialitation structure, // additionally before we attempt to connect with the client we have to ensure it is configued by then. From 46fca3daee5f459fccb7f6e61d5bc75b85fabb64 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Fri, 15 Sep 2023 20:48:17 +0200 Subject: [PATCH 50/80] Improve and update documentation of components --- src/Arduino_ESP32_Updater.h | 3 +- src/Arduino_ESP8266_Updater.h | 3 +- src/Arduino_HTTP_Client.h | 4 +- src/Arduino_MQTT_Client.h | 2 +- src/Attribute_Request_Callback.cpp | 4 +- src/Attribute_Request_Callback.h | 85 ++++++++---- src/Callback.h | 29 ++-- src/Callback_Watchdog.h | 35 +++-- src/Configuration.h | 30 ++-- src/Constants.h | 28 ++-- src/Espressif_MQTT_Client.cpp | 4 +- src/Espressif_MQTT_Client.h | 46 ++++--- src/Espressif_Updater.h | 3 +- src/HashGenerator.h | 16 ++- src/Helper.h | 4 +- src/IHTTP_Client.h | 6 +- src/IMQTT_Client.h | 29 ++-- src/OTA_Failure_Response.h | 9 +- src/OTA_Handler.h | 60 ++++---- src/OTA_Update_Callback.h | 71 +++++----- src/Provision_Callback.cpp | 16 +-- src/Provision_Callback.h | 21 +-- src/RPC_Callback.h | 17 ++- src/RPC_Request_Callback.cpp | 8 +- src/RPC_Request_Callback.h | 30 ++-- src/RPC_Response.h | 2 + src/Shared_Attribute_Callback.h | 82 ++++++----- src/Telemetry.h | 23 ++-- src/ThingsBoard.h | 212 ++++++++++++++++++----------- src/ThingsBoardDefaultLogger.h | 4 +- src/ThingsBoardHttp.h | 42 +++--- src/Vector.h | 6 +- 32 files changed, 558 insertions(+), 376 deletions(-) diff --git a/src/Arduino_ESP32_Updater.h b/src/Arduino_ESP32_Updater.h index ba8fec6f..bdf6c426 100644 --- a/src/Arduino_ESP32_Updater.h +++ b/src/Arduino_ESP32_Updater.h @@ -18,7 +18,8 @@ #include "IUpdater.h" -/// @brief IUpdater implementation using the Arduino Update class in the background, all calls are simply forwarded directly +/// @brief IUpdater implementation that uses the Arduino UpdaterClass (https://github.com/espressif/arduino-esp32/tree/master/libraries/Update), +/// under the hood to write the given binary firmware data into flash memory so we can restart with newly received firmware class Arduino_ESP32_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; diff --git a/src/Arduino_ESP8266_Updater.h b/src/Arduino_ESP8266_Updater.h index 718c61a8..7c4ea26d 100644 --- a/src/Arduino_ESP8266_Updater.h +++ b/src/Arduino_ESP8266_Updater.h @@ -18,7 +18,8 @@ #include "IUpdater.h" -/// @brief IUpdater implementation using the Arduino Updater class in the background, all calls are simply forwarded directly +/// @brief IUpdater implementation that uses the Arduino UpdaterClass (https://github.com/esp8266/Arduino/blob/master/cores/esp8266/Updater.h), +/// under the hood to write the given binary firmware data into flash memory so we can restart with newly received firmware class Arduino_ESP8266_Updater : public IUpdater { public: bool begin(const size_t& firmware_size) override; diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index c88b52ee..49fc5355 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -17,7 +17,7 @@ /// @brief HTTP Client interface implementation that uses the ArduinoHttpClient (https://github.com/arduino-libraries/ArduinoHttpClient), -/// under the hood to establish and communicate over a HTTP connection +/// under the hood to establish and communicate over an HTTP connection class Arduino_HTTP_Client : public IHTTP_Client { public: /// @brief Constructs a IHTTP_Client implementation with the given network client @@ -27,7 +27,7 @@ class Arduino_HTTP_Client : public IHTTP_Client { /// @param port Port that will be used to establish a connection and send / receive data /// Should be either 80 for unencrypted HTTP or 443 for HTTPS with encryption. /// The latter is recommended if relevant data is sent or if the client receives and handles requests from the server, - /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. + /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways Arduino_HTTP_Client(Client& transport_client, const char *host, const uint16_t& port); void set_keep_alive(const bool& keep_alive) override; diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index 46a02137..480b4ede 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -20,7 +20,7 @@ /// under the hood to establish and communicate over a MQTT connection. The fork includes fixes to solve issues with using std::function callbacks for non ESP boards class Arduino_MQTT_Client : public IMQTT_Client { public: - /// @brief Constructs a IMQTT_Client implementation without a network client, meaning it has to be added later with the set_client() and set_buffer_size() method + /// @brief Constructs a IMQTT_Client implementation without a network client, meaning it has to be added later with the set_client() method Arduino_MQTT_Client(); /// @brief Constructs a IMQTT_Client implementation with the given network client diff --git a/src/Attribute_Request_Callback.cpp b/src/Attribute_Request_Callback.cpp index d1e202c2..c8b370aa 100644 --- a/src/Attribute_Request_Callback.cpp +++ b/src/Attribute_Request_Callback.cpp @@ -12,8 +12,8 @@ Attribute_Request_Callback::Attribute_Request_Callback() : #if !THINGSBOARD_ENABLE_STL -Attribute_Request_Callback::Attribute_Request_Callback(const char *attributes, function cb) : - Callback(cb, ATT_REQUEST_CB_IS_NULL), +Attribute_Request_Callback::Attribute_Request_Callback(const char *attributes, function callback) : + Callback(callback, ATT_REQUEST_CB_IS_NULL), m_attributes(attributes), m_request_id(0U), m_attribute_key(nullptr) diff --git a/src/Attribute_Request_Callback.h b/src/Attribute_Request_Callback.h index 3850733f..eac39c55 100644 --- a/src/Attribute_Request_Callback.h +++ b/src/Attribute_Request_Callback.h @@ -28,10 +28,18 @@ constexpr char ATT_REQUEST_CB_IS_NULL[] = "Client-side or shared attribute reque // Convenient aliases -// JSON object const (read only twice as small as JSON object), is used to communicate Shared Attributes and Provision Data to the client +// JSON object const (read only twice as small as JSON object), is used to communicate Attributes data to the client using Attribute_Data = const JsonObjectConst; -/// @brief Client-side or shared attributes request callback wrapper + +/// @brief Client-side or shared attributes request callback wrapper, +/// contains the needed configuration settings to create the request that should be sent to the server. +/// Which attribute scope will be requested from either client-side or shared, is decided depending on which method the class instance is passed to as an argument. +/// If that method is the Client_Attributes_Request() then the passed attributes are requested and if they exist are received from the client scope, +/// but if that method is the Shared_Attributes_Request() then the passed attributes are requested and if they exist are received from the shared scope instead. +/// To achieve that some internal member variables get set automatically by those methods, the first one being a string to differentiate which attribute scope was requested +/// and the second being the id of the mqtt request, where the response by the server will use the same id, which makes it easy to know which method intially requested the data and should now receive it. +/// Documentation about the specific use of Requesting client-side or shared scope atrributes in ThingsBoard can be found here https://thingsboard.io/docs/reference/mqtt-api/#request-attribute-values-from-the-server class Attribute_Request_Callback : public Callback { public: /// @brief Constructs empty callback, will result in never being called @@ -40,16 +48,22 @@ class Attribute_Request_Callback : public Callback #if THINGSBOARD_ENABLE_STL /// @brief Constructs callback, will be called upon client-side or shared attribute request arrival - /// where the given multiple requested client-side or shared attributes were sent by the cloud and received by the client - /// @tparam InputIterator Class that points to the begin and end iterator - /// of the given data container, allows for using / passing either std::vector or std::array - /// @param first_itr Iterator pointing to the first element in the data container - /// @param last_itr Iterator pointing to the end of the data container (last element + 1) - /// @param cb Callback method that will be called - template - inline Attribute_Request_Callback(const InputIterator &first_itr, const InputIterator &last_itr, function cb) - : Callback(cb, ATT_REQUEST_CB_IS_NULL) - , m_attributes(first_itr, last_itr) + /// where the given multiple requested client-side or shared attributes were sent by the cloud and received by the client. + /// Directly forwards the given arguments to the overloaded vector constructor, + /// meaning all combinatons of arguments that would initalize a vector can be used to call this constructor. + /// See possible vector constructors https://en.cppreference.com/w/cpp/container/vector/vector, for the possible passed parameters. + /// The possibilites mainly consist out of the fill constructor, where a value and a number n is given and then that many elements of the value will be copied into + /// or be created with their default value, or out of the range constructor where we can pass an interator the start of another data container + /// and to the end of the data container (last element + 1) + /// and then every element between those iteratos will be copied, in the same order as in the original data container. + /// The last option is a copy constructor where we pass a vector and the values of that vector will be copied into our buffer + /// @tparam ...Args Holds the multiple arguments that will simply be forwarded to the vector constructor and therefore allow to use every overloaded vector constructor without having to implement them + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument + /// @param ...args Arguments that will be forwarded into the overloaded vector constructor see https://en.cppreference.com/w/cpp/container/vector/vector for more information + template + inline Attribute_Request_Callback(function callback, Args... args) + : Callback(callback, ATT_REQUEST_CB_IS_NULL) + , m_attributes(std::forward(args)...) , m_request_id(0U) , m_attribute_key(nullptr) { @@ -60,11 +74,13 @@ class Attribute_Request_Callback : public Callback /// @brief Constructs callback, will be called upon client-side or shared attribute request arrival /// where the given multiple requested client-side or shared attributes were sent by the cloud and received by the client - /// @param attributes Comma seperated string containing all attributes we want to request (test1, test2, ...) - /// @param cb Callback method that will be called - Attribute_Request_Callback(const char *attributes, function cb); + /// @tparam InputIterator Class that points to the begin and end iterator + /// @param attributes Comma seperated string containing all attributes we want to request the format should be (test1, test2, ...) + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument + Attribute_Request_Callback(const char *attributes, function callback); #endif // THINGSBOARD_ENABLE_STL + /// @brief Gets the unique request identifier that is connected to the original request, /// and will be later used to verifiy which Attribute_Request_Callback /// is connected to which received client-side or shared attributes @@ -73,53 +89,64 @@ class Attribute_Request_Callback : public Callback /// @brief Sets the unique request identifier that is connected to the original request, /// and will be later used to verifiy which Attribute_Request_Callback - /// is connected to which received client-side or shared attributes + /// is connected to which received client-side or shared attributes. + /// Not meant for external use, because the value is overwritten by the ThingsBoard class once the class instance has been passed as a parameter anyway, + /// this is the case because only the ThingsBoard class knows the current request id that this callback will be attached too. /// @param request_id Unqiue identifier of the request for client-side or shared attributes void Set_Request_ID(const size_t &request_id); /// @brief Gets the response key of the key-value pair, /// that we expect the client-side or shared attribute payload json data to be contained in /// @return Key that the data is saved into, - /// client for client-side attributes and shared for shared attributes + /// "client" for client-side attributes and "shared" for shared scope attributes const char* Get_Attribute_Key() const; /// @brief Sets the response key of the key-value pair, /// that we expect the client-side or shared attribute payload json data to be contained in + /// Not meant for external use, because the value is overwritten by the ThingsBoard class once the class instance has been passed as a parameter anyway, + /// this is the case because the json key changes depending on if we request client-side or shared scope attributes + /// and which type we requests depends on which method the class instance is passed as a parameter to /// @param attribute_key Key that the data is saved into, - /// client for client-side attributes and shared for shared attributes + /// "client" for client-side attributes and "shared" for shared scope attributes void Set_Attribute_Key(const char *attribute_key); #if THINGSBOARD_ENABLE_STL /// @brief Gets all the requested client-side or shared attributes that will result, /// in the subscribed method being called when the response with their current value - // is sent from the cloud and received by the client + /// is sent from the cloud and received by the client /// @return Requested client-side or shared attributes const std::vector& Get_Attributes() const; /// @brief Sets all the requested client-side or shared attributes that will result, /// in the subscribed method being called when the response with their current value - // is sent from the cloud and received by the client - /// @tparam InputIterator Class that points to the begin and end iterator - /// of the given data container, allows for using / passing either std::vector or std::array - /// @param first_itr Iterator pointing to the first element in the data container - /// @param last_itr Iterator pointing to the end of the data container (last element + 1) - template - inline void Set_Attributes(const InputIterator &first_itr, const InputIterator &last_itr) { - m_attributes.assign(first_itr, last_itr); + /// is sent from the cloud and received by the client. + /// Directly forwards the given arguments to the overloaded vector assign method, + /// meaning all combinatons of arguments that can call the assign method on a vector can be used to call this method. + /// See possible overloaded vector assign methods https://en.cppreference.com/w/cpp/container/vector/assign, for the possible passed parameters. + /// The possibilites mainly consist out of the fill assign method, where a value and a number n is given and then that many elements of the value will be copied into + /// or be created with their default value, or out of the range assign method where we can pass an interator the start of another data container + /// and to the end of the data container (last element + 1) + /// and then every element between those iteratos will be copied, in the same order as in the original data container. + /// The last option is a copy assign method where we pass a vector and the values of that vector will be copied into our buffer + /// @tparam ...Args Holds the multiple arguments that will simply be forwarded to the vector assign method and therefore allow to use every overloaded vector assign without having to implement them + /// @param ...args Arguments that will be forwarded into the overloaded vector assign method see https://en.cppreference.com/w/cpp/container/vector/assign for more information + template + inline void Set_Attributes(Args... args) { + m_attributes.assign(std::forward(args)...); } #else /// @brief Gets the string containing all the requested client-side or shared attributes that will result, /// in the subscribed method being called when the response with their current value - // is sent from the cloud and received by the client + /// is sent from the cloud and received by the client /// @return Requested client-side or shared attributes const char* Get_Attributes() const; /// @brief Sets the string containing all the requested client-side or shared attributes that will result, /// in the subscribed method being called when the response with their current value - // is sent from the cloud and received by the client + /// is sent from the cloud and received by the client /// @param attributes Requested client-side or shared attributes void Set_Attributes(const char *attributes); diff --git a/src/Callback.h b/src/Callback.h index 5b1fc9ab..fcbbb701 100644 --- a/src/Callback.h +++ b/src/Callback.h @@ -16,7 +16,8 @@ #endif // THINGSBOARD_ENABLE_STL -/// @brief General purpose callback wrapper +/// @brief General purpose callback wrapper. Expects either c-style or c++ style function pointer, +/// depending on if the C++ STL has been implemented on the given device or not /// @tparam returnType Type the given callback method should return /// @tparam argumentTypes Types the given callback method should receive template @@ -31,38 +32,40 @@ class Callback { /// @brief Constructs base callback, will be called upon specific arrival of json message /// where the requested data was sent by the cloud and received by the client - /// @param cb Callback method that will be called - /// @param message Message that is logged if the callback given initally is a nullptr and can therefore not be called - inline Callback(function cb, const char *message) - : m_cb(cb) + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into the given arguemnt types + /// @param message Message that is logged if the callback given initally is a nullptr and can therefore not be called, + /// used to ensure users are informed that the initalization of the child class is invalid + inline Callback(function callback, const char *message) + : m_callback(callback) , m_message(message) { // Nothing to do } /// @brief Calls the callback that was subscribed, when this class instance was initally created - /// @tparam Logger Logging class that should be used to print messages + /// @tparam Logger Logging class that should be used to print messages generated by internal processes /// @param arguments Received client-side or shared attribute request data that include /// the client-side or shared attributes that were requested and their current values template inline returnType Call_Callback(argumentTypes... arguments) const { // Check if the callback is a nullptr, // meaning it has not been assigned any valid callback method - if (!m_cb) { + if (!m_callback) { Logger::log(m_message); return returnType(); } - return m_cb(arguments...); + return m_callback(arguments...); } - /// @brief Sets the callback method that will be called - /// @param cb Callback method that will be called - inline void Set_Callback(function cb) { - m_cb = cb; + /// @brief Sets the callback method that will be called upon data arrival with the given data that was received serialized into the given argument types, + /// used to change the callback initally passed or to set the callback if it was not passed as an argument initally + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into the given argument types + inline void Set_Callback(function callback) { + m_callback = callback; } private: - function m_cb; // Callback to call + function m_callback; // Callback to call const char *m_message; // Message to print if callback is a nullptr }; diff --git a/src/Callback_Watchdog.h b/src/Callback_Watchdog.h index 950e5536..19fa05cb 100644 --- a/src/Callback_Watchdog.h +++ b/src/Callback_Watchdog.h @@ -19,10 +19,19 @@ #endif // !THINGSBOARD_USE_ESP_TIMER -/// @brief Wrapper class around the esp_timer, allowing to start and stop timers. Is meant to be started with once(), -/// which will then call the registered callback after timeout if the detach() method has not been called before the time passed. -/// Resulting in behaviour similair to a watchdog but without as high of an accuracy and without restarting the device, -/// allowing to fail and handle the error case silently +/// @brief Wrapper class which allows to start a timer and if it is not stopped in the given time then the callback that was passed will be called, +/// which informs the user of the failure to stop the timer in time, meaning a timeout has occured. +/// The class wraps around either the Arduino Ticker class from Arduino (https://github.com/sstaub/Ticker) or the offical ESP Timer implementation from Espressif (https://github.com/espressif/esp-idf/tree/2bc1f2f574/examples/system/esp_timer), the latter takes precendence if it exists. +/// This is done because the usage is more efficient, under the hood we simply start and then stop and if needed start a oneshot timer again. +/// When using the esp timer directly the same timer can be reused to stop and start and therefore does not need to create a new timer everytime, +/// the Ticker in comparsion automatically deletes a timer once it has been stopped and we therefore need to create it again once the timer is started. +/// This causes a lot of overhead therefore if the esp timer exists we do not use the Ticker class but instead the esp timer directly. +/// For all other use cases where the esp timer does not exists we instead use the Ticker as a fallback, because in that case its implementation is completly different anyway. +/// The class instance is meant to be started with once() which will then call the registered callback after the timeout has passed, +/// if the detach() method has not been called yet. +/// This results in behaviour similair to a esp task watchdog but without as high of an accuracy and without restarting the device, +/// allowing to let it fail and handle the error case silently by the user in the callback method. +/// Documentation about the specific use and caviates of the ESP Timer implementation can be found here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html class Callback_Watchdog { public: /// @brief Constructor @@ -33,7 +42,7 @@ class Callback_Watchdog { ~Callback_Watchdog(); /// @brief Starts the watchdog timer once for the given timeout - /// @param timeout_microseconds Amount of microseconds until the detach() method is excpected to have been called or the given callback method will be called + /// @param timeout_microseconds Amount of microseconds until the detach() method is excpected to have been called or the initally given callback method will be called void once(const uint64_t& timeout_microseconds); /// @brief Stops the currently ongoing watchdog timer and ensures the callback is not called. Timer can simply be restarted with calling once() again. @@ -42,21 +51,25 @@ class Callback_Watchdog { private: std::function m_callback; #if THINGSBOARD_USE_ESP_TIMER - void *m_oneshot_timer; + void *m_oneshot_timer; // ESP Timer handle that is used to start and stop the oneshot timer #else - Ticker m_oneshot_timer; + Ticker m_oneshot_timer; // Ticker instance that handles the timer under the hood, if possible we directly use esp timer instead because it is more efficient #endif // THINGSBOARD_USE_ESP_TIMER - static Callback_Watchdog *m_instance; + static Callback_Watchdog *m_instance; // Instance to the created class, will be set once the constructor has been called and reset once the destructor has been called, used to call private member method from static callback #if THINGSBOARD_USE_ESP_TIMER - /// @brief Creates and initally configures the timer, has to be done once before either esp_timer_start_once or esp_timer_stop is called. + + /// @brief Creates and initally configures the timer, has to be done once before either esp_timer_start_once or esp_timer_stop is called + /// It can not be created in the constructor, because that would possibly be called before we have executed the main app code, meaning the esp timer base is not initalized yet. + /// This would result in an invalid configuration which would cause crashes when used in combination with once() or detach() void create_timer(); + #endif // THINGSBOARD_USE_ESP_TIMER - /// @brief Static callback used to call the initally subscribed callback, if the internal watchdog has not been fed with detach() + /// @brief Static callback used to call the initally subscribed callback, if the internal watchdog has not been reset in time with detach() #if THINGSBOARD_USE_ESP_TIMER - /// @param arg Possible argument passed to the timer callback + /// @param arg Possible argument passed to the timer callback is always nullptr, because we simply forward the call to an internal member method instead static void oneshot_timer_callback(void *arg); #else static void oneshot_timer_callback(); diff --git a/src/Configuration.h b/src/Configuration.h index 1d74718d..0ec110e3 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -7,20 +7,24 @@ #ifndef Configuration_h #define Configuration_h -// Include sdkconfig file it it exists to allow overwriting of some defines with the configuration entered in the ESP IDF menuconfig +// Include sdkconfig file it it exists to allow overwriting of some defines with the configuration entered in the Espressif IDF menuconfig. +// Only available when compiling for Espressif IDF, but allows to more easily change some configurations with a GUI instead of code. # ifdef __has_include # if __has_include() # include # endif # endif - // Enabled the usage of int64_t and double values with ArduinoJson. Making the JsonVariant store double and int64_t instead of float and int32_t. -// See https://arduinojson.org/v6/api/config/use_long_long/ for more information +// See https://arduinojson.org/v6/api/config/use_long_long/ for more information. #define ARDUINOJSON_USE_LONG_LONG 1 #define ARDUINOJSON_USE_DOUBLE 1 -// Enable the usage of the STL library, depending on if needed STL base functionality is supported +// Enable the usage of the C++ STL library, depending on if needed STL base functionality is supported, +// allows to use c++ style function pointers as callbacks removing the need to store a static pointer to the instance of the class. +// Additionally it allows to store data in the vector class, which in case it does not exist we have to fall back to an own custom implementation +// of the vector class which is less efficient. Additionally if possible only c++ strings are used +// and if it does not exist we fall back to the Arduino String class. # ifdef __has_include # if __has_include() && __has_include() && __has_include() && __has_include() # ifndef THINGSBOARD_ENABLE_STL @@ -43,7 +47,8 @@ # endif # endif -// Enable the usage of OTA (Over the air) updates, only possible with STL base functionality +// Enable the usage of OTA (Over the air) updates, only possible with STL base functionality, theoretically possible without STL support, +// but the code would have to be adjusted at compile time depending on if the C++ STL is supported or not and that has not been implemented for OTA yet. # ifndef THINGSBOARD_ENABLE_OTA # if THINGSBOARD_ENABLE_STL # define THINGSBOARD_ENABLE_OTA 1 @@ -116,7 +121,8 @@ # define THINGSBOARD_USE_ESP_PARTITION 0 # endif -// Enable the usage of the PROGMEM header for constants variables (variables are placed into flash memory instead of sram). +// Use the pgmspace header internally for enalbing the usage of the PROGMEm header for constant variables, as long as the header exists, +// to allow variables to be placed into flash memory instead of sram, meaning the sram can be allocated for other things. # ifdef __has_include # if __has_include() # ifndef THINGSBOARD_ENABLE_PROGMEM @@ -145,8 +151,8 @@ # endif // Enables the ThingsBoard class to print all received and sent messages and their topic, from and to the server, -// additionally some more debug messages will be printed. Requires more flash memory, and more Serial calls requiring more performance. -// Recommended to disable when building for release. +// additionally some more debug messages will be printed. Requires more flash memory, and more calls to the console requiring more performance. +// Recommended to disable when building for release, should only be enabled to debug where a issue might stem from. // Can also optionally be configured via the ESP-IDF menuconfig, if that is the done the value is set to the value entered in the menuconfig, // if the value is manually overriden tough with a #define before including ThingsBoard then the hardcoded value takes precendence. # ifndef THINGSBOARD_ENABLE_DEBUG @@ -159,8 +165,12 @@ // Enables the usage of an additonal library as a fallback, to directly serialize a json message that is sent to the cloud, // if the size of that message would be bigger than the internal buffer size of the client. -// Allows sending much bigger messages than would otherwise be possible, and without much decrease stack or heap requirements, but at the cost of increased send times. -// See https://arduinojson.org/v6/how-to/use-arduinojson-with-pubsubclient/#serializing-a-json-document-into-an-mqtt-message for the main difference int he underlying code. +// Allows sending much bigger messages than would otherwise be possible, and without the need to increase stack or heap requirements, but at the cost of increased send times. +// See https://arduinojson.org/v6/how-to/use-arduinojson-with-pubsubclient/#serializing-a-json-document-into-an-mqtt-message for the main difference in the underlying code. +// Option can only be enabled when using Arduino, because this feature relies on Arduino as it improves the underlying data streams to directly write the data into the MQTT Client, +// but writing each byte one by one, would be too slow, therefore the ArduinoStreamUtils (https://github.com/bblanchon/ArduinoStreamUtils) library is used to buffer those calls into bigger packets. +// This allows sending data that is very big without requiring to allocate that much memory, because it is sent in smaller packets. +// To support this feature, however this interface needs to additionally implement the Print interface, because that is required by the wrapper class BufferingPrint. # ifndef THINGSBOARD_ENABLE_STREAM_UTILS # define THINGSBOARD_ENABLE_STREAM_UTILS 0 # endif diff --git a/src/Constants.h b/src/Constants.h index 74572197..ead334af 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -65,25 +65,25 @@ constexpr char UNABLE_TO_ALLOCATE_MEMORY[] = "Allocating memory for the JsonDocu #if THINGSBOARD_ENABLE_PSRAM - #include +#include - struct SpiRamAllocator { - void* allocate(size_t size) { - return heap_caps_malloc(size, MALLOC_CAP_SPIRAM); - } +struct SpiRamAllocator { + void* allocate(size_t size) { + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM); + } - void deallocate(void* pointer) { - heap_caps_free(pointer); - } + void deallocate(void* pointer) { + heap_caps_free(pointer); + } - void* reallocate(void* ptr, size_t new_size) { - return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM); - } - }; + void* reallocate(void* ptr, size_t new_size) { + return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM); + } +}; - using TBJsonDocument = BasicJsonDocument; +using TBJsonDocument = BasicJsonDocument; #elif THINGSBOARD_ENABLE_DYNAMIC - using TBJsonDocument = DynamicJsonDocument; +using TBJsonDocument = DynamicJsonDocument; #endif #endif // Constants_h diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index 741a651e..d93c1095 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -90,8 +90,8 @@ bool Espressif_MQTT_Client::set_network_timeout(const uint16_t& network_timeout_ return update_configuration(); } -void Espressif_MQTT_Client::set_callback(function cb) { - m_received_data_callback = cb; +void Espressif_MQTT_Client::set_callback(function callback) { + m_received_data_callback = callback; } bool Espressif_MQTT_Client::set_buffer_size(const uint16_t& buffer_size) { diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h index 1136436c..8472f66f 100644 --- a/src/Espressif_MQTT_Client.h +++ b/src/Espressif_MQTT_Client.h @@ -18,16 +18,17 @@ // Library includes. #include + /// @brief MQTT Client interface implementation that uses the offical ESP MQTT client from Espressif (https://github.com/espressif/esp-mqtt), /// under the hood to establish and communicate over a MQTT connection. This component works with both Espressif IDF v4.X and v5.X, meaning it is version idependent, this is the case /// because depending on the used version the implementation automatically adjusts to still initalize the client correctly. -/// Documentation about the specific use and caviates of the ESP MQTT client can be found here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html. +/// Documentation about the specific use and caviates of the ESP MQTT client can be found here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html class Espressif_MQTT_Client : public IMQTT_Client { public: - /// @brief Constructs a IMQTT_Client implementation which creates and empty esp_mqtt_client_config_t, which then has to be configured with the other methods in the class. + /// @brief Constructs a IMQTT_Client implementation which creates and empty esp_mqtt_client_config_t, which then has to be configured with the other methods in the class Espressif_MQTT_Client(); - /// @brief Configured the server certificate, which allows to connected to the MQTT broker over a secure TLS / SSL conenction instead of the default unencrypted channel. + /// @brief Configured the server certificate, which allows to connect to the MQTT broker over a secure TLS / SSL conenction instead of the default unencrypted channel. /// Has to be called before initally calling connect() on the client to ensure the certificate is set before the connection is established, if that is not done the connection will not be encrypted. /// Encryption is recommended if relevant data is sent or if the client receives and handles Remote Procedure Calls or Shared Attribute Update Callbacks from the server, /// because using an unencrpyted connection, will allow 3rd parties to listen to the communication and impersonate the server sending payloads which might influence the device in unexpected ways. @@ -35,38 +36,40 @@ class Espressif_MQTT_Client : public IMQTT_Client { /// which is then flashed onto the device instead of the real firmware. Which depeding on the payload might even be able to destroy the device or make it otherwise unusable. /// See https://stackoverflow.blog/2020/12/14/security-considerations-for-ota-software-updates-for-iot-gateway-devices/ for more information on the aforementioned security risk /// @param server_certificate_pem Null-terminated string containg the root certificate in PEM format, of the server we are attempting to send to and receive MQTT data from - /// @return Whether chaing the internal server certificate was successful or not, ensure to disconnect and reconnect to actually apply the change. + /// @return Whether chaing the internal server certificate was successful or not, ensure to disconnect and reconnect to actually apply the change bool set_server_certificate(const char *server_certificate_pem); /// @brief Sets the keep alive timeout in seconds, if the value is 0 then the default of 120 seconds is used instead to disable the keep alive mechanism use set_disable_keep_alive() instead. /// The default timeout value ThingsBoard expectes to receive any message including a keep alive to not show the device as inactive can be found here https://thingsboard.io/docs/user-guide/install/config/#mqtt-server-parameters /// under the transport.sessions.inactivity_timeout section and is 300 seconds. Meaning a value bigger than 300 seconds with the default config defeats the purpose of the keep alive alltogether - /// @param keep_alive_timeout_seconds Timeout until we send another PINGREQ control packets to the broker to establish that we are still connected + /// @param keep_alive_timeout_seconds Timeout until we send another PINGREQ control packet to the broker to establish that we are still connected /// @return Whether changing the internal keep alive timeout was successful or not bool set_keep_alive_timeout(const uint16_t& keep_alive_timeout_seconds); /// @brief Whether to disable or enable the keep alive mechanism, meaning we do not send any PINGREQ control packets to the broker anymore, meaning if we do not send other packet the device will be marked as inactive. /// The default value is false meaning the keep alive mechanism is enabled. /// The default timeout value ThingsBoard expectes to receive any message including a keep alive to not show the device as inactive can be found here https://thingsboard.io/docs/user-guide/install/config/#mqtt-server-parameters - /// under the transport.sessions.inactivity_timeout section and is 300 seconds. Meaning if we disable the keep alive mechanism and do not send a packet atleast every 300 seconds with the default config then the device will change states between active and inactive + /// under the transport.sessions.inactivity_timeout section and is 300 seconds. Meaning if we disable the keep alive mechanism and do not send another packet atleast every 300 seconds with the default config + /// then the device will change states between active and inactive depending on when we send the packets /// @param disable_keep_alive Whether to enable or disable the internal keep alive mechanism, which sends PINGREQ control packets every keep alive timeout seconds, configurable in set_keep_alive_timeout() /// @return Whether enabling or disabling the internal keep alive mechanism was successful or not bool set_disable_keep_alive(const bool& disable_keep_alive); - /// @brief Wheter to disable or enable that the MQTT client will reconnect to the server automatically if it errors or disconnects. The default is false meaning we will automatially reconnect + /// @brief Wheter to disable or enable that the MQTT client will reconnect to the server automatically if it errors or disconnects. The default is false meaning we will automatically reconnect /// @param disable_auto_reconnect Whether to automatically reconnect if the the client errors or disconnects /// @return Whether enabling or disabling the internal auto reconnect mechanism was successful or not bool set_disable_auto_reconnect(const bool& disable_auto_reconnect); - /// @brief Sets the priority of the MQTT task running in the background and handling the receiving and sending of any outstanding MQTT messages to or from the broker. - /// The default value for the priority is 5 and can also be changed in the ESp IDF menuconfig and the default value for the stack size - /// @param priority Task priority with which the MQTT task should run, higher priority means it takes more precedence over other tasks, making it more important, default value is 5 and can be changed in the ESP IDF menuconfig - /// @param stack_size Task stack size meaning how much stack size the MQTT task can use before the device crashes with a StackOverflow, default value is 6144 bytes, can be changed in the ESP IDF menuconfig + /// @brief Sets the priority and stack size of the MQTT task running in the background, that is handling the receiving and sending of any outstanding MQTT messages to or from the broker. + /// The default value for the priority is 5 and can also be changed in the ESP IDF menuconfig, whereas the default value for the stack size is 6144 bytes and can also be changed in the ESP IDF menuconfig + /// @param priority Task priority with which the MQTT task should run, higher priority means it takes more precedence over other tasks, making it more important + /// @param stack_size Task stack size meaning how much stack size the MQTT task can allocate before the device crashes with a StackOverflow, might need to be increased + /// if the user allocates a lot of memory on the stack in a request callback method, because those functions are dispatched from the MQTT event task /// @return Whether changing the internal MQTT task configurations were successfull or not bool set_mqtt_task_configuration(const uint8_t& priority, const uint16_t& stack_size); /// @brief Sets the amount of time in milliseconds that we wait before we automatically reconnect to the MQTT broker if the connection has been lost. The default value is 10 seconds. - /// Will be ignored if set_disable_auto_reconnect() has been set to true, because we will not reconnect automatically anymore, instead that would have to be done by the user + /// Will be ignored if set_disable_auto_reconnect() has been set to true, because we will not reconnect automatically anymore, instead that would have to be done by the user themselves /// @param reconnect_timeout_milliseconds Time in milliseconds until we automatically reconnect to the MQTT broker /// @return Whether changing the internal reconnect timeout was successfull or not bool set_reconnect_timeout(const uint16_t& reconnect_timeout_milliseconds); @@ -78,7 +81,7 @@ class Espressif_MQTT_Client : public IMQTT_Client { /// @return Whether changing the internal network timeout was successfull or not bool set_network_timeout(const uint16_t& network_timeout_milliseconds); - void set_callback(function cb) override; + void set_callback(function callback) override; bool set_buffer_size(const uint16_t& buffer_size) override; @@ -101,16 +104,17 @@ class Espressif_MQTT_Client : public IMQTT_Client { bool connected() override; private: - function m_received_data_callback; - bool m_connected; - esp_mqtt_client_config_t m_mqtt_configuration; - esp_mqtt_client_handle_t m_mqtt_client; + function m_received_data_callback; // Callback that will be called as soon as the mqtt client receives any data + bool m_connected; // Wheter the client has received the connected or disconnected event + esp_mqtt_client_config_t m_mqtt_configuration; // Configuration of the underlying mqtt client, saved as a private variable to allow changes after inital configuration with the same options for all non changed settings + esp_mqtt_client_handle_t m_mqtt_client; // Handle to the underlying mqtt client, used to establish the communication - static Espressif_MQTT_Client *m_instance; + static Espressif_MQTT_Client *m_instance; // Instance to the created class, will be set once the constructor has been called and reset once the destructor has been called, used to call private member method from static callback - /// @brief Is internalyl used to allow changes to the underlying configuration of the esp_mqtt_client_handle_t after it has connected, - /// to for example increase the buffer size or increase the timeouts or stack size - /// @return Whether updating the configuration with the changed settings was successfull or not. + /// @brief Is internally used to allow changes to the underlying configuration of the esp_mqtt_client_handle_t after it has connected, + /// to for example increase the buffer size or increase the timeouts or stack size, allows to change the underlying client configuration, + /// without the need to completly disconnect and reconnect the client + /// @return Whether updating the configuration with the changed settings was successfull or not bool update_configuration(); /// @brief Event handler registered to receive MQTT events. Is called by the MQTT client event loop, whenever a new event occurs diff --git a/src/Espressif_Updater.h b/src/Espressif_Updater.h index c99a4b06..6c4fb7dc 100644 --- a/src/Espressif_Updater.h +++ b/src/Espressif_Updater.h @@ -18,7 +18,8 @@ #include "IUpdater.h" -/// @brief IUpdater implementation, which uses the Over the Air Update API of Espressif in the background, see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html for more information +/// @brief IUpdater implementation that uses the Over the Air Update API from Espressif (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html) +/// under the hood to write the given binary firmware data into flash memory so we can restart with newly received firmware class Espressif_Updater : public IUpdater { public: Espressif_Updater(); diff --git a/src/HashGenerator.h b/src/HashGenerator.h index 272c10cc..0d8d3b2e 100644 --- a/src/HashGenerator.h +++ b/src/HashGenerator.h @@ -21,8 +21,14 @@ #include -/// @brief Allows generating a hash of the given type -/// with partial data and then retreiving the completed hash once it has been completed +/// @brief Wrapper class which allows generating a hash of the given type from any arbitrary byte payload, which is hashable in chunks. +/// The class wraps around either the Arduino Seeed mbedtls library from Seed Studio (https://github.com/Seeed-Studio/Seeed_Arduino_mbedtls) or the offical ESP Mbed TLS implementation from Mbed TLS (https://github.com/Mbed-TLS/mbedtls), the latter takes precendence if it exists. +/// This is done because it removes the need to include another library, because the component already exists on the system and we can therefore simply utilize that one. +/// The ESP Mbed TLS implementationt works with both Espressif IDF v4.X and v5.X, meaning it is version idependent, this is the case +/// because depending on the used version the implementation automatically adjusts to still initalize correctly. +/// The class instance is meant to be started with start() which will then create the configuration for a hash of the given type +/// and we then expect the complete binary payload to be called in multiple calls to update() and the final result to be read with get_hash_string() +/// Documentation about the specific use and caviates of the ESP Mbedt TLS implementation can be found here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mbedtls.html class HashGenerator { public: /// @brief Constructor @@ -31,7 +37,7 @@ class HashGenerator { /// @brief Destructor ~HashGenerator(void); - /// @brief Start the hashing process + /// @brief Starts the hashing process /// @param type Supported type of hash that should be generated from this class void start(const mbedtls_md_type_t& type); @@ -46,11 +52,11 @@ class HashGenerator { std::string get_hash_string(); private: + mbedtls_md_context_t m_ctx; // Context used to access the already written bytes and update them latter + /// @brief Calculates the final hash value /// @param hash Output byte array that the hash value will be copied into void finish(unsigned char *hash); - - mbedtls_md_context_t m_ctx; }; #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/Helper.h b/src/Helper.h index ba0aae97..8991406a 100644 --- a/src/Helper.h +++ b/src/Helper.h @@ -18,7 +18,7 @@ #endif // THINGSBOARD_ENABLE_STL -/// @brief Static helper class that includes some uniliterally used functionalities in multiple places +/// @brief Static helper class that includes some uniliterally used functionalities in multiple places, especially the ThingsBoardHttp and ThingsBoard implementations class Helper { public: /// @brief Returns the length in characters needed for a given value with the given argument string to be displayed completly @@ -38,7 +38,7 @@ class Helper { /// See https://arduinojson.org/v6/api/json/measurejson/ for more information on the underlying method used /// @tparam TSource Source class that should be used to serialize the json that is sent to the server /// @param source Data source containing our json key value pairs we want to measure - /// @return Total size of the string produced by serializeJson + 1 + /// @return Total size of the string produced by serializeJson + 1 byte for the string null terminator template inline static size_t Measure_Json(const TSource& source) { return JSON_STRING_SIZE(measureJson(source)); diff --git a/src/IHTTP_Client.h b/src/IHTTP_Client.h index 6f4158bb..aaaadfc6 100644 --- a/src/IHTTP_Client.h +++ b/src/IHTTP_Client.h @@ -16,10 +16,12 @@ /// @brief HTTP Client interface that contains the method that a class that can be used to send and receive data over an HTTP conection should implement. -/// Seperates the specific implementation used from the ThingsBoardHttp client, allows to use differnt clients depending on different needs. +/// Seperates the specific implementation used from the ThingsBoardHttp client, allows to use different clients depending on different needs. /// In this case the main use case of the seperation is to both support Espressif IDF and Arduino with the following libraries as recommendations. /// The default HTTP Client for Arduino is the ArduinoHttpClient (https://github.com/arduino-libraries/ArduinoHttpClient), /// For Espressif IDF however the default HTTP Client is the esp-http-client (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_http_client.html) component. +// The aforementioned recommendations are already partially implemented in the library and can can simply be used and included when using the library, for Arduino we can simply include Arduino_HTTP_Client +/// and for Espressif IDF the implementations has not been created yet, the implementations have been tested and should be compatible when used in conjunction with the ThingsBoardHttp client. class IHTTP_Client { public: /// @brief Sets whether to close the HTTP connection for every single request and reconnect once a new request is sent @@ -54,7 +56,7 @@ class IHTTP_Client { /// @brief Connects to the server and sends a GET request /// @param url_path URL the GET request should be sent too - /// @return hether the request was successful or not, returns 0 if successful or if not the internal error code + /// @return Whether the request was successful or not, returns 0 if successful or if not the internal error code virtual int get(const char *url_path) = 0; /// @brief Returns the response body of a previously sent message as a string object, diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index d9ae1a6b..15209559 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -22,16 +22,18 @@ /// @brief MQTT Client interface that contains the method that a class that can be used to send and receive data over an MQTT connection should implement. -/// Seperates the specific implementation used from the ThingsBoard client, allows to use differnt clients depending on different needs. +/// Seperates the specific implementation used from the ThingsBoard client, allows to use different clients depending on different needs. /// In this case the main use case of the seperation is to both support Espressif IDF and Arduino with the following libraries as recommendations. /// The default MQTT Client for Arduino is the PubSubClient forked from ThingsBoard (https://github.com/thingsboard/pubsubclient), /// it includes fixes to solve issues with using std::function callbacks for non ESP boards. /// For Espressif IDF however the default MQTT Client is the esp-mqtt (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html) component. -/// When using an implementation that does not allow for Arduino it is additional important to disable support for THINGSBOARD_ENABLE_STREAM_UTILS, +/// The aforementioned recommendations are already implemented in the library and can can simply be used and included when using the library, for Arduino we can simply include Arduino_MQTT_Client +/// and for Espressif IDF we can simply include the Espressif_MQTT_Client, the implementations have been tested and should be compatible when used in conjunction with the ThingsBoard client. +/// However when using an implementation that does not allow for Arduino it is additional important to disable support for THINGSBOARD_ENABLE_STREAM_UTILS, /// because this feature relies on Arduino as it improves the underlying data streams to directly write the data into the MQTT Client, -/// but wrirting each byte one by one, would be too slow, therefore the ArduinoStreamUtils (https://github.com/bblanchon/ArduinoStreamUtils) library is used to buffer thoose calls into bigger packets. +/// but writing each byte one by one, would be too slow, therefore the ArduinoStreamUtils (https://github.com/bblanchon/ArduinoStreamUtils) library is used to buffer those calls into bigger packets. /// This allows sending data that is very big without requiring to allocate that much memory, because it is sent in smaller packets. -/// To support this feature, however this interface needs to additionally implement the Print interface, because that is required by the wrapper class BufferingPrint +/// To support this feature, however this interface needs to additionally implement the Print interface, because that is required by the wrapper class BufferingPrint. #if THINGSBOARD_ENABLE_STREAM_UTILS class IMQTT_Client : public Print { #else @@ -47,8 +49,8 @@ class IMQTT_Client { /// @brief Sets the callback that is called, if any message is received by the MQTT broker, including the topic string that the message was received over, /// as well as the payload data and the size of that payload data - /// @param cb Method that should be called on received MQTT response - virtual void set_callback(function cb) = 0; + /// @param callback Method that should be called on received MQTT response + virtual void set_callback(function callback) = 0; /// @brief Changes the size of the buffer for sent and received MQTT messages, /// using a bigger value than uint16_t for passing the buffer size does not make any sense because the maximum message size received @@ -77,14 +79,14 @@ class IMQTT_Client { /// @brief Connects to the previously with set_server configured server instance that should be connected to over the previously defined port /// @param id Client identification code, that allows to differentiate which MQTT device is sending the traffic to the MQTT broker - /// @param user Client usernam that is used to authenticate, who is connecting over MQTT - /// @param pass Client password that isused to authenticate, who is connecting over MQTT + /// @param user Client username that is used to authenticate, who is connecting over MQTT + /// @param pass Client password that is used to authenticate, who is connecting over MQTT /// @return Whether the client could establish the connection successfully or not virtual bool connect(const char *client_id, const char *user_name, const char *password) = 0; /// @brief Disconnects from a previously connected server and should release all used resources virtual void disconnect() = 0; - + /// @brief Receives and sends any outstanding messages from and to the MQTT broker /// @return Whether sending or receiving the oustanding the messages was successful or not, /// should return false if an internal error occured or the connection has been lost @@ -137,16 +139,13 @@ class IMQTT_Client { /// Once the complete payload has been written ensure to call end_publish() to send any remaining bytes. /// Because payload bytes are sent one by one this method is extremly inefficient, /// if possible package the payload into bigger chunks and use the write() method with arrays instead - /// @param payload_byte Byte containg part of the payload that should be sent + /// @param payload_byte Byte containing part of the payload that should be sent /// @return The amount of bytes successfully written virtual size_t write(uint8_t payload_byte) = 0; - // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish) - // Returns the number of bytes written - - /// @brief Sends a buffer containg multiple bytes of payload to be published, is meant to be used after having calling begin_publish() + /// @brief Sends a buffer containing multiple bytes of payload to be published, is meant to be used after having calling begin_publish() /// Once the complete payload has been written ensure to call end_publish() to send any remaining bytes - /// @param buffer Buffer containg part of the payload that should be sent + /// @param buffer Buffer containing part of the payload that should be sent /// @param size Amount of bytes contained in the buffer that should be sent /// @return The amount of bytes successfully written virtual size_t write(const uint8_t *buffer, size_t size) = 0; diff --git a/src/OTA_Failure_Response.h b/src/OTA_Failure_Response.h index 5d9620d9..9890badb 100644 --- a/src/OTA_Failure_Response.h +++ b/src/OTA_Failure_Response.h @@ -11,10 +11,13 @@ #include +/// @brief Possible responses to error states the OTA update might fall into, +/// allows to react to certain issues in the most appropriate way, because some of them require us to restart the complete update, +/// whereas other issues can be solved if we simply attempt to refetch the current chunk enum class OTA_Failure_Response : const uint8_t { - RETRY_CHUNK, - RETRY_UPDATE, - RETRY_NOTHING + RETRY_CHUNK, // Fetching the current chunk failed somehow, but we can still continue the update we just have to refetch the current chunk, mainly occurs from timeouts with requesting the chunks + RETRY_UPDATE, // Internal process failed in the OTA that makes the complete already downloaded data not recoverable anymore, hashing or writing to flash memory failed, requires to restart the update from the first chunk and reinitalize the needed components + RETRY_NOTHING // Initally passed arguments are invalid and would cause crashes or the update was forcefully stopped by the user, therefore we immediately stop the update and do not restart it }; #endif // OTA_Failure_Response diff --git a/src/OTA_Handler.h b/src/OTA_Handler.h index 8b818b9f..0c709c20 100644 --- a/src/OTA_Handler.h +++ b/src/OTA_Handler.h @@ -72,15 +72,16 @@ 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 +/// @brief Handles the complete processing of received binary firmware data, including flashing it onto the device, +/// creating a hash of the received data and in the end ensuring that the complete OTA firmware was flashes successfully and that the hash is the one we initally received +/// @tparam Logger Logging class that should be used to print messages generated by internal processes template class OTA_Handler { public: /// @brief Constructor /// @param publish_callback Callback that is used to request the firmware chunk of the firmware binary with the given chunk number /// @param send_fw_state_callback Callback that is used to send information about the current state of the over the air update - /// @param finish_callback Callback that is called once the update has been finished and the user has been informed of the failure or success + /// @param finish_callback Callback that is called once the update has been finished and the user should be informed of the failure or success of the over the air update inline OTA_Handler(std::function publish_callback, std::function send_fw_state_callback, std::function finish_callback) : m_fw_callback(nullptr) , m_publish_callback(publish_callback) @@ -102,9 +103,9 @@ class OTA_Handler { /// @brief Starts the firmware update with requesting the first firmware packet and initalizes the underlying needed components /// @param fw_callback Callback method that contains configuration information, about the over the air update /// @param fw_size Complete size of the firmware binary that will be downloaded and flashed onto this device - /// @param fw_algorithm String of the algorithm used to hash the firmware binary + /// @param fw_algorithm String of the algorithm type used to hash the firmware binary /// @param fw_checksum Checksum of the complete firmware binary, should be the same as the actually written data in the end - /// @param fw_checksum_algorithm Algorithm used to hash the firmware binary + /// @param fw_checksum_algorithm Algorithm type used to hash the firmware binary inline void Start_Firmware_Update(const OTA_Update_Callback *fw_callback, const size_t& fw_size, const std::string& fw_algorithm, const std::string& fw_checksum, const mbedtls_md_type_t& fw_checksum_algorithm) { m_fw_callback = fw_callback; m_fw_size = fw_size; @@ -122,7 +123,9 @@ class OTA_Handler { Request_First_Firmware_Packet(); } - /// @brief Stops the firmware update + /// @brief Stops the firmware update completly and informs that user that the update has failed because it has been aborted, ongoing communication is discarded. + /// Be aware the written partition is not erased so the already written binary firmware data still remains in the flash partition, + /// shouldn't really matter, because if we start the update process again the partition will be overwritten anyway and a partially written firmware will not be bootable inline void Stop_Firmware_Update() { m_watchdog.detach(); m_fw_updater->reset(); @@ -133,7 +136,7 @@ class OTA_Handler { } /// @brief Uses the given firmware packet data and process it. Starting with writing the given amount of bytes of the packet data into flash memory and - /// into a hash function that will be used to compare the expected complete binary file and the actually downloaded binary file + /// into a hash function that will be used to compare the expected complete binary file and the actually received binary file /// @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 @@ -194,22 +197,22 @@ class OTA_Handler { } private: - const OTA_Update_Callback *m_fw_callback; - std::function m_publish_callback; - std::function m_send_fw_state_callback; - std::function m_finish_callback; - // Allows for a binary size of up to theoretically 4 GB. - size_t m_fw_size; - 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; - size_t m_total_chunks; - size_t m_requested_chunks; - uint8_t m_retries; - Callback_Watchdog m_watchdog; - + const OTA_Update_Callback *m_fw_callback; // Callback method that contains configuration information, about the over the air update + std::function m_publish_callback; // Callback that is used to request the firmware chunk of the firmware binary with the given chunk number + std::function m_send_fw_state_callback; // Callback that is used to send information about the current state of the over the air update + std::function m_finish_callback; // Callback that is called once the update has been finished and the user should be informed of the failure or success of the over the air update + size_t m_fw_size; // Total size of the firmware binary we will receive. Allows for a binary size of up to theoretically 4 GB + std::string m_fw_algorithm; // String of the algorithm type used to hash the firmware binary + std::string m_fw_checksum; // Checksum of the complete firmware binary, should be the same as the actually written data in the end + mbedtls_md_type_t m_fw_checksum_algorithm; // Algorithm type used to hash the firmware binary + IUpdater *m_fw_updater; // Interface implementation that writes received firmware binary data onto the given device + HashGenerator m_hash; // Class instance that allows to generate a hash from received firmware binary data + size_t m_total_chunks; // Total amount of chunks that need to be received to get the complete firmware binary + size_t m_requested_chunks; // Amount of successfully requested and received firmware binary chunks + uint8_t m_retries; // Amount of request retries we attempt for each chunk, increasing makes the connection more stable + Callback_Watchdog m_watchdog; // Class instances that allows to timeout if we do not receive a response for a requested chunk in the given time + + /// @brief Restarts or starts the firmware update and its needed components and then requests the first firmware chunk inline void Request_First_Firmware_Packet() { m_requested_chunks = 0U; m_retries = m_fw_callback->Get_Chunk_Retries(); @@ -219,6 +222,8 @@ class OTA_Handler { Request_Next_Firmware_Packet(); } + /// @brief Requests the next firmware chunk of the OTA firmware if there are any left + /// and starts the timer that ensures we request the same chunk again if we have not received a response yet inline void Request_Next_Firmware_Packet() { // Check if we have already requested and handled the last remaining chunk if (m_requested_chunks >= m_total_chunks) { @@ -236,6 +241,9 @@ class OTA_Handler { m_watchdog.once(m_fw_callback->Get_Timeout()); } + /// @brief Completes the firmware update, which consists of checking the complete hash of the firmware binary if the initally received value, + /// both should be the same and if that is not the case that means that we received invalid firmware binary data and have to restart the update. + /// If checking the hash was successfull we attempt to finish flashing the ota partition and then inform the user that the update was successfull inline void Finish_Firmware_Update() { (void)m_send_fw_state_callback(FW_STATE_DOWNLOADED, nullptr); @@ -271,6 +279,9 @@ class OTA_Handler { (void)m_finish_callback(); } + /// @brief Handles errors with the received failure response so that the firmware update can regenerate from any possible issue. + /// Will only execute the given failure response as long as there are still retries remaining, if there are not any further issue will cause the update to be aborted + /// @param failure_response Possible response to a failure that the method should handle inline void Handle_Failure(const OTA_Failure_Response& failure_response) { if (m_retries <= 0) { m_fw_callback->Call_Callback(false); @@ -299,8 +310,9 @@ class OTA_Handler { } } + /// @brief Callback that will be called if we did not receive the firmware chunk response in the given timeout time inline void Handle_Request_Timeout() { - return Handle_Failure(OTA_Failure_Response::RETRY_CHUNK); + Handle_Failure(OTA_Failure_Response::RETRY_CHUNK); } }; diff --git a/src/OTA_Update_Callback.h b/src/OTA_Update_Callback.h index 26f409a1..34807957 100644 --- a/src/OTA_Update_Callback.h +++ b/src/OTA_Update_Callback.h @@ -42,7 +42,9 @@ constexpr uint64_t REQUEST_TIMEOUT = (5U * 1000U * 1000U); #endif // THINGSBOARD_ENABLE_PROGMEM -/// @brief OTA firmware update callback wrapper +/// @brief Over the air firmware update callback wrapper, +/// contains the needed configuration settings to create the request that should be sent to the server. +/// Documentation about the specific use of Over the air updates in ThingsBoard can be found here https://thingsboard.io/docs/user-guide/ota-updates/ class OTA_Update_Callback : public Callback { public: /// @brief OTA firmware update callback signature @@ -57,14 +59,14 @@ class OTA_Update_Callback : public Callback { /// has been completly sent by the cloud, received by the client and written to the flash partition /// @param endCb End callback method that will be called as soon as the OTA firmware update, either finished successfully or failed /// @param currFwTitle Firmware title the device has choosen, is used to only allow updates with the same given title, other updates will be canceled - /// @param currFwVersion Firmware version the device is currently on, is used to decide to inform the cloud about the previous version we updatet from + /// @param currFwVersion Firmware version the device is currently on, is usded to only allow updates with a different version, other updates will be canceled /// @param updater Updater implementation that writes the given firmware data /// @param chunkRetries Amount of retries the OTA firmware update has to download each seperate chunk with a given size, /// before the complete download is stopped and registered as failed - /// @param chunkSize Maximum size of OTA firmware update for each seperate chunk that should be downloaded, + /// @param chunkSize Size of the chunks that the firmware binary data will be split into, /// increased chunkSize might speed up the process by a little bit, but requires more heap memory, - // because the whole chunk is saved into the heap before it can be processed and is then cleared again - /// @param timeout Maximum amount of time in millseconds for the OTA firmware update for each seperate chunk, + // because the whole chunk is saved into the heap before it can be processed and is then erased again after it has been used + /// @param timeout Maximum amount of time in microseconds for the OTA firmware update for each seperate chunk, /// until that chunk counts as a timeout, retries is then subtraced by one and the download is retried OTA_Update_Callback(function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries = CHUNK_RETRIES, const uint16_t &chunkSize = CHUNK_SIZE, const uint64_t &timeout = REQUEST_TIMEOUT); @@ -75,21 +77,21 @@ class OTA_Update_Callback : public Callback { /// this makes it possible to display a progress bar or signal easily how far we are in the current downloading process /// @param endCb End callback method that will be called as soon as the OTA firmware update, either finished successfully or failed /// @param currFwTitle Firmware title the device has choosen, is used to only allow updates with the same given title, other updates will be canceled - /// @param currFwVersion Firmware version the device is currently on, is used to decide to inform the cloud about the previous version we updatet from + /// @param currFwVersion Firmware version the device is currently on, is usded to only allow updates with a different version, other updates will be canceled /// @param updater Updater implementation that writes the given firmware data /// @param chunkRetries Amount of retries the OTA firmware update has to download each seperate chunk with a given size, /// before the complete download is stopped and registered as failed - /// @param chunkSize Maximum size of OTA firmware update for each seperate chunk that should be downloaded, + /// @param chunkSize Size of the chunks that the firmware binary data will be split into, /// increased chunkSize might speed up the process by a little bit, but requires more heap memory, - // because the whole chunk is saved into the heap before it can be processed and is then cleared again - /// @param timeout Maximum amount of time in millseconds for the OTA firmware update for each seperate chunk, + // because the whole chunk is saved into the heap before it can be processed and is then erased again after it has been used + /// @param timeout Maximum amount of time in microseconds for the OTA firmware update for each seperate chunk, /// until that chunk counts as a timeout, retries is then subtraced by one and the download is retried OTA_Update_Callback(progressFn progressCb, function endCb, const char *currFwTitle, const char *currFwVersion, IUpdater *updater, const uint8_t &chunkRetries = CHUNK_RETRIES, const uint16_t &chunkSize = CHUNK_SIZE, const uint64_t &timeout = REQUEST_TIMEOUT); /// @brief Calls the progress callback that was subscribed, when this class instance was initally created - /// @tparam Logger Logging class that should be used to print messages - /// @param current Received data that shows the current progress - /// @param total Received data that shows the total progress until the update is completed + /// @tparam Logger Logging class that should be used to print messages generated by internal processes + /// @param current Already received and processs amount of chunks + /// @param total Total amount of chunks we need to receive and process until the update has completed template inline returnType Call_Progress_Callback(progressArgumentType current, progressArgumentType total) const { // Check if the callback is a nullptr, @@ -106,23 +108,23 @@ class OTA_Update_Callback : public Callback { /// @param progressCb Progress callback method that will be called void Set_Progress_Callback(progressFn progressCb); - /// @brief Gets the current firmware title, used to decide if an OTA firmware update is actually installed, - /// this is only done if the title of the update and the current firmware title are the same + /// @brief Gets the current firmware title, used to decide if an OTA firmware update is already installed and therefore should not be downladed, + /// this is only done if the title of the update and the current firmware title are the same because if they are not then this firmware is meant for another device type /// @return Current firmware title of the device const char* Get_Firmware_Title() const; - /// @brief Sets the current firmware title, used to decide if an OTA firmware update is actually installed, - /// this is only done if the title of the update and the current firmware title are the same + /// @brief Sets the current firmware title, used to decide if an OTA firmware update is already installed and therefore should not be downladed, + /// this is only done if the title of the update and the current firmware title are the same because if they are not then this firmware is meant for another device type /// @param currFwTitle Current firmware title of the device void Set_Firmware_Title(const char *currFwTitle); - /// @brief Gets the current firmware version, used to decide if an OTA firmware update is actually installed, - /// this is only done if the version of the update and the current firmware title are different + /// @brief Gets the current firmware version, used to decide if an OTA firmware update is already installed and therefore should not be downladed, + /// this is only done if the version of the update and the current firmware version are different, because if they are not then we would download the same firmware as is already on the device /// @return Current firmware version of the device const char* Get_Firmware_Version() const; - /// @brief Sets the current firmware version, used to decide if an OTA firmware update is actually installed, - /// this is only done if the version of the update and the current firmware title are different + /// @brief Sets the current firmware version, used to decide if an OTA firmware update is already installed and therefore should not be downladed, + /// this is only done if the version of the update and the current firmware version are different, because if they are not then we would download the same firmware as is already on the device /// @param currFwVersion Current firmware version of the device void Set_Firmware_Version(const char *currFwVersion); @@ -131,36 +133,41 @@ class OTA_Update_Callback : public Callback { /// @return Updater implementation that writes the given firmware data IUpdater* Get_Updater() const; - /// @brief Sets the updater implementation + /// @brief Sets the updater implementation, used to write the actual firmware data into the needed memory location, + /// so it can be used to reboot the given device with that new flashed firmware /// @param updater Updater implementation that writes the given firmware data void Set_Updater(IUpdater *updater); /// @brief Gets the amount of times we attempt to download each chunk of the OTA firmware binary file, /// if the download fails because it times out, doesn't let itself write into flash memory, ... - /// the retries are decreased by 1 until we hit 0, if that is the case then we instead stop the OTA firmware update completly - /// @return Amount of retries for each single chunk to be downloaded successfully + /// the retries are decreased by 1 until we hit 0, if that is the case then we instead stop the OTA firmware update completely + /// @return Amount of retries for each single chunk before we abort the update const uint8_t& Get_Chunk_Retries() const; /// @brief Sets the amount of times we attempt to download each chunk of the OTA firmware binary file, /// if the download fails because it times out, doesn't let itself write into flash memory, ... - /// the retries are decreased by 1 until we hit 0, if that is the case then we instead stop the OTA firmware update completly - /// @param chunkRetries Amount of retries for each single chunk to be downloaded successfully + /// the retries are decreased by 1 until we hit 0, if that is the case then we instead stop the OTA firmware update completely + /// @param chunkRetries Amount of retries for each single chunk before we abort the update void Set_Chunk_Retries(const uint8_t &chunkRetries); - /// @brief Gets the size a single chunk of the OTA firmware binary file we attempt to download should have + /// @brief Gets the size of the chunks that the firmware binary data will be split into, + /// increased chunkSize might speed up the process by a little bit, but requires more heap memory, + // because the whole chunk is saved into the heap before it can be processed and is then erased again after it has been used /// @return Size of each single chunk to be downloaded const uint16_t& Get_Chunk_Size() const; - /// @brief Sets the size a single chunk of the OTA firmware binary file we attempt to download should have + /// @brief Sets the size of the chunks that the firmware binary data will be split into, + /// increased chunkSize might speed up the process by a little bit, but requires more heap memory, + // because the whole chunk is saved into the heap before it can be processed and is then erased again after it has been used /// @param chunkSize Size of each single chunk to be downloaded void Set_Chunk_Size(const uint16_t &chunkSize); /// @brief Gets the time in microseconds we wait until we declare a single chunk we attempted to download as a failure - /// @return Gets the timeout time for each single chunk to be downloaded + /// @return Timeout time until we expect a response from the server const uint64_t& Get_Timeout() const; /// @brief Sets the time in microseconds we wait until we decleare a single chunk we attempted to download as a timeout - /// @param timeout_microseconds Gets the timeout time for each single chunk to be downloaded + /// @param timeout_microseconds Timeout time until we expect a response from the server void Set_Timeout(const uint64_t &timeout_microseconds); private: @@ -168,9 +175,9 @@ class OTA_Update_Callback : public Callback { const char *m_fwTitel; // Current firmware title of device const char *m_fwVersion; // Current firmware version of device IUpdater *m_updater; // Updater implementation used to write firmware data - uint8_t m_retries; // Maximum amount of retries - uint16_t m_size; // Maximum size of the chuncks we are downloading - uint64_t m_timeout; // How long we maximum wait for each chunck to arrive + uint8_t m_retries; // Maximum amount of retries for a single chunk to be downloaded and flashes successfully + uint16_t m_size; // Size of chunks the firmware data will be split into + uint64_t m_timeout; // How long we wait for each chunck to arrive before declaring it as failed }; #endif // THINGSBOARD_ENABLE_OTA diff --git a/src/Provision_Callback.cpp b/src/Provision_Callback.cpp index c88340f7..1b9b6462 100644 --- a/src/Provision_Callback.cpp +++ b/src/Provision_Callback.cpp @@ -22,8 +22,8 @@ Provision_Callback::Provision_Callback() : // Nothing to do } -Provision_Callback::Provision_Callback(Access_Token, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *deviceName) : - Callback(cb, PROVISION_CB_IS_NULL), +Provision_Callback::Provision_Callback(Access_Token, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *deviceName) : + Callback(callback, PROVISION_CB_IS_NULL), m_deviceKey(provisionDeviceKey), m_deviceSecret(provisionDeviceSecret), m_deviceName(deviceName), @@ -37,8 +37,8 @@ Provision_Callback::Provision_Callback(Access_Token, function cb, const char *pr // Nothing to do } -Provision_Callback::Provision_Callback(Device_Access_Token, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *accessToken, const char *deviceName) : - Callback(cb, PROVISION_CB_IS_NULL), +Provision_Callback::Provision_Callback(Device_Access_Token, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *accessToken, const char *deviceName) : + Callback(callback, PROVISION_CB_IS_NULL), m_deviceKey(provisionDeviceKey), m_deviceSecret(provisionDeviceSecret), m_deviceName(deviceName), @@ -52,8 +52,8 @@ Provision_Callback::Provision_Callback(Device_Access_Token, function cb, const c // Nothing to do } -Provision_Callback::Provision_Callback(Basic_MQTT_Credentials, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *username, const char *password, const char *clientID, const char *deviceName) : - Callback(cb, PROVISION_CB_IS_NULL), +Provision_Callback::Provision_Callback(Basic_MQTT_Credentials, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *username, const char *password, const char *clientID, const char *deviceName) : + Callback(callback, PROVISION_CB_IS_NULL), m_deviceKey(provisionDeviceKey), m_deviceSecret(provisionDeviceSecret), m_deviceName(deviceName), @@ -67,8 +67,8 @@ Provision_Callback::Provision_Callback(Basic_MQTT_Credentials, function cb, cons // Nothing to do } -Provision_Callback::Provision_Callback(X509_Certificate, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *hash, const char *deviceName) : - Callback(cb, PROVISION_CB_IS_NULL), +Provision_Callback::Provision_Callback(X509_Certificate, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *hash, const char *deviceName) : + Callback(callback, PROVISION_CB_IS_NULL), m_deviceKey(provisionDeviceKey), m_deviceSecret(provisionDeviceSecret), m_deviceName(deviceName), diff --git a/src/Provision_Callback.h b/src/Provision_Callback.h index 7972f720..daf32af9 100644 --- a/src/Provision_Callback.h +++ b/src/Provision_Callback.h @@ -15,6 +15,7 @@ // Convenient aliases +// JSON object const (read only twice as small as JSON object), is used to communicate Provision data to the client using Provision_Data = const JsonObjectConst; // Struct dispatch tags, to differentiate between constructors, allows the same paramter types to be passed @@ -24,7 +25,9 @@ struct Basic_MQTT_Credentials{}; struct X509_Certificate{}; -/// @brief Provisioning callback wrapper +/// @brief Provisioning callback wrapper, +/// contains the needed configuration settings to create the request that should be sent to the server. +/// Documentation about the specific use of Provisioning devices in ThingsBoard can be found here https://thingsboard.io/docs/user-guide/device-provisioning/ class Provision_Callback : public Callback { public: /// @brief Constructs empty callback, will result in never being called @@ -33,28 +36,28 @@ class Provision_Callback : public Callback { /// @brief Constructs callback that will be fired upon a provision request arrival, /// where the requested credentials were sent by the cloud and received by the client. /// Using the credentials generated by the ThingsBoard server method. See https://thingsboard.io/docs/user-guide/device-provisioning/?mqttprovisioning=without#mqtt-device-apis - /// @param cb Callback method that will be called + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument /// @param provisionDeviceKey Device profile provisioning key of the device profile that should be used to create the device under /// @param provisionDeviceSecret Device profile provisioning secret of the device profile that should be used to create the device under /// @param deviceName Name the created device should have on the cloud, /// pass nullptr or an empty string if a random string should be used as a name instead - Provision_Callback(Access_Token, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *deviceName = nullptr); + Provision_Callback(Access_Token, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *deviceName = nullptr); /// @brief Constructs callback that will be fired upon a provision request arrival, /// where the requested credentials were sent by the cloud and received by the client. /// Using the device supplies access token method. See https://thingsboard.io/docs/user-guide/device-provisioning/?mqttprovisioning=without#mqtt-device-apis - /// @param cb Callback method that will be called + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument /// @param provisionDeviceKey Device profile provisioning key of the device profile that should be used to create the device under /// @param provisionDeviceSecret Device profile provisioning secret of the device profile that should be used to create the device under /// @param accessToken Access token generated by the device, that will be used by the provisioned device, alternative to letting the access token be generated by the cloud instead /// @param deviceName Name the created device should have on the cloud, /// pass nullptr or an empty string if a random string should be used as a name instead - Provision_Callback(Device_Access_Token, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *accessToken, const char *deviceName = nullptr); + Provision_Callback(Device_Access_Token, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *accessToken, const char *deviceName = nullptr); /// @brief Constructs callback that will be fired upon a provision request arrival, /// where the requested credentials were sent by the cloud and received by the client. /// Using the device supplies basic MQTT credentials method. See https://thingsboard.io/docs/user-guide/device-provisioning/?mqttprovisioning=without#mqtt-device-apis - /// @param cb Callback method that will be called + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument /// @param provisionDeviceKey Device profile provisioning key of the device profile that should be used to create the device under /// @param provisionDeviceSecret Device profile provisioning secret of the device profile that should be used to create the device under /// @param username Basic MQTT credentials username, that will be used by the provisioned device @@ -62,18 +65,18 @@ class Provision_Callback : public Callback { /// @param clientID Basic MQTT credentials clientID, that will be used by the provisioned device /// @param deviceName Name the created device should have on the cloud, /// pass nullptr or an empty string if a random string should be used as a name instead - Provision_Callback(Basic_MQTT_Credentials, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *username, const char *password, const char *clientID, const char *deviceName = nullptr); + Provision_Callback(Basic_MQTT_Credentials, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *username, const char *password, const char *clientID, const char *deviceName = nullptr); /// @brief Constructs callback that will be fired upon a provision request arrival, /// where the requested credentials were sent by the cloud and received by the client. /// Using the device supplies X.509 certificate method. See https://thingsboard.io/docs/user-guide/device-provisioning/?mqttprovisioning=without#mqtt-device-apis - /// @param cb Callback method that will be called + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument /// @param provisionDeviceKey Device profile provisioning key of the device profile that should be used to create the device under /// @param provisionDeviceSecret Device profile provisioning secret of the device profile that should be used to create the device under /// @param hash Public X.509 certificate hash, that will be used by the provisioned device /// @param deviceName Name the created device should have on the cloud, /// pass nullptr or an empty string if a random string should be used as a name instead - Provision_Callback(X509_Certificate, function cb, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *hash, const char *deviceName = nullptr); + Provision_Callback(X509_Certificate, function callback, const char *provisionDeviceKey, const char *provisionDeviceSecret, const char *hash, const char *deviceName = nullptr); /// @brief Gets the device profile provisioning key of the device profile, /// that should be used to create the device under diff --git a/src/RPC_Callback.h b/src/RPC_Callback.h index b4659a44..aae295ed 100644 --- a/src/RPC_Callback.h +++ b/src/RPC_Callback.h @@ -12,26 +12,29 @@ #include "RPC_Response.h" -// JSON variant const (read only twice as small as JSON variant), is used to communicate RPC parameters to the client +// JSON variant const (read only twice as small as JSON variant), is used to communicate server-side RPC parameters to the client using RPC_Data = const JsonVariantConst; -/// @brief RPC callback wrapper +/// @brief Server-side RPC callback wrapper, +/// contains the needed configuration settings to create the request that should be sent to the server. +/// Documentation about the specific use of Server-side RPC in ThingsBoard can be found here https://thingsboard.io/docs/user-guide/rpc/#server-side-rpc class RPC_Callback : public Callback { public: /// @brief Constructs empty callback, will result in never being called RPC_Callback(); - /// @brief Constructs callback, will be called upon RPC request arrival with the given methodName - /// @param methodName Name we expect to be sent via. RPC so this callback will be called - /// @param cb Callback method that will be called and should return a response if excpected + /// @brief Constructs callback, will be called upon server-side RPC request arrival with the given methodName + /// @param methodName Name we expect to be sent via. server-side RPC so that this method callback will be called + /// @param cb Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument + /// and should return a RPC_Response, the RPC_Response can be empty if the RPC widget does not expect any response RPC_Callback(const char *methodName, function cb); - /// @brief Gets the poiner to the underlying name + /// @brief Gets the poiner to the underlying name we expect to be sent via. server-side RPC so that this method callback will be called /// @return Pointer to the passed methodName const char* Get_Name() const; - /// @brief Sets the poiner to the underlying name + /// @brief Sets the poiner to the underlying name we expect to be sent via. server-side RPC so that this method callback will be called /// @param methodName Pointer to the passed methodName void Set_Name(const char *methodName); diff --git a/src/RPC_Request_Callback.cpp b/src/RPC_Request_Callback.cpp index 0787182f..5d2b504c 100644 --- a/src/RPC_Request_Callback.cpp +++ b/src/RPC_Request_Callback.cpp @@ -16,14 +16,14 @@ RPC_Request_Callback::RPC_Request_Callback() : // Nothing to do } -RPC_Request_Callback::RPC_Request_Callback(const char *methodName, function cb) : - RPC_Request_Callback(methodName, nullptr, cb) +RPC_Request_Callback::RPC_Request_Callback(const char *methodName, function callback) : + RPC_Request_Callback(methodName, nullptr, callback) { // Nothing to do } -RPC_Request_Callback::RPC_Request_Callback(const char *methodName, const JsonArray *parameteres, function cb) : - Callback(cb, RPC_REQUEST_CB_NULL), +RPC_Request_Callback::RPC_Request_Callback(const char *methodName, const JsonArray *parameteres, function callback) : + Callback(callback, RPC_REQUEST_CB_NULL), m_methodName(methodName), m_parameters(parameteres), m_request_id(0U) diff --git a/src/RPC_Request_Callback.h b/src/RPC_Request_Callback.h index 9fc68ffb..378b01c7 100644 --- a/src/RPC_Request_Callback.h +++ b/src/RPC_Request_Callback.h @@ -14,51 +14,53 @@ #include -/// @brief RPC request callback wrapper +/// @brief Client-side RPC callback wrapper, +/// contains the needed configuration settings to create the request that should be sent to the server. +/// Documentation about the specific use of client-side RPC in ThingsBoard can be found here https://thingsboard.io/docs/user-guide/rpc/#client-side-rpc class RPC_Request_Callback : public Callback { public: /// @brief Constructs empty callback, will result in never being called RPC_Request_Callback(); - /// @brief Constructs callback, will be called upon RPC response arrival originating + /// @brief Constructs callback, will be called upon client-side RPC response arrival originating /// from the original client side RPC request without any parameters - /// @param methodName Name of the client side RPC we want to call on the cloud - /// @param cb Callback method that will be called - RPC_Request_Callback(const char *methodName, function cb); + /// @param methodName Name of the client side RPC method we want to call on the cloud + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument + RPC_Request_Callback(const char *methodName, function callback); /// @brief Constructs callback, will be called upon RPC response arrival originating /// from the original client side RPC request with additional parameters that should be passed to the method - /// @param methodName Name of the client side RPC we want to call on the cloud + /// @param methodName Name of the client side RPC method we want to call on the cloud /// @param parameteres Parameters that will be passed to the client side RPC, /// Optional, pass NULL if there are no argument for the given method - /// @param cb Callback method that will be called - RPC_Request_Callback(const char *methodName, const JsonArray *parameteres, function cb); + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument + RPC_Request_Callback(const char *methodName, const JsonArray *parameteres, function callback); /// @brief Gets the unique request identifier that is connected to the original request, /// and will be later used to verifiy which RPC_Request_Callback - /// is connected to which received RPC response + /// is connected to which received client-side RPC response /// @return Unique identifier connected to the request for client side rpc const size_t& Get_Request_ID() const; /// @brief Sets the unique request identifier that is connected to the original request, /// and will be later used to verifiy which RPC_Request_Callback - /// is connected to which received RPC response + /// is connected to which received client-side RPC response /// @param request_id Unique identifier connected to the request for client side rpc void Set_Request_ID(const size_t &request_id); - /// @brief Gets the poiner to the underlying name + /// @brief Gets the poiner to the underlying name of the client side RPC method we want to call on the cloud /// @return Pointer to the passed methodName const char* Get_Name() const; - /// @brief Sets the poiner to the underlying name + /// @brief Sets the poiner to the underlying name of the client side RPC method we want to call on the cloud /// @param methodName Pointer to the passed methodName void Set_Name(const char *methodName); - /// @brief Gets the pointer to the underlying paramaters + /// @brief Gets the pointer to the underlying paramaters we want to call the client side RPC method on the cloud with /// @return Pointer to the passed parameters const JsonArray* Get_Parameters() const; - /// @brief Sets the pointer to the underlying paramaters + /// @brief Sets the pointer to the underlying paramaters we want to call the client side RPC method on the cloud with /// @param parameteres Pointer to the passed parameters void Set_Parameters(const JsonArray *parameteres); diff --git a/src/RPC_Response.h b/src/RPC_Response.h index c1178d1c..32c0e749 100644 --- a/src/RPC_Response.h +++ b/src/RPC_Response.h @@ -11,6 +11,8 @@ #include "Telemetry.h" +/// @brief RPC response expected to be sent by the user to the server once an RPC method has been called by the server, +/// is a simple wrapper around the Telemetry and JsonVariant class, which allow to easily serialize the response into a json string class RPC_Response : public JsonVariant { public: /// @brief Constructor diff --git a/src/Shared_Attribute_Callback.h b/src/Shared_Attribute_Callback.h index 1d04e75a..6cc1c32f 100644 --- a/src/Shared_Attribute_Callback.h +++ b/src/Shared_Attribute_Callback.h @@ -27,11 +27,13 @@ constexpr char ATT_CB_IS_NULL[] = "Shared attribute update callback is NULL"; #endif // THINGSBOARD_ENABLE_PROGMEM // Convenient aliases -// JSON object const (read only twice as small as JSON object), is used to communicate Shared Attributes and Provision Data to the client +// JSON object const (read only twice as small as JSON object), is used to communicate Shared Attributes to the client using Shared_Attribute_Data = const JsonObjectConst; -/// @brief Shared attributes callback wrapper +/// @brief Shared attribute update callback wrapper, +/// contains the needed configuration settings to create the request that should be sent to the server. +/// Documentation about the specific use of shared attribute update in ThingsBoard can be found here https://thingsboard.io/docs/reference/mqtt-api/#subscribe-to-attribute-updates-from-the-server class Shared_Attribute_Callback : public Callback { public: /// @brief Constructs empty callback, will result in never being called @@ -39,41 +41,55 @@ class Shared_Attribute_Callback : public Callback - Shared_Attribute_Callback(const InputIterator &first_itr, const InputIterator &last_itr, function cb) - : Callback(cb, ATT_CB_IS_NULL) - , m_attributes(first_itr, last_itr) - { - // Nothing to do - } + /// If the update does not include any of the given shared attributes the callback is not called. + /// Directly forwards the given arguments to the overloaded vector constructor, + /// meaning all combinatons of arguments that would initalize a vector can be used to call this constructor. + /// See possible vector constructors https://en.cppreference.com/w/cpp/container/vector/vector, for the possible passed parameters. + /// The possibilites mainly consist out of the fill constructor, where a value and a number n is given and then that many elements of the value will be copied into + /// or be created with their default value, or out of the range constructor where we can pass an interator the start of another data container + /// and to the end of the data container (last element + 1) + /// and then every element between those iteratos will be copied, in the same order as in the original data container. + /// The last option is a copy constructor where we pass a vector and the values of that vector will be copied into our buffer + /// @tparam ...Args Holds the multiple arguments that will simply be forwarded to the vector constructor and therefore allow to use every overloaded vector constructor without having to implement them + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument + /// @param ...args Arguments that will be forwarded into the overloaded vector constructor see https://en.cppreference.com/w/cpp/container/vector/vector for more information + template + inline Attribute_Request_Callback(function callback, Args... args) + : Callback(callback, ATT_CB_IS_NULL) + , m_attributes(std::forward(args)...) + { + // Nothing to do + } /// @brief Gets all the subscribed shared attributes that will result, - /// in the subscribed method being called if changed by the cloud + /// in the subscribed method being called if any of those attributes values is changed by the cloud, + /// with their current value they have been changed to /// @return Subscribed shared attributes const std::vector& Get_Attributes() const; /// @brief Sets all the subscribed shared attributes that will result, - /// in the subscribed method being called if changed by the cloud - /// @tparam InputIterator Class that points to the begin and end iterator - /// of the given data container, allows for using / passing either std::vector or std::array - /// @param first_itr Iterator pointing to the first element in the data container - /// @param last_itr Iterator pointing to the end of the data container (last element + 1) - template - inline void Set_Attributes(const InputIterator &first_itr, const InputIterator &last_itr) { - m_attributes.assign(first_itr, last_itr); + /// in the subscribed method being called if any of those attributes values is changed by the cloud, + /// with their current value they have been changed to. + /// Directly forwards the given arguments to the overloaded vector assign method, + /// meaning all combinatons of arguments that can call the assign method on a vector can be used to call this method. + /// See possible overloaded vector assign methods https://en.cppreference.com/w/cpp/container/vector/assign, for the possible passed parameters. + /// The possibilites mainly consist out of the fill assign method, where a value and a number n is given and then that many elements of the value will be copied into + /// or be created with their default value, or out of the range assign method where we can pass an interator the start of another data container + /// and to the end of the data container (last element + 1) + /// and then every element between those iteratos will be copied, in the same order as in the original data container. + /// The last option is a copy assign method where we pass a vector and the values of that vector will be copied into our buffer + /// @tparam ...Args Holds the multiple arguments that will simply be forwarded to the vector assign method and therefore allow to use every overloaded vector assign without having to implement them + /// @param ...args Arguments that will be forwarded into the overloaded vector assign method see https://en.cppreference.com/w/cpp/container/vector/assign for more information + template + inline void Set_Attributes(Args... args) { + m_attributes.assign(std::forward(args)...); } #else @@ -86,24 +102,24 @@ class Shared_Attribute_Callback : public Callback m_attributes; // Attribute we want to request + std::vector m_attributes; // Shared attribute we want to subscribe to receive a message if they change #else - const char *m_attributes; // Attribute we want to request + const char *m_attributes; // Shared attribute we want to subscribe to receive a message if they change #endif // THINGSBOARD_ENABLE_STL }; diff --git a/src/Telemetry.h b/src/Telemetry.h index b374ec26..a45e0cfa 100644 --- a/src/Telemetry.h +++ b/src/Telemetry.h @@ -17,7 +17,8 @@ #endif // THINGSBOARD_ENABLE_STL -/// @brief Telemetry record class, allows to store different data using a common interface. +/// @brief Telemetry record class, allows to store different data using a common interface, +/// is used to allow to easily create a key-value pair of multiple different types that can then be deserialized into a json message class Telemetry { public: /// @brief Creates an empty Telemetry record containg neither a key nor value @@ -62,7 +63,7 @@ class Telemetry { m_key(key), m_value() { - m_value.real = value; + m_value.real = value; } /// @brief Constructs telemetry record from boolean value @@ -93,18 +94,18 @@ class Telemetry { double real; }; - // Data type inside a container + // Data type that is set inside the container enum class DataType: const uint8_t { - TYPE_NONE, - TYPE_BOOL, - TYPE_INT, - TYPE_REAL, - TYPE_STR + TYPE_NONE, // Telemetry instance is empty and has not been assigned a value + TYPE_BOOL, // Telemetry instance is a key value-pair with a boolean value + TYPE_INT, // Telemetry instance is a key value-pair with an integral value + TYPE_REAL, // Telemetry instance is a key value-pair with a real (float, double) value + TYPE_STR // Telemetry isntance is a key value-pair with a string value }; - DataType m_type; // Data type flag - const char *m_key; // Data key - Data m_value; // Data value + DataType m_type; // Data type flag, showing which value is saved in the class instance + const char *m_key; // Data key of the key-value pair + Data m_value; // Data value of the key-value pair }; // Convenient aliases diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index f482722a..e34fce24 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -122,25 +122,28 @@ constexpr char RPC_EMPTY_PARAMS_VALUE[] = "{}"; #if THINGSBOARD_ENABLE_PROGMEM constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] PROGMEM = "Unable to de-serialize received json data with error (DeserializationError::%s)"; constexpr char INVALID_BUFFER_SIZE[] PROGMEM = "Buffer size (%u) to small for the given payloads size (%u), increase with setBufferSize accordingly or set THINGSBOARD_ENABLE_STREAM_UTILS to 1 before including ThingsBoard"; +#if THINGSBOARD_ENABLE_OTA constexpr char NUMBER_PRINTF[] PROGMEM = "%u"; +#endif // THINGSBOARD_ENABLE_OTA #if !THINGSBOARD_ENABLE_DYNAMIC constexpr char MAX_RPC_EXCEEDED[] PROGMEM = "Too many server-side RPC subscriptions, increase MaxFieldsAmt or unsubscribe"; constexpr char MAX_RPC_REQUEST_EXCEEDED[] PROGMEM = "Too many client-side RPC subscriptions, increase MaxFieldsAmt or unsubscribe"; constexpr char MAX_SHARED_ATT_UPDATE_EXCEEDED[] PROGMEM = "Too many shared attribute update callback subscriptions, increase MaxFieldsAmt or unsubscribe"; constexpr char MAX_SHARED_ATT_REQUEST_EXCEEDED[] PROGMEM = "Too many shared attribute request callback subscriptions, increase MaxFieldsAmt"; +#else +constexpr char COLON PROGMEM = ':'; #endif // !THINGSBOARD_ENABLE_DYNAMIC constexpr char COMMA PROGMEM = ','; -constexpr char COLON PROGMEM = ':'; constexpr char NO_KEYS_TO_REQUEST[] PROGMEM = "No keys to request were given"; constexpr char RPC_METHOD_NULL[] PROGMEM = "RPC methodName is NULL"; +constexpr char SUBSCRIBE_TOPIC_FAILED[] PROGMEM = "Subscribing the given topic failed"; +#if THINGSBOARD_ENABLE_DEBUG constexpr char NO_RPC_PARAMS_PASSED[] PROGMEM = "No parameters passed with RPC, passing null JSON"; constexpr char NOT_FOUND_ATT_UPDATE[] PROGMEM = "Shared attribute update key not found"; +constexpr char ATT_KEY_NOT_FOUND[] PROGMEM = "Attribute key not found"; constexpr char ATT_CB_NO_KEYS[] PROGMEM = "No keys subscribed. Calling subscribed callback for any updated attributes, assumed to be subscribed to every possible key"; constexpr char ATT_IS_NULL[] PROGMEM = "Subscribed shared attribute update key is NULL"; constexpr char ATT_NO_CHANGE[] PROGMEM = "No keys that we subscribed too were changed, skipping callback"; -constexpr char SUBSCRIBE_TOPIC_FAILED[] PROGMEM = "Subscribing the given topic failed"; -constexpr char ATT_KEY_NOT_FOUND[] PROGMEM = "Attribute key not found"; -#if THINGSBOARD_ENABLE_DEBUG constexpr char CALLING_RPC_CB[] PROGMEM = "Calling subscribed callback for rpc with methodname (%s)"; constexpr char CALLING_ATT_CB[] PROGMEM = "Calling subscribed callback for updated shared attribute (%s)"; constexpr char CALLING_REQUEST_CB[] PROGMEM = "Calling subscribed callback for request with response id (%u)"; @@ -151,25 +154,28 @@ constexpr char SEND_SERIALIZED[] PROGMEM = "Hidden, because json data is bigger #else constexpr char UNABLE_TO_DE_SERIALIZE_JSON[] = "Unable to de-serialize received json data with error (DeserializationError::%s)"; constexpr char INVALID_BUFFER_SIZE[] = "Buffer size (%u) to small for the given payloads size (%u), increase with setBufferSize accordingly or set THINGSBOARD_ENABLE_STREAM_UTILS to 1 before including ThingsBoard"; +#if THINGSBOARD_ENABLE_OTA constexpr char NUMBER_PRINTF[] = "%u"; +#endif // THINGSBOARD_ENABLE_OTA #if !THINGSBOARD_ENABLE_DYNAMIC constexpr char MAX_RPC_EXCEEDED[] = "Too many server-side RPC subscriptions, increase MaxFieldsAmt or unsubscribe"; constexpr char MAX_RPC_REQUEST_EXCEEDED[] = "Too many client-side RPC subscriptions, increase MaxFieldsAmt or unsubscribe"; constexpr char MAX_SHARED_ATT_UPDATE_EXCEEDED[] = "Too many shared attribute update callback subscriptions, increase MaxFieldsAmt or unsubscribe"; constexpr char MAX_SHARED_ATT_REQUEST_EXCEEDED[] = "Too many shared attribute request callback subscriptions, increase MaxFieldsAmt"; +#else +constexpr char COLON = ':'; #endif // !THINGSBOARD_ENABLE_DYNAMIC constexpr char COMMA = ','; -constexpr char COLON = ':'; constexpr char NO_KEYS_TO_REQUEST[] = "No keys to request were given"; constexpr char RPC_METHOD_NULL[] = "RPC methodName is NULL"; +constexpr char SUBSCRIBE_TOPIC_FAILED[] = "Subscribing the given topic failed"; +#if THINGSBOARD_ENABLE_DEBUG constexpr char NO_RPC_PARAMS_PASSED[] = "No parameters passed with RPC, passing null JSON"; constexpr char NOT_FOUND_ATT_UPDATE[] = "Shared attribute update key not found"; +constexpr char ATT_KEY_NOT_FOUND[] = "Attribute key not found"; constexpr char ATT_CB_NO_KEYS[] = "No keys subscribed. Calling subscribed callback for any updated attributes, assumed to be subscribed to every possible key"; constexpr char ATT_IS_NULL[] = "Subscribed shared attribute update key is NULL"; constexpr char ATT_NO_CHANGE[] = "No keys that we subscribed too were changed, skipping callback"; -constexpr char SUBSCRIBE_TOPIC_FAILED[] = "Subscribing the given topic failed"; -constexpr char ATT_KEY_NOT_FOUND[] = "Attribute key not found"; -#if THINGSBOARD_ENABLE_DEBUG constexpr char CALLING_RPC_CB[] = "Calling subscribed callback for rpc with methodname (%s)"; constexpr char CALLING_ATT_CB[] = "Calling subscribed callback for updated shared attribute (%s)"; constexpr char CALLING_REQUEST_CB[] = "Calling subscribed callback for request with response id (%u)"; @@ -298,18 +304,18 @@ constexpr char DOWNLOADING_FW[] = "Attempting to download over MQTT..."; #if THINGSBOARD_ENABLE_DYNAMIC /// @brief Wrapper around any arbitrary MQTT Client implementing the IMQTT_Client interface, to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. -/// BufferSize of the underlying data buffer as well as the maximum amount of data points that can ever be sent are either dynamic or can be changed during runtime. -/// If this feature is not needed and the values can be sent once as template arguements it is recommended to use the static ThingsBoard instance instead. +/// BufferSize of the underlying data buffer can be changed during the runtime and the maximum amount of data points that can ever be sent or received are automatically deduced at runtime +/// If this feature of automatic deduction, is not needed, or not wanted because it allocates memory on the heap, then the values can be set once as template arguements. /// Simply set THINGSBOARD_ENABLE_DYNAMIC to 0, before including ThingsBoard.h -/// @tparam Logger Logging class that should be used to print messages generated by ThingsBoard, default = ThingsBoardDefaultLogger +/// @tparam Logger Logging class that should be used to print messages generated by internal processes, default = ThingsBoardDefaultLogger template #else /// @brief Wrapper around any arbitrary MQTT Client implementing the IMQTT_Client interface, to allow connecting and sending / retrieving data from ThingsBoard over the MQTT or MQTT with TLS/SSL protocol. -/// BufferSize of the underlying data buffer as well as the maximum amount of data points that can ever be sent have to defined as template arguments. -/// Changing is only possible if a new instance of this class is created. If these values should be changeable and dynamic instead. -/// Simply set THINGSBOARD_ENABLE_DYNAMIC to 1, before including ThingsBoard.h -/// @tparam MaxFieldsAmt Maximum amount of key value pair that we will be able to sent to ThingsBoard in one call, default = 8 -/// @tparam Logger Logging class that should be used to print messages generated by ThingsBoard, default = ThingsBoardDefaultLogger +/// BufferSize of the underlying data buffer can be changed during the runtime and the maximum amount of data points that can ever be can be set once as template arguements. +/// Changing is only possible if a new instance of this class is created. If these values should be automatically deduced at runtime instead then, +/// simply set THINGSBOARD_ENABLE_DYNAMIC to 1, before including ThingsBoard.h +/// @tparam MaxFieldsAmt Maximum amount of key value pair that we will be able to sent or received by ThingsBoard in one call, default = 8 +/// @tparam Logger Logging class that should be used to print messages generated by internal processes, default = ThingsBoardDefaultLogger template #endif // THINGSBOARD_ENABLE_DYNAMIC @@ -319,7 +325,7 @@ class ThingsBoardSized { /// @param client MQTT Client implementation that should be used to establish the connection to ThingsBoard /// @param bufferSize Maximum amount of data that can be either received or sent to ThingsBoard at once, if bigger packets are received they are discarded /// and if we attempt to send data that is bigger, it will not be sent, the internal value can be changed later at any time with the setBufferSize() method - /// alternatively setting THINGSBOARD_ENABLE_STREAM_UTILS to 1 allows to send arbitrary size payloads if that is done the internal buffer of the PubSubClient + /// alternatively setting THINGSBOARD_ENABLE_STREAM_UTILS to 1 allows to send arbitrary size payloads if that is done the internal buffer of the MQTT Client implementation /// can be theoretically set to only be as big as the biggest message we should every receive from ThingsBoard, /// this will mean though that all messages are sent over the StreamUtils library as long as they are bigger than the internal buffer, /// which needs more time than sending a message directly but has the advantage of requiring less memory. @@ -327,7 +333,8 @@ class ThingsBoardSized { /// and decrease the internal buffer size of the mqtt client to what is needed to receive all MQTT messages, /// that size can vary but if all ThingsBoard features are used a buffer size of 256 bytes should suffice for receiving most responses. /// If the aforementioned feature is not enabled the buffer size might need to be much bigger though, - /// but in that case if a message was too big to be sent the user will be informed with a message to the Logger, default = Default_Payload + /// but in that case if a message was too big to be sent the user will be informed with a message to the Logger. + /// The aforementioned options can only be enabled if Arduino is used to build this library, because the StreamUtils library requires it, default = Default_Payload /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack, default = Default_Max_Stack_Size /// @param bufferingSize Amount of bytes allocated to speed up serialization, default = Default_Buffering_Size inline ThingsBoardSized(IMQTT_Client& client, const uint16_t& bufferSize = Default_Payload, const size_t& maxStackSize = Default_Max_Stack_Size, const size_t& bufferingSize = Default_Buffering_Size) @@ -379,7 +386,7 @@ class ThingsBoardSized { return m_client; } - /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before allocating on the heap instead + /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before the memory is allocated on the heap instead /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack inline void setMaximumStackSize(const size_t& maxStackSize) { m_max_stack = maxStackSize; @@ -398,16 +405,50 @@ class ThingsBoardSized { /// @brief Sets the size of the buffer for the underlying network client that will be used to establish the connection to ThingsBoard /// @param bufferSize Maximum amount of data that can be either received or sent to ThingsBoard at once, if bigger packets are received they are discarded - /// and if we attempt to send data that is bigger, it will not be sent - /// @return Whether allocating the needed memory for the given bufferSize was successful or not. + /// and if we attempt to send data that is bigger, it will not be sent, the internal value can be changed later at any time with the setBufferSize() method + /// alternatively setting THINGSBOARD_ENABLE_STREAM_UTILS to 1 allows to send arbitrary size payloads if that is done the internal buffer of the MQTT Client implementation + /// can be theoretically set to only be as big as the biggest message we should every receive from ThingsBoard, + /// this will mean though that all messages are sent over the StreamUtils library as long as they are bigger than the internal buffer, + /// which needs more time than sending a message directly but has the advantage of requiring less memory. + /// So if that is a problem on the board it might be useful to enable the THINGSBOARD_ENABLE_STREAM_UTILS option + /// and decrease the internal buffer size of the mqtt client to what is needed to receive all MQTT messages, + /// that size can vary but if all ThingsBoard features are used a buffer size of 256 bytes should suffice for receiving most responses. + /// If the aforementioned feature is not enabled the buffer size might need to be much bigger though, + /// but in that case if a message was too big to be sent the user will be informed with a message to the Logger. + /// The aforementioned options can only be enabled if Arduino is used to build this library, because the StreamUtils library requires it + /// @return Whether allocating the needed memory for the given bufferSize was successful or not inline bool setBufferSize(const uint16_t& bufferSize) { return m_client.set_buffer_size(bufferSize); } + /// @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(); + } + /// @brief Connects to the specified ThingsBoard server over the given port as the given device /// @param host ThingsBoard server instance we want to connect to /// @param access_token Access token that connects this device with a created device on the ThingsBoard server, - /// can be "provision", if the device creates itself instead + /// can be "provision", if the device creates itself instead. See https://thingsboard.io/docs/user-guide/device-provisioning/?mqttprovisioning=without#provision-device-apis for more information /// @param port Port that will be used to establish a connection and send / receive data from ThingsBoard over /// @param client_id Client username that can be used to differentiate the user that is connecting the given device to ThingsBoard /// @param password Client password that can be used to authenticate the user that is connecting the given device to ThingsBoard @@ -447,7 +488,7 @@ class ThingsBoardSized { template inline bool Send_Json(const char* topic, const TSource& source, const size_t& jsonSize) { // Check if allocating needed memory failed when trying to create the JsonObject, - // if it did the method will return true. See https://arduinojson.org/v6/api/jsonvariant/isnull/ for more information. + // if it did the isNull() method will return true. See https://arduinojson.org/v6/api/jsonvariant/isnull/ for more information if (source.isNull()) { Logger::log(UNABLE_TO_ALLOCATE_MEMORY); return false; @@ -536,7 +577,8 @@ class ThingsBoardSized { /// @brief Sends a claiming request for the given device, allowing any given user on the cloud to assign the device as their own (claim), /// as long as they enter the given device name and secret key in the given amount of time. - /// Optionally a secret key can be passed or be left empty (cloud will allow any user to claim the device for the given amount of time) + /// Optionally a secret key can be passed or be left empty (cloud will allow any user to claim the device for the given amount of time). + /// See https://thingsboard.io/docs/user-guide/claiming-devices/ for more information /// @param secretKey Password the user additionaly to the device name needs to enter to claim it as their own, /// pass nullptr or an empty string if the user should be able to claim the device without any password /// @param durationMs Total time in milliseconds the user has to claim their device as their own @@ -563,8 +605,9 @@ class ThingsBoardSized { /// where the given provision device key / secret decide which device profile is used to create the given device with. /// Optionally a device name can be passed or be left empty (cloud will use a random string as the name instead). /// The cloud then sends back json data containing our credentials, that will call the given callback, if creating the device was successful. - /// The data contained in that callbackcan then be used to disconnect and reconnect to the ThingsBoard server as our newly created device - /// @param callback Callback method that will be called + /// The data contained in that callbackcan then be used to disconnect and reconnect to the ThingsBoard server as our newly created device. + /// See https://thingsboard.io/docs/user-guide/device-provisioning/ for more information + /// @param callback Callback method that will be called upon data arrival with the given data that was received serialized into a JsonDocument /// @return Whether sending the provisioning request was successful or not inline bool Provision_Request(const Provision_Callback& callback) { StaticJsonDocument requestBuffer; @@ -592,8 +635,8 @@ class ThingsBoardSized { // Make the key-value pairs we don't want to send optional, // meaning if it is an empty string or null instead we don't send it at all. // Deciding which underlying provisioning method is restricted, by the Provision_Callback class. - // Meaning only the key-value pairs that are needed for the given provisionign method are set, - // meaning the rest will not be sent and therefore the provisoning request has the correct formatting + // Meaning only the key-value pairs that are needed for the given provisioning method are set, + // meaning the rest will not be sent and therefore the provisioning request has the correct formatting if (deviceName != nullptr && deviceName[0] != '\0') { requestObject[DEVICE_NAME_KEY] = deviceName; } @@ -625,7 +668,8 @@ class ThingsBoardSized { //---------------------------------------------------------------------------- // Telemetry API - /// @brief Attempts to send telemetry data with the given key and value of the given type + /// @brief Attempts to send telemetry data with the given key and value of the given type. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @tparam T Type of the passed value /// @param key Key of the key value pair we want to send /// @param value Value of the key value pair we want to send @@ -635,7 +679,8 @@ class ThingsBoardSized { return sendKeyValue(key, value); } - /// @brief Attempts to send aggregated telemetry data + /// @brief Attempts to send aggregated telemetry data. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @param data Array containing all the data we want to send /// @param data_count Amount of data entries in the array that we want to send /// @return Whether sending the data was successful or not @@ -643,14 +688,16 @@ class ThingsBoardSized { return sendDataArray(data, data_count); } - /// @brief Attempts to send custom json telemetry string + /// @brief Attempts to send custom json telemetry string. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @param json String containing our json key value pairs we want to attempt to send /// @return Whether sending the data was successful or not inline bool sendTelemetryJson(const char *json) { return Send_Json_String(TELEMETRY_TOPIC, json); } - /// @brief Attempts to send telemetry key value pairs from custom source to the server + /// @brief Attempts to send telemetry key value pairs from custom source to the server. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @tparam TSource Source class that should be used to serialize the json that is sent to the server /// @param source Data source containing our json key value pairs we want to send /// @param jsonSize Size of the data inside the source @@ -663,7 +710,8 @@ class ThingsBoardSized { //---------------------------------------------------------------------------- // Attribute API - /// @brief Attempts to send attribute data with the given key and value of the given type + /// @brief Attempts to send attribute data with the given key and value of the given type. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @tparam T Type of the passed value /// @param key Key of the key value pair we want to send /// @param value Value of the key value pair we want to send @@ -673,7 +721,8 @@ class ThingsBoardSized { return sendKeyValue(key, value, false); } - /// @brief Attempts to send aggregated attribute data + /// @brief Attempts to send aggregated attribute data. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @param data Array containing all the data we want to send /// @param data_count Amount of data entries in the array that we want to send /// @return Whether sending the data was successful or not @@ -681,14 +730,16 @@ class ThingsBoardSized { return sendDataArray(data, data_count, false); } - /// @brief Attempts to send custom json attribute string + /// @brief Attempts to send custom json attribute string. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @param json String containing our json key value pairs we want to attempt to send /// @return Whether sending the data was successful or not inline bool sendAttributeJSON(const char *json) { return Send_Json_String(ATTRIBUTE_TOPIC, json); } - /// @brief Attempts to send attribute key value pairs from custom source to the server + /// @brief Attempts to send attribute key value pairs from custom source to the server. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @tparam TSource Source class that should be used to serialize the json that is sent to the server /// @param source Data source containing our json key value pairs we want to send /// @param jsonSize Size of the data inside the source @@ -699,7 +750,8 @@ class ThingsBoardSized { } /// @brief Requests one client-side attribute calllback, - /// that will be called if the key-value pair from the server for the given client-side attributes is received + /// that will be called if the key-value pair from the server for the given client-side attributes is received. + /// See https://thingsboard.io/docs/reference/mqtt-api/#request-attribute-values-from-the-server for more information /// @param callback Callback method that will be called /// @return Whether requesting the given callback was successful or not inline bool Client_Attributes_Request(const Attribute_Request_Callback& callback) { @@ -709,11 +761,11 @@ class ThingsBoardSized { //---------------------------------------------------------------------------- // Server-side RPC API - #if THINGSBOARD_ENABLE_STL /// @brief Subscribes multiple server-side RPC callbacks, - /// that will be called if a request from the server for the method with the given name is received + /// that will be called if a request from the server for the method with the given name is received. + /// See https://thingsboard.io/docs/user-guide/rpc/#server-side-rpc for more information /// @tparam InputIterator Class that points to the begin and end iterator /// of the given data container, allows for using / passing either std::vector or std::array /// @param first_itr Iterator pointing to the first element in the data container @@ -741,7 +793,8 @@ class ThingsBoardSized { #else /// @brief Subscribes multiple server-side RPC callbacks, - /// that will be called if a request from the server for the method with the given name is received + /// that will be called if a request from the server for the method with the given name is received. + /// See https://thingsboard.io/docs/user-guide/rpc/#server-side-rpc for more information /// @param callbacks Pointer to the c-style array /// @param callbacksSize Amount of values that should be subscribed, ensure size matches the actual array, /// if not the system might crash unexpectedly at a later point @@ -767,7 +820,8 @@ class ThingsBoardSized { #endif // THINGSBOARD_ENABLE_STL /// @brief Subscribe one server-side RPC callback, - /// that will be called if a request from the server for the method with the given name is received + /// that will be called if a request from the server for the method with the given name is received. + /// See https://thingsboard.io/docs/user-guide/rpc/#server-side-rpc for more information /// @param callback Callback method that will be called /// @return Whether subscribing the given callback was successful or not inline bool RPC_Subscribe(const RPC_Callback& callback) { @@ -787,7 +841,8 @@ class ThingsBoardSized { return true; } - /// @brief Unsubcribes all server-side RPC callbacks + /// @brief Unsubcribes all server-side RPC callbacks. + /// See https://thingsboard.io/docs/user-guide/rpc/#server-side-rpc for more information /// @return Whether unsubcribing all the previously subscribed callbacks /// and from the rpc topic, was successful or not inline bool RPC_Unsubscribe() { @@ -800,7 +855,8 @@ class ThingsBoardSized { // Client-side RPC API /// @brief Requests one client-side RPC callback, - /// that will be called if a response from the server for the method with the given name is received + /// that will be called if a response from the server for the method with the given name is received. + /// See https://thingsboard.io/docs/user-guide/rpc/#client-side-rpc for more information /// @param callback Callback method that will be called /// @return Whether requesting the given callback was successful or not inline bool RPC_Request(const RPC_Request_Callback& callback) { @@ -863,7 +919,8 @@ class ThingsBoardSized { #if THINGSBOARD_ENABLE_OTA - /// @brief Immediately starts a firmware update if firmware is assigned to the given device + /// @brief Immediately starts a firmware update if firmware is assigned to the given device. + /// See https://thingsboard.io/docs/user-guide/ota-updates/ for more information /// @param callback Callback method that will be called /// @return Whether subscribing the given callback was successful or not inline bool Start_Firmware_Update(const OTA_Update_Callback& callback) { @@ -876,13 +933,15 @@ class ThingsBoardSized { return Shared_Attributes_Request(m_fw_request_callback); } - /// @brief Stops the currently running firmware update, calls the finish callback with a failure if the update is running + /// @brief Stops the currently running firmware update, calls the finish callback with a failure if the update is running. + /// See https://thingsboard.io/docs/user-guide/ota-updates/ for more information inline void Stop_Firmware_Update() { m_ota.Stop_Firmware_Update(); } /// @brief Subscribes for any assignment of firmware to the given device device, - /// which will then start a firmware update + /// which will then start a firmware update. + /// See https://thingsboard.io/docs/user-guide/ota-updates/ for more information /// @param callback Callback method that will be called /// @return Whether subscribing the given callback was successful or not inline bool Subscribe_Firmware_Update(const OTA_Update_Callback& callback) { @@ -895,7 +954,8 @@ class ThingsBoardSized { return Shared_Attributes_Subscribe(m_fw_update_callback); } - /// @brief Sends the given firmware title and firmware version to the cloud + /// @brief Sends the given firmware title and firmware version to the cloud. + /// See https://thingsboard.io/docs/user-guide/ota-updates/ for more information /// @param currFwTitle Current device firmware title /// @param currFwVersion Current device firmware version /// @return Whether sending the current device firmware information was successful or not @@ -908,7 +968,8 @@ class ThingsBoardSized { return sendTelemetryJson(currentFirmwareInfoObject, Helper::Measure_Json(currentFirmwareInfoObject)); } - /// @brief Sends the given firmware state to the cloud + /// @brief Sends the given firmware state to the cloud. + /// See https://thingsboard.io/docs/user-guide/ota-updates/ for more information /// @param currFwState Current firmware download state /// @param fwError Firmware error message that describes the current firmware state, /// pass nullptr or an empty string if the current state is not a failure state @@ -933,7 +994,8 @@ class ThingsBoardSized { // Shared attributes API /// @brief Requests one shared attribute calllback, - /// that will be called if the key-value pair from the server for the given shared attributes is received + /// that will be called if the key-value pair from the server for the given shared attributes is received. + /// See https://thingsboard.io/docs/reference/mqtt-api/#request-attribute-values-from-the-server for more information /// @param callback Callback method that will be called /// @return Whether requesting the given callback was successful or not inline bool Shared_Attributes_Request(const Attribute_Request_Callback& callback) { @@ -943,7 +1005,8 @@ class ThingsBoardSized { #if THINGSBOARD_ENABLE_STL /// @brief Subscribes multiple shared attribute callbacks, - /// that will be called if the key-value pair from the server for the given shared attributes is received + /// that will be called if the key-value pair from the server for the given shared attributes is received. + /// See https://thingsboard.io/docs/reference/mqtt-api/#subscribe-to-attribute-updates-from-the-server for more information /// @tparam InputIterator Class that points to the begin and end iterator /// of the given data container, allows for using / passing either std::vector or std::array /// @param first_itr Iterator pointing to the first element in the data container @@ -971,7 +1034,8 @@ class ThingsBoardSized { #else /// @brief Subscribes multiple shared attribute callbacks, - /// that will be called if the key-value pair from the server for the given shared attributes is received + /// that will be called if the key-value pair from the server for the given shared attributes is received. + /// See https://thingsboard.io/docs/reference/mqtt-api/#subscribe-to-attribute-updates-from-the-server for more information /// @param callbacks Pointer to the c-style array /// @param callbacksSize Amount of values that should be subscribed, ensure size matches the actual array, /// if not the system might crash unexpectedly at a later point @@ -997,7 +1061,8 @@ class ThingsBoardSized { #endif // THINGSBOARD_ENABLE_STL /// @brief Subscribe one shared attribute callback, - /// that will be called if the key-value pair from the server for the given shared attributes is received + /// that will be called if the key-value pair from the server for the given shared attributes is received. + /// See https://thingsboard.io/docs/reference/mqtt-api/#subscribe-to-attribute-updates-from-the-server for more information /// @param callback Callback method that will be called /// @return Whether subscribing the given callback was successful or not inline bool Shared_Attributes_Subscribe(const Shared_Attribute_Callback& callback) { @@ -1017,7 +1082,8 @@ class ThingsBoardSized { return true; } - /// @brief Unsubcribes all shared attribute callbacks + /// @brief Unsubcribes all shared attribute callbacks. + /// See https://thingsboard.io/docs/reference/mqtt-api/#subscribe-to-attribute-updates-from-the-server for more information /// @return Whether unsubcribing all the previously subscribed callbacks /// and from the attribute topic, was successful or not inline bool Shared_Attributes_Unsubscribe() { @@ -1025,30 +1091,6 @@ class ThingsBoardSized { m_shared_attribute_update_callbacks.clear(); 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: @@ -1136,7 +1178,9 @@ class ThingsBoardSized { return false; } else if (attributeRequestKey == nullptr || attributeResponseKey == nullptr) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(ATT_KEY_NOT_FOUND); +#endif // THINGSBOARD_ENABLE_DEBUG return false; } #else @@ -1581,7 +1625,9 @@ class ThingsBoardSized { // Do not inform client, if parameter field is missing for some reason if (!data.containsKey(RPC_PARAMS_KEY)) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(NO_RPC_PARAMS_PASSED); +#endif // THINGSBOARD_ENABLE_DEBUG } #if THINGSBOARD_ENABLE_DEBUG @@ -1671,7 +1717,9 @@ class ThingsBoardSized { /// @param data Payload sent by the server over our given topic, that contains our key value pairs inline void process_shared_attribute_update_message(char *topic, JsonObjectConst& data) { if (!data) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(NOT_FOUND_ATT_UPDATE); +#endif // THINGSBOARD_ENABLE_DEBUG return; } @@ -1685,7 +1733,9 @@ class ThingsBoardSized { #else if (shared_attribute.Get_Attributes() == nullptr) { #endif // THINGSBOARD_ENABLE_STL +#if THINGSBOARD_ENABLE_DEBUG Logger::log(ATT_CB_NO_KEYS); +#endif // THINGSBOARD_ENABLE_DEBUG // No specifc keys were subscribed so we call the callback anyway shared_attribute.Call_Callback(data); continue; @@ -1714,7 +1764,9 @@ class ThingsBoardSized { const char *att = stringToSplit.substring(previousIndex, currentIndex).c_str(); #endif // THINGSBOARD_ENABLE_STL if (att == nullptr) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(ATT_IS_NULL); +#endif // THINGSBOARD_ENABLE_DEBUG continue; } // Check if the request contained any of our requested keys. @@ -1734,7 +1786,9 @@ class ThingsBoardSized { // This callback did not request any keys that were in this response, // therefore we continue with the next element in the loop. if (!containsKey || requested_att == nullptr) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(ATT_NO_CHANGE); +#endif // THINGSBOARD_ENABLE_DEBUG continue; } @@ -1781,11 +1835,15 @@ class ThingsBoardSized { } const char *attributeResponseKey = attribute_request.Get_Attribute_Key(); if (attributeResponseKey == nullptr) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(ATT_KEY_NOT_FOUND); +#endif // THINGSBOARD_ENABLE_DEBUG goto delete_callback; } else if (!data) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(ATT_KEY_NOT_FOUND); +#endif // THINGSBOARD_ENABLE_DEBUG goto delete_callback; } diff --git a/src/ThingsBoardDefaultLogger.h b/src/ThingsBoardDefaultLogger.h index 834942b2..5a17f93f 100644 --- a/src/ThingsBoardDefaultLogger.h +++ b/src/ThingsBoardDefaultLogger.h @@ -10,8 +10,8 @@ /// @brief Default logger class used by the ThingsBoard class to log messages into the console class ThingsBoardDefaultLogger { public: - /// @brief Logs the given message to the Serial console - /// Ensure to initalize the Serial before calling this method (Serial.begin) + /// @brief Logs the given message to the serial console + /// Ensure to initalize the serial before calling this method /// @param msg Message we want to print into the console static void log(const char* msg); }; diff --git a/src/ThingsBoardHttp.h b/src/ThingsBoardHttp.h index c0ff8170..521f1471 100644 --- a/src/ThingsBoardHttp.h +++ b/src/ThingsBoardHttp.h @@ -52,7 +52,7 @@ constexpr char HTTP_FAILED[] = "(%s) failed HTTP response (%d)"; /// BufferSize of the underlying data buffer as well as the maximum amount of data points that can ever be sent are either dynamic or can be changed during runtime. /// If this feature is not needed and the values can be sent once as template arguements it is recommended to use the static ThingsBoard instance instead. /// Simply set THINGSBOARD_ENABLE_DYNAMIC to 0, before including ThingsBoardHttp.h -/// @tparam Logger Logging class that should be used to print messages generated by ThingsBoard, default = ThingsBoardDefaultLogger +/// @tparam Logger Logging class that should be used to print messages generated by internal processes, default = ThingsBoardDefaultLogger template #else /// @brief Wrapper around the ArduinoHttpClient or HTTPClient to allow connecting and sending / retrieving data from ThingsBoard over the HTTP orHTTPS protocol. @@ -60,7 +60,7 @@ template /// Changing is only possible if a new instance of this class is created. If theese values should be changeable and dynamic instead. /// Simply set THINGSBOARD_ENABLE_DYNAMIC to 1, before including ThingsBoardHttp.h. /// @tparam MaxFieldsAmt Maximum amount of key value pair that we will be able to sent to ThingsBoard in one call, default = 8 -/// @tparam Logger Logging class that should be used to print messages generated by ThingsBoard, default = ThingsBoardDefaultLogger +/// @tparam Logger Logging class that should be used to print messages generated by internal processes, default = ThingsBoardDefaultLogger template #endif // THINGSBOARD_ENABLE_DYNAMIC @@ -86,13 +86,13 @@ class ThingsBoardHttpSized { m_client.connect(m_host, m_port); } - /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before allocating on the heap instead + /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before the memory is allocated on the heap instead /// @param maxStackSize Maximum amount of bytes we want to allocate on the stack inline void setMaximumStackSize(const size_t& maxStackSize) { m_max_stack = maxStackSize; } - /// @brief Attempts to send custom attribute source + /// @brief Attempts to send key value pairs from custom source over the given topic to the server /// @tparam TSource Source class that should be used to serialize the json that is sent to the server /// @param topic Topic we want to send the data over /// @param source Data source containing our json key value pairs we want to send @@ -101,7 +101,7 @@ class ThingsBoardHttpSized { template inline bool Send_Json(const char* topic, const TSource& source, const size_t& jsonSize) { // Check if allocating needed memory failed when trying to create the JsonObject, - // if it did the method will return true. See https://arduinojson.org/v6/api/jsonvariant/isnull/ for more information. + // if it did the isNull() method will return true. See https://arduinojson.org/v6/api/jsonvariant/isnull/ for more information if (source.isNull()) { Logger::log(UNABLE_TO_ALLOCATE_MEMORY); return false; @@ -159,7 +159,8 @@ class ThingsBoardHttpSized { //---------------------------------------------------------------------------- // Telemetry API - /// @brief Attempts to send telemetry data with the given key and value of the given type + /// @brief Attempts to send telemetry data with the given key and value of the given type. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @tparam T Type of the passed value /// @param key Key of the key value pair we want to send /// @param value Value of the key value pair we want to send @@ -169,7 +170,8 @@ class ThingsBoardHttpSized { return sendKeyValue(key, value); } - /// @brief Attempts to send aggregated telemetry data + /// @brief Attempts to send aggregated telemetry data. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @param data Array containing all the data we want to send /// @param data_count Amount of data entries in the array that we want to send /// @return Whetherr sending the data was successful or not @@ -177,14 +179,16 @@ class ThingsBoardHttpSized { return sendDataArray(data, data_count); } - /// @brief Attempts to send custom json telemetry string + /// @brief Attempts to send custom json telemetry string. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @param json String containing our json key value pairs we want to attempt to send /// @return Whetherr sending the data was successful or not inline bool sendTelemetryJson(const char *json) { return Send_Json_String(HTTP_TELEMETRY_TOPIC, json); } - /// @brief Attempts to send telemetry key value pairs from custom source to the server + /// @brief Attempts to send telemetry key value pairs from custom source to the server. + /// See https://thingsboard.io/docs/user-guide/telemetry/ for more information /// @tparam TSource Source class that should be used to serialize the json that is sent to the server /// @param source Data source containing our json key value pairs we want to send /// @param jsonSize Size of the data inside the source @@ -218,7 +222,8 @@ class ThingsBoardHttpSized { //---------------------------------------------------------------------------- // Attribute API - /// @brief Attempts to send attribute data with the given key and value of the given type + /// @brief Attempts to send attribute data with the given key and value of the given type. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @tparam T Type of the passed value /// @param key Key of the key value pair we want to send /// @param value Value of the key value pair we want to send @@ -228,7 +233,8 @@ class ThingsBoardHttpSized { return sendKeyValue(key, value, false); } - /// @brief Attempts to send aggregated attribute data + /// @brief Attempts to send aggregated attribute data. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @param data Array containing all the data we want to send /// @param data_count Amount of data entries in the array that we want to send /// @return Whetherr sending the data was successful or not @@ -236,14 +242,16 @@ class ThingsBoardHttpSized { return sendDataArray(data, data_count, false); } - /// @brief Attempts to send custom json attribute string + /// @brief Attempts to send custom json attribute string. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @param json String containing our json key value pairs we want to attempt to send /// @return Whetherr sending the data was successful or not inline bool sendAttributeJSON(const char *json) { return Send_Json_String(HTTP_ATTRIBUTES_TOPIC, json); } - /// @brief Attempts to send attribute key value pairs from custom source to the server + /// @brief Attempts to send attribute key value pairs from custom source to the server. + /// See https://thingsboard.io/docs/user-guide/attributes/ for more information /// @tparam TSource Source class that should be used to serialize the json that is sent to the server /// @param source Data source containing our json key value pairs we want to send /// @param jsonSize Size of the data inside the source @@ -375,10 +383,10 @@ class ThingsBoardHttpSized { } IHTTP_Client& m_client; // HttpClient instance - size_t m_max_stack; // Maximum stack size we allocate at once on the stack. - const char *m_host; // Host address we connect too - const uint16_t m_port; // Port we connect over - const char *m_token; // Access token used to connect with + size_t m_max_stack; // Maximum stack size we allocate at once on the stack. + const char *m_host; // Host address we connect too + const uint16_t m_port; // Port we connect over + const char *m_token; // Access token used to connect with }; using ThingsBoardHttp = ThingsBoardHttpSized<>; diff --git a/src/Vector.h b/src/Vector.h index 207d7e51..723c7827 100644 --- a/src/Vector.h +++ b/src/Vector.h @@ -155,9 +155,9 @@ class Vector { } private: - T* m_elements; - size_t m_capacity; - size_t m_size; + T* m_elements; // Pointer to the start of our elements + size_t m_capacity; // Allocated capacity that shows how many elements we could hold + size_t m_size; // Used size that shows how many elements we entered }; #endif // !THINGSBOARD_ENABLE_STL From 04f08e94f88c307613cf088e858875494f512481 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Fri, 15 Sep 2023 21:02:01 +0200 Subject: [PATCH 51/80] Fix missing adjustments to new vector construction --- src/Shared_Attribute_Callback.h | 2 +- src/ThingsBoard.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Shared_Attribute_Callback.h b/src/Shared_Attribute_Callback.h index 6cc1c32f..5d85670a 100644 --- a/src/Shared_Attribute_Callback.h +++ b/src/Shared_Attribute_Callback.h @@ -61,7 +61,7 @@ class Shared_Attribute_Callback : public Callback - inline Attribute_Request_Callback(function callback, Args... args) + inline Shared_Attribute_Callback(function callback, Args... args) : Callback(callback, ATT_CB_IS_NULL) , m_attributes(std::forward(args)...) { diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index e34fce24..6cc525af 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -352,8 +352,8 @@ class ThingsBoardSized { , m_previous_buffer_size(0U) , m_change_buffer_size(false) , m_fw_shared_keys{FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TITLE_KEY, FW_VER_KEY} - , m_fw_request_callback(m_fw_shared_keys.cbegin(), m_fw_shared_keys.cend(), std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1)) - , m_fw_update_callback(m_fw_shared_keys.cbegin(), m_fw_shared_keys.cend(), std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1)) + , m_fw_request_callback(std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1), m_fw_shared_keys.cbegin(), m_fw_shared_keys.cend()) + , m_fw_update_callback(std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1), m_fw_shared_keys.cbegin(), m_fw_shared_keys.cend()) , m_ota(std::bind(&ThingsBoardSized::Publish_Chunk_Request, this, std::placeholders::_1), std::bind(&ThingsBoardSized::Firmware_Send_State, this, std::placeholders::_1, std::placeholders::_2), std::bind(&ThingsBoardSized::Firmware_OTA_Unsubscribe, this)) #endif // THINGSBOARD_ENABLE_OTA { @@ -1217,7 +1217,9 @@ class ThingsBoardSized { for (const char *att : attributes) { // Check if the given attribute is null, if it is skip it if (att == nullptr) { +#if THINGSBOARD_ENABLE_DEBUG Logger::log(ATT_IS_NULL); +#endif // THINGSBOARD_ENABLE_DEBUG continue; } From 63b029f484ee66931ac7c8eb3dc23b7ba379e77a Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Fri, 15 Sep 2023 22:27:00 +0200 Subject: [PATCH 52/80] Adjust examples to changes --- .../0006-esp8266_esp32_process_shared_attribute_update.ino | 2 +- .../0012-esp8266_esp32_request_shared_attribute.ino | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino b/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino index 2fbd4fd6..055b95c3 100644 --- a/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino +++ b/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino @@ -242,7 +242,7 @@ void processSharedAttributeUpdate(const Shared_Attribute_Data &data) { Serial.println(buffer); } -const Shared_Attribute_Callback callback(SUBSCRIBED_SHARED_ATTRIBUTES.cbegin(), SUBSCRIBED_SHARED_ATTRIBUTES.cend(), processSharedAttributeUpdate); +const Shared_Attribute_Callback callback(&processSharedAttributeUpdate, SUBSCRIBED_SHARED_ATTRIBUTES.cbegin(), SUBSCRIBED_SHARED_ATTRIBUTES.cend()); void setup() { // Initalize serial connection for debugging diff --git a/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino b/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino index 5102798c..070bb620 100644 --- a/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino +++ b/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino @@ -266,8 +266,8 @@ void processClientAttributeRequest(const Shared_Attribute_Data &data) { Serial.println(buffer); } -const Attribute_Request_Callback sharedCallback(REQUESTED_SHARED_ATTRIBUTES.cbegin(), REQUESTED_SHARED_ATTRIBUTES.cend(), &processSharedAttributeRequest); -const Attribute_Request_Callback clientCallback(REQUESTED_CLIENT_ATTRIBUTES.cbegin(), REQUESTED_CLIENT_ATTRIBUTES.cend(), &processClientAttributeRequest); +const Attribute_Request_Callback sharedCallback(&processSharedAttributeRequest, REQUESTED_SHARED_ATTRIBUTES.cbegin(), REQUESTED_SHARED_ATTRIBUTES.cend()); +const Attribute_Request_Callback clientCallback(&processClientAttributeRequest, REQUESTED_CLIENT_ATTRIBUTES.cbegin(), REQUESTED_CLIENT_ATTRIBUTES.cend()); void setup() { // Initalize serial connection for debugging From 46158d09a77bbd462667bcde41e9b483fd3db9a1 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Sun, 17 Sep 2023 19:00:24 +0200 Subject: [PATCH 53/80] Add option to publish non blocking with esp mqtt --- src/Espressif_MQTT_Client.cpp | 27 ++++++++++++++++++++++----- src/Espressif_MQTT_Client.h | 16 +++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index d93c1095..b4856bd9 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -3,6 +3,10 @@ #if THINGSBOARD_USE_ESP_MQTT +// The error integer -1 means a general failure while handling the mqtt client, +// where as -2 means that the outbox is filled and the message can therefore not be sent. +// Therefore we have to check if the value is smaller or equal to the MQTT_FAILURE_MESSAGE_ID, +// to ensure other errors are indentified as well constexpr int MQTT_FAILURE_MESSAGE_ID = -1; Espressif_MQTT_Client *Espressif_MQTT_Client::m_instance = nullptr; @@ -10,6 +14,7 @@ Espressif_MQTT_Client *Espressif_MQTT_Client::m_instance = nullptr; Espressif_MQTT_Client::Espressif_MQTT_Client() : m_received_data_callback(nullptr), m_connected(false), + m_enqueue_messages(false), m_mqtt_configuration(), m_mqtt_client(nullptr) { @@ -90,6 +95,10 @@ bool Espressif_MQTT_Client::set_network_timeout(const uint16_t& network_timeout_ return update_configuration(); } +void Espressif_MQTT_Client::set_enqueue_messages(const bool& enqueue_messages) { + m_enqueue_messages = enqueue_messages; +} + void Espressif_MQTT_Client::set_callback(function callback) { m_received_data_callback = callback; } @@ -195,23 +204,31 @@ bool Espressif_MQTT_Client::loop() { } bool Espressif_MQTT_Client::publish(const char *topic, const uint8_t *payload, const size_t& length) { + int message_id = MQTT_FAILURE_MESSAGE_ID; + + if (m_enqueue_messages) { + message_id = esp_mqtt_client_enqueue(m_mqtt_client, topic, reinterpret_cast(payload), length, 0U, 0U, true); + return message_id > MQTT_FAILURE_MESSAGE_ID; + } + // The blocking version esp_mqtt_client_publish() it is sent directly from the users task context. // This way is used to send messages to the cloud, because like that no internal buffer has to be used to store the message until it should be sent, // because all messages are sent with QoS level 0. If this is not wanted esp_mqtt_client_enqueue() could be used with store = true, // to ensure the sending is done in the mqtt event context instead of the users task context. - // Allows to use the publish method without having to worry about any CPU overhead, so it can even be used in callbacks or high priority tasks, without starving other tasks. - const int message_id = esp_mqtt_client_publish(m_mqtt_client, topic, reinterpret_cast(payload), length, 0U, 0U); - return message_id != MQTT_FAILURE_MESSAGE_ID; + // Allows to use the publish method without having to worry about any CPU overhead, so it can even be used in callbacks or high priority tasks, without starving other tasks, + // but compared to the other method esp_mqtt_client_enqueue() requires to save the message in the outbox, which increases the memory requirements for the internal buffer size + message_id = esp_mqtt_client_publish(m_mqtt_client, topic, reinterpret_cast(payload), length, 0U, 0U); + return message_id > MQTT_FAILURE_MESSAGE_ID; } bool Espressif_MQTT_Client::subscribe(const char *topic) { const int message_id = esp_mqtt_client_subscribe(m_mqtt_client, topic, 0U); - return message_id != MQTT_FAILURE_MESSAGE_ID; + return message_id > MQTT_FAILURE_MESSAGE_ID; } bool Espressif_MQTT_Client::unsubscribe(const char *topic) { const int message_id = esp_mqtt_client_unsubscribe(m_mqtt_client, topic); - return message_id != MQTT_FAILURE_MESSAGE_ID; + return message_id > MQTT_FAILURE_MESSAGE_ID; } bool Espressif_MQTT_Client::connected() { diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h index 8472f66f..28fbd2b4 100644 --- a/src/Espressif_MQTT_Client.h +++ b/src/Espressif_MQTT_Client.h @@ -44,8 +44,8 @@ class Espressif_MQTT_Client : public IMQTT_Client { /// under the transport.sessions.inactivity_timeout section and is 300 seconds. Meaning a value bigger than 300 seconds with the default config defeats the purpose of the keep alive alltogether /// @param keep_alive_timeout_seconds Timeout until we send another PINGREQ control packet to the broker to establish that we are still connected /// @return Whether changing the internal keep alive timeout was successful or not - bool set_keep_alive_timeout(const uint16_t& keep_alive_timeout_seconds); - + bool set_keep_alive_timeout(const uint16_t& keep_alive_timeout_seconds); + /// @brief Whether to disable or enable the keep alive mechanism, meaning we do not send any PINGREQ control packets to the broker anymore, meaning if we do not send other packet the device will be marked as inactive. /// The default value is false meaning the keep alive mechanism is enabled. /// The default timeout value ThingsBoard expectes to receive any message including a keep alive to not show the device as inactive can be found here https://thingsboard.io/docs/user-guide/install/config/#mqtt-server-parameters @@ -81,6 +81,15 @@ class Espressif_MQTT_Client : public IMQTT_Client { /// @return Whether changing the internal network timeout was successfull or not bool set_network_timeout(const uint16_t& network_timeout_milliseconds); + /// @brief Sets whether to enqueue published messages or not, enqueueing has to save them in the out buffer, meaning the internal buffer size might need to be increased, + /// but the MQTT client can in exchange send the publish messages once the main MQTT task is running again, instead of blocking in the task that has called the publish method. + /// This furthermore, allows to use nearly all internal ThingsBoard calls without having to worry about blocking the task the method was called for, + /// or having to worry about CPU overhead. + /// If enqueueing the messages to be published fails once this options has been enabled, then the internal buffer size might need to be increased. Ensure to call set_buffer_size() with a bigger value + /// and check if enqueueing the messages to be published works successfully again + /// @param enqueue_messages Whether to enqueue published messages or not, where setting the value to true means that the messages are enqueued and therefor non blocking on the called from task + void set_enqueue_messages(const bool& enqueue_messages); + void set_callback(function callback) override; bool set_buffer_size(const uint16_t& buffer_size) override; @@ -105,7 +114,8 @@ class Espressif_MQTT_Client : public IMQTT_Client { private: function m_received_data_callback; // Callback that will be called as soon as the mqtt client receives any data - bool m_connected; // Wheter the client has received the connected or disconnected event + bool m_connected; // Whether the client has received the connected or disconnected event + bool m_enqueue_messages; // Whether we enqueue messages making nearly all ThingsBoard calls non blocking or wheter we publish instead esp_mqtt_client_config_t m_mqtt_configuration; // Configuration of the underlying mqtt client, saved as a private variable to allow changes after inital configuration with the same options for all non changed settings esp_mqtt_client_handle_t m_mqtt_client; // Handle to the underlying mqtt client, used to establish the communication From b3994ae39457f831a34ab3e7c5026de32a54df7a Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:00:56 +0200 Subject: [PATCH 54/80] Adjust code to make use of callback copy --- .../0002-arduino_rpc/0002-arduino_rpc.ino | 12 ++-- ..._esp32_process_shared_attribute_update.ino | 15 +---- .../0009-esp8266_esp32_process_OTA_MQTT.ino | 19 +++---- .../0010-esp8266_esp32_rpc.ino | 9 ++- .../0011-esp8266_esp32_subscribe_OTA_MQTT.ino | 19 +++---- ...esp8266_esp32_request_shared_attribute.ino | 24 ++------ .../0013-esp8266_esp32_request_rpc.ino | 3 +- .../0015-espressif_esp32_process_OTA_MQTT.ino | 8 +-- src/Arduino_HTTP_Client.h | 2 +- src/Arduino_MQTT_Client.h | 2 +- src/Espressif_MQTT_Client.cpp | 11 ++++ src/Espressif_MQTT_Client.h | 3 + src/ThingsBoard.h | 55 +++++++++++-------- src/ThingsBoardHttp.h | 6 +- 14 files changed, 90 insertions(+), 98 deletions(-) diff --git a/examples/0002-arduino_rpc/0002-arduino_rpc.ino b/examples/0002-arduino_rpc/0002-arduino_rpc.ino index a6b33566..8a044ca9 100644 --- a/examples/0002-arduino_rpc/0002-arduino_rpc.ino +++ b/examples/0002-arduino_rpc/0002-arduino_rpc.ino @@ -208,12 +208,6 @@ RPC_Response processSwitchChange(const RPC_Data &data) { return RPC_Response(doc); } -const uint8_t callback_size = 2U; -const RPC_Callback callbacks[callback_size] = { - { RPC_TEMPERATURE_METHOD, processTemperatureChange }, - { RPC_SWITCH_METHOD, processSwitchChange } -}; - void loop() { delay(1000); @@ -243,7 +237,11 @@ void loop() { #else Serial.println("Subscribing for RPC..."); #endif - + const uint8_t callback_size = 2U; + const RPC_Callback callbacks[callback_size] = { + { RPC_TEMPERATURE_METHOD, processTemperatureChange }, + { RPC_SWITCH_METHOD, processSwitchChange } + }; // Perform a subscription. All consequent data processing will happen in // processTemperatureChange() and processSwitchChange() functions, // as denoted by callbacks array. diff --git a/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino b/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino index 055b95c3..9c6869b1 100644 --- a/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino +++ b/examples/0006-esp8266_esp32_process_shared_attribute_update/0006-esp8266_esp32_process_shared_attribute_update.ino @@ -157,16 +157,6 @@ constexpr const char FW_TAG_KEY[] PROGMEM = "fw_tag"; constexpr const char FW_TAG_KEY[] = "fw_tag"; #endif -// Shared attributes we want to request from the server -constexpr std::array SUBSCRIBED_SHARED_ATTRIBUTES = { - FW_CHKS_KEY, - FW_CHKS_ALGO_KEY, - FW_SIZE_KEY, - FW_TAG_KEY, - FW_TITLE_KEY, - FW_VER_KEY -}; - // Initialize underlying client, used to establish a connection #if ENCRYPTED @@ -242,8 +232,6 @@ void processSharedAttributeUpdate(const Shared_Attribute_Data &data) { Serial.println(buffer); } -const Shared_Attribute_Callback callback(&processSharedAttributeUpdate, SUBSCRIBED_SHARED_ATTRIBUTES.cbegin(), SUBSCRIBED_SHARED_ATTRIBUTES.cend()); - void setup() { // Initalize serial connection for debugging Serial.begin(SERIAL_DEBUG_BAUD); @@ -278,6 +266,9 @@ void loop() { #else Serial.println("Subscribing for shared attribute updates..."); #endif + // Shared attributes we want to request from the server + constexpr std::array SUBSCRIBED_SHARED_ATTRIBUTES = {FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TAG_KEY, FW_TITLE_KEY, FW_VER_KEY}; + const Shared_Attribute_Callback callback(&processSharedAttributeUpdate, SUBSCRIBED_SHARED_ATTRIBUTES.cbegin(), SUBSCRIBED_SHARED_ATTRIBUTES.cend()); if (!tb.Shared_Attributes_Subscribe(callback)) { #if THINGSBOARD_ENABLE_PROGMEM Serial.println(F("Failed to subscribe for shared attribute updates")); diff --git a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino index 6bf018c5..ce5cda9b 100644 --- a/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino +++ b/examples/0009-esp8266_esp32_process_OTA_MQTT/0009-esp8266_esp32_process_OTA_MQTT.ino @@ -203,6 +203,14 @@ WiFiClient espClient; Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); +// Initalize the Updater client instance used to flash binary to flash memory +#ifdef ESP8266 +Arduino_ESP8266_Updater updater; +#else +#ifdef ESP32 +Espressif_Updater updater; +#endif // ESP32 +#endif // ESP8266 // Statuses for updating bool currentFWSent = false; @@ -285,16 +293,6 @@ void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { Serial.printf("Progress %.2f%%\n", static_cast(currentChunk * 100U) / totalChuncks); } -#ifdef ESP8266 -Arduino_ESP8266_Updater updater; -#else -#ifdef ESP32 -Espressif_Updater updater; -#endif // ESP32 -#endif // ESP8266 - -const OTA_Update_Callback callback(&progressCallback, &updatedCallback, CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION, &updater, FIRMWARE_FAILURE_RETRIES, FIRMWARE_PACKET_SIZE); - void setup() { // Initalize serial connection for debugging Serial.begin(SERIAL_DEBUG_BAUD); @@ -337,6 +335,7 @@ void loop() { #else Serial.println("Firwmare Update..."); #endif + const OTA_Update_Callback callback(&progressCallback, &updatedCallback, CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION, &updater, FIRMWARE_FAILURE_RETRIES, FIRMWARE_PACKET_SIZE); // See https://thingsboard.io/docs/user-guide/ota-updates/ // to understand how to create a new OTA pacakge and assign it to a device so it can download it. updateRequestSent = tb.Start_Firmware_Update(callback); diff --git a/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino b/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino index 5a328a92..a2f2bd6a 100644 --- a/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino +++ b/examples/0010-esp8266_esp32_rpc/0010-esp8266_esp32_rpc.ino @@ -280,11 +280,6 @@ RPC_Response processSwitchChange(const RPC_Data &data) { return RPC_Response(doc); } -const std::array callbacks = { - RPC_Callback{ RPC_TEMPERATURE_METHOD, processTemperatureChange }, - RPC_Callback{ RPC_SWITCH_METHOD, processSwitchChange } -}; - void setup() { // Initalize serial connection for debugging Serial.begin(SERIAL_DEBUG_BAUD); @@ -319,6 +314,10 @@ void loop() { #else Serial.println("Subscribing for RPC..."); #endif + const std::array callbacks = { + RPC_Callback{ RPC_TEMPERATURE_METHOD, processTemperatureChange }, + RPC_Callback{ RPC_SWITCH_METHOD, processSwitchChange } + }; // Perform a subscription. All consequent data processing will happen in // processTemperatureChange() and processSwitchChange() functions, // as denoted by callbacks array. diff --git a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino index 0fefe852..8f35a883 100644 --- a/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino +++ b/examples/0011-esp8266_esp32_subscribe_OTA_MQTT/0011-esp8266_esp32_subscribe_OTA_MQTT.ino @@ -203,6 +203,14 @@ WiFiClient espClient; Arduino_MQTT_Client mqttClient(espClient); // Initialize ThingsBoard instance with the maximum needed buffer size ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); +// Initalize the Updater client instance used to flash binary to flash memory +#ifdef ESP8266 +Arduino_ESP8266_Updater updater; +#else +#ifdef ESP32 +Espressif_Updater updater; +#endif // ESP32 +#endif // ESP8266 // Statuses for updating bool currentFWSent = false; @@ -285,16 +293,6 @@ void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { Serial.printf("Progress %.2f%%\n", static_cast(currentChunk * 100U) / totalChuncks); } -#ifdef ESP8266 -Arduino_ESP8266_Updater updater; -#else -#ifdef ESP32 -Espressif_Updater updater; -#endif // ESP32 -#endif // ESP8266 - -const OTA_Update_Callback callback(&progressCallback, &updatedCallback, CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION, &updater, FIRMWARE_FAILURE_RETRIES, FIRMWARE_PACKET_SIZE); - void setup() { // Initalize serial connection for debugging Serial.begin(SERIAL_DEBUG_BAUD); @@ -337,6 +335,7 @@ void loop() { #else Serial.println("Firwmare Update Subscription..."); #endif + const OTA_Update_Callback callback(&progressCallback, &updatedCallback, CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION, &updater, FIRMWARE_FAILURE_RETRIES, FIRMWARE_PACKET_SIZE); // See https://thingsboard.io/docs/user-guide/ota-updates/ // to understand how to create a new OTA pacakge and assign it to a device so it can download it. updateRequestSent = tb.Subscribe_Firmware_Update(callback); diff --git a/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino b/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino index 070bb620..0254d1df 100644 --- a/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino +++ b/examples/0012-esp8266_esp32_request_shared_attribute/0012-esp8266_esp32_request_shared_attribute.ino @@ -159,21 +159,6 @@ constexpr const char FW_TAG_KEY[] = "fw_tag"; constexpr const char TEST_KEY[] = "test"; #endif -// Shared attributes we want to request from the server -constexpr std::array REQUESTED_SHARED_ATTRIBUTES = { - FW_CHKS_KEY, - FW_CHKS_ALGO_KEY, - FW_SIZE_KEY, - FW_TAG_KEY, - FW_TITLE_KEY, - FW_VER_KEY -}; - -// Client-side attributes we want to request from the server -constexpr std::array REQUESTED_CLIENT_ATTRIBUTES = { - TEST_KEY -}; - // Initialize underlying client, used to establish a connection #if ENCRYPTED @@ -266,9 +251,6 @@ void processClientAttributeRequest(const Shared_Attribute_Data &data) { Serial.println(buffer); } -const Attribute_Request_Callback sharedCallback(&processSharedAttributeRequest, REQUESTED_SHARED_ATTRIBUTES.cbegin(), REQUESTED_SHARED_ATTRIBUTES.cend()); -const Attribute_Request_Callback clientCallback(&processClientAttributeRequest, REQUESTED_CLIENT_ATTRIBUTES.cbegin(), REQUESTED_CLIENT_ATTRIBUTES.cend()); - void setup() { // Initalize serial connection for debugging Serial.begin(SERIAL_DEBUG_BAUD); @@ -303,6 +285,9 @@ void loop() { #else Serial.println("Requesting shared attributes..."); #endif + // Shared attributes we want to request from the server + constexpr std::array REQUESTED_SHARED_ATTRIBUTES = {FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TAG_KEY, FW_TITLE_KEY, FW_VER_KEY}; + const Attribute_Request_Callback sharedCallback(&processSharedAttributeRequest, REQUESTED_SHARED_ATTRIBUTES.cbegin(), REQUESTED_SHARED_ATTRIBUTES.cend()); requestedShared = tb.Shared_Attributes_Request(sharedCallback); if (!requestedShared) { #if THINGSBOARD_ENABLE_PROGMEM @@ -319,6 +304,9 @@ void loop() { #else Serial.println("Requesting client-side attributes..."); #endif + // Client-side attributes we want to request from the server + constexpr std::array REQUESTED_CLIENT_ATTRIBUTES = {TEST_KEY}; + const Attribute_Request_Callback clientCallback(&processClientAttributeRequest, REQUESTED_CLIENT_ATTRIBUTES.cbegin(), REQUESTED_CLIENT_ATTRIBUTES.cend()); requestedClient = tb.Client_Attributes_Request(clientCallback); if (!requestedClient) { #if THINGSBOARD_ENABLE_PROGMEM diff --git a/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino b/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino index 4c136304..49fd040c 100644 --- a/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino +++ b/examples/0013-esp8266_esp32_request_rpc/0013-esp8266_esp32_request_rpc.ino @@ -223,8 +223,6 @@ void processTime(const JsonVariantConst &data) { serializeJsonPretty(data, Serial); } -const RPC_Request_Callback callback(RPC_REQUEST_CALLBACK_METHOD_NAME, &processTime); - void setup() { // Initalize serial connection for debugging Serial.begin(SERIAL_DEBUG_BAUD); @@ -259,6 +257,7 @@ void loop() { #else Serial.println("Requesting RPC..."); #endif + const RPC_Request_Callback callback(RPC_REQUEST_CALLBACK_METHOD_NAME, &processTime); // Perform a request of the given RPC method. Optional responses are handled in processTime if (!tb.RPC_Request(callback)) { #if THINGSBOARD_ENABLE_PROGMEM diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino b/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino index 3c7560de..be7c02a6 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino @@ -103,6 +103,9 @@ emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= Espressif_MQTT_Client mqttClient; // Initialize ThingsBoard instance with the maximum needed buffer size ThingsBoard tb(mqttClient, MAX_MESSAGE_SIZE); +// Initalize the Updater client instance used to flash binary to flash memory +Espressif_Updater updater; + // Status for successfully connecting to the given WiFi bool wifi_connected = false; @@ -129,10 +132,6 @@ void progressCallback(const size_t& currentChunk, const size_t& totalChuncks) { ESP_LOGI("MAIN", "Downwloading firmware progress %.2f%%", static_cast(currentChunk * 100U) / totalChuncks); } -Espressif_Updater updater; -const OTA_Update_Callback callback(&progressCallback, &updatedCallback, CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION, &updater, FIRMWARE_FAILURE_RETRIES, FIRMWARE_PACKET_SIZE); - - /// @brief Callback method that is called if we got an ip address from the connected WiFi meaning we successfully established a connection /// @param event_handler_arg User data registered to the event /// @param event_base Event base for the handler @@ -206,6 +205,7 @@ extern "C" void app_main() { } if (!updateRequestSent) { + const OTA_Update_Callback callback(&progressCallback, &updatedCallback, CURRENT_FIRMWARE_TITLE, CURRENT_FIRMWARE_VERSION, &updater, FIRMWARE_FAILURE_RETRIES, FIRMWARE_PACKET_SIZE); // See https://thingsboard.io/docs/user-guide/ota-updates/ // to understand how to create a new OTA pacakge and assign it to a device so it can download it. updateRequestSent = tb.Start_Firmware_Update(callback); diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index 49fc5355..8905ae65 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -49,7 +49,7 @@ class Arduino_HTTP_Client : public IHTTP_Client { #endif // THINGSBOARD_ENABLE_STL private: - HttpClient m_http_client; + HttpClient m_http_client; // Underlying HTTP client instance used to send data }; #endif // ARDUINO diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index 480b4ede..e7c19788 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -72,7 +72,7 @@ class Arduino_MQTT_Client : public IMQTT_Client { #endif // THINGSBOARD_ENABLE_STREAM_UTILS private: - PubSubClient m_mqtt_client; + PubSubClient m_mqtt_client; // Underlying MQTT client instance used to send data }; #endif // ARDUINO diff --git a/src/Espressif_MQTT_Client.cpp b/src/Espressif_MQTT_Client.cpp index b4856bd9..bc9af9dc 100644 --- a/src/Espressif_MQTT_Client.cpp +++ b/src/Espressif_MQTT_Client.cpp @@ -21,6 +21,11 @@ Espressif_MQTT_Client::Espressif_MQTT_Client() : m_instance = this; } +Espressif_MQTT_Client::~Espressif_MQTT_Client() { + m_instance = nullptr; + (void)esp_mqtt_client_destroy(m_mqtt_client); +} + bool Espressif_MQTT_Client::set_server_certificate(const char *server_certificate_pem) { // ESP_IDF_VERSION_MAJOR Version 5 is a major breaking changes were the complete esp_mqtt_client_config_t structure changed completely. // Because PEM format is expected for the server certificate we do not need to set the certificate_len, @@ -266,6 +271,12 @@ void Espressif_MQTT_Client::mqtt_event_handler(void *handler_args, esp_event_bas // Nothing to do break; case esp_mqtt_event_id_t::MQTT_EVENT_DATA: + // Check wheter the given message has not bee received completly, but instead would be received in multiple chunks, + // if it were we discard the message because receiving a message over multiple chunks is currently not supported + if (event->data_len != event->total_data_len) { + break; + } + if (m_received_data_callback != nullptr) { m_received_data_callback(event->topic, reinterpret_cast(event->data), event->data_len); } diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h index 28fbd2b4..6666f63a 100644 --- a/src/Espressif_MQTT_Client.h +++ b/src/Espressif_MQTT_Client.h @@ -28,6 +28,9 @@ class Espressif_MQTT_Client : public IMQTT_Client { /// @brief Constructs a IMQTT_Client implementation which creates and empty esp_mqtt_client_config_t, which then has to be configured with the other methods in the class Espressif_MQTT_Client(); + /// @brief Destructor + ~Espressif_MQTT_Client(); + /// @brief Configured the server certificate, which allows to connect to the MQTT broker over a secure TLS / SSL conenction instead of the default unencrypted channel. /// Has to be called before initally calling connect() on the client to ensure the certificate is set before the connection is established, if that is not done the connection will not be encrypted. /// Encryption is recommended if relevant data is sent or if the client receives and handles Remote Procedure Calls or Shared Attribute Update Callbacks from the server, diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index 6cc525af..f26fe56e 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -351,9 +351,6 @@ class ThingsBoardSized { , m_fw_callback(nullptr) , m_previous_buffer_size(0U) , m_change_buffer_size(false) - , m_fw_shared_keys{FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TITLE_KEY, FW_VER_KEY} - , m_fw_request_callback(std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1), m_fw_shared_keys.cbegin(), m_fw_shared_keys.cend()) - , m_fw_update_callback(std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1), m_fw_shared_keys.cbegin(), m_fw_shared_keys.cend()) , m_ota(std::bind(&ThingsBoardSized::Publish_Chunk_Request, this, std::placeholders::_1), std::bind(&ThingsBoardSized::Firmware_Send_State, this, std::placeholders::_1, std::placeholders::_2), std::bind(&ThingsBoardSized::Firmware_OTA_Unsubscribe, this)) #endif // THINGSBOARD_ENABLE_OTA { @@ -930,7 +927,9 @@ class ThingsBoardSized { } // Request the firmware information - return Shared_Attributes_Request(m_fw_request_callback); + const std::vector fw_shared_keys{FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TITLE_KEY, FW_VER_KEY}; + const Attribute_Request_Callback fw_request_callback(std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1), fw_shared_keys.cbegin(), fw_shared_keys.cend()); + return Shared_Attributes_Request(fw_request_callback); } /// @brief Stops the currently running firmware update, calls the finish callback with a failure if the update is running. @@ -951,7 +950,9 @@ class ThingsBoardSized { } // Subscribes to changes of the firmware information - return Shared_Attributes_Subscribe(m_fw_update_callback); + const std::vector fw_shared_keys(FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TITLE_KEY, FW_VER_KEY); + const Shared_Attribute_Callback fw_update_callback(std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1), fw_shared_keys.cbegin(), fw_shared_keys.cend()); + return Shared_Attributes_Subscribe(fw_update_callback); } /// @brief Sends the given firmware title and firmware version to the cloud. @@ -1919,13 +1920,15 @@ class ThingsBoardSized { size_t m_max_stack; // Maximum stack size we allocate at once. size_t m_buffering_size; // Buffering size used to serialize directly into client. -#if THINGSBOARD_ENABLE_STL // Vectors hold copy of the actual passed data, this is to ensure they stay valid, // even if the user only temporarily created the object before the method was called. // This can be done because all Callback methods mostly consists of pointers to actual object so copying them // does not require a huge memory overhead and is acceptable especially in comparsion to possible problems that could // arise if references were used and the end user does not take care to ensure the Callbacks live on for the entirety - // of its usage, which will lead to dangling references and undefined behaviour + // of its usage, which will lead to dangling references and undefined behaviour. + // Therefore copy-by-value has been choosen as for this specific use case it is more advantageous, + // especially because at most we copy a vector, that will only ever contain a few pointers +#if THINGSBOARD_ENABLE_STL std::vector m_rpc_callbacks; // Server side RPC callbacks vector std::vector m_rpc_request_callbacks; // Client side RPC callbacks vector std::vector m_shared_attribute_update_callbacks; // Shared attribute update callbacks vector @@ -1941,14 +1944,10 @@ class ThingsBoardSized { size_t m_request_id; // Allows nearly 4.3 million requests before wrapping back to 0 #if THINGSBOARD_ENABLE_OTA - const OTA_Update_Callback *m_fw_callback; - uint16_t m_previous_buffer_size; - bool m_change_buffer_size; - const std::vector m_fw_shared_keys; - const Attribute_Request_Callback m_fw_request_callback; - const Shared_Attribute_Callback m_fw_update_callback; - OTA_Handler m_ota; - + const OTA_Update_Callback *m_fw_callback; // Ota update response callback + uint16_t m_previous_buffer_size; // Previous buffer size of the underlying client, used to revert to the previously configured buffer size if it was temporarily increased by the OTA update + bool m_change_buffer_size; // Whether the buffer size had to be changed, because the previous internal buffer size was to small to hold the firmware chunks + OTA_Handler m_ota; // Class instance that handles the flashing and creating a hash from the given received binary firmware data #endif // THINGSBOARD_ENABLE_OTA /// @brief MQTT callback that will be called if a publish message is received from the server @@ -1986,25 +1985,35 @@ class ThingsBoardSized { // See https://arduinojson.org/v6/doc/deserialization/ for more info on ArduinoJson deserialization const DeserializationError error = deserializeJson(jsonBuffer, payload, length); if (error) { - char message[Helper::detectSize(UNABLE_TO_DE_SERIALIZE_JSON, error.c_str())]; - snprintf_P(message, sizeof(message), UNABLE_TO_DE_SERIALIZE_JSON, error.c_str()); - Logger::log(message); - return; + char message[Helper::detectSize(UNABLE_TO_DE_SERIALIZE_JSON, error.c_str())]; + snprintf_P(message, sizeof(message), UNABLE_TO_DE_SERIALIZE_JSON, error.c_str()); + Logger::log(message); + return; } // .as() is used instead of .to(), because it is meant to cast the JsonDocument to the given type, // but it does not change the actual content of the JsonDocument, we don't want that because it already contents content // and would result in the data simply being "null", instead .as() allows accessing the data over a JsonObjectConst instead. JsonObjectConst data = jsonBuffer.template as(); + // Checks to ensure we forward the already json serialized data to the correct process function, + // especially important is the order of ATTRIBUTE_RESPONSE_TOPIC and ATTRIBUTE_TOPIC, + // that is the case because the more specific one that is longer but contains the same text has to be checked first, + // because if we do not do that then even if we receive a message from the ATTRIBUTE_RESPONSE_TOPIC + // we would call the process method for the ATTRIBUTE_TOPIC because we only compare until the end of the ATTRIBUTE_TOPIC string, + // therefore the received topic is exactly the same. Therefore the ordering needs to stay the same for thoose two specific checks if (strncmp_P(RPC_RESPONSE_TOPIC, topic, strlen(RPC_RESPONSE_TOPIC)) == 0) { process_rpc_request_message(topic, data); - } else if (strncmp_P(RPC_REQUEST_TOPIC, topic, strlen(RPC_REQUEST_TOPIC)) == 0) { + } + else if (strncmp_P(RPC_REQUEST_TOPIC, topic, strlen(RPC_REQUEST_TOPIC)) == 0) { process_rpc_message(topic, data); - } else if (strncmp_P(ATTRIBUTE_RESPONSE_TOPIC, topic, strlen(ATTRIBUTE_RESPONSE_TOPIC)) == 0) { + } + else if (strncmp_P(ATTRIBUTE_RESPONSE_TOPIC, topic, strlen(ATTRIBUTE_RESPONSE_TOPIC)) == 0) { process_attribute_request_message(topic, data); - } else if (strncmp_P(ATTRIBUTE_TOPIC, topic, strlen(ATTRIBUTE_TOPIC)) == 0) { + } + else if (strncmp_P(ATTRIBUTE_TOPIC, topic, strlen(ATTRIBUTE_TOPIC)) == 0) { process_shared_attribute_update_message(topic, data); - } else if (strncmp_P(PROV_RESPONSE_TOPIC, topic, strlen(PROV_RESPONSE_TOPIC)) == 0) { + } + else if (strncmp_P(PROV_RESPONSE_TOPIC, topic, strlen(PROV_RESPONSE_TOPIC)) == 0) { process_provisioning_response(topic, data); } } diff --git a/src/ThingsBoardHttp.h b/src/ThingsBoardHttp.h index 521f1471..d2f62e93 100644 --- a/src/ThingsBoardHttp.h +++ b/src/ThingsBoardHttp.h @@ -78,12 +78,10 @@ class ThingsBoardHttpSized { const char *host, const uint16_t& port = 80U, const bool& keepAlive = true, const size_t& maxStackSize = Default_Max_Stack_Size) : m_client(client) , m_max_stack(maxStackSize) - , m_host(host) - , m_port(port) , m_token(access_token) { m_client.set_keep_alive(keepAlive); - m_client.connect(m_host, m_port); + m_client.connect(host, port); } /// @brief Sets the maximum amount of bytes that we want to allocate on the stack, before the memory is allocated on the heap instead @@ -384,8 +382,6 @@ class ThingsBoardHttpSized { IHTTP_Client& m_client; // HttpClient instance size_t m_max_stack; // Maximum stack size we allocate at once on the stack. - const char *m_host; // Host address we connect too - const uint16_t m_port; // Port we connect over const char *m_token; // Access token used to connect with }; From 173706ca1673226d80e152bdb8a37c8604d36320 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:26:41 +0200 Subject: [PATCH 55/80] Use initializer list --- src/ThingsBoard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index f26fe56e..f3500310 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -950,7 +950,7 @@ class ThingsBoardSized { } // Subscribes to changes of the firmware information - const std::vector fw_shared_keys(FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TITLE_KEY, FW_VER_KEY); + const std::vector fw_shared_keys{FW_CHKS_KEY, FW_CHKS_ALGO_KEY, FW_SIZE_KEY, FW_TITLE_KEY, FW_VER_KEY}; const Shared_Attribute_Callback fw_update_callback(std::bind(&ThingsBoardSized::Firmware_Shared_Attribute_Received, this, std::placeholders::_1), fw_shared_keys.cbegin(), fw_shared_keys.cend()); return Shared_Attributes_Subscribe(fw_update_callback); } From b16fabd8e60b4de47ee5707d7330819b88df0736 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Fri, 22 Sep 2023 16:34:38 +0200 Subject: [PATCH 56/80] Remove redundant header notice --- src/Arduino_ESP8266_Updater.h | 6 ------ src/Arduino_HTTP_Client.h | 6 ------ src/Arduino_MQTT_Client.h | 6 ------ src/Attribute_Request_Callback.h | 6 ------ src/Callback.h | 6 ------ src/Callback_Watchdog.h | 6 ------ src/Configuration.h | 6 ------ src/Constants.h | 6 ------ src/Espressif_MQTT_Client.h | 6 ------ src/Espressif_Updater.h | 6 ------ src/HashGenerator.h | 6 ------ src/Helper.h | 6 ------ src/IMQTT_Client.h | 6 ------ src/IUpdater.h | 6 ------ src/OTA_Failure_Response.h | 6 ------ src/OTA_Handler.h | 6 ------ src/OTA_Update_Callback.h | 6 ------ src/Provision_Callback.h | 6 ------ src/RPC_Callback.h | 6 ------ src/RPC_Request_Callback.h | 6 ------ src/RPC_Response.h | 6 ------ src/Shared_Attribute_Callback.h | 6 ------ src/Telemetry.h | 6 ------ src/ThingsBoard.h | 19 ++++++------------- src/ThingsBoardDefaultLogger.h | 6 ------ src/Vector.h | 6 ------ 26 files changed, 6 insertions(+), 163 deletions(-) diff --git a/src/Arduino_ESP8266_Updater.h b/src/Arduino_ESP8266_Updater.h index 7c4ea26d..997574a6 100644 --- a/src/Arduino_ESP8266_Updater.h +++ b/src/Arduino_ESP8266_Updater.h @@ -1,9 +1,3 @@ -/* - Arduino_ESP8266_Updater.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Arduino_ESP8266_Updater_h #define Arduino_ESP8266_Updater_h diff --git a/src/Arduino_HTTP_Client.h b/src/Arduino_HTTP_Client.h index 8905ae65..e470c3a6 100644 --- a/src/Arduino_HTTP_Client.h +++ b/src/Arduino_HTTP_Client.h @@ -1,9 +1,3 @@ -/* - Arduino_HTTP_Client.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Arduino_HTTP_Client_h #define Arduino_HTTP_Client_h diff --git a/src/Arduino_MQTT_Client.h b/src/Arduino_MQTT_Client.h index e7c19788..1cf54629 100644 --- a/src/Arduino_MQTT_Client.h +++ b/src/Arduino_MQTT_Client.h @@ -1,9 +1,3 @@ -/* - Arduino_MQTT_Client.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Arduino_MQTT_Client_h #define Arduino_MQTT_Client_h diff --git a/src/Attribute_Request_Callback.h b/src/Attribute_Request_Callback.h index eac39c55..39096830 100644 --- a/src/Attribute_Request_Callback.h +++ b/src/Attribute_Request_Callback.h @@ -1,9 +1,3 @@ -/* - Attribute_Request_Callback.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Attribute_Request_Callback_h #define Attribute_Request_Callback_h diff --git a/src/Callback.h b/src/Callback.h index fcbbb701..6e294028 100644 --- a/src/Callback.h +++ b/src/Callback.h @@ -1,9 +1,3 @@ -/* - Callback.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Callback_h #define Callback_h diff --git a/src/Callback_Watchdog.h b/src/Callback_Watchdog.h index 19fa05cb..2b7a5021 100644 --- a/src/Callback_Watchdog.h +++ b/src/Callback_Watchdog.h @@ -1,9 +1,3 @@ -/* - Callback_Watchdog.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Callback_Watchdog_h #define Callback_Watchdog_h diff --git a/src/Configuration.h b/src/Configuration.h index 0ec110e3..e81c73fd 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -1,9 +1,3 @@ -/* - Configuration.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Configuration_h #define Configuration_h diff --git a/src/Constants.h b/src/Constants.h index ead334af..63314e9e 100644 --- a/src/Constants.h +++ b/src/Constants.h @@ -1,9 +1,3 @@ -/* - Constants.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Constants_h #define Constants_h diff --git a/src/Espressif_MQTT_Client.h b/src/Espressif_MQTT_Client.h index 6666f63a..401c9495 100644 --- a/src/Espressif_MQTT_Client.h +++ b/src/Espressif_MQTT_Client.h @@ -1,9 +1,3 @@ -/* - Espressif_MQTT_Client.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Espressif_MQTT_Client_h #define Espressif_MQTT_Client_h diff --git a/src/Espressif_Updater.h b/src/Espressif_Updater.h index 6c4fb7dc..6d936131 100644 --- a/src/Espressif_Updater.h +++ b/src/Espressif_Updater.h @@ -1,9 +1,3 @@ -/* - Espressif_Updater.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Espressif_Updater_h #define Espressif_Updater_h diff --git a/src/HashGenerator.h b/src/HashGenerator.h index 0d8d3b2e..93dc0c12 100644 --- a/src/HashGenerator.h +++ b/src/HashGenerator.h @@ -1,9 +1,3 @@ -/* - HashGenerator.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Hash_Generator_h #define Hash_Generator_h diff --git a/src/Helper.h b/src/Helper.h index 8991406a..18870175 100644 --- a/src/Helper.h +++ b/src/Helper.h @@ -1,9 +1,3 @@ -/* - Helper.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Helper_h #define Helper_h diff --git a/src/IMQTT_Client.h b/src/IMQTT_Client.h index 15209559..fa66295d 100644 --- a/src/IMQTT_Client.h +++ b/src/IMQTT_Client.h @@ -1,9 +1,3 @@ -/* - IMQTT_Client.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef IMQTT_Client_h #define IMQTT_Client_h diff --git a/src/IUpdater.h b/src/IUpdater.h index 7aa536b7..71da1c93 100644 --- a/src/IUpdater.h +++ b/src/IUpdater.h @@ -1,9 +1,3 @@ -/* - IUpdater.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef IUpdater_h #define IUpdater_h diff --git a/src/OTA_Failure_Response.h b/src/OTA_Failure_Response.h index 9890badb..32064cb5 100644 --- a/src/OTA_Failure_Response.h +++ b/src/OTA_Failure_Response.h @@ -1,9 +1,3 @@ -/* - OTA_Failure_Response.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef OTA_Failure_Response_h #define OTA_Failure_Response_h diff --git a/src/OTA_Handler.h b/src/OTA_Handler.h index 0c709c20..9cbf3ad2 100644 --- a/src/OTA_Handler.h +++ b/src/OTA_Handler.h @@ -1,9 +1,3 @@ -/* - OTA_Handler.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef OTA_Handler_h #define OTA_Handler_h diff --git a/src/OTA_Update_Callback.h b/src/OTA_Update_Callback.h index 34807957..be5e5602 100644 --- a/src/OTA_Update_Callback.h +++ b/src/OTA_Update_Callback.h @@ -1,9 +1,3 @@ -/* - OTA_Update_Callback.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef OTA_Update_Callback_h #define OTA_Update_Callback_h diff --git a/src/Provision_Callback.h b/src/Provision_Callback.h index daf32af9..f4ebe020 100644 --- a/src/Provision_Callback.h +++ b/src/Provision_Callback.h @@ -1,9 +1,3 @@ -/* - Provision_Callback.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Provision_Callback_h #define Provision_Callback_h diff --git a/src/RPC_Callback.h b/src/RPC_Callback.h index aae295ed..0728fb83 100644 --- a/src/RPC_Callback.h +++ b/src/RPC_Callback.h @@ -1,9 +1,3 @@ -/* - RPC_Callback.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef RPC_Callback_h #define RPC_Callback_h diff --git a/src/RPC_Request_Callback.h b/src/RPC_Request_Callback.h index 378b01c7..e079921f 100644 --- a/src/RPC_Request_Callback.h +++ b/src/RPC_Request_Callback.h @@ -1,9 +1,3 @@ -/* - RPC_Request_Callback.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef RPC_Request_Callback_h #define RPC_Request_Callback_h diff --git a/src/RPC_Response.h b/src/RPC_Response.h index 32c0e749..85cb3e33 100644 --- a/src/RPC_Response.h +++ b/src/RPC_Response.h @@ -1,9 +1,3 @@ -/* - RPC_Response.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef RPC_RESPONSE_H #define RPC_RESPONSE_H diff --git a/src/Shared_Attribute_Callback.h b/src/Shared_Attribute_Callback.h index 5d85670a..81c4d63f 100644 --- a/src/Shared_Attribute_Callback.h +++ b/src/Shared_Attribute_Callback.h @@ -1,9 +1,3 @@ -/* - Shared_Attribute_Callback.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Shared_Attribute_Callback_h #define Shared_Attribute_Callback_h diff --git a/src/Telemetry.h b/src/Telemetry.h index a45e0cfa..b51c5f4e 100644 --- a/src/Telemetry.h +++ b/src/Telemetry.h @@ -1,9 +1,3 @@ -/* - Telemetry.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Telemetry_h #define Telemetry_h diff --git a/src/ThingsBoard.h b/src/ThingsBoard.h index f3500310..5391db5d 100644 --- a/src/ThingsBoard.h +++ b/src/ThingsBoard.h @@ -1,9 +1,3 @@ -/* - ThingsBoard.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef ThingsBoard_h #define ThingsBoard_h @@ -1916,6 +1910,12 @@ class ThingsBoardSized { return telemetry ? sendTelemetryJson(object, Helper::Measure_Json(object)) : sendAttributeJSON(object, Helper::Measure_Json(object)); } + /// @brief Vector signature +#if THINGSBOARD_ENABLE_STL + template + using Vector = std::vector; +#endif // THINGSBOARD_ENABLE_STL + IMQTT_Client& m_client; // MQTT client instance. size_t m_max_stack; // Maximum stack size we allocate at once. size_t m_buffering_size; // Buffering size used to serialize directly into client. @@ -1928,17 +1928,10 @@ class ThingsBoardSized { // of its usage, which will lead to dangling references and undefined behaviour. // Therefore copy-by-value has been choosen as for this specific use case it is more advantageous, // especially because at most we copy a vector, that will only ever contain a few pointers -#if THINGSBOARD_ENABLE_STL - std::vector m_rpc_callbacks; // Server side RPC callbacks vector - std::vector m_rpc_request_callbacks; // Client side RPC callbacks vector - std::vector m_shared_attribute_update_callbacks; // Shared attribute update callbacks vector - std::vector m_attribute_request_callbacks; // Client-side or shared attribute request callbacks vector -#else Vector m_rpc_callbacks; // Server side RPC callbacks vector, replacement for non C++ STL boards Vector m_rpc_request_callbacks; // Client side RPC callbacks vector, replacement for non C++ STL boards Vector m_shared_attribute_update_callbacks; // Shared attribute update callbacks vector, replacement for non C++ STL boards Vector m_attribute_request_callbacks; // Client-side or shared attribute request callback vector, replacement for non C++ STL boards -#endif Provision_Callback m_provision_callback; // Provision response callback size_t m_request_id; // Allows nearly 4.3 million requests before wrapping back to 0 diff --git a/src/ThingsBoardDefaultLogger.h b/src/ThingsBoardDefaultLogger.h index 5a17f93f..6047b456 100644 --- a/src/ThingsBoardDefaultLogger.h +++ b/src/ThingsBoardDefaultLogger.h @@ -1,9 +1,3 @@ -/* - ThingsBoardDefaultLogger.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Thingsboard_Default_Logger_h #define Thingsboard_Default_Logger_h diff --git a/src/Vector.h b/src/Vector.h index 723c7827..cbbec9fa 100644 --- a/src/Vector.h +++ b/src/Vector.h @@ -1,9 +1,3 @@ -/* - Vector.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Vector_h #define Vector_h From 256e057b8b3d4bb62606137f5644f02154dde29e Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Fri, 22 Sep 2023 16:43:52 +0200 Subject: [PATCH 57/80] Remove more class notes --- src/Arduino_ESP32_Updater.h | 6 ------ src/IHTTP_Client.h | 6 ------ 2 files changed, 12 deletions(-) diff --git a/src/Arduino_ESP32_Updater.h b/src/Arduino_ESP32_Updater.h index bdf6c426..d2ab5fc5 100644 --- a/src/Arduino_ESP32_Updater.h +++ b/src/Arduino_ESP32_Updater.h @@ -1,9 +1,3 @@ -/* - Arduino_ESP32_Updater.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef Arduino_ESP32_Updater_h #define Arduino_ESP32_Updater_h diff --git a/src/IHTTP_Client.h b/src/IHTTP_Client.h index aaaadfc6..63bc1bbe 100644 --- a/src/IHTTP_Client.h +++ b/src/IHTTP_Client.h @@ -1,9 +1,3 @@ -/* - IHTTP_Client.h - Library API for sending data to the ThingsBoard - Based on PubSub MQTT library. - Created by Olender M. Oct 2018. - Released into the public domain. -*/ #ifndef IHTTP_Client_h #define IHTTP_Client_h From 8b695d402ef748fad99be442c491058297534760 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:00:36 +0200 Subject: [PATCH 58/80] Add workflow to build for ESP-IDF --- .github/workflows/espidf-compile.yml | 42 +++++++++++++++++++ README.md | 1 + ...0015-espressif_esp32_process_OTA_MQTT.cpp} | 0 3 files changed, 43 insertions(+) create mode 100644 .github/workflows/espidf-compile.yml rename examples/0015-espressif_esp32_process_OTA_MQTT/{0015-espressif_esp32_process_OTA_MQTT.ino => 0015-espressif_esp32_process_OTA_MQTT.cpp} (100%) diff --git a/.github/workflows/espidf-compile.yml b/.github/workflows/espidf-compile.yml new file mode 100644 index 00000000..14212cad --- /dev/null +++ b/.github/workflows/espidf-compile.yml @@ -0,0 +1,42 @@ +name: Compile ESP-IDF Sketches + +on: + pull_request: + push: + workflow_dispatch: + repository_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Build send data example on v4.4 + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v4.4 + target: esp32 + path: 'examples/0014-espressif_esp32_send_data' + - name: Build process OTA over MQTT example on v4.4 + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v4.4 + target: esp32 + path: 'examples/0015-espressif_esp32_process_OTA_MQTT' + - name: Build send data example on v5.1 + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v5.1 + target: esp32 + path: 'examples/0014-espressif_esp32_send_data' + - name: Build process OTA over MQTT example on v5.1 + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v5.1 + target: esp32 + path: 'examples/0015-espressif_esp32_process_OTA_MQTT' \ No newline at end of file diff --git a/README.md b/README.md index 30864b72..b287a9e2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![GitHub release](https://img.shields.io/github/release/thingsboard/thingsboard-arduino-sdk/all.svg?style=flat-square)](https://github.com/thingsboard/thingsboard-arduino-sdk/releases/) [![GitHub downloads](https://img.shields.io/github/downloads/thingsboard/thingsboard-arduino-sdk/all.svg?style=flat-square)](https://github.com/thingsboard/thingsboard-arduino-sdk/releases/) [![Arduino actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/arduino-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/arduino-compile.yml) +[![Espressif IDF actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile.yml) [![ESP32 actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp32-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp32-compile.yml) [![ESP8266 actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp8266-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp8266-compile.yml) ![GitHub stars](https://img.shields.io/github/stars/thingsboard/thingsboard-arduino-sdk?style=social) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino b/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.cpp similarity index 100% rename from examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.ino rename to examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.cpp From 438043dbbd7bfa0cc9f6e0c09f68511bb2365e6d Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:32:54 +0200 Subject: [PATCH 59/80] Adding CMakeLists.txt file --- examples/0014-espressif_esp32_send_data/CMakeLists.txt | 6 ++++++ .../0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 examples/0014-espressif_esp32_send_data/CMakeLists.txt create mode 100644 examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt diff --git a/examples/0014-espressif_esp32_send_data/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/CMakeLists.txt new file mode 100644 index 00000000..9a229e0f --- /dev/null +++ b/examples/0014-espressif_esp32_send_data/CMakeLists.txt @@ -0,0 +1,6 @@ +# Specify all source files to keep the project compatible with espidf build system +idf_component_register( + SRCS + "0014-espressif_esp32_send_data.cpp" + INCLUDE_DIRS "." +) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt new file mode 100644 index 00000000..0df74189 --- /dev/null +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt @@ -0,0 +1,6 @@ +# Specify all source files to keep the project compatible with espidf build system +idf_component_register( + SRCS + "0015-espressif_esp32_process_OTA_MQTT.cpp" + INCLUDE_DIRS "." +) From 2dc5ebe6b347a6b226ff5bc03b7f2935f9413680 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:40:50 +0200 Subject: [PATCH 60/80] Adding cmake minimum required --- examples/0014-espressif_esp32_send_data/CMakeLists.txt | 5 +++++ .../0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/examples/0014-espressif_esp32_send_data/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/CMakeLists.txt index 9a229e0f..baa4fcfe 100644 --- a/examples/0014-espressif_esp32_send_data/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/CMakeLists.txt @@ -1,3 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +cmake_minimum_required(VERSION 3.5) +project(ESP32_SEND_DATA) + # Specify all source files to keep the project compatible with espidf build system idf_component_register( SRCS diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt index 0df74189..198da8b8 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt @@ -1,3 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +cmake_minimum_required(VERSION 3.5) +project(ESP32_PROCESS_OTA) + # Specify all source files to keep the project compatible with espidf build system idf_component_register( SRCS From 4ab0e775fcd874aa11ce6620c76d7ed9aa5c39bb Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:43:43 +0200 Subject: [PATCH 61/80] Move app into main folder --- examples/0014-espressif_esp32_send_data/CMakeLists.txt | 7 ------- .../{ => main}/0014-espressif_esp32_send_data.cpp | 0 .../0014-espressif_esp32_send_data/main/CMakeLists.txt | 6 ++++++ .../0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt | 7 ------- .../{ => main}/0015-espressif_esp32_process_OTA_MQTT.cpp | 0 .../main/CMakeLists.txt | 6 ++++++ 6 files changed, 12 insertions(+), 14 deletions(-) rename examples/0014-espressif_esp32_send_data/{ => main}/0014-espressif_esp32_send_data.cpp (100%) create mode 100644 examples/0014-espressif_esp32_send_data/main/CMakeLists.txt rename examples/0015-espressif_esp32_process_OTA_MQTT/{ => main}/0015-espressif_esp32_process_OTA_MQTT.cpp (100%) create mode 100644 examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt diff --git a/examples/0014-espressif_esp32_send_data/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/CMakeLists.txt index baa4fcfe..41a1814e 100644 --- a/examples/0014-espressif_esp32_send_data/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/CMakeLists.txt @@ -2,10 +2,3 @@ # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html cmake_minimum_required(VERSION 3.5) project(ESP32_SEND_DATA) - -# Specify all source files to keep the project compatible with espidf build system -idf_component_register( - SRCS - "0014-espressif_esp32_send_data.cpp" - INCLUDE_DIRS "." -) diff --git a/examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.cpp b/examples/0014-espressif_esp32_send_data/main/0014-espressif_esp32_send_data.cpp similarity index 100% rename from examples/0014-espressif_esp32_send_data/0014-espressif_esp32_send_data.cpp rename to examples/0014-espressif_esp32_send_data/main/0014-espressif_esp32_send_data.cpp diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt new file mode 100644 index 00000000..9a229e0f --- /dev/null +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -0,0 +1,6 @@ +# Specify all source files to keep the project compatible with espidf build system +idf_component_register( + SRCS + "0014-espressif_esp32_send_data.cpp" + INCLUDE_DIRS "." +) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt index 198da8b8..5410cba7 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt @@ -2,10 +2,3 @@ # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html cmake_minimum_required(VERSION 3.5) project(ESP32_PROCESS_OTA) - -# Specify all source files to keep the project compatible with espidf build system -idf_component_register( - SRCS - "0015-espressif_esp32_process_OTA_MQTT.cpp" - INCLUDE_DIRS "." -) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.cpp b/examples/0015-espressif_esp32_process_OTA_MQTT/main/0015-espressif_esp32_process_OTA_MQTT.cpp similarity index 100% rename from examples/0015-espressif_esp32_process_OTA_MQTT/0015-espressif_esp32_process_OTA_MQTT.cpp rename to examples/0015-espressif_esp32_process_OTA_MQTT/main/0015-espressif_esp32_process_OTA_MQTT.cpp diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt new file mode 100644 index 00000000..0df74189 --- /dev/null +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -0,0 +1,6 @@ +# Specify all source files to keep the project compatible with espidf build system +idf_component_register( + SRCS + "0015-espressif_esp32_process_OTA_MQTT.cpp" + INCLUDE_DIRS "." +) From a9c8db9f3906e35eea4d6afafb4d268b5b87bd8c Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:51:55 +0200 Subject: [PATCH 62/80] Add missing CMake calls --- examples/0014-espressif_esp32_send_data/CMakeLists.txt | 5 +++++ .../0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/examples/0014-espressif_esp32_send_data/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/CMakeLists.txt index 41a1814e..6ecbd46e 100644 --- a/examples/0014-espressif_esp32_send_data/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/CMakeLists.txt @@ -1,4 +1,9 @@ # For more information about build system see # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html cmake_minimum_required(VERSION 3.5) + +# Set project version +set(PROJECT_VER "0.1.1") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(ESP32_SEND_DATA) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt index 5410cba7..745a3fd2 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/CMakeLists.txt @@ -1,4 +1,9 @@ # For more information about build system see # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html cmake_minimum_required(VERSION 3.5) + +# Set project version +set(PROJECT_VER "0.1.1") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(ESP32_PROCESS_OTA) From ef5b2f94f3597ff45022b19ca712030bd925a4df Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:58:55 +0200 Subject: [PATCH 63/80] Adjust include dirs to point to header files --- examples/0014-espressif_esp32_send_data/main/CMakeLists.txt | 2 +- .../0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt index 9a229e0f..b4c8c0ad 100644 --- a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0014-espressif_esp32_send_data.cpp" - INCLUDE_DIRS "." + INCLUDE_DIRS "..\..\src" ) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index 0df74189..762b0a15 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0015-espressif_esp32_process_OTA_MQTT.cpp" - INCLUDE_DIRS "." + INCLUDE_DIRS "..\..\src" ) From 320dc1ce0437d7e9a08417b48285f3159ef759e8 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:02:40 +0200 Subject: [PATCH 64/80] Fix invalid escape character --- examples/0014-espressif_esp32_send_data/main/CMakeLists.txt | 2 +- .../0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt index b4c8c0ad..a26a615e 100644 --- a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0014-espressif_esp32_send_data.cpp" - INCLUDE_DIRS "..\..\src" + INCLUDE_DIRS "../../src" ) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index 762b0a15..f06ef531 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0015-espressif_esp32_process_OTA_MQTT.cpp" - INCLUDE_DIRS "..\..\src" + INCLUDE_DIRS "../../src" ) From 083f9b4c4b5b87ee4826bb16d55bb9922f2782da Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:21:52 +0200 Subject: [PATCH 65/80] Fix path typo --- examples/0014-espressif_esp32_send_data/main/CMakeLists.txt | 2 +- .../0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt index a26a615e..ea3f2b5b 100644 --- a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0014-espressif_esp32_send_data.cpp" - INCLUDE_DIRS "../../src" + INCLUDE_DIRS "../../../src" ) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index f06ef531..60c8fee7 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0015-espressif_esp32_process_OTA_MQTT.cpp" - INCLUDE_DIRS "../../src" + INCLUDE_DIRS "../../../src" ) From 6fac9a7138421842bf4d5c18b15ccd3385cda5e4 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:34:19 +0200 Subject: [PATCH 66/80] Add component manager file --- examples/0014-espressif_esp32_send_data/main/idf_component.yml | 3 +++ .../main/idf_component.yml | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 examples/0014-espressif_esp32_send_data/main/idf_component.yml create mode 100644 examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml diff --git a/examples/0014-espressif_esp32_send_data/main/idf_component.yml b/examples/0014-espressif_esp32_send_data/main/idf_component.yml new file mode 100644 index 00000000..df94bed5 --- /dev/null +++ b/examples/0014-espressif_esp32_send_data/main/idf_component.yml @@ -0,0 +1,3 @@ +## IDF Component Manager Manifest File +dependencies: + bblanchon/arduinojson: "^6.21.2" diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml new file mode 100644 index 00000000..df94bed5 --- /dev/null +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml @@ -0,0 +1,3 @@ +## IDF Component Manager Manifest File +dependencies: + bblanchon/arduinojson: "^6.21.2" From a7f3696994dfc44f016bd3a266109ce80e520bfd Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:39:41 +0200 Subject: [PATCH 67/80] Add dependency to library that needs to be added --- examples/0014-espressif_esp32_send_data/main/CMakeLists.txt | 2 +- examples/0014-espressif_esp32_send_data/main/idf_component.yml | 2 +- .../0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt | 2 +- .../main/idf_component.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt index ea3f2b5b..9a229e0f 100644 --- a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0014-espressif_esp32_send_data.cpp" - INCLUDE_DIRS "../../../src" + INCLUDE_DIRS "." ) diff --git a/examples/0014-espressif_esp32_send_data/main/idf_component.yml b/examples/0014-espressif_esp32_send_data/main/idf_component.yml index df94bed5..025c0d8b 100644 --- a/examples/0014-espressif_esp32_send_data/main/idf_component.yml +++ b/examples/0014-espressif_esp32_send_data/main/idf_component.yml @@ -1,3 +1,3 @@ ## IDF Component Manager Manifest File dependencies: - bblanchon/arduinojson: "^6.21.2" + thingsboard/thingsboard: "*" diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index 60c8fee7..0df74189 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0015-espressif_esp32_process_OTA_MQTT.cpp" - INCLUDE_DIRS "../../../src" + INCLUDE_DIRS "." ) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml index df94bed5..025c0d8b 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml @@ -1,3 +1,3 @@ ## IDF Component Manager Manifest File dependencies: - bblanchon/arduinojson: "^6.21.2" + thingsboard/thingsboard: "*" From 050aff19226edcb2b5e18e33c83b9c59fca5a69a Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:46:48 +0200 Subject: [PATCH 68/80] Revert "Add dependency to library that needs to be added" This reverts commit a7f3696994dfc44f016bd3a266109ce80e520bfd. --- examples/0014-espressif_esp32_send_data/main/CMakeLists.txt | 2 +- examples/0014-espressif_esp32_send_data/main/idf_component.yml | 2 +- .../0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt | 2 +- .../main/idf_component.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt index 9a229e0f..ea3f2b5b 100644 --- a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0014-espressif_esp32_send_data.cpp" - INCLUDE_DIRS "." + INCLUDE_DIRS "../../../src" ) diff --git a/examples/0014-espressif_esp32_send_data/main/idf_component.yml b/examples/0014-espressif_esp32_send_data/main/idf_component.yml index 025c0d8b..df94bed5 100644 --- a/examples/0014-espressif_esp32_send_data/main/idf_component.yml +++ b/examples/0014-espressif_esp32_send_data/main/idf_component.yml @@ -1,3 +1,3 @@ ## IDF Component Manager Manifest File dependencies: - thingsboard/thingsboard: "*" + bblanchon/arduinojson: "^6.21.2" diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index 0df74189..60c8fee7 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -2,5 +2,5 @@ idf_component_register( SRCS "0015-espressif_esp32_process_OTA_MQTT.cpp" - INCLUDE_DIRS "." + INCLUDE_DIRS "../../../src" ) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml index 025c0d8b..df94bed5 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml @@ -1,3 +1,3 @@ ## IDF Component Manager Manifest File dependencies: - thingsboard/thingsboard: "*" + bblanchon/arduinojson: "^6.21.2" From 733ac5496598eec3a89c2c8e221dfb3299df0849 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:50:20 +0200 Subject: [PATCH 69/80] Attempt to actually compile internal library code --- examples/0014-espressif_esp32_send_data/main/CMakeLists.txt | 1 + .../0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt index ea3f2b5b..ee885dc3 100644 --- a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -1,6 +1,7 @@ # Specify all source files to keep the project compatible with espidf build system idf_component_register( SRCS + "../../../src" "0014-espressif_esp32_send_data.cpp" INCLUDE_DIRS "../../../src" ) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index 60c8fee7..4ddb3518 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -1,6 +1,7 @@ # Specify all source files to keep the project compatible with espidf build system idf_component_register( SRCS + "../../../src" "0015-espressif_esp32_process_OTA_MQTT.cpp" INCLUDE_DIRS "../../../src" ) From e886eea49f089edf3bec89a65f96159762c73f56 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:59:09 +0200 Subject: [PATCH 70/80] Ensure to add implementation files --- .../main/CMakeLists.txt | 30 ++++++++++++++++--- .../main/idf_component.yml | 4 ++- .../main/CMakeLists.txt | 30 ++++++++++++++++--- .../main/idf_component.yml | 4 ++- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt index ee885dc3..f0bfa301 100644 --- a/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt +++ b/examples/0014-espressif_esp32_send_data/main/CMakeLists.txt @@ -1,7 +1,29 @@ -# Specify all source files to keep the project compatible with espidf build system +# Be aware this file should not be takes as inspiration on how to set up compilation with the CMake build system when using ESP-IDF, because it directly includes the implementation files. +# This has to be done because the examples are build to test if they are still working and to automatically inform the library if a pull request would break examples. +# To actually include the library in your ESP-IDF project read the documentation especially the Installation section +set(srcs + 0014-espressif_esp32_send_data.cpp + ../../../src/Arduino_HTTP_Client.cpp + ../../../src/Arduino_MQTT_Client.cpp + ../../../src/Attribute_Request_Callback.cpp + ../../../src/Callback_Watchdog.cpp + ../../../src/Arduino_ESP32_Updater.cpp + ../../../src/Arduino_ESP8266_Updater.cpp + ../../../src/Espressif_Updater.cpp + ../../../src/Espressif_MQTT_Client.cpp + ../../../src/HashGenerator.cpp + ../../../src/Helper.cpp + ../../../src/OTA_Update_Callback.cpp + ../../../src/Provision_Callback.cpp + ../../../src/RPC_Callback.cpp + ../../../src/RPC_Request_Callback.cpp + ../../../src/RPC_Response.cpp + ../../../src/Shared_Attribute_Callback.cpp + ../../../src/Telemetry.cpp + ../../../src/ThingsBoardDefaultLogger.cpp +) + idf_component_register( - SRCS - "../../../src" - "0014-espressif_esp32_send_data.cpp" + SRCS ${srcs} INCLUDE_DIRS "../../../src" ) diff --git a/examples/0014-espressif_esp32_send_data/main/idf_component.yml b/examples/0014-espressif_esp32_send_data/main/idf_component.yml index df94bed5..b4b3eaa3 100644 --- a/examples/0014-espressif_esp32_send_data/main/idf_component.yml +++ b/examples/0014-espressif_esp32_send_data/main/idf_component.yml @@ -1,3 +1,5 @@ -## IDF Component Manager Manifest File +# Be aware this file should not be takes as inspiration on how to set up compilation with the CMake build system when using ESP-IDF, because it directly includes the needed libraries for the implementation. +# This has to be done because the examples are build to test if they are still working and to automatically inform the library if a pull request would break examples. +# To actually include the library in your ESP-IDF project read the documentation especially the Installation section dependencies: bblanchon/arduinojson: "^6.21.2" diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index 4ddb3518..f0bfa301 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -1,7 +1,29 @@ -# Specify all source files to keep the project compatible with espidf build system +# Be aware this file should not be takes as inspiration on how to set up compilation with the CMake build system when using ESP-IDF, because it directly includes the implementation files. +# This has to be done because the examples are build to test if they are still working and to automatically inform the library if a pull request would break examples. +# To actually include the library in your ESP-IDF project read the documentation especially the Installation section +set(srcs + 0014-espressif_esp32_send_data.cpp + ../../../src/Arduino_HTTP_Client.cpp + ../../../src/Arduino_MQTT_Client.cpp + ../../../src/Attribute_Request_Callback.cpp + ../../../src/Callback_Watchdog.cpp + ../../../src/Arduino_ESP32_Updater.cpp + ../../../src/Arduino_ESP8266_Updater.cpp + ../../../src/Espressif_Updater.cpp + ../../../src/Espressif_MQTT_Client.cpp + ../../../src/HashGenerator.cpp + ../../../src/Helper.cpp + ../../../src/OTA_Update_Callback.cpp + ../../../src/Provision_Callback.cpp + ../../../src/RPC_Callback.cpp + ../../../src/RPC_Request_Callback.cpp + ../../../src/RPC_Response.cpp + ../../../src/Shared_Attribute_Callback.cpp + ../../../src/Telemetry.cpp + ../../../src/ThingsBoardDefaultLogger.cpp +) + idf_component_register( - SRCS - "../../../src" - "0015-espressif_esp32_process_OTA_MQTT.cpp" + SRCS ${srcs} INCLUDE_DIRS "../../../src" ) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml index df94bed5..b4b3eaa3 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/idf_component.yml @@ -1,3 +1,5 @@ -## IDF Component Manager Manifest File +# Be aware this file should not be takes as inspiration on how to set up compilation with the CMake build system when using ESP-IDF, because it directly includes the needed libraries for the implementation. +# This has to be done because the examples are build to test if they are still working and to automatically inform the library if a pull request would break examples. +# To actually include the library in your ESP-IDF project read the documentation especially the Installation section dependencies: bblanchon/arduinojson: "^6.21.2" From a174f66a1f603ff5e9c4dcde384da0e2b2f7a094 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:03:47 +0200 Subject: [PATCH 71/80] Fix typo --- .../0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt index f0bfa301..fcfacbb4 100644 --- a/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/main/CMakeLists.txt @@ -2,7 +2,7 @@ # This has to be done because the examples are build to test if they are still working and to automatically inform the library if a pull request would break examples. # To actually include the library in your ESP-IDF project read the documentation especially the Installation section set(srcs - 0014-espressif_esp32_send_data.cpp + 0015-espressif_esp32_process_OTA_MQTT.cpp ../../../src/Arduino_HTTP_Client.cpp ../../../src/Arduino_MQTT_Client.cpp ../../../src/Attribute_Request_Callback.cpp From c6560b1fa5a23204e48a0e4e9e7f58353ab4d3cd Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:24:02 +0200 Subject: [PATCH 72/80] Adding custom sdkconfig to example --- .../sdkconfig | 532 ++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 examples/0015-espressif_esp32_process_OTA_MQTT/sdkconfig diff --git a/examples/0015-espressif_esp32_process_OTA_MQTT/sdkconfig b/examples/0015-espressif_esp32_process_OTA_MQTT/sdkconfig new file mode 100644 index 00000000..685f270f --- /dev/null +++ b/examples/0015-espressif_esp32_process_OTA_MQTT/sdkconfig @@ -0,0 +1,532 @@ +# +# SDK tool configuration +# +CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-" + +# +# Bootloader config +# +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_BOOTLOADER_LOG_LEVEL_INFO= +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=2 +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V= +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y + +# +# Security features +# +CONFIG_SECURE_BOOT= +CONFIG_SECURE_FLASH_ENC_ENABLED= + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO= +CONFIG_ESPTOOLPY_FLASHMODE_QOUT= +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHMODE_DOUT= +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M= +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +CONFIG_ESPTOOLPY_FLASHFREQ_26M= +CONFIG_ESPTOOLPY_FLASHFREQ_20M= +CONFIG_ESPTOOLPY_FLASHFREQ="40m" +CONFIG_ESPTOOLPY_FLASHSIZE_1MB= +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB= +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +CONFIG_ESPTOOLPY_BEFORE_NORESET= +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +CONFIG_ESPTOOLPY_AFTER_NORESET= +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y +CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB= +CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER= +CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP= +CONFIG_PARTITION_TABLE_TWO_OTA= +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +CONFIG_PARTITION_TABLE_CUSTOM= +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME= +CONFIG_PARTITION_TABLE_FILENAME= +CONFIG_PARTITION_TABLE_MD5=y + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +CONFIG_COMPILER_OPTIMIZATION_SIZE= +CONFIG_COMPILER_OPTIMIZATION_NONE= +CONFIG_COMPILER_OPTIMIZATION_PERF= +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT= +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE= +CONFIG_COMPILER_CXX_EXCEPTIONS= +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +CONFIG_COMPILER_STACK_CHECK_MODE_NORM= +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG= +CONFIG_COMPILER_STACK_CHECK_MODE_ALL= +CONFIG_COMPILER_STACK_CHECK= +CONFIG_COMPILER_WARN_WRITE_STRINGS= + +# +# Component config +# + +# +# Application Level Tracing +# +CONFIG_ESP32_APPTRACE_DEST_TRAX= +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_ENABLE= +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y + +# +# FreeRTOS SystemView Tracing +# +CONFIG_AWS_IOT_SDK= + +# +# Bluetooth +# +CONFIG_BT_ENABLED= +CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 +CONFIG_BTDM_RESERVE_DRAM=0 + +# +# ADC configuration +# +CONFIG_ADC_FORCE_XPD_FSM= +CONFIG_ADC_DISABLE_DAC=y + +# +# ESP32-specific +# +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80= +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160= +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 +CONFIG_SPIRAM= +CONFIG_ESP32_MEMMAP_TRACEMEM= +CONFIG_ESP32_MEMMAP_TRACEMEM_TWOBANKS= +CONFIG_ESP32_TRAX= +CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 +CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH= +CONFIG_ESP_COREDUMP_ENABLE_TO_UART= +CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y +CONFIG_ESP_COREDUMP_ENABLE= +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO= +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2048 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 +CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_LF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +CONFIG_NEWLIB_NANO_FORMAT= +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +CONFIG_ESP_CONSOLE_UART_CUSTOM= +CONFIG_ESP_CONSOLE_UART_NONE= +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ULP_COPROC_ENABLED= +CONFIG_ULP_COPROC_RESERVE_MEM=0 +CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT= +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT= +CONFIG_ESP_SYSTEM_PANIC_GDBSTUB= +CONFIG_ESP_DEBUG_OCDAWARE=y +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +CONFIG_ESP_TASK_WDT_PANIC= +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7= +CONFIG_ESP_BROWNOUT_DET_LVL=0 +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC= +CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT= +CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE= +CONFIG_RTC_CLK_SRC_INT_RC=y +CONFIG_RTC_CLK_SRC_EXT_CRYS= +CONFIG_RTC_CLK_CAL_CYCLES=1024 +CONFIG_RTC_XTAL_BOOTSTRAP_CYCLES=100 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_XTAL_FREQ_40=y +CONFIG_XTAL_FREQ_26= +CONFIG_XTAL_FREQ_AUTO= +CONFIG_XTAL_FREQ=40 +CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE= +CONFIG_ESP_TIMER_PROFILING= +CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS= +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_STATIC_TX_BUFFER= +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1 +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP_WIFI_TX_BA_WIN=6 +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP_WIFI_RX_BA_WIN=6 +CONFIG_ESP_WIFI_NVS_ENABLED=y + +# +# PHY +# +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION= +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 + +# +# Power Management +# +CONFIG_PM_ENABLE= + +# +# ADC-Calibration +# +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y + +# +# Ethernet +# +CONFIG_ETH_DMA_RX_BUF_NUM=10 +CONFIG_ETH_DMA_TX_BUF_NUM=10 +CONFIG_ETH_EMAC_L2_TO_L3_RX_BUF_MODE= +CONFIG_ETH_EMAC_TASK_PRIORITY=20 + +# +# FAT Filesystem support +# +CONFIG_FATFS_CODEPAGE_DYNAMIC= +CONFIG_FATFS_CODEPAGE_437=y +CONFIG_FATFS_CODEPAGE_720= +CONFIG_FATFS_CODEPAGE_737= +CONFIG_FATFS_CODEPAGE_771= +CONFIG_FATFS_CODEPAGE_775= +CONFIG_FATFS_CODEPAGE_850= +CONFIG_FATFS_CODEPAGE_852= +CONFIG_FATFS_CODEPAGE_855= +CONFIG_FATFS_CODEPAGE_857= +CONFIG_FATFS_CODEPAGE_860= +CONFIG_FATFS_CODEPAGE_861= +CONFIG_FATFS_CODEPAGE_862= +CONFIG_FATFS_CODEPAGE_863= +CONFIG_FATFS_CODEPAGE_864= +CONFIG_FATFS_CODEPAGE_865= +CONFIG_FATFS_CODEPAGE_866= +CONFIG_FATFS_CODEPAGE_869= +CONFIG_FATFS_CODEPAGE_932= +CONFIG_FATFS_CODEPAGE_936= +CONFIG_FATFS_CODEPAGE_949= +CONFIG_FATFS_CODEPAGE_950= +CONFIG_FATFS_CODEPAGE=437 +CONFIG_FATFS_LFN_NONE=y +CONFIG_FATFS_LFN_HEAP= +CONFIG_FATFS_LFN_STACK= +CONFIG_FATFS_FS_LOCK=0 +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE=y + +# +# FreeRTOS +# +CONFIG_FREERTOS_UNICORE=y +CONFIG_FREERTOS_CORETIMER_0=y +CONFIG_FREERTOS_CORETIMER_1= +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE= +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY= +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK= +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 +CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y +CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= +CONFIG_FREERTOS_ASSERT_DISABLE= +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_LEGACY_HOOKS= +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION= +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_USE_TRACE_FACILITY= +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= +CONFIG_FREERTOS_DEBUG_INTERNALS= + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +CONFIG_HEAP_POISONING_LIGHT= +CONFIG_HEAP_POISONING_COMPREHENSIVE= +CONFIG_HEAP_TRACING= + +# +# libsodium +# +CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y + +# +# Log output +# +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_COLORS=y + +# +# LWIP +# +CONFIG_LWIP_L2_TO_L3_COPY= +CONFIG_LWIP_IRAM_OPTIMIZATION= +CONFIG_LWIP_MAX_SOCKETS=4 +CONFIG_LWIP_SO_REUSE= +CONFIG_LWIP_SO_RCVBUF= +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 +CONFIG_LWIP_IP_FRAG= +CONFIG_LWIP_IP_REASSEMBLY= +CONFIG_LWIP_STATS= +CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 +CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y + +# +# DHCP server +# +CONFIG_LWIP_DHCPS_LEASE_UNIT=60 +CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 +CONFIG_LWIP_AUTOIP= +CONFIG_LWIP_NETIF_LOOPBACK=y +CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# +CONFIG_LWIP_MAX_ACTIVE_TCP=16 +CONFIG_LWIP_MAX_LISTENING_TCP=16 +CONFIG_LWIP_TCP_MAXRTX=12 +CONFIG_LWIP_TCP_SYNMAXRTX=6 +CONFIG_LWIP_TCP_MSS=1436 +CONFIG_LWIP_TCP_MSL=60000 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5744 +CONFIG_LWIP_TCP_WND_DEFAULT=5744 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCP_QUEUE_OOSEQ=y +CONFIG_LWIP_TCP_OVERSIZE_MSS=y +CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS= +CONFIG_LWIP_TCP_OVERSIZE_DISABLE= + +# +# UDP +# +CONFIG_LWIP_MAX_UDP_PCBS=16 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048 +CONFIG_LWIP_PPP_SUPPORT= + +# +# ICMP +# +CONFIG_LWIP_MULTICAST_PING= +CONFIG_LWIP_BROADCAST_PING= + +# +# LWIP RAW API +# +CONFIG_LWIP_MAX_RAW_PCBS=16 + +# +# mbedTLS +# +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_DEBUG= +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +CONFIG_MBEDTLS_HARDWARE_SHA= +CONFIG_MBEDTLS_HAVE_TIME=y +CONFIG_MBEDTLS_HAVE_TIME_DATE= +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +CONFIG_MBEDTLS_TLS_SERVER_ONLY= +CONFIG_MBEDTLS_TLS_CLIENT_ONLY= +CONFIG_MBEDTLS_TLS_DISABLED= +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +CONFIG_MBEDTLS_PSK_MODES= +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_SSL3= +CONFIG_MBEDTLS_SSL_PROTO_TLS1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS= +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +CONFIG_MBEDTLS_CAMELLIA_C= +CONFIG_MBEDTLS_DES_C= +CONFIG_MBEDTLS_RC4_DISABLED=y +CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT= +CONFIG_MBEDTLS_RC4_ENABLED= +CONFIG_MBEDTLS_BLOWFISH_C= +CONFIG_MBEDTLS_XTEA_C= +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +CONFIG_MBEDTLS_RIPEMD160_C= + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y + +# +# OpenSSL +# +CONFIG_OPENSSL_DEBUG= +CONFIG_OPENSSL_ASSERT_DO_NOTHING=y +CONFIG_OPENSSL_ASSERT_EXIT= + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 + +# +# SPI Flash driver +# +CONFIG_SPI_FLASH_VERIFY_WRITE= +CONFIG_SPI_FLASH_ENABLE_COUNTERS= +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS= +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED= + +# +# SPIFFS Configuration +# +CONFIG_SPIFFS_MAX_PARTITIONS=3 + +# +# SPIFFS Cache Configuration +# +CONFIG_SPIFFS_CACHE=y +CONFIG_SPIFFS_CACHE_WR=y +CONFIG_SPIFFS_CACHE_STATS= +CONFIG_SPIFFS_PAGE_CHECK=y +CONFIG_SPIFFS_GC_MAX_RUNS=10 +CONFIG_SPIFFS_GC_STATS= +CONFIG_SPIFFS_PAGE_SIZE=256 +CONFIG_SPIFFS_OBJ_NAME_LEN=32 +CONFIG_SPIFFS_USE_MAGIC=y +CONFIG_SPIFFS_USE_MAGIC_LENGTH=y +CONFIG_SPIFFS_FOLLOW_SYMLINKS= +CONFIG_SPIFFS_META_LENGTH=4 +CONFIG_SPIFFS_USE_MTIME=y + +# +# Debug Configuration +# +CONFIG_SPIFFS_DBG= +CONFIG_SPIFFS_API_DBG= +CONFIG_SPIFFS_GC_DBG= +CONFIG_SPIFFS_CACHE_DBG= +CONFIG_SPIFFS_CHECK_DBG= +CONFIG_SPIFFS_TEST_VISUALISATION= + +# +# esp_netif +# +CONFIG_NETIF_IP_LOST_TIMER_INTERVAL=120 + +# +# Wear Levelling +# +CONFIG_WL_SECTOR_SIZE_512= +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 From 0929dc41e1112d65304ba17d67b2717c7265917f Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:50:15 +0200 Subject: [PATCH 73/80] Attempt to fix ninja fail --- .github/workflows/espidf-compile.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/espidf-compile.yml b/.github/workflows/espidf-compile.yml index 14212cad..40402217 100644 --- a/.github/workflows/espidf-compile.yml +++ b/.github/workflows/espidf-compile.yml @@ -28,6 +28,10 @@ jobs: esp_idf_version: v4.4 target: esp32 path: 'examples/0015-espressif_esp32_process_OTA_MQTT' + - name: Checkout repo + uses: actions/checkout@v2 + with: + submodules: 'recursive' - name: Build send data example on v5.1 uses: espressif/esp-idf-ci-action@v1 with: From bb3c479f7cc24c890a1e291e9cffa7b415efe702 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:57:55 +0200 Subject: [PATCH 74/80] Improve clean up command --- .github/workflows/espidf-compile.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/espidf-compile.yml b/.github/workflows/espidf-compile.yml index 40402217..6b963a7e 100644 --- a/.github/workflows/espidf-compile.yml +++ b/.github/workflows/espidf-compile.yml @@ -28,10 +28,12 @@ jobs: esp_idf_version: v4.4 target: esp32 path: 'examples/0015-espressif_esp32_process_OTA_MQTT' - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' + - name: 'Cleanup build folder' + run: | + ls -la ./ + rm -rf ./* || true + rm -rf ./.??* || true + ls -la ./ - name: Build send data example on v5.1 uses: espressif/esp-idf-ci-action@v1 with: From 4067ca75f4814f6c4c5effe54540521f6c3de4a0 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 20:05:33 +0200 Subject: [PATCH 75/80] Add missing checkout after clean --- .github/workflows/espidf-compile.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/espidf-compile.yml b/.github/workflows/espidf-compile.yml index 6b963a7e..824751f4 100644 --- a/.github/workflows/espidf-compile.yml +++ b/.github/workflows/espidf-compile.yml @@ -34,6 +34,9 @@ jobs: rm -rf ./* || true rm -rf ./.??* || true ls -la ./ + uses: actions/checkout@v2 + with: + submodules: 'recursive' - name: Build send data example on v5.1 uses: espressif/esp-idf-ci-action@v1 with: From 2a192caba10e5fe4909af1916b7ee60b6ebd894e Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 20:10:26 +0200 Subject: [PATCH 76/80] Only run job on changes on certain paths --- .github/workflows/esp32-compile.yml | 4 ++-- .github/workflows/esp8266-compile.yml | 4 ++-- .github/workflows/espidf-compile.yml | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/esp32-compile.yml b/.github/workflows/esp32-compile.yml index 58ee9445..4b2ba853 100644 --- a/.github/workflows/esp32-compile.yml +++ b/.github/workflows/esp32-compile.yml @@ -4,13 +4,13 @@ on: pull_request: paths: - ".github/workflows/esp32-compile.yml" - - "library.properties" + - "library.json" - "examples/**" - "src/**" push: paths: - ".github/workflows/esp32-compile.yml" - - "library.properties" + - "library.json" - "examples/**" - "src/**" workflow_dispatch: diff --git a/.github/workflows/esp8266-compile.yml b/.github/workflows/esp8266-compile.yml index 8af0e01c..16adbf1e 100644 --- a/.github/workflows/esp8266-compile.yml +++ b/.github/workflows/esp8266-compile.yml @@ -4,13 +4,13 @@ on: pull_request: paths: - ".github/workflows/esp8266-compile.yml" - - "library.properties" + - "library.json" - "examples/**" - "src/**" push: paths: - ".github/workflows/esp8266-compile.yml" - - "library.properties" + - "library.json" - "examples/**" - "src/**" workflow_dispatch: diff --git a/.github/workflows/espidf-compile.yml b/.github/workflows/espidf-compile.yml index 824751f4..006a960a 100644 --- a/.github/workflows/espidf-compile.yml +++ b/.github/workflows/espidf-compile.yml @@ -2,7 +2,15 @@ name: Compile ESP-IDF Sketches on: pull_request: + paths: + - ".github/workflows/espidf-compile.yml" + - "examples/**" + - "src/**" push: + paths: + - ".github/workflows/espidf-compile.yml" + - "examples/**" + - "src/**" workflow_dispatch: repository_dispatch: From 8e5eb8a10f5addb7cf37951e91ff3ce1d933ca30 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 20:11:45 +0200 Subject: [PATCH 77/80] Fix typo --- .github/workflows/espidf-compile.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/espidf-compile.yml b/.github/workflows/espidf-compile.yml index 006a960a..3bfb6974 100644 --- a/.github/workflows/espidf-compile.yml +++ b/.github/workflows/espidf-compile.yml @@ -42,6 +42,7 @@ jobs: rm -rf ./* || true rm -rf ./.??* || true ls -la ./ + - name: Checkout repo uses: actions/checkout@v2 with: submodules: 'recursive' From be8f16d76d93d4b049895210b5358efc6e7e82d6 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 20:19:43 +0200 Subject: [PATCH 78/80] Seperate esp idf compilation into seperate jobs --- ...df-compile.yml => espidf-compile-v4.4.yml} | 24 +----------- .github/workflows/espidf-compile-v5.1.yml | 38 +++++++++++++++++++ README.md | 3 +- 3 files changed, 41 insertions(+), 24 deletions(-) rename .github/workflows/{espidf-compile.yml => espidf-compile-v4.4.yml} (54%) create mode 100644 .github/workflows/espidf-compile-v5.1.yml diff --git a/.github/workflows/espidf-compile.yml b/.github/workflows/espidf-compile-v4.4.yml similarity index 54% rename from .github/workflows/espidf-compile.yml rename to .github/workflows/espidf-compile-v4.4.yml index 3bfb6974..e7ce66fb 100644 --- a/.github/workflows/espidf-compile.yml +++ b/.github/workflows/espidf-compile-v4.4.yml @@ -1,4 +1,4 @@ -name: Compile ESP-IDF Sketches +name: Compile ESP-IDF Sketches for v4.4 on: pull_request: @@ -36,25 +36,3 @@ jobs: esp_idf_version: v4.4 target: esp32 path: 'examples/0015-espressif_esp32_process_OTA_MQTT' - - name: 'Cleanup build folder' - run: | - ls -la ./ - rm -rf ./* || true - rm -rf ./.??* || true - ls -la ./ - - name: Checkout repo - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: Build send data example on v5.1 - uses: espressif/esp-idf-ci-action@v1 - with: - esp_idf_version: v5.1 - target: esp32 - path: 'examples/0014-espressif_esp32_send_data' - - name: Build process OTA over MQTT example on v5.1 - uses: espressif/esp-idf-ci-action@v1 - with: - esp_idf_version: v5.1 - target: esp32 - path: 'examples/0015-espressif_esp32_process_OTA_MQTT' \ No newline at end of file diff --git a/.github/workflows/espidf-compile-v5.1.yml b/.github/workflows/espidf-compile-v5.1.yml new file mode 100644 index 00000000..181d0440 --- /dev/null +++ b/.github/workflows/espidf-compile-v5.1.yml @@ -0,0 +1,38 @@ +name: Compile ESP-IDF Sketches for v5.1 + +on: + pull_request: + paths: + - ".github/workflows/espidf-compile.yml" + - "examples/**" + - "src/**" + push: + paths: + - ".github/workflows/espidf-compile.yml" + - "examples/**" + - "src/**" + workflow_dispatch: + repository_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Build send data example on v5.1 + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v5.1 + target: esp32 + path: 'examples/0014-espressif_esp32_send_data' + - name: Build process OTA over MQTT example on v5.1 + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v5.1 + target: esp32 + path: 'examples/0015-espressif_esp32_process_OTA_MQTT' diff --git a/README.md b/README.md index b287a9e2..cb7ac72f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ [![GitHub release](https://img.shields.io/github/release/thingsboard/thingsboard-arduino-sdk/all.svg?style=flat-square)](https://github.com/thingsboard/thingsboard-arduino-sdk/releases/) [![GitHub downloads](https://img.shields.io/github/downloads/thingsboard/thingsboard-arduino-sdk/all.svg?style=flat-square)](https://github.com/thingsboard/thingsboard-arduino-sdk/releases/) [![Arduino actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/arduino-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/arduino-compile.yml) -[![Espressif IDF actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile.yml) +[![Espressif IDF v4.4 actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile-v4.4.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile-v4.4.yml) +[![Espressif IDF v5.1 actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile-v5.1.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/espidf-compile-v5.1.yml) [![ESP32 actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp32-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp32-compile.yml) [![ESP8266 actions status](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp8266-compile.yml/badge.svg)](https://github.com/thingsboard/thingsboard-arduino-sdk/actions/workflows/esp8266-compile.yml) ![GitHub stars](https://img.shields.io/github/stars/thingsboard/thingsboard-arduino-sdk?style=social) From 27840e9689da06d18b399ba8b43a6413c4c82a62 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Wed, 27 Sep 2023 20:23:47 +0200 Subject: [PATCH 79/80] Add sdkconfig for larger app size --- .../0014-espressif_esp32_send_data/sdkconfig | 532 ++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 examples/0014-espressif_esp32_send_data/sdkconfig diff --git a/examples/0014-espressif_esp32_send_data/sdkconfig b/examples/0014-espressif_esp32_send_data/sdkconfig new file mode 100644 index 00000000..685f270f --- /dev/null +++ b/examples/0014-espressif_esp32_send_data/sdkconfig @@ -0,0 +1,532 @@ +# +# SDK tool configuration +# +CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-" + +# +# Bootloader config +# +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_BOOTLOADER_LOG_LEVEL_INFO= +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=2 +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V= +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y + +# +# Security features +# +CONFIG_SECURE_BOOT= +CONFIG_SECURE_FLASH_ENC_ENABLED= + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO= +CONFIG_ESPTOOLPY_FLASHMODE_QOUT= +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHMODE_DOUT= +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M= +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +CONFIG_ESPTOOLPY_FLASHFREQ_26M= +CONFIG_ESPTOOLPY_FLASHFREQ_20M= +CONFIG_ESPTOOLPY_FLASHFREQ="40m" +CONFIG_ESPTOOLPY_FLASHSIZE_1MB= +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB= +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +CONFIG_ESPTOOLPY_BEFORE_NORESET= +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +CONFIG_ESPTOOLPY_AFTER_NORESET= +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y +CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B= +CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB= +CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER= +CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP= +CONFIG_PARTITION_TABLE_TWO_OTA= +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +CONFIG_PARTITION_TABLE_CUSTOM= +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME= +CONFIG_PARTITION_TABLE_FILENAME= +CONFIG_PARTITION_TABLE_MD5=y + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +CONFIG_COMPILER_OPTIMIZATION_SIZE= +CONFIG_COMPILER_OPTIMIZATION_NONE= +CONFIG_COMPILER_OPTIMIZATION_PERF= +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT= +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE= +CONFIG_COMPILER_CXX_EXCEPTIONS= +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +CONFIG_COMPILER_STACK_CHECK_MODE_NORM= +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG= +CONFIG_COMPILER_STACK_CHECK_MODE_ALL= +CONFIG_COMPILER_STACK_CHECK= +CONFIG_COMPILER_WARN_WRITE_STRINGS= + +# +# Component config +# + +# +# Application Level Tracing +# +CONFIG_ESP32_APPTRACE_DEST_TRAX= +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_ENABLE= +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y + +# +# FreeRTOS SystemView Tracing +# +CONFIG_AWS_IOT_SDK= + +# +# Bluetooth +# +CONFIG_BT_ENABLED= +CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 +CONFIG_BTDM_RESERVE_DRAM=0 + +# +# ADC configuration +# +CONFIG_ADC_FORCE_XPD_FSM= +CONFIG_ADC_DISABLE_DAC=y + +# +# ESP32-specific +# +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80= +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160= +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 +CONFIG_SPIRAM= +CONFIG_ESP32_MEMMAP_TRACEMEM= +CONFIG_ESP32_MEMMAP_TRACEMEM_TWOBANKS= +CONFIG_ESP32_TRAX= +CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 +CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH= +CONFIG_ESP_COREDUMP_ENABLE_TO_UART= +CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y +CONFIG_ESP_COREDUMP_ENABLE= +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO= +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2048 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 +CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_LF= +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +CONFIG_NEWLIB_NANO_FORMAT= +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +CONFIG_ESP_CONSOLE_UART_CUSTOM= +CONFIG_ESP_CONSOLE_UART_NONE= +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ULP_COPROC_ENABLED= +CONFIG_ULP_COPROC_RESERVE_MEM=0 +CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT= +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT= +CONFIG_ESP_SYSTEM_PANIC_GDBSTUB= +CONFIG_ESP_DEBUG_OCDAWARE=y +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +CONFIG_ESP_TASK_WDT_PANIC= +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6= +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7= +CONFIG_ESP_BROWNOUT_DET_LVL=0 +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC= +CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT= +CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE= +CONFIG_RTC_CLK_SRC_INT_RC=y +CONFIG_RTC_CLK_SRC_EXT_CRYS= +CONFIG_RTC_CLK_CAL_CYCLES=1024 +CONFIG_RTC_XTAL_BOOTSTRAP_CYCLES=100 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_XTAL_FREQ_40=y +CONFIG_XTAL_FREQ_26= +CONFIG_XTAL_FREQ_AUTO= +CONFIG_XTAL_FREQ=40 +CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE= +CONFIG_ESP_TIMER_PROFILING= +CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS= +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_STATIC_TX_BUFFER= +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1 +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP_WIFI_TX_BA_WIN=6 +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP_WIFI_RX_BA_WIN=6 +CONFIG_ESP_WIFI_NVS_ENABLED=y + +# +# PHY +# +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION= +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 + +# +# Power Management +# +CONFIG_PM_ENABLE= + +# +# ADC-Calibration +# +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y + +# +# Ethernet +# +CONFIG_ETH_DMA_RX_BUF_NUM=10 +CONFIG_ETH_DMA_TX_BUF_NUM=10 +CONFIG_ETH_EMAC_L2_TO_L3_RX_BUF_MODE= +CONFIG_ETH_EMAC_TASK_PRIORITY=20 + +# +# FAT Filesystem support +# +CONFIG_FATFS_CODEPAGE_DYNAMIC= +CONFIG_FATFS_CODEPAGE_437=y +CONFIG_FATFS_CODEPAGE_720= +CONFIG_FATFS_CODEPAGE_737= +CONFIG_FATFS_CODEPAGE_771= +CONFIG_FATFS_CODEPAGE_775= +CONFIG_FATFS_CODEPAGE_850= +CONFIG_FATFS_CODEPAGE_852= +CONFIG_FATFS_CODEPAGE_855= +CONFIG_FATFS_CODEPAGE_857= +CONFIG_FATFS_CODEPAGE_860= +CONFIG_FATFS_CODEPAGE_861= +CONFIG_FATFS_CODEPAGE_862= +CONFIG_FATFS_CODEPAGE_863= +CONFIG_FATFS_CODEPAGE_864= +CONFIG_FATFS_CODEPAGE_865= +CONFIG_FATFS_CODEPAGE_866= +CONFIG_FATFS_CODEPAGE_869= +CONFIG_FATFS_CODEPAGE_932= +CONFIG_FATFS_CODEPAGE_936= +CONFIG_FATFS_CODEPAGE_949= +CONFIG_FATFS_CODEPAGE_950= +CONFIG_FATFS_CODEPAGE=437 +CONFIG_FATFS_LFN_NONE=y +CONFIG_FATFS_LFN_HEAP= +CONFIG_FATFS_LFN_STACK= +CONFIG_FATFS_FS_LOCK=0 +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE=y + +# +# FreeRTOS +# +CONFIG_FREERTOS_UNICORE=y +CONFIG_FREERTOS_CORETIMER_0=y +CONFIG_FREERTOS_CORETIMER_1= +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE= +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY= +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK= +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 +CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y +CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= +CONFIG_FREERTOS_ASSERT_DISABLE= +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024 +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_LEGACY_HOOKS= +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION= +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_USE_TRACE_FACILITY= +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= +CONFIG_FREERTOS_DEBUG_INTERNALS= + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +CONFIG_HEAP_POISONING_LIGHT= +CONFIG_HEAP_POISONING_COMPREHENSIVE= +CONFIG_HEAP_TRACING= + +# +# libsodium +# +CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y + +# +# Log output +# +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_COLORS=y + +# +# LWIP +# +CONFIG_LWIP_L2_TO_L3_COPY= +CONFIG_LWIP_IRAM_OPTIMIZATION= +CONFIG_LWIP_MAX_SOCKETS=4 +CONFIG_LWIP_SO_REUSE= +CONFIG_LWIP_SO_RCVBUF= +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 +CONFIG_LWIP_IP_FRAG= +CONFIG_LWIP_IP_REASSEMBLY= +CONFIG_LWIP_STATS= +CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 +CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y + +# +# DHCP server +# +CONFIG_LWIP_DHCPS_LEASE_UNIT=60 +CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 +CONFIG_LWIP_AUTOIP= +CONFIG_LWIP_NETIF_LOOPBACK=y +CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# +CONFIG_LWIP_MAX_ACTIVE_TCP=16 +CONFIG_LWIP_MAX_LISTENING_TCP=16 +CONFIG_LWIP_TCP_MAXRTX=12 +CONFIG_LWIP_TCP_SYNMAXRTX=6 +CONFIG_LWIP_TCP_MSS=1436 +CONFIG_LWIP_TCP_MSL=60000 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5744 +CONFIG_LWIP_TCP_WND_DEFAULT=5744 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCP_QUEUE_OOSEQ=y +CONFIG_LWIP_TCP_OVERSIZE_MSS=y +CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS= +CONFIG_LWIP_TCP_OVERSIZE_DISABLE= + +# +# UDP +# +CONFIG_LWIP_MAX_UDP_PCBS=16 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048 +CONFIG_LWIP_PPP_SUPPORT= + +# +# ICMP +# +CONFIG_LWIP_MULTICAST_PING= +CONFIG_LWIP_BROADCAST_PING= + +# +# LWIP RAW API +# +CONFIG_LWIP_MAX_RAW_PCBS=16 + +# +# mbedTLS +# +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_DEBUG= +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +CONFIG_MBEDTLS_HARDWARE_SHA= +CONFIG_MBEDTLS_HAVE_TIME=y +CONFIG_MBEDTLS_HAVE_TIME_DATE= +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +CONFIG_MBEDTLS_TLS_SERVER_ONLY= +CONFIG_MBEDTLS_TLS_CLIENT_ONLY= +CONFIG_MBEDTLS_TLS_DISABLED= +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +CONFIG_MBEDTLS_PSK_MODES= +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_SSL3= +CONFIG_MBEDTLS_SSL_PROTO_TLS1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS= +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +CONFIG_MBEDTLS_CAMELLIA_C= +CONFIG_MBEDTLS_DES_C= +CONFIG_MBEDTLS_RC4_DISABLED=y +CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT= +CONFIG_MBEDTLS_RC4_ENABLED= +CONFIG_MBEDTLS_BLOWFISH_C= +CONFIG_MBEDTLS_XTEA_C= +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +CONFIG_MBEDTLS_RIPEMD160_C= + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y + +# +# OpenSSL +# +CONFIG_OPENSSL_DEBUG= +CONFIG_OPENSSL_ASSERT_DO_NOTHING=y +CONFIG_OPENSSL_ASSERT_EXIT= + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 + +# +# SPI Flash driver +# +CONFIG_SPI_FLASH_VERIFY_WRITE= +CONFIG_SPI_FLASH_ENABLE_COUNTERS= +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS= +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED= + +# +# SPIFFS Configuration +# +CONFIG_SPIFFS_MAX_PARTITIONS=3 + +# +# SPIFFS Cache Configuration +# +CONFIG_SPIFFS_CACHE=y +CONFIG_SPIFFS_CACHE_WR=y +CONFIG_SPIFFS_CACHE_STATS= +CONFIG_SPIFFS_PAGE_CHECK=y +CONFIG_SPIFFS_GC_MAX_RUNS=10 +CONFIG_SPIFFS_GC_STATS= +CONFIG_SPIFFS_PAGE_SIZE=256 +CONFIG_SPIFFS_OBJ_NAME_LEN=32 +CONFIG_SPIFFS_USE_MAGIC=y +CONFIG_SPIFFS_USE_MAGIC_LENGTH=y +CONFIG_SPIFFS_FOLLOW_SYMLINKS= +CONFIG_SPIFFS_META_LENGTH=4 +CONFIG_SPIFFS_USE_MTIME=y + +# +# Debug Configuration +# +CONFIG_SPIFFS_DBG= +CONFIG_SPIFFS_API_DBG= +CONFIG_SPIFFS_GC_DBG= +CONFIG_SPIFFS_CACHE_DBG= +CONFIG_SPIFFS_CHECK_DBG= +CONFIG_SPIFFS_TEST_VISUALISATION= + +# +# esp_netif +# +CONFIG_NETIF_IP_LOST_TIMER_INTERVAL=120 + +# +# Wear Levelling +# +CONFIG_WL_SECTOR_SIZE_512= +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 From 445f8559e474700da2fc689af803f1e6a11f2434 Mon Sep 17 00:00:00 2001 From: MathewHDYT <48954742+MathewHDYT@users.noreply.github.com> Date: Thu, 28 Sep 2023 08:47:40 +0200 Subject: [PATCH 80/80] Adjust board to compile for mega instead --- .github/workflows/arduino-compile.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/arduino-compile.yml b/.github/workflows/arduino-compile.yml index deb47144..f3fa0537 100644 --- a/.github/workflows/arduino-compile.yml +++ b/.github/workflows/arduino-compile.yml @@ -18,7 +18,7 @@ on: env: SKETCHES_REPORTS_PATH: sketches-reports - SKETCHES_REPORTS_NAME: esp32 + SKETCHES_REPORTS_NAME: arduino jobs: build: @@ -38,8 +38,9 @@ jobs: strategy: matrix: board: - - fqbn: "arduino:avr:nano" - platform-name: avr:nano + # The possible board types for Arduino can be found here https://github.com/arduino/ArduinoCore-avr/blob/master/.github/workflows/compile-platform-examples.yml + - fqbn: "arduino:avr:mega:cpu=atmega2560" + platform-name: avr:mega steps: - uses: actions/checkout@v3