ESPMegaPRO-v3-SDK/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/ESPMegaDisplay.cpp

778 lines
24 KiB
C++
Raw Normal View History

2023-12-28 19:36:18 +00:00
#include <ESPMegaDisplay.hpp>
2023-12-29 13:04:25 +00:00
/**
* @brief Receives and processes serial commands from the display adapter.
* @param process Flag indicating whether to process the received commands.
* @return True if data is received, false otherwise.
*/
2023-12-30 15:50:19 +00:00
bool ESPMegaDisplay::recieveSerialCommand(bool process)
{
2023-12-29 04:55:59 +00:00
bool dataRecieved = false;
2024-01-15 09:44:36 +00:00
2023-12-30 15:50:19 +00:00
while (displayAdapter->available())
{
2024-01-15 09:44:36 +00:00
// 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;
}
2023-12-28 19:36:18 +00:00
rx_buffer[rx_buffer_index] = displayAdapter->read();
2024-01-15 09:44:36 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-28 19:36:18 +00:00
rx_buffer_index++;
// Check for overflow
2023-12-30 15:50:19 +00:00
if (rx_buffer_index >= 256)
{
2023-12-28 19:36:18 +00:00
rx_buffer_index = 0;
}
2023-12-30 15:50:19 +00:00
if (process)
this->processSerialCommand();
2023-12-29 04:55:59 +00:00
dataRecieved = true;
2023-12-28 19:36:18 +00:00
}
2023-12-29 04:55:59 +00:00
return dataRecieved;
}
2023-12-29 13:04:25 +00:00
/**
* @brief Receives and processes serial commands from the display adapter.
* @return True if data is received, false otherwise.
*/
2023-12-30 15:50:19 +00:00
bool ESPMegaDisplay::recieveSerialCommand()
{
2023-12-29 04:55:59 +00:00
return recieveSerialCommand(true);
2023-12-28 19:36:18 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Processes the received serial command.
* @note This function interacts directly with the rx_buffer.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::processSerialCommand()
{
2023-12-28 19:36:18 +00:00
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
2023-12-30 15:50:19 +00:00
if (!payloadIsValid())
return;
2023-12-28 19:36:18 +00:00
// The rx buffer ended with stop bytes
// Check if the rx buffer is a push payload
// The payload type is the first byte of the payload
// 0x00 is invalid instruction
// 0x01 is success
// 0x65 is a touch event
// 0x66 is a page report event
2023-12-30 15:50:19 +00:00
if (rx_buffer[0] == 0x65)
{
2023-12-28 19:36:18 +00:00
processTouchPayload();
}
2023-12-30 15:50:19 +00:00
else if (rx_buffer[0] == 0x66)
{
2023-12-28 19:36:18 +00:00
processPageReportPayload();
}
2024-01-14 07:09:20 +00:00
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)
{
2024-01-14 07:09:20 +00:00
callback.second(rx_buffer[0], reinterpret_cast<unsigned char *>(&rx_buffer[1]), rx_buffer_index - 4);
;
}
}
2023-12-30 15:50:19 +00:00
this->rx_buffer_index = 0;
2023-12-28 19:36:18 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Processes the touch event payload.
* @note This function interacts directly with the rx_buffer.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::processTouchPayload()
{
if (rx_buffer_index != 7)
return;
2023-12-28 19:36:18 +00:00
// The second byte of the payload is the page number
uint8_t page = rx_buffer[1];
// The third byte of the payload is the component id
uint8_t component = rx_buffer[2];
// The fourth byte of the payload is the event
uint8_t event = rx_buffer[3];
// 0x01 is press, 0x00 is release
2023-12-30 11:51:13 +00:00
ESP_LOGV("ESPMegaDisplay", "Touch event: page=%d, component=%d, event=%d", page, component, event);
2023-12-30 15:50:19 +00:00
for (auto const &callback : touch_callbacks)
{
callback.second(page, component, event);
}
2023-12-28 19:36:18 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Processes the page report event payload.
* @note This function interacts directly with the rx_buffer.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::processPageReportPayload()
{
if (rx_buffer_index != 5)
return;
2023-12-28 19:36:18 +00:00
// The second byte of the payload is the page number
2023-12-31 08:53:39 +00:00
// Check if the page number is different from the current page
// If it is different, we call the page change callbacks
if (rx_buffer[1] == this->currentPage)
return;
2023-12-28 19:36:18 +00:00
this->currentPage = rx_buffer[1];
2023-12-30 11:51:13 +00:00
ESP_LOGV("ESPMegaDisplay", "Page change event: page=%d", this->currentPage);
2023-12-30 15:50:19 +00:00
for (auto const &callback : page_change_callbacks)
{
callback.second(this->currentPage);
}
2023-12-29 04:55:59 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Sends stop bytes to the display adapter.
2024-01-14 07:09:20 +00:00
*
* @note This function does not take the serial mutex, it is assumed that the caller has already taken the mutex.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::sendStopBytes()
{
2023-12-29 04:55:59 +00:00
displayAdapter->write(0xFF);
displayAdapter->write(0xFF);
displayAdapter->write(0xFF);
}
2023-12-29 13:04:25 +00:00
/**
* @brief Sends a command to the display adapter.
* @param command The command to send.
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::sendCommand(char *command)
{
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2023-12-29 04:55:59 +00:00
displayAdapter->print(command);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 04:55:59 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Jumps to the specified page on the display.
* @param page The page number to jump to.
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::jumpToPage(int page)
{
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2023-12-29 04:55:59 +00:00
this->displayAdapter->print("page ");
this->displayAdapter->print(page);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 04:55:59 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Sets the value of a number component on the display.
* @param component The component name.
* @param value The value to set.
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::setNumber(const char *component, int value)
{
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2023-12-29 04:55:59 +00:00
this->displayAdapter->print(component);
this->displayAdapter->print("=");
this->displayAdapter->print(value);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 04:55:59 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Sets the value of a string component on the display.
* @param component The component name.
* @param value The value to set.
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::setString(const char *component, const char *value)
{
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2023-12-29 04:55:59 +00:00
this->displayAdapter->print(component);
this->displayAdapter->print("=\"");
this->displayAdapter->print(value);
this->displayAdapter->print("\"");
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 04:55:59 +00:00
}
2023-12-29 13:04:25 +00:00
/**
* @brief Gets the value of a number component from the display.
* @warning This function is blocking.
* @warning If the display does not respond or is not connected, this function will block for up to DISPLAY_FETCH_RETRY_COUNT * DISPLAY_FETCH_TIMEOUT milliseconds.
2023-12-29 13:04:25 +00:00
* @param component The component name.
* @return The value of the number component.
*/
2023-12-30 15:50:19 +00:00
uint32_t ESPMegaDisplay::getNumber(const char *component)
{
// We might be in the middle of a serial command
// We reset the rx buffer index to 0 to ensure that we don't read the wrong data
this->rx_buffer_index = 0;
2023-12-29 13:04:25 +00:00
uint32_t start = millis();
// Send the get command
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return 0;
}
2023-12-29 13:04:25 +00:00
this->displayAdapter->print("get ");
this->displayAdapter->print(component);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-30 15:50:19 +00:00
// Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times
2023-12-29 13:04:25 +00:00
// Wait for the response
2023-12-30 15:50:19 +00:00
bool validPayload = false;
for (int i = 0; i < DISPLAY_FETCH_RETRY_COUNT; i++)
{
if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT))
{
ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response");
return 0;
}
if (rx_buffer[0] != 0x71)
{
ESP_LOGI("ESPMegaDisplay", "Invalid payload type: %d, retrying.", rx_buffer[0]);
// The rx buffer is invalid, reset the rx buffer index
rx_buffer_index = 0;
continue;
2023-12-31 08:53:39 +00:00
}
else
{
2023-12-30 15:50:19 +00:00
validPayload = true;
break;
}
}
2023-12-31 08:53:39 +00:00
if (!validPayload)
{
2023-12-30 15:50:19 +00:00
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
return 0;
}
2023-12-29 13:04:25 +00:00
// The rx buffer is valid
// The expected payload is type 0x71
2023-12-30 15:50:19 +00:00
2023-12-29 13:04:25 +00:00
// The 2nd to 5th byte of the payload is the value
// It's a 4 byte 32-bit value in little endian order.
// Convert the 4 bytes to a 32-bit value
uint32_t value = 0;
value |= rx_buffer[1];
2023-12-30 15:50:19 +00:00
value |= rx_buffer[2] << 8;
value |= rx_buffer[3] << 16;
value |= rx_buffer[4] << 24;
2023-12-29 13:04:25 +00:00
return value;
}
/**
* @brief Gets the value of a string component from the display.
* @warning This function is blocking.
* @warning If the display does not respond or is not connected, this function will block for up to DISPLAY_FETCH_RETRY_COUNT * DISPLAY_FETCH_TIMEOUT milliseconds.
2023-12-29 13:04:25 +00:00
* @param component The component name.
* @return The value of the string component.
* @note The returned char array must be freed after use.
*/
2023-12-30 15:50:19 +00:00
const char *ESPMegaDisplay::getString(const char *component)
{
// We might be in the middle of a serial command
// We reset the rx buffer index to 0 to ensure that we don't read the wrong data
this->rx_buffer_index = 0;
2023-12-29 04:55:59 +00:00
uint32_t start = millis();
// Send the get command
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return nullptr;
}
2023-12-29 04:55:59 +00:00
this->displayAdapter->print("get ");
this->displayAdapter->print(component);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 04:55:59 +00:00
// Wait for the response
2023-12-30 15:50:19 +00:00
// Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times
// Wait for the response
bool validPayload = false;
for (int i = 0; i < DISPLAY_FETCH_RETRY_COUNT; i++)
{
if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT))
{
ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response");
2023-12-31 08:53:39 +00:00
return nullptr;
2023-12-30 15:50:19 +00:00
}
if (rx_buffer[0] != 0x70)
{
ESP_LOGI("ESPMegaDisplay", "Invalid payload type: %d, retrying.", rx_buffer[0]);
// The rx buffer is invalid, reset the rx buffer index
rx_buffer_index = 0;
continue;
2023-12-31 08:53:39 +00:00
}
else
{
2023-12-30 15:50:19 +00:00
validPayload = true;
break;
}
}
2023-12-31 08:53:39 +00:00
if (!validPayload)
{
2023-12-30 15:50:19 +00:00
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
2023-12-31 08:53:39 +00:00
return nullptr;
2023-12-30 15:50:19 +00:00
}
2023-12-29 13:04:25 +00:00
// The 2nd bytes onwards before the stop bytes is the string
// The length of the string is the length of the payload minus 4
2023-12-30 15:50:19 +00:00
uint8_t length = rx_buffer_index - 4;
2023-12-29 13:04:25 +00:00
// First we malloc a char array with the length of the string
2023-12-30 15:50:19 +00:00
char *value = (char *)malloc(length + 1);
2023-12-29 13:04:25 +00:00
// Copy the string from the rx buffer to the char array
2023-12-30 15:50:19 +00:00
memcpy(value, rx_buffer + 1, length);
2023-12-29 13:04:25 +00:00
// Add the null terminator
value[length] = '\0';
// Return the char array
return value;
2023-12-29 04:55:59 +00:00
}
/**
* @brief Gets the value of a number component from the display and stores it in a buffer.
* @warning This function is blocking.
* @warning If the display does not respond or is not connected, this function will block for up to DISPLAY_FETCH_RETRY_COUNT * DISPLAY_FETCH_TIMEOUT milliseconds.
* @param component The component name.
* @param buffer The buffer to store the value.
* @param buffer_size The size of the buffer.
* @return True if the value is successfully stored in the buffer, false otherwise.
*/
2023-12-30 15:50:19 +00:00
bool ESPMegaDisplay::getStringToBuffer(const char *component, char *buffer, uint8_t buffer_size)
{
2023-12-31 08:53:39 +00:00
// We might be in the middle of a serial command
// We reset the rx buffer index to 0 to ensure that we don't read the wrong data
this->rx_buffer_index = 0;
2023-12-29 13:04:25 +00:00
uint32_t start = millis();
// Send the get command
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return false;
}
2023-12-29 13:04:25 +00:00
this->displayAdapter->print("get ");
this->displayAdapter->print(component);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 13:04:25 +00:00
// Wait for the response
2023-12-31 08:53:39 +00:00
// Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times
// Wait for the response
bool validPayload = false;
for (int i = 0; i < DISPLAY_FETCH_RETRY_COUNT; i++)
{
if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT))
{
ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response");
this->sendStopBytes();
return 0;
}
if (rx_buffer[0] != 0x70)
{
ESP_LOGI("ESPMegaDisplay", "Invalid payload type: %d, retrying.", rx_buffer[0]);
this->sendStopBytes();
// The rx buffer is invalid, reset the rx buffer index
rx_buffer_index = 0;
continue;
}
else
{
validPayload = true;
break;
}
}
if (!validPayload)
{
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
this->sendStopBytes();
return 0;
}
2023-12-29 13:04:25 +00:00
// The 2nd bytes onwards before the stop bytes is the string
// The length of the string is the length of the payload minus 4
2023-12-30 15:50:19 +00:00
uint8_t length = rx_buffer_index - 4;
2023-12-29 13:04:25 +00:00
// Check if the buffer is large enough to hold the string
2023-12-30 15:50:19 +00:00
if (length > buffer_size)
2023-12-31 08:53:39 +00:00
{
ESP_LOGE("ESPMegaDisplay", "Buffer size too small");
this->sendStopBytes();
2023-12-30 15:50:19 +00:00
return false;
2023-12-31 08:53:39 +00:00
}
2023-12-29 13:04:25 +00:00
// Copy the string from the rx buffer to the char array
2023-12-30 15:50:19 +00:00
memcpy(buffer, rx_buffer + 1, length);
2023-12-29 13:04:25 +00:00
// Add the null terminator
buffer[length] = '\0';
2023-12-31 08:53:39 +00:00
return 1;
2023-12-29 13:04:25 +00:00
}
/**
* @brief Waits for a valid payload from the display adapter.
* @param timeout The timeout value in milliseconds.
* @return True if a valid payload is received, false otherwise.
*/
2023-12-30 15:50:19 +00:00
bool ESPMegaDisplay::waitForValidPayload(uint32_t timeout)
{
2023-12-29 04:55:59 +00:00
uint32_t start = millis();
// If the payload is not valid, keep reading the serial buffer until timeout
2023-12-30 15:50:19 +00:00
while (!this->payloadIsValid())
{
if (millis() - start > timeout)
{
2023-12-29 04:55:59 +00:00
return false;
}
recieveSerialCommand(false);
}
return true;
}
2023-12-29 13:04:25 +00:00
/**
* @brief Checks if the received payload is valid.
* @return True if the payload is valid, false otherwise.
*/
2023-12-30 15:50:19 +00:00
bool ESPMegaDisplay::payloadIsValid()
{
2023-12-29 04:55:59 +00:00
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
2023-12-30 15:50:19 +00:00
if (rx_buffer_index < 3)
return false;
if (rx_buffer[rx_buffer_index - 1] != 0xFF)
return false;
if (rx_buffer[rx_buffer_index - 2] != 0xFF)
return false;
if (rx_buffer[rx_buffer_index - 3] != 0xFF)
return false;
2023-12-29 04:55:59 +00:00
return true;
2023-12-29 13:04:25 +00:00
}
/**
* @brief Sets the brightness of the display.
* @param value The brightness value.
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::setBrightness(int value)
{
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2023-12-29 13:04:25 +00:00
this->displayAdapter->print("dim=");
this->displayAdapter->print(value);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 13:04:25 +00:00
}
/**
* @brief Sets the volume of the display.
* @param value The volume value.
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::setVolume(int value)
{
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2023-12-29 13:04:25 +00:00
this->displayAdapter->print("vol=");
this->displayAdapter->print(value);
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 13:04:25 +00:00
}
/**
* @brief Restarts the display.
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::reset()
{
2023-12-29 13:04:25 +00:00
// First we send a stop bytes to clear the serial buffer
// This ensures that the display is ready to receive the reset command
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2023-12-29 13:04:25 +00:00
sendStopBytes();
this->displayAdapter->print("rest");
sendStopBytes();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-29 13:04:25 +00:00
}
/**
2023-12-30 15:50:19 +00:00
* @brief Constructor for the ESPMegaDisplay class.
* @param displayAdapter The serial adapter connected to the display.
2023-12-29 13:04:25 +00:00
*/
ESPMegaDisplay::ESPMegaDisplay(HardwareSerial *displayAdapter, uint32_t baudRate, uint32_t uploadBaudRate, uint8_t txPin, uint8_t rxPin)
2023-12-30 15:50:19 +00:00
{
2024-01-16 15:08:13 +00:00
this->baudRate = baudRate;
this->uploadBaudRate = uploadBaudRate;
this->txPin = txPin;
this->rxPin = rxPin;
2024-01-14 07:11:09 +00:00
this->serialMutex = xSemaphoreCreateMutex();
2024-01-15 14:56:07 +00:00
this->otaBytesWritten = 0;
2023-12-30 15:50:19 +00:00
this->displayAdapter = displayAdapter;
this->currentPage = 0;
this->rx_buffer_index = 0;
2023-12-29 13:04:25 +00:00
}
/**
2023-12-30 15:50:19 +00:00
* @brief Initializes the display.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::begin()
{
2024-01-14 07:09:20 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGE("ESPMegaDisplay", "Failed to take serial mutex");
return;
}
2024-01-16 15:08:13 +00:00
this->displayAdapter->begin(this->baudRate, SERIAL_8N1, this->rxPin, this->txPin);
2023-12-30 15:50:19 +00:00
this->displayAdapter->setTimeout(100);
this->displayAdapter->flush();
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
2023-12-30 15:50:19 +00:00
this->reset();
2023-12-29 13:04:25 +00:00
}
/**
2023-12-30 15:50:19 +00:00
* @brief The main loop function of the display.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
void ESPMegaDisplay::loop()
{
// Check if there is data in the serial buffer
// If there is data, process the data
recieveSerialCommand();
2023-12-29 13:04:25 +00:00
}
/**
2023-12-30 15:50:19 +00:00
* @brief Registers a callback function for touch events.
* @param callback The callback function.
* @return The handle of the callback function.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
uint16_t ESPMegaDisplay::registerTouchCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback)
{
uint16_t handle = touch_callbacks.size();
touch_callbacks[handle] = callback;
return handle;
2023-12-29 13:04:25 +00:00
}
/**
2023-12-31 06:41:48 +00:00
* @brief Unregisters a callback function for touch events.
2023-12-30 15:50:19 +00:00
* @param handle The handle of the callback function.
2023-12-29 13:04:25 +00:00
*/
2023-12-31 06:41:48 +00:00
void ESPMegaDisplay::unregisterTouchCallback(uint16_t handle)
2023-12-30 15:50:19 +00:00
{
touch_callbacks.erase(handle);
2023-12-29 13:04:25 +00:00
}
/**
2023-12-30 15:50:19 +00:00
* @brief Registers a callback function for page change events.
* @param callback The callback function.
* @return The handle of the callback function.
2023-12-29 13:04:25 +00:00
*/
2023-12-30 15:50:19 +00:00
uint16_t ESPMegaDisplay::registerPageChangeCallback(std::function<void(uint8_t)> callback)
{
uint16_t handle = page_change_callbacks.size();
page_change_callbacks[handle] = callback;
return handle;
2023-12-29 13:04:25 +00:00
}
/**
2023-12-31 06:41:48 +00:00
* @brief Unregisters a callback function for page change events.
2023-12-30 15:50:19 +00:00
* @param handle The handle of the callback function.
2023-12-29 13:04:25 +00:00
*/
2023-12-31 06:41:48 +00:00
void ESPMegaDisplay::unregisterPageChangeCallback(uint16_t handle)
2023-12-30 15:50:19 +00:00
{
page_change_callbacks.erase(handle);
2023-12-29 13:04:25 +00:00
}
/**
* @brief Registers a callback function for payloads.
* @param callback The callback function.
* @return The handle of the callback function.
*/
2024-01-14 07:09:20 +00:00
uint16_t ESPMegaDisplay::registerPayloadCallback(std::function<void(uint8_t, uint8_t *, uint8_t)> callback)
{
uint16_t handle = payload_callbacks.size();
payload_callbacks[handle] = callback;
return handle;
}
/**
* @brief Unregisters a callback function for payloads.
* @param handle The handle of the callback function.
*/
void ESPMegaDisplay::unregisterPayloadCallback(uint16_t handle)
{
payload_callbacks.erase(handle);
2024-01-14 07:09:20 +00:00
}
/**
* @brief Takes the serial mutex.
2024-01-15 09:44:36 +00:00
*
* @note only neccessary if you are using the display adapter directly, otherwise the mutex is taken by the helper functions.
2024-01-14 07:09:20 +00:00
*/
2024-01-15 09:44:36 +00:00
bool ESPMegaDisplay::takeSerialMutex()
2024-01-14 07:09:20 +00:00
{
2024-01-15 09:44:36 +00:00
if (xSemaphoreTake(this->serialMutex, DISPLAY_MUTEX_TAKE_TIMEOUT) == pdFALSE)
{
ESP_LOGI("ESPMegaDisplay", "Failed to take serial mutex");
return false;
}
return true;
2024-01-14 07:09:20 +00:00
}
/**
* @brief Gives the serial mutex.
*/
void ESPMegaDisplay::giveSerialMutex()
{
2024-01-15 09:44:36 +00:00
ESP_LOGD("ESPMegaDisplay", "Giving serial mutex");
2024-01-14 07:09:20 +00:00
xSemaphoreGive(this->serialMutex);
}
2024-01-16 15:08:13 +00:00
bool ESPMegaDisplay::beginUpdate(size_t size)
{
// The display's baudrate might be stuck at 9600 if the display is not initialized
// We try to initiate the display at the user specified baud rate first, if it fails, we try again at 9600
if (!beginUpdate(size, uploadBaudRate))
{
ESP_LOGW("ESPMegaDisplay", "Failed to initiate LCD update at %d baud, retrying at 9600 baud.", uploadBaudRate);
if (!beginUpdate(size, 9600))
{
ESP_LOGE("ESPMegaDisplay", "Failed to initiate LCD update at 9600 baud.");
return false;
}
}
return true;
}
2024-01-14 07:09:20 +00:00
/**
* @brief Starts an OTA update.
* @param size The size of the update.
2024-01-16 15:08:13 +00:00
* @param baudRate The baud rate use to connect to the display to initiate the update.
* @note The baud rate that is used to transfer the data is defined by the uploadBaudRate parameter in the constructor.
2024-01-14 07:09:20 +00:00
* @return True if the OTA update is started, false otherwise.
*/
bool ESPMegaDisplay::beginUpdate(size_t size, uint32_t baudRate)
2024-01-14 07:09:20 +00:00
{
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
2024-01-16 15:08:13 +00:00
this->displayAdapter->begin(baudRate, SERIAL_8N1, this->rxPin, this->txPin);
2024-01-14 07:09:20 +00:00
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);
2024-01-16 15:08:13 +00:00
this->displayAdapter->print(",");
this->displayAdapter->print(uploadBaudRate);
this->displayAdapter->print(",res0");
2024-01-14 07:09:20 +00:00
this->sendStopBytes();
2024-01-16 15:08:13 +00:00
this->displayAdapter->begin(uploadBaudRate, SERIAL_8N1, this->rxPin, this->txPin);
2024-01-14 07:09:20 +00:00
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)
{
2024-01-14 17:39:12 +00:00
ESP_LOGV("ESPMegaDisplay", "LCD Update Subroutine is ready to receive data.");
2024-01-14 07:09:20 +00:00
return true;
}
}
}
// FLush the serial recieve buffer
while (this->displayAdapter->available())
this->displayAdapter->read();
2024-01-14 17:39:12 +00:00
ESP_LOGE("ESPMegaDisplay", "LCD Update Subroutine failed to initialize.");
2024-01-16 15:08:13 +00:00
xSemaphoreGive(this->serialMutex);
2024-01-14 07:09:20 +00:00
return false;
}
/**
2024-01-14 17:39:12 +00:00
* @brief Writes data to the display during an update.
2024-01-14 07:09:20 +00:00
* @param data The data to write.
* @param size The size of the data.
* @return True if the data is written, false otherwise.
*/
2024-01-14 17:39:12 +00:00
bool ESPMegaDisplay::writeUpdate(uint8_t *data, size_t size)
2024-01-14 07:09:20 +00:00
{
// Check if the data size is too large
2024-01-15 08:19:49 +00:00
if (size > 4096)
2024-01-14 07:09:20 +00:00
{
2024-01-14 17:39:12 +00:00
ESP_LOGE("ESPMegaDisplay", "LCD Update Subroutine failed to write data, data size is too large.");
2024-01-14 07:09:20 +00:00
return false;
}
// Flush the serial recieve buffer
while (this->displayAdapter->available())
this->displayAdapter->read();
// Write the data
for (int i = 0; i < size; i++)
{
2024-01-15 08:19:49 +00:00
this->displayAdapter->write(data[i]);
// After every 4096 bytes, we have to wait for the display to send a 0x05 byte
// If it doesn't, return false
2024-01-15 14:12:02 +00:00
otaBytesWritten ++;
2024-01-15 08:19:49 +00:00
if (otaBytesWritten % 4096 == 0)
2024-01-14 07:09:20 +00:00
{
2024-01-15 08:19:49 +00:00
unsigned long startTime = millis();
2024-01-15 14:30:22 +00:00
bool ready = false;
2024-01-15 08:19:49 +00:00
while (millis() - startTime < OTA_WAIT_TIMEOUT)
2024-01-14 07:09:20 +00:00
{
2024-01-15 08:19:49 +00:00
if (this->displayAdapter->available())
{
if (this->displayAdapter->read() == 0x05)
{
2024-01-15 14:30:22 +00:00
ready = true;
break;
2024-01-15 08:19:49 +00:00
}
}
2024-01-14 07:09:20 +00:00
}
2024-01-15 14:30:22 +00:00
if (!ready)
{
ESP_LOGE("ESPMegaDisplay", "LCD Update Subroutine failed to write data, display is not ready.");
return false;
}
2024-01-14 07:09:20 +00:00
}
}
2024-01-15 08:19:49 +00:00
return true;
2024-01-14 07:09:20 +00:00
}
/**
2024-01-14 17:39:12 +00:00
* @brief Ends an LCD update.
2024-01-14 07:09:20 +00:00
*/
2024-01-14 17:39:12 +00:00
void ESPMegaDisplay::endUpdate()
2024-01-14 07:09:20 +00:00
{
xSemaphoreGive(this->serialMutex);
this->reset();
delay(500);
this->begin();
2024-01-15 14:56:07 +00:00
}
/**
* @brief Gets the number of bytes written during an OTA update.
* @return The number of bytes written.
*/
2024-01-16 15:08:13 +00:00
size_t ESPMegaDisplay::getUpdateBytesWritten()
2024-01-15 14:56:07 +00:00
{
return this->otaBytesWritten;
}