From 87f0ee073bbe3d8184869d586cc4a7d317509c1f Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Wed, 14 Feb 2024 16:30:46 +0700 Subject: [PATCH 1/5] recovery mode class --- .../lib/ESPMegaPRO/ESPMegaRecovery.cpp | 66 +++++++++++++++++++ .../lib/ESPMegaPRO/ESPMegaRecovery.hpp | 33 ++++++++++ .../lib/ESPMegaPRO/ESPMegaWebServer.hpp | 22 +++---- 3 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp create mode 100644 ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp new file mode 100644 index 0000000..f9343a3 --- /dev/null +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp @@ -0,0 +1,66 @@ +#include +ESPMegaRecovery::ESPMegaRecovery() +{ + // Initialize all the pointers to null + this->fram = nullptr; + this->web_server = nullptr; + this->fram_address = 0; + this->bootloop_counter = 0; + this->recovery_mode = false; +} +void ESPMegaRecovery::begin() { + // Retrieve the bootloop counter from the FRAM + if(this->fram != nullptr) { + this->bootloop_counter = this->fram->read8(this->fram_address); + } + // Inclement the bootloop counter + this->inclementBootloopCounter(); + // If the bootloop counter is greater than 5, enter recovery mode + if(this->getBootloopCounter() > 5) { + this->enterRecoveryMode(); + } +} +void ESPMegaRecovery::loop() { + // If the device is in recovery mode, block all other tasks + if(this->isRecoveryMode()) { + while(true) { + // This code will become the new loop + delay(1000); + } + } +} +void ESPMegaRecovery::enterRecoveryMode() { + // Set the recovery mode flag + this->recovery_mode = true; + // Remove web server binding + AsyncWebServer *server = this->web_server->getServer(); + server->reset(); + // Add OTA update and restart endpoint + auto bindedOtaRequestHandler = std::bind(&ESPMegaWebServer::otaRequestHandler, this->web_server, std::placeholders::_1); + auto bindedOtaUploadHandler = std::bind(&ESPMegaWebServer::otaUploadHandler, this->web_server, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6); + server->on("/ota_update", HTTP_POST, bindedOtaRequestHandler, bindedOtaUploadHandler); + server->on("/reboot", HTTP_GET, std::bind(&ESPMegaWebServer::rebootHandler, this->web_server, std::placeholders::_1)); + +} +void ESPMegaRecovery::bindFRAM(FRAM *fram, uint32_t address) { + this->fram = fram; + this->fram_address = address; +} +uint8_t ESPMegaRecovery::getBootloopCounter() { + return this->bootloop_counter; +} +void ESPMegaRecovery::inclementBootloopCounter() { + this->bootloop_counter++; + if(this->fram != nullptr) { + this->fram->write8(this->fram_address, this->bootloop_counter); + } +} +void ESPMegaRecovery::resetBootloopCounter() { + this->bootloop_counter = 0; + if(this->fram != nullptr) { + this->fram->write8(this->fram_address, this->bootloop_counter); + } +} +bool ESPMegaRecovery::isRecoveryMode() { + return this->recovery_mode; +} diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp new file mode 100644 index 0000000..16415cf --- /dev/null +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp @@ -0,0 +1,33 @@ +#include +#include +/** + * @brief Recovery mode for ESPMega + * Recovery mode is a mode that is entered when the device is in a bootloop + * In this mode, the device will block all other tasks and wait for a new firmware to be uploaded over the air + * To exit recovery mode, the device must be restarted or a new firmware must be uploaded + * This is useful when the device is in a bootloop and the user can't access the device to upload a new firmware (ota bricking) + * The device is considered to be in a bootloop when it is restarted before RECOVERY_WATCHDOG_TIMEOUT seconds for 5 consecutive times + * The timer is started when the device's initialization is complete (enter loop) + */ + +#define RECOVERY_WATCHDOG_TIMEOUT 15 // The time in seconds to wait for a restart before considering it a bootloop + +class ESPMegaRecovery { +public: + ESPMegaRecovery(); + void begin(); + void loop(); + void enterRecoveryMode(); // Enter recovery mode, Block all other tasks, start OTA server + void bindFRAM(FRAM* fram, uint32_t address); // Bind the FRAM block to store the bootloop counter + uint8_t getBootloopCounter(); // Get the bootloop counter + void inclementBootloopCounter(); // Increment the bootloop counter + void resetBootloopCounter(); // Reset the bootloop counter + bool isRecoveryMode(); // Check if the device is in recovery mode +private: + + FRAM* fram; + uint32_t fram_address; + uint8_t bootloop_counter; + ESPMegaWebServer* web_server; + bool recovery_mode; +}; \ No newline at end of file diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaWebServer.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaWebServer.hpp index 6559b67..ebe9bff 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaWebServer.hpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaWebServer.hpp @@ -35,17 +35,6 @@ class ESPMegaWebServer void saveCredentialsToFRAM(); AsyncWebServer* getServer(); bool checkAuthentication(AsyncWebServerRequest *request); - private: - // FRAM - FRAM *fram; - // Credentials - char webUsername[32]; - char webPassword[32]; - // Web Server - AsyncWebServer *server; - uint16_t port; - // ESPMegaIoT - ESPMegaIoT *iot; // Endpoints Handlers void dashboardHandler(AsyncWebServerRequest *request); String dashboardProcessor(const String& var); @@ -57,4 +46,15 @@ class ESPMegaWebServer void otaUploadHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); void restAPIHandler(AsyncWebServerRequest *request); void rebootHandler(AsyncWebServerRequest *request); + private: + // FRAM + FRAM *fram; + // Credentials + char webUsername[32]; + char webPassword[32]; + // Web Server + AsyncWebServer *server; + uint16_t port; + // ESPMegaIoT + ESPMegaIoT *iot; }; \ No newline at end of file From ca0ee31e951efbcdcd49c748f793380504197454 Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Wed, 14 Feb 2024 16:32:49 +0700 Subject: [PATCH 2/5] watchdog timer --- ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp index f9343a3..863b194 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp @@ -28,6 +28,15 @@ void ESPMegaRecovery::loop() { delay(1000); } } + // Watchdog timer + static bool booted = false; + static uint32_t boot_time = millis(); + if(!booted) { + if(millis() - boot_time > RECOVERY_WATCHDOG_TIMEOUT * 1000) { + this->resetBootloopCounter(); + booted = true; + } + } } void ESPMegaRecovery::enterRecoveryMode() { // Set the recovery mode flag From ee924b8b305e1c0355c3615145531e9155784347 Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Sun, 19 May 2024 17:15:49 +0700 Subject: [PATCH 3/5] implemented entering recovery mode recovery mode itself does not work yet --- ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.cpp | 5 ++++- ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.hpp | 6 ++++++ .../lib/ESPMegaPRO/ESPMegaRecovery.cpp | 13 +++++++++++++ .../lib/ESPMegaPRO/ESPMegaRecovery.hpp | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.cpp index 47d85b5..5db70e5 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.cpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.cpp @@ -73,7 +73,9 @@ bool ESPMegaPRO::begin() esp_restart(); } } - + // Recovery Mode + recovery.bindFRAM(&fram, 600); + recovery.begin(); return true; } @@ -89,6 +91,7 @@ void ESPMegaPRO::loop() { inputs.loop(); outputs.loop(); + recovery.loop(); for (int i = 0; i < 255; i++) { if (cardInstalled[i]) diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.hpp index b20f1ce..4b6bd14 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.hpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaProOS.hpp @@ -15,6 +15,7 @@ #include #include #include +#include // ESPMega Pro R3 Board Address #define FRAM_ADDRESS 0x56 @@ -82,6 +83,11 @@ class ESPMegaPRO { * @note You must call enableWebServer() before using this component. */ ESPMegaWebServer *webServer; + /** + * @brief This component is used to enter recovery mode when the ESPMegaPRO board is in a bootloop. + * @typedef ESPMegaRecovery + */ + ESPMegaRecovery recovery; private: bool iotEnabled = false; bool internalDisplayEnabled = false; diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp index 863b194..0c145c4 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp @@ -15,17 +15,29 @@ void ESPMegaRecovery::begin() { } // Inclement the bootloop counter this->inclementBootloopCounter(); + ESP_LOGV("ESPMegaRecovery", "Bootloop counter: %d", this->getBootloopCounter()); // If the bootloop counter is greater than 5, enter recovery mode if(this->getBootloopCounter() > 5) { + ESP_LOGW("ESPMegaRecovery", "Entering recovery mode"); this->enterRecoveryMode(); + // Reset the bootloop counter to prevent re-entering recovery mode + // The device might unintentionally restart multiple times + // By resetting the counter, the user can press reset once in recovery mode to exit + ESP_LOGD("ESPMegaRecovery", "Resetting bootloop counter"); + this->resetBootloopCounter(); } } void ESPMegaRecovery::loop() { // If the device is in recovery mode, block all other tasks if(this->isRecoveryMode()) { + int i = 0; while(true) { + if (i%10 == 0) + ESP_LOGV("ESPMegaRecovery", "System is in recovery mode, no tasks will be executed"); + ESP_LOGV("ESPMegaRecovery", "Please upload a new firmware to exit recovery mode"); // This code will become the new loop delay(1000); + i++; } } // Watchdog timer @@ -33,6 +45,7 @@ void ESPMegaRecovery::loop() { static uint32_t boot_time = millis(); if(!booted) { if(millis() - boot_time > RECOVERY_WATCHDOG_TIMEOUT * 1000) { + ESP_LOGI("ESPMegaRecovery", "System booted successfully, resetting bootloop counter"); this->resetBootloopCounter(); booted = true; } diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp index 16415cf..1687017 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp @@ -1,5 +1,6 @@ #include #include +#include /** * @brief Recovery mode for ESPMega * Recovery mode is a mode that is entered when the device is in a bootloop From c4d2db98d5dd485be475286e244cd6adc920fd42 Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Sun, 19 May 2024 17:40:11 +0700 Subject: [PATCH 4/5] enter recovery mode --- .../lib/ESPMegaPRO/ESPMegaRecovery.cpp | 31 +++++++++++++++---- .../lib/ESPMegaPRO/ESPMegaRecovery.hpp | 1 + 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp index 0c145c4..06c1618 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp @@ -3,10 +3,11 @@ ESPMegaRecovery::ESPMegaRecovery() { // Initialize all the pointers to null this->fram = nullptr; - this->web_server = nullptr; this->fram_address = 0; this->bootloop_counter = 0; this->recovery_mode = false; + this->web_server = nullptr; + this->iot = nullptr; } void ESPMegaRecovery::begin() { // Retrieve the bootloop counter from the FRAM @@ -18,13 +19,15 @@ void ESPMegaRecovery::begin() { ESP_LOGV("ESPMegaRecovery", "Bootloop counter: %d", this->getBootloopCounter()); // If the bootloop counter is greater than 5, enter recovery mode if(this->getBootloopCounter() > 5) { - ESP_LOGW("ESPMegaRecovery", "Entering recovery mode"); - this->enterRecoveryMode(); + ESP_LOGE("ESPMegaRecovery", "Bootloop detected"); // Reset the bootloop counter to prevent re-entering recovery mode // The device might unintentionally restart multiple times // By resetting the counter, the user can press reset once in recovery mode to exit ESP_LOGD("ESPMegaRecovery", "Resetting bootloop counter"); this->resetBootloopCounter(); + ESP_LOGW("ESPMegaRecovery", "Entering recovery mode"); + this->enterRecoveryMode(); + this->loop(); } } void ESPMegaRecovery::loop() { @@ -32,9 +35,10 @@ void ESPMegaRecovery::loop() { if(this->isRecoveryMode()) { int i = 0; while(true) { - if (i%10 == 0) + if (i%10 == 0) { ESP_LOGV("ESPMegaRecovery", "System is in recovery mode, no tasks will be executed"); ESP_LOGV("ESPMegaRecovery", "Please upload a new firmware to exit recovery mode"); + } // This code will become the new loop delay(1000); i++; @@ -54,10 +58,25 @@ void ESPMegaRecovery::loop() { void ESPMegaRecovery::enterRecoveryMode() { // Set the recovery mode flag this->recovery_mode = true; - // Remove web server binding + // Enabling the IoT module + ESP_LOGI("ESPMegaRecovery", "Enabling the IoT module"); + this->iot = new ESPMegaIoT(); + this->iot->bindFRAM(this->fram); + ETH.begin(); + this->iot->bindEthernetInterface(Ð); + ESP_LOGI("ESPMegaRecovery", "Loading network configuration"); + this->iot->loadNetworkConfig(); + ESP_LOGI("ESPMegaRecovery", "Attempting to connect to the network"); + this->iot->connectNetwork(); + // Start the web server + ESP_LOGI("ESPMegaRecovery", "Starting the web server"); + this->web_server = new ESPMegaWebServer(80, this->iot); + this->web_server->bindFRAM(this->fram); + this->web_server->loadCredentialsFromFRAM(); + ESP_LOGI("ESPMegaRecovery", "Aquiring the web server instance"); AsyncWebServer *server = this->web_server->getServer(); - server->reset(); // Add OTA update and restart endpoint + ESP_LOGI("ESPMegaRecovery", "Adding OTA update and reboot endpoints"); auto bindedOtaRequestHandler = std::bind(&ESPMegaWebServer::otaRequestHandler, this->web_server, std::placeholders::_1); auto bindedOtaUploadHandler = std::bind(&ESPMegaWebServer::otaUploadHandler, this->web_server, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6); server->on("/ota_update", HTTP_POST, bindedOtaRequestHandler, bindedOtaUploadHandler); diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp index 1687017..a8e9662 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp @@ -29,6 +29,7 @@ private: FRAM* fram; uint32_t fram_address; uint8_t bootloop_counter; + ESPMegaIoT* iot; ESPMegaWebServer* web_server; bool recovery_mode; }; \ No newline at end of file From 5ce76bfef7a2790d24055ba61dfd464c2f48669d Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Mon, 20 May 2024 00:05:16 +0700 Subject: [PATCH 5/5] working recovery mode --- .../lib/ESPMegaPRO/ESPMegaRecovery.cpp | 36 ++++++++++++++++++- .../lib/ESPMegaPRO/ESPMegaRecovery.hpp | 3 +- ESPMegaPRO-OS-SDK/src/main.cpp | 2 +- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp index 06c1618..f6fe70e 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.cpp @@ -75,13 +75,19 @@ void ESPMegaRecovery::enterRecoveryMode() { this->web_server->loadCredentialsFromFRAM(); ESP_LOGI("ESPMegaRecovery", "Aquiring the web server instance"); AsyncWebServer *server = this->web_server->getServer(); + server->begin(); // Add OTA update and restart endpoint ESP_LOGI("ESPMegaRecovery", "Adding OTA update and reboot endpoints"); + auto bindedDashboardHandler = std::bind(&ESPMegaWebServer::dashboardHandler, this->web_server, std::placeholders::_1); auto bindedOtaRequestHandler = std::bind(&ESPMegaWebServer::otaRequestHandler, this->web_server, std::placeholders::_1); auto bindedOtaUploadHandler = std::bind(&ESPMegaWebServer::otaUploadHandler, this->web_server, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6); + auto bindedDeviceInfoHandler = std::bind(&ESPMegaRecovery::getDeviceInfoHandler, this, std::placeholders::_1); + auto bindedConfigHandler = std::bind(&ESPMegaRecovery::configHandler, this, std::placeholders::_1); + server->on("/", HTTP_GET, bindedDashboardHandler); server->on("/ota_update", HTTP_POST, bindedOtaRequestHandler, bindedOtaUploadHandler); server->on("/reboot", HTTP_GET, std::bind(&ESPMegaWebServer::rebootHandler, this->web_server, std::placeholders::_1)); - + server->on("/get_device_info", HTTP_GET, bindedDeviceInfoHandler); + server->on("/config", HTTP_GET, bindedConfigHandler); } void ESPMegaRecovery::bindFRAM(FRAM *fram, uint32_t address) { this->fram = fram; @@ -105,3 +111,31 @@ void ESPMegaRecovery::resetBootloopCounter() { bool ESPMegaRecovery::isRecoveryMode() { return this->recovery_mode; } + +void ESPMegaRecovery::getDeviceInfoHandler(AsyncWebServerRequest *request) { + if (!request->authenticate(this->web_server->getWebUsername(), this->web_server->getWebPassword())) + { + return request->requestAuthentication(); + } + StaticJsonDocument<512> doc; + doc["hostname"] = this->iot->getNetworkConfig()->hostname; + doc["ip_address"] = this->iot->getIp().toString(); + doc["mac_address"] = this->iot->getMac(); + doc["model"] = BOARD_MODEL; + doc["mqtt_server"] = "Recovery"; + doc["mqtt_port"] = "Mode"; + doc["base_topic"] = "Recovery Mode"; + doc["mqtt_connected"] = "Recovery Mode"; + doc["software_version"] = "EMG-SAFE-1.0.0"; + doc["sdk_version"] = SDK_VESRION; + doc["idf_version"] = IDF_VER; + char buffer[512]; + serializeJson(doc, buffer); + request->send(200, "application/json", buffer); +} + +void ESPMegaRecovery::configHandler(AsyncWebServerRequest *request){ + // Say Not Available in Recovery Mode + // Wait 3s then redirect to / + request->send(500, "text/html", "

RECOVERY MODE

Configuration is not available in recovery mode

"); +} \ No newline at end of file diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp index a8e9662..407e3eb 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaRecovery.hpp @@ -25,7 +25,8 @@ public: void resetBootloopCounter(); // Reset the bootloop counter bool isRecoveryMode(); // Check if the device is in recovery mode private: - + void getDeviceInfoHandler(AsyncWebServerRequest *request); + void configHandler(AsyncWebServerRequest *request); FRAM* fram; uint32_t fram_address; uint8_t bootloop_counter; diff --git a/ESPMegaPRO-OS-SDK/src/main.cpp b/ESPMegaPRO-OS-SDK/src/main.cpp index 10419de..e8e43af 100644 --- a/ESPMegaPRO-OS-SDK/src/main.cpp +++ b/ESPMegaPRO-OS-SDK/src/main.cpp @@ -14,7 +14,7 @@ // #define FRAM_DEBUG // #define MQTT_DEBUG -// #define WRITE_DEFAULT_NETCONF +#define WRITE_DEFAULT_NETCONF //#define CLIMATE_CARD_ENABLE #define MQTT_CARD_REGISTER #define DISPLAY_ENABLE