diff --git a/docker/Dockerfile b/docker/Dockerfile index 572da15a..ca2b20bd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,8 @@ FROM python:latest +RUN apt-get update \ + && apt-get -y upgrade + RUN apt-get update \ && apt-get install -y --no-install-recommends \ postgresql-client curl inotify-tools net-tools \ diff --git a/docker/web/nspanelmanager/data_file.bin b/docker/web/nspanelmanager/data_file.bin index 9038c2bd..2f5be0c4 100644 Binary files a/docker/web/nspanelmanager/data_file.bin and b/docker/web/nspanelmanager/data_file.bin differ diff --git a/docker/web/nspanelmanager/firmware.bin b/docker/web/nspanelmanager/firmware.bin index e9ebbeb5..6087d2fb 100644 Binary files a/docker/web/nspanelmanager/firmware.bin and b/docker/web/nspanelmanager/firmware.bin differ diff --git a/docker/web/nspanelmanager/merged_flash.bin b/docker/web/nspanelmanager/merged_flash.bin index e34446a1..15f7b410 100644 Binary files a/docker/web/nspanelmanager/merged_flash.bin and b/docker/web/nspanelmanager/merged_flash.bin differ diff --git a/docker/web/requirements.txt b/docker/web/requirements.txt index d98977a6..0f3431da 100755 --- a/docker/web/requirements.txt +++ b/docker/web/requirements.txt @@ -9,6 +9,7 @@ gevent==22.10.2 greenlet==2.0.2 idna==3.4 paho-mqtt==1.6.1 +pip-upgrade==0.0.6 psutil==5.9.4 pytz==2023.3 requests==2.28.2 diff --git a/firmware/NSPanelManagerFirmware/data/index.html b/firmware/NSPanelManagerFirmware/data/index.html index 2d616cfa..82cf40cc 100644 --- a/firmware/NSPanelManagerFirmware/data/index.html +++ b/firmware/NSPanelManagerFirmware/data/index.html @@ -319,7 +319,7 @@
NSPanel TFT upload
- The upload baud rate that this specific NSPanel can can not be + The upload baud rate that this specific NSPanel is able to perform at can not be calculated. This is something you will have to experiment with if you wish to have different value than default. Chaning this from default may cause the NSPanel to fail to update the screen TFT. @@ -331,12 +331,13 @@
NSPanel TFT upload
+ - - - + + +
diff --git a/firmware/NSPanelManagerFirmware/data/static/index.css b/firmware/NSPanelManagerFirmware/data/static/index.css index 699ef87d..0ee15b5a 100644 --- a/firmware/NSPanelManagerFirmware/data/static/index.css +++ b/firmware/NSPanelManagerFirmware/data/static/index.css @@ -2,6 +2,13 @@ html { background-color: #101410; } +body { + color: #4a4a4a; + font-size: 1em; + font-weight: 400; + line-height: 1.5; +} + #page-title { color: #ffffff; } @@ -41,3 +48,80 @@ html { .hidden { display: none; } + +.title { + color: #363636; + font-size: 2rem; + font-weight: 600; + line-height: 1.125; +} + +.subtitle { + word-break: break-word; +} + +@media screen and (min-width: 1408px) { + .container:not(.is-max-desktop):not(.is-max-widescreen) { + max-width: 1344px; + } +} +@media screen and (min-width: 1216px) { + .container:not(.is-max-desktop) { + max-width: 1152px; + } +} +.container { + margin: 0 auto; + position: relative; + width: auto; +} +@media screen and (min-width: 1024px) { + .container { + max-width: 960px; + } +} + +.modal { + align-items: center; + display: none; + flex-direction: column; + justify-content: center; + overflow: hidden; + position: fixed; + z-index: 40; +} + +.section { + padding: 3rem 3rem; +} + +.box { + background-color: #fff; + border-radius: 6px; + box-shadow: 0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02); + color: #4a4a4a; + display: block; + padding: 1.25rem; +} + +.label { + color: #363636; + display: block; + font-size: 1rem; + font-weight: 700; +} + +.notification { + border-radius: 4px; + position: relative; + padding: 1.25rem 2.5rem 1.25rem 1.5rem; +} + +.mt-4 { + margin-top: 1rem !important; +} + +.is-warning { + background-color: #ffe08a; + color: rgba(0,0,0,.7); +} diff --git a/firmware/NSPanelManagerFirmware/include/nspm-bin-version.h b/firmware/NSPanelManagerFirmware/include/nspm-bin-version.h index ef6db00e..e906b9de 100644 --- a/firmware/NSPanelManagerFirmware/include/nspm-bin-version.h +++ b/firmware/NSPanelManagerFirmware/include/nspm-bin-version.h @@ -1 +1 @@ -#define NSPanelManagerFirmwareVersion "0.0.1442" +#define NSPanelManagerFirmwareVersion "0.0.1483" diff --git a/firmware/NSPanelManagerFirmware/lib/ChunkDownloader/ChunkDownloader.cpp b/firmware/NSPanelManagerFirmware/lib/ChunkDownloader/ChunkDownloader.cpp index f1b8f1de..0a18b308 100644 --- a/firmware/NSPanelManagerFirmware/lib/ChunkDownloader/ChunkDownloader.cpp +++ b/firmware/NSPanelManagerFirmware/lib/ChunkDownloader/ChunkDownloader.cpp @@ -39,8 +39,12 @@ bool ChunkDownloader::_downloadChunks() { uint read_chunks = 0; while (read_chunks < this->_download_chunks) { if (!httpClient.getStreamPtr()->available()) { // No data avilable from WiFi, wait 1000ms and try again - vTaskDelay(1000 / portTICK_PERIOD_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); num_failed_reads++; + + if (num_failed_reads >= 10) { + return false; + } continue; } @@ -58,7 +62,7 @@ bool ChunkDownloader::_downloadChunks() { uint16_t num_read_bytes = 0; while (num_read_bytes < next_read_chunk_size) { while (httpClient.getStreamPtr()->available() <= 0) { - vTaskDelay(250 / portTICK_PERIOD_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); num_failed_reads++; if (num_failed_reads >= 10) { // We have had 10 failed reads in a row for a total of 2.5 seconds, cancel this request and try again. diff --git a/firmware/NSPanelManagerFirmware/lib/HttpLib/HttpLib.cpp b/firmware/NSPanelManagerFirmware/lib/HttpLib/HttpLib.cpp index c9c7212c..3a84266c 100644 --- a/firmware/NSPanelManagerFirmware/lib/HttpLib/HttpLib.cpp +++ b/firmware/NSPanelManagerFirmware/lib/HttpLib/HttpLib.cpp @@ -1,6 +1,7 @@ #include #include #include +#include size_t HttpLib::GetFileSize(const char *url) { HTTPClient httpClient; @@ -45,11 +46,16 @@ size_t HttpLib::DownloadChunk(uint8_t *buffer, const char *address, size_t offse } size_t sizeReceived = 0; + uint8_t num_retries = 0; while (sizeReceived < size) { if (!httpClient.getStreamPtr()->available()) { // No data avilable from WiFi, wait 100ms and try again - vTaskDelay(500 / portTICK_PERIOD_MS); - LOG_DEBUG("Still waiting for data from address '", address, "'."); - continue; + vTaskDelay(50 / portTICK_PERIOD_MS); + num_retries++; + if (num_retries >= 5) { + break; + } else { + continue; + } } sizeReceived += httpClient.getStreamPtr()->readBytes(&buffer[sizeReceived], httpClient.getStreamPtr()->available() >= size - sizeReceived ? size - sizeReceived : httpClient.getStreamPtr()->available()); } diff --git a/firmware/NSPanelManagerFirmware/lib/InterfaceManager/InterfaceManager.cpp b/firmware/NSPanelManagerFirmware/lib/InterfaceManager/InterfaceManager.cpp index 4812cead..dfe7b8fa 100644 --- a/firmware/NSPanelManagerFirmware/lib/InterfaceManager/InterfaceManager.cpp +++ b/firmware/NSPanelManagerFirmware/lib/InterfaceManager/InterfaceManager.cpp @@ -110,6 +110,13 @@ void InterfaceManager::_taskLoadConfigAndInit(void *param) { NSPanel::attachSleepCallback(InterfaceManager::processSleepEvent); NSPanel::attachWakeCallback(InterfaceManager::processWakeEvent); + // Attach screen clock MQTT callback if configured from manager. + if (InterfaceConfig::show_screensaver_clock) { + PageManager::GetScreensaverPage()->attachMqttTimeCallback(); + } else { + LOG_DEBUG("Not attaching MQTT clock callback is panel is confiugred to now show clock on screensaver."); + } + LOG_INFO("Config initialized. Closing taskLoadConfigAndInit"); vTaskDelete(NULL); // Delete task, we are done } @@ -181,8 +188,8 @@ void InterfaceManager::handleNSPanelCommand(char *topic, byte *payload, unsigned } else if (command.compare("firmware_update") == 0) { WebManager::startOTAUpdate(); } else if (command.compare("tft_update") == 0) { - InterfaceManager::stop(); NSPanel::instance->startOTAUpdate(); + InterfaceManager::stop(); } else { LOG_WARNING("Received unknown command on MQTT: ", command.c_str()); } @@ -220,11 +227,6 @@ void InterfaceManager::subscribeToMqttTopics() { // Subscribe to command to wake/put to sleep the display vTaskDelay(100 / portTICK_PERIOD_MS); MqttManager::subscribeToTopic(NSPMConfig::instance->mqtt_screen_cmd_topic.c_str(), &InterfaceManager::mqttCallback); - if (InterfaceConfig::show_screensaver_clock) { - PageManager::GetScreensaverPage()->attachMqttTimeCallback(); - } else { - LOG_DEBUG("Not attaching MQTT clock callback is panel is confiugred to now show clock on screensaver."); - } MqttManager::subscribeToTopic(NSPMConfig::instance->mqtt_panel_cmd_topic.c_str(), &InterfaceManager::handleNSPanelCommand); MqttManager::subscribeToTopic(NSPMConfig::instance->mqtt_panel_screen_brightness_topic.c_str(), &InterfaceManager::handleNSPanelScreenBrightnessCommand); diff --git a/firmware/NSPanelManagerFirmware/lib/NSPanel/NSPanel.cpp b/firmware/NSPanelManagerFirmware/lib/NSPanel/NSPanel.cpp index add19c4c..b19e9b5b 100644 --- a/firmware/NSPanelManagerFirmware/lib/NSPanel/NSPanel.cpp +++ b/firmware/NSPanelManagerFirmware/lib/NSPanel/NSPanel.cpp @@ -1,3 +1,4 @@ +#include "freertos/portmacro.h" #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -165,9 +167,9 @@ bool NSPanel::init() { // Pin 4 controls screen on/off. pinMode(4, OUTPUT); digitalWrite(4, HIGH); // Turn off power to the display - vTaskDelay(500 / portTICK_PERIOD_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); digitalWrite(4, LOW); // Turn on power to the display - vTaskDelay(500 / portTICK_PERIOD_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); std::string result = ""; this->_readDataToString(&result, 2500, false); @@ -237,14 +239,16 @@ bool NSPanel::init() { this->_startListeningToPanel(); // Connect to display and start it + this->restart(); + vTaskDelay(250 / portTICK_PERIOD_MS); this->_sendCommandWithoutResponse("bkcmd=0"); this->_sendCommandWithoutResponse("sleep=0"); this->_sendCommandWithoutResponse("bkcmd=0"); this->_sendCommandWithoutResponse("sleep=0"); - // this->_sendCommandClearResponse("rest"); xSemaphoreGive(NSPanel::instance->_mutexWriteSerialData); xSemaphoreGive(NSPanel::instance->_mutexReadSerialData); + LOG_INFO("NSPanel::init complete."); return this->_has_received_nspm; } @@ -340,22 +344,6 @@ void NSPanel::attachWakeCallback(void (*callback)()) { void NSPanel::_taskReadNSPanelData(void *param) { LOG_INFO("Starting taskReadNSPanelData."); for (;;) { - // if (xSemaphoreTake(NSPanel::instance->_mutexReadSerialData, portMAX_DELAY) == pdTRUE) { - // if (Serial2.available() > 0) { - // std::vector data; - // while (Serial2.available() > 0) { - // data.push_back(Serial2.read()); - // if (Serial2.available() == 0) { - // vTaskDelay(50 / portTICK_PERIOD_MS); - // } - // } - // NSPanel::instance->_processQueue.push(data); - // xTaskNotifyGive(NSPanel::instance->_taskHandleProcessPanelOutput); - // } - // xSemaphoreGive(NSPanel::instance->_mutexReadSerialData); - // } - // vTaskDelay(25 / portTICK_PERIOD_MS); - while (Serial2.available() == 0) { vTaskDelay(25 / portTICK_PERIOD_MS); } @@ -567,7 +555,7 @@ void NSPanel::_taskUpdateTFTConfigOTA(void *param) { LOG_INFO("Starting TFT update..."); while (true) { - if (xSemaphoreTake(NSPanel::instance->_mutexReadSerialData, portMAX_DELAY)) { + if (xSemaphoreTake(NSPanel::instance->_mutexReadSerialData, 1000 / portTICK_PERIOD_MS)) { break; } else { LOG_ERROR("Failed to take serial read mutex, trying again in 3 seconds."); @@ -576,7 +564,7 @@ void NSPanel::_taskUpdateTFTConfigOTA(void *param) { } while (true) { - if (xSemaphoreTake(NSPanel::instance->_mutexWriteSerialData, portMAX_DELAY)) { + if (xSemaphoreTake(NSPanel::instance->_mutexWriteSerialData, portTICK_PERIOD_MS)) { break; } else { LOG_ERROR("Failed to take serial write mutex, trying again in 3 seconds."); @@ -643,7 +631,7 @@ bool NSPanel::_updateTFTOTA() { downloadUrl.append("/download_tft"); LOG_INFO("Will download TFT file from: ", downloadUrl.c_str()); - ChunkDownloader *cd = new ChunkDownloader(downloadUrl, 4096, 4); + size_t file_size = HttpLib::GetFileSize(downloadUrl.c_str()); LOG_INFO("Force restarting screen via power switch."); digitalWrite(4, HIGH); // Turn off power to the display @@ -677,52 +665,53 @@ bool NSPanel::_updateTFTOTA() { LOG_DEBUG("Got comok: ", comok_string.c_str()); // Switch to desiered upload buad rate. - // int32_t baud_diff = NSPMConfig::instance->tft_upload_baud - Serial2.baudRate(); - // if (baud_diff < 0) { - // baud_diff = baud_diff / -1; - // } - // if (baud_diff >= 10) { - // std::string uploadBaudRateString = "baud="; - // uploadBaudRateString.append(std::to_string(NSPMConfig::instance->tft_upload_baud)); - // Serial2.print(uploadBaudRateString.c_str()); - // NSPanel::instance->_sendCommandEndSequence(); - - // std::string read_data = ""; - // NSPanel::instance->_readDataToString(&read_data, 1000, false); - // if (read_data.compare("") != 0) { - // LOG_INFO("Baud rate switch successful, switching Serial2 from ", Serial2.baudRate(), " to ", NSPMConfig::instance->tft_upload_baud); - - // NSPanel::_clearSerialBuffer(); - // Serial2.end(); - // Serial2.setTxBufferSize(0); - // Serial2.begin(NSPMConfig::instance->tft_upload_baud, SERIAL_8N1, 17, 16); - // } else { - // LOG_ERROR("Baud rate switch failed. Will restart. Try setting the default 115200."); - // vTaskDelay(5000 / portTICK_PERIOD_MS); - // ESP.restart(); - // return false; - // } - // } - - LOG_DEBUG("Will start TFT upload, TFT file size: ", cd->getTotalFileSize()); + int32_t baud_diff = NSPMConfig::instance->tft_upload_baud - Serial2.baudRate(); + if (baud_diff < 0) { + baud_diff = baud_diff / -1; + } + + // Wait for panel to finish whatever it is doing. + vTaskDelay(5000 / portTICK_PERIOD_MS); + + if (baud_diff >= 10) { + std::string uploadBaudRateString = "baud="; + uploadBaudRateString.append(std::to_string(NSPMConfig::instance->tft_upload_baud)); + Serial2.print(uploadBaudRateString.c_str()); + NSPanel::instance->_sendCommandEndSequence(); + + std::string read_data = ""; + NSPanel::instance->_readDataToString(&read_data, 10000, false); + if (read_data.empty()) { + LOG_ERROR("Baud rate switch failed. Will continue anyways."); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + LOG_INFO("Baud rate switching Serial2 from ", Serial2.baudRate(), " to ", NSPMConfig::instance->tft_upload_baud); + NSPanel::_clearSerialBuffer(); + Serial2.end(); + Serial2.setTxBufferSize(0); + Serial2.begin(NSPMConfig::instance->tft_upload_baud, SERIAL_8N1, 17, 16); + } + + LOG_DEBUG("Will start TFT upload, TFT file size: ", file_size); // TODO: Detect if new protocol is not supported, in that case set flag in flash and restart and then continue flash with legacy mode. // Send whmi-wri command to initiate upload std::string commandString; if (NSPMConfig::instance->use_new_upload_protocol) { LOG_INFO("Starting upload using v1.2 protocol."); commandString = "whmi-wris "; - commandString.append(std::to_string(cd->getTotalFileSize())); + commandString.append(std::to_string(file_size)); commandString.append(","); // commandString.append(std::to_string(NSPMConfig::instance->tft_upload_baud)); - commandString.append("115200"); + commandString.append(std::to_string(NSPMConfig::instance->tft_upload_baud)); commandString.append(",1"); } else { LOG_INFO("Starting upload using v1.1 protocol."); commandString = "whmi-wri "; - commandString.append(std::to_string(cd->getTotalFileSize())); - commandString.append(","); + commandString.append(std::to_string(file_size)); + commandString.append(",1"); // commandString.append(std::to_string(NSPMConfig::instance->tft_upload_baud)); - commandString.append("115200"); + commandString.append(std::to_string(NSPMConfig::instance->tft_upload_baud)); commandString.append(",1"); } Serial2.print(commandString.c_str()); @@ -756,19 +745,25 @@ bool NSPanel::_updateTFTOTA() { // Loop until break when all firmware has finished uploading (data available in stream == 0) while (true) { - - // Write the chunk to the display - uint32_t current_location = cd->getCurrentChunkPosition(); - size_t bytesReceived = cd->readNextChunk(dataBuffer); - lastReadByte = current_location + bytesReceived; + // Calculate next chunk size + int next_write_size; + if (file_size - lastReadByte > 4096) { + next_write_size = 4096; + } else { + next_write_size = file_size - lastReadByte; + } + size_t bytesReceived = HttpLib::DownloadChunk(dataBuffer, downloadUrl.c_str(), nextStartWriteOffset, next_write_size); + LOG_DEBUG("Bytes received: ", bytesReceived, " requested ", next_write_size); vTaskDelay(500 / portTICK_PERIOD_MS); Serial2.write(dataBuffer, bytesReceived); - NSPanel::instance->_update_progress = ((float)lastReadByte / (float)cd->getTotalFileSize()) * 100; + nextStartWriteOffset += bytesReceived; + lastReadByte = nextStartWriteOffset; + NSPanel::instance->_update_progress = ((float)lastReadByte / (float)file_size) * 100; std::string return_string; uint16_t recevied_bytes = NSPanel::instance->_readDataToString(&return_string, 5000, true); - if (lastReadByte >= cd->getTotalFileSize()) { + if (lastReadByte >= file_size) { NSPanel::instance->_update_progress = 100; LOG_INFO("TFT Upload complete, processed ", lastReadByte, " bytes."); break; @@ -790,7 +785,6 @@ bool NSPanel::_updateTFTOTA() { if (readNextOffset > 0) { nextStartWriteOffset = readNextOffset; LOG_INFO("Got 0x08 with offset, jumping to: ", nextStartWriteOffset, " please wait."); - cd->seek(readNextOffset); } } else { LOG_DEBUG("Got unexpected return data from panel. Received ", recevied_bytes, " bytes: "); diff --git a/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.cpp b/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.cpp index 476b61d9..158eca8c 100644 --- a/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.cpp +++ b/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.cpp @@ -1,3 +1,4 @@ +#include "esp32-hal.h" #include #include #include @@ -17,13 +18,20 @@ void RoomManager::init() { MqttManager::subscribeToTopic("nspanel/config/reload", &RoomManager::reloadCallback); RoomManager::currentRoom = RoomManager::rooms.end(); + RoomManager::_lastReloadCommand = millis(); } void RoomManager::reloadCallback(char *topic, byte *payload, unsigned int length) { + if (RoomManager::_lastReloadCommand + 5000 >= millis()) { + LOG_ERROR("Received reload command within 5 seconds of a reload. Ignoring command."); + return; + } + std::string payload_str = std::string((char *)payload, 1); if (payload_str.compare("1") == 0) { LOG_DEBUG("Got reload command, reloading rooms."); RoomManager::loadAllRooms(true); + RoomManager::_lastReloadCommand = millis(); } } diff --git a/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.hpp b/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.hpp index cd09a673..a497d312 100644 --- a/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.hpp +++ b/firmware/NSPanelManagerFirmware/lib/RoomManager/RoomManager.hpp @@ -23,6 +23,7 @@ class RoomManager { private: static inline std::list _roomChangeObservers; + static inline unsigned long _lastReloadCommand; static void _callRoomChangeCallbacks(); }; diff --git a/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.cpp b/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.cpp index 80759670..488348bc 100644 --- a/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.cpp +++ b/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.cpp @@ -16,6 +16,11 @@ WebManager *WebManager::instance; void WebManager::init(const char *nspmFirmwareVersion) { + if (this->_has_already_been_started) { + LOG_ERROR("Trying to start WebManager while it has already been started."); + return; + } + this->instance = this; this->_nspmFirmwareVersion = nspmFirmwareVersion; this->_state = WebManagerState::ONLINE; @@ -35,6 +40,7 @@ void WebManager::init(const char *nspmFirmwareVersion) { this->_server.serveStatic("/static", LittleFS, "/static"); this->_server.begin(); + this->_has_already_been_started = true; } String WebManager::processIndexTemplate(const String &templateVar) { @@ -270,6 +276,7 @@ uint8_t WebManager::getUpdateProgress() { } bool WebManager::_update(uint8_t type, const char *url) { + InterfaceManager::stop(); LOG_INFO("Starting ", type == U_FLASH ? "Firmware" : "LittleFS", " OTA update..."); WebManager::_update_progress = 0; std::string downloadUrl = "http://"; @@ -300,6 +307,7 @@ bool WebManager::_update(uint8_t type, const char *url) { size_t writtenSize = 0; uint8_t buffer[8192]; + uint8_t last_update_percent = 0; while (writtenSize < totalSize) { size_t downloadSize = totalSize - writtenSize; if (downloadSize > sizeof(buffer)) { @@ -310,12 +318,12 @@ bool WebManager::_update(uint8_t type, const char *url) { // Update percent update completed WebManager::_update_progress = (uint8_t)(((float)writtenSize / (float)totalSize) * 100); - if (type == U_FLASH) { + if (type == U_FLASH && last_update_percent != WebManager::_update_progress) { std::string update_string = "Updating FW "; update_string.append(std::to_string(WebManager::_update_progress)); update_string.append("%"); PageManager::GetNSPanelManagerPage()->setText(update_string); - } else { + } else if (type == U_SPIFFS && last_update_percent != WebManager::_update_progress) { std::string update_string = "Updating FS "; update_string.append(std::to_string(WebManager::_update_progress)); update_string.append("%"); diff --git a/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.hpp b/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.hpp index 0cf16811..8c1e495a 100644 --- a/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.hpp +++ b/firmware/NSPanelManagerFirmware/lib/WebManager/WebManager.hpp @@ -39,6 +39,8 @@ class WebManager { static inline WebManagerState _state; /// @brief If updating, contains the % done of the update static inline uint8_t _update_progress; + + static inline bool _has_already_been_started = false; }; #endif diff --git a/firmware/NSPanelManagerFirmware/merged-flash.bin b/firmware/NSPanelManagerFirmware/merged-flash.bin index e34446a1..15f7b410 100644 Binary files a/firmware/NSPanelManagerFirmware/merged-flash.bin and b/firmware/NSPanelManagerFirmware/merged-flash.bin differ diff --git a/firmware/NSPanelManagerFirmware/src/main.cpp b/firmware/NSPanelManagerFirmware/src/main.cpp index 23885e88..09b28711 100644 --- a/firmware/NSPanelManagerFirmware/src/main.cpp +++ b/firmware/NSPanelManagerFirmware/src/main.cpp @@ -1,3 +1,4 @@ +#include "freertos/portmacro.h" #include #include #include