diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.cpp index 8ddeb0e..f1135cd 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.cpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.cpp @@ -9,6 +9,11 @@ bool ESPMegaDisplay::recieveSerialCommand(bool process) { bool dataRecieved = false; // Read the serial buffer if available + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return false; + } while (displayAdapter->available()) { rx_buffer[rx_buffer_index] = displayAdapter->read(); @@ -22,6 +27,7 @@ bool ESPMegaDisplay::recieveSerialCommand(bool process) this->processSerialCommand(); dataRecieved = true; } + xSemaphoreGive(this->serialMutex); return dataRecieved; } @@ -58,13 +64,15 @@ void ESPMegaDisplay::processSerialCommand() { processPageReportPayload(); } - else { + else + { // The payload does not match any of the expected payload types // Pass the payload to the payload callbacks // type, payload, length for (auto const &callback : payload_callbacks) { - callback.second(rx_buffer[0], reinterpret_cast(&rx_buffer[1]), rx_buffer_index - 4);; + callback.second(rx_buffer[0], reinterpret_cast(&rx_buffer[1]), rx_buffer_index - 4); + ; } } this->rx_buffer_index = 0; @@ -115,6 +123,8 @@ void ESPMegaDisplay::processPageReportPayload() /** * @brief Sends stop bytes to the display adapter. + * + * @note This function does not take the serial mutex, it is assumed that the caller has already taken the mutex. */ void ESPMegaDisplay::sendStopBytes() { @@ -129,8 +139,14 @@ void ESPMegaDisplay::sendStopBytes() */ void ESPMegaDisplay::sendCommand(char *command) { + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } displayAdapter->print(command); sendStopBytes(); + xSemaphoreGive(this->serialMutex); } /** @@ -139,9 +155,15 @@ void ESPMegaDisplay::sendCommand(char *command) */ void ESPMegaDisplay::jumpToPage(int page) { + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } this->displayAdapter->print("page "); this->displayAdapter->print(page); sendStopBytes(); + xSemaphoreGive(this->serialMutex); } /** @@ -151,10 +173,16 @@ void ESPMegaDisplay::jumpToPage(int page) */ void ESPMegaDisplay::setNumber(const char *component, int value) { + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } this->displayAdapter->print(component); this->displayAdapter->print("="); this->displayAdapter->print(value); sendStopBytes(); + xSemaphoreGive(this->serialMutex); } /** @@ -164,11 +192,17 @@ void ESPMegaDisplay::setNumber(const char *component, int value) */ void ESPMegaDisplay::setString(const char *component, const char *value) { + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } this->displayAdapter->print(component); this->displayAdapter->print("=\""); this->displayAdapter->print(value); this->displayAdapter->print("\""); sendStopBytes(); + xSemaphoreGive(this->serialMutex); } /** @@ -185,9 +219,15 @@ uint32_t ESPMegaDisplay::getNumber(const char *component) this->rx_buffer_index = 0; uint32_t start = millis(); // Send the get command + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return 0; + } this->displayAdapter->print("get "); this->displayAdapter->print(component); sendStopBytes(); + xSemaphoreGive(this->serialMutex); // Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times // Wait for the response bool validPayload = false; @@ -246,9 +286,15 @@ const char *ESPMegaDisplay::getString(const char *component) this->rx_buffer_index = 0; uint32_t start = millis(); // Send the get command + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return nullptr; + } this->displayAdapter->print("get "); this->displayAdapter->print(component); sendStopBytes(); + xSemaphoreGive(this->serialMutex); // Wait for the response // Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times // Wait for the response @@ -307,9 +353,15 @@ bool ESPMegaDisplay::getStringToBuffer(const char *component, char *buffer, uint this->rx_buffer_index = 0; uint32_t start = millis(); // Send the get command + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return false; + } this->displayAdapter->print("get "); this->displayAdapter->print(component); sendStopBytes(); + xSemaphoreGive(this->serialMutex); // Wait for the response // Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times // Wait for the response @@ -404,9 +456,15 @@ bool ESPMegaDisplay::payloadIsValid() */ void ESPMegaDisplay::setBrightness(int value) { + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } this->displayAdapter->print("dim="); this->displayAdapter->print(value); sendStopBytes(); + xSemaphoreGive(this->serialMutex); } /** @@ -415,9 +473,15 @@ void ESPMegaDisplay::setBrightness(int value) */ void ESPMegaDisplay::setVolume(int value) { + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } this->displayAdapter->print("vol="); this->displayAdapter->print(value); sendStopBytes(); + xSemaphoreGive(this->serialMutex); } /** @@ -427,9 +491,15 @@ void ESPMegaDisplay::reset() { // First we send a stop bytes to clear the serial buffer // This ensures that the display is ready to receive the reset command + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } sendStopBytes(); this->displayAdapter->print("rest"); sendStopBytes(); + xSemaphoreGive(this->serialMutex); } /** @@ -448,8 +518,14 @@ ESPMegaDisplay::ESPMegaDisplay(HardwareSerial *displayAdapter) */ void ESPMegaDisplay::begin() { + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGE("ESPMegaDisplay", "Failed to take serial mutex"); + return; + } this->displayAdapter->setTimeout(100); this->displayAdapter->flush(); + xSemaphoreGive(this->serialMutex); this->reset(); } @@ -510,7 +586,7 @@ void ESPMegaDisplay::unregisterPageChangeCallback(uint16_t handle) * @param callback The callback function. * @return The handle of the callback function. */ -uint16_t ESPMegaDisplay::registerPayloadCallback(std::function callback) +uint16_t ESPMegaDisplay::registerPayloadCallback(std::function callback) { uint16_t handle = payload_callbacks.size(); payload_callbacks[handle] = callback; @@ -524,4 +600,120 @@ uint16_t ESPMegaDisplay::registerPayloadCallback(std::functionserialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT); +} + +/** + * @brief Gives the serial mutex. + */ +void ESPMegaDisplay::giveSerialMutex() +{ + xSemaphoreGive(this->serialMutex); +} + +/** + * @brief Starts an OTA update. + * @param size The size of the update. + * @return True if the OTA update is started, false otherwise. + */ +bool ESPMegaDisplay::beginOTA(size_t size) +{ + if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE) + { + ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex"); + return false; + } + ESP_LOGD("ESPMegaDisplay", "LCD OTA Subroutine has taken the serial mutex, all other tasks will be blocked until the OTA update is complete."); + // We have taken the serial mutex, all helper functions will be blocked until the OTA update is complete + // Thus, we have to interact directly with the display adapter + this->sendStopBytes(); + this->displayAdapter->print("rest"); + this->sendStopBytes(); + delay(500); + this->displayAdapter->print("connect"); + this->sendStopBytes(); + delay(1000); + // Flush the serial recieve buffer + while (this->displayAdapter->available()) + this->displayAdapter->read(); + this->displayAdapter->print("whmi-wri "); + this->displayAdapter->print(size); + this->displayAdapter->print(",115200,res0"); + this->sendStopBytes(); + delay(1000); + // If the display is ready, it will send a 0x05 byte + // If it does, return true, otherwise return false + unsigned long startTime = millis(); + while (millis() - startTime < OTA_WAIT_TIMEOUT) + { + if (this->displayAdapter->available()) + { + if (this->displayAdapter->read() == 0x05) + { + ESP_LOGV("ESPMegaDisplay", "LCD OTA Subroutine is ready to receive data."); + return true; + } + } + } + // FLush the serial recieve buffer + while (this->displayAdapter->available()) + this->displayAdapter->read(); + ESP_LOGE("ESPMegaDisplay", "LCD OTA Subroutine failed to initialize."); + return false; +} + +/** + * @brief Writes data to the display during an OTA update. + * @param data The data to write. + * @param size The size of the data. + * @return True if the data is written, false otherwise. + */ +bool ESPMegaDisplay::writeOTA(uint8_t *data, size_t size) +{ + // Check if the data size is too large + if(size>4096) + { + ESP_LOGE("ESPMegaDisplay", "LCD OTA Subroutine failed to write data, data size is too large."); + return false; + } + // Flush the serial recieve buffer + while (this->displayAdapter->available()) + this->displayAdapter->read(); + // Write the data + for (int i = 0; i < size; i++) + this->displayAdapter->write(data[i]); + // After writing the data, the display will send a 0x05 byte + // If it does, return true, otherwise return false + unsigned long startTime = millis(); + while (millis() - startTime < OTA_WAIT_TIMEOUT) + { + if (this->displayAdapter->available()) + { + if (this->displayAdapter->read() == 0x05) + { + ESP_LOGV("ESPMegaDisplay", "LCD OTA Subroutine is ready to receive data."); + return true; + } + } + } + ESP_LOGE("ESPMegaDisplay", "LCD OTA Subroutine failed to write data."); + return false; +} + +/** + * @brief Ends an LCD OTA update. + */ +void ESPMegaDisplay::endOTA() +{ + xSemaphoreGive(this->serialMutex); + this->reset(); + delay(500); + this->begin(); } \ No newline at end of file diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.hpp index df19d09..ce0ef88 100644 --- a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.hpp +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.hpp @@ -2,6 +2,9 @@ #include #include +#define DISPLAY_MUTEX_TAKE_TIMEOUT 1000 // ms +#define OTA_WAIT_TIMEOUT 1000 // ms + #define DISPLAY_FETCH_TIMEOUT 100 // ms #define DISPLAY_FETCH_RETRY_COUNT 5 @@ -32,8 +35,13 @@ class ESPMegaDisplay void unregisterPageChangeCallback(uint16_t handle); uint16_t registerPayloadCallback(std::function callback); void unregisterPayloadCallback(uint16_t handle); - + void takeSerialMutex(); + void giveSerialMutex(); + bool beginOTA(size_t size); + bool writeOTA(uint8_t* data, size_t size); + void endOTA(); protected: + SemaphoreHandle_t serialMutex; uint8_t currentPage; uint8_t rx_buffer_index; char rx_buffer[256];