oop output card display binding

This commit is contained in:
Siwat Sirichai 2023-12-30 22:50:19 +07:00
parent 59b1ade59d
commit 2d0d38ecf3
6 changed files with 355 additions and 124 deletions

View file

@ -5,17 +5,21 @@
* @param process Flag indicating whether to process the received commands.
* @return True if data is received, false otherwise.
*/
bool ESPMegaDisplay::recieveSerialCommand(bool process){
bool ESPMegaDisplay::recieveSerialCommand(bool process)
{
bool dataRecieved = false;
// Read the serial buffer if available
while(displayAdapter->available()) {
while (displayAdapter->available())
{
rx_buffer[rx_buffer_index] = displayAdapter->read();
rx_buffer_index++;
// Check for overflow
if(rx_buffer_index>=256){
if (rx_buffer_index >= 256)
{
rx_buffer_index = 0;
}
if(process) this-> processSerialCommand();
if (process)
this->processSerialCommand();
dataRecieved = true;
}
return dataRecieved;
@ -25,16 +29,19 @@ bool ESPMegaDisplay::recieveSerialCommand(bool process){
* @brief Receives and processes serial commands from the display adapter.
* @return True if data is received, false otherwise.
*/
bool ESPMegaDisplay::recieveSerialCommand(){
bool ESPMegaDisplay::recieveSerialCommand()
{
return recieveSerialCommand(true);
}
/**
* @brief Processes the received serial command.
*/
void ESPMegaDisplay::processSerialCommand(){
void ESPMegaDisplay::processSerialCommand()
{
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
if(!payloadIsValid()) return;
if (!payloadIsValid())
return;
// 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
@ -42,19 +49,24 @@ void ESPMegaDisplay::processSerialCommand(){
// 0x01 is success
// 0x65 is a touch event
// 0x66 is a page report event
if(rx_buffer[0]==0x65){
if (rx_buffer[0] == 0x65)
{
processTouchPayload();
}
else if(rx_buffer[0]==0x66){
else if (rx_buffer[0] == 0x66)
{
processPageReportPayload();
}
this->rx_buffer_index = 0;
}
/**
* @brief Processes the touch event payload.
*/
void ESPMegaDisplay::processTouchPayload() {
if (rx_buffer_index != 4) return;
void ESPMegaDisplay::processTouchPayload()
{
if (rx_buffer_index != 7)
return;
// 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
@ -63,25 +75,33 @@ void ESPMegaDisplay::processTouchPayload() {
uint8_t event = rx_buffer[3];
// 0x01 is press, 0x00 is release
ESP_LOGV("ESPMegaDisplay", "Touch event: page=%d, component=%d, event=%d", page, component, event);
for (auto const &callback : touch_callbacks)
{
callback.second(page, component, event);
}
}
/**
* @brief Processes the page report event payload.
*/
void ESPMegaDisplay::processPageReportPayload() {
void ESPMegaDisplay::processPageReportPayload()
{
if (rx_buffer_index != 5)
return;
// The second byte of the payload is the page number
this->currentPage = rx_buffer[1];
if(pageChangeCallback!=NULL){
pageChangeCallback(this->currentPage);
}
rx_buffer_index = 0;
ESP_LOGV("ESPMegaDisplay", "Page change event: page=%d", this->currentPage);
for (auto const &callback : page_change_callbacks)
{
callback.second(this->currentPage);
}
}
/**
* @brief Sends stop bytes to the display adapter.
*/
void ESPMegaDisplay::sendStopBytes() {
void ESPMegaDisplay::sendStopBytes()
{
displayAdapter->write(0xFF);
displayAdapter->write(0xFF);
displayAdapter->write(0xFF);
@ -91,7 +111,8 @@ void ESPMegaDisplay::sendStopBytes() {
* @brief Sends a command to the display adapter.
* @param command The command to send.
*/
void ESPMegaDisplay::sendCommand(char* command) {
void ESPMegaDisplay::sendCommand(char *command)
{
displayAdapter->print(command);
sendStopBytes();
}
@ -100,7 +121,8 @@ void ESPMegaDisplay::sendCommand(char* command) {
* @brief Jumps to the specified page on the display.
* @param page The page number to jump to.
*/
void ESPMegaDisplay::jumpToPage(int page) {
void ESPMegaDisplay::jumpToPage(int page)
{
this->displayAdapter->print("page ");
this->displayAdapter->print(page);
sendStopBytes();
@ -111,7 +133,8 @@ void ESPMegaDisplay::jumpToPage(int page) {
* @param component The component name.
* @param value The value to set.
*/
void ESPMegaDisplay::setNumber(const char* component, int value) {
void ESPMegaDisplay::setNumber(const char *component, int value)
{
this->displayAdapter->print(component);
this->displayAdapter->print("=");
this->displayAdapter->print(value);
@ -123,7 +146,8 @@ void ESPMegaDisplay::setNumber(const char* component, int value) {
* @param component The component name.
* @param value The value to set.
*/
void ESPMegaDisplay::setString(const char* component, const char* value) {
void ESPMegaDisplay::setString(const char *component, const char *value)
{
this->displayAdapter->print(component);
this->displayAdapter->print("=\"");
this->displayAdapter->print(value);
@ -136,25 +160,53 @@ void ESPMegaDisplay::setString(const char* component, const char* value) {
* @param component The component name.
* @return The value of the number component.
*/
uint32_t ESPMegaDisplay::getNumber(const char* component) {
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;
uint32_t start = millis();
// Send the get command
this->displayAdapter->print("get ");
this->displayAdapter->print(component);
sendStopBytes();
// Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times
// Wait for the response
if(!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) return 0;
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;
} else {
validPayload = true;
break;
}
}
if (!validPayload) {
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
return 0;
}
// The rx buffer is valid
// The expected payload is type 0x71
if(rx_buffer[0]!=0x71) return 0;
// 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];
value |= rx_buffer[2]<<8;
value |= rx_buffer[3]<<16;
value |= rx_buffer[4]<<24;
value |= rx_buffer[2] << 8;
value |= rx_buffer[3] << 16;
value |= rx_buffer[4] << 24;
return value;
}
@ -164,48 +216,77 @@ uint32_t ESPMegaDisplay::getNumber(const char* component) {
* @return The value of the string component.
* @note The returned char array must be freed after use.
*/
const char* ESPMegaDisplay::getString(const char* component) {
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;
uint32_t start = millis();
// Send the get command
this->displayAdapter->print("get ");
this->displayAdapter->print(component);
sendStopBytes();
// Wait for the response
if(!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) return "";
// The rx buffer is valid
// The expected payload is type 0x70
if(rx_buffer[0]!=0x70) return "";
// 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");
return 0;
}
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;
} else {
validPayload = true;
break;
}
}
if (!validPayload) {
ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]);
return 0;
}
// The 2nd bytes onwards before the stop bytes is the string
// The length of the string is the length of the payload minus 4
uint8_t length = rx_buffer_index-4;
uint8_t length = rx_buffer_index - 4;
// First we malloc a char array with the length of the string
char* value = (char*)malloc(length+1);
char *value = (char *)malloc(length + 1);
// Copy the string from the rx buffer to the char array
memcpy(value, rx_buffer+1, length);
memcpy(value, rx_buffer + 1, length);
// Add the null terminator
value[length] = '\0';
// Return the char array
return value;
}
bool ESPMegaDisplay::getStringToBuffer(const char* component, char* buffer, uint8_t buffer_size) {
bool ESPMegaDisplay::getStringToBuffer(const char *component, char *buffer, uint8_t buffer_size)
{
uint32_t start = millis();
// Send the get command
this->displayAdapter->print("get ");
this->displayAdapter->print(component);
sendStopBytes();
// Wait for the response
if(!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) return false;
if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT))
return false;
// The rx buffer is valid
// The expected payload is type 0x70
if(rx_buffer[0]!=0x70) return false;
if (rx_buffer[0] != 0x70)
return false;
// The 2nd bytes onwards before the stop bytes is the string
// The length of the string is the length of the payload minus 4
uint8_t length = rx_buffer_index-4;
uint8_t length = rx_buffer_index - 4;
// Check if the buffer is large enough to hold the string
if(length>buffer_size) return false;
if (length > buffer_size)
return false;
// Copy the string from the rx buffer to the char array
memcpy(buffer, rx_buffer+1, length);
memcpy(buffer, rx_buffer + 1, length);
// Add the null terminator
buffer[length] = '\0';
return true;
@ -216,11 +297,14 @@ bool ESPMegaDisplay::getStringToBuffer(const char* component, char* buffer, uint
* @param timeout The timeout value in milliseconds.
* @return True if a valid payload is received, false otherwise.
*/
bool ESPMegaDisplay::waitForValidPayload(uint32_t timeout) {
bool ESPMegaDisplay::waitForValidPayload(uint32_t timeout)
{
uint32_t start = millis();
// If the payload is not valid, keep reading the serial buffer until timeout
while(!this->payloadIsValid()){
if(millis()-start>timeout){
while (!this->payloadIsValid())
{
if (millis() - start > timeout)
{
return false;
}
recieveSerialCommand(false);
@ -232,12 +316,17 @@ bool ESPMegaDisplay::waitForValidPayload(uint32_t timeout) {
* @brief Checks if the received payload is valid.
* @return True if the payload is valid, false otherwise.
*/
bool ESPMegaDisplay::payloadIsValid() {
bool ESPMegaDisplay::payloadIsValid()
{
// Check if the rx buffer ended with stop bytes (0xFF 0xFF 0xFF)
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;
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;
return true;
}
@ -245,7 +334,8 @@ bool ESPMegaDisplay::payloadIsValid() {
* @brief Sets the brightness of the display.
* @param value The brightness value.
*/
void ESPMegaDisplay::setBrightness(int value) {
void ESPMegaDisplay::setBrightness(int value)
{
this->displayAdapter->print("dim=");
this->displayAdapter->print(value);
sendStopBytes();
@ -255,7 +345,8 @@ void ESPMegaDisplay::setBrightness(int value) {
* @brief Sets the volume of the display.
* @param value The volume value.
*/
void ESPMegaDisplay::setVolume(int value) {
void ESPMegaDisplay::setVolume(int value)
{
this->displayAdapter->print("vol=");
this->displayAdapter->print(value);
sendStopBytes();
@ -264,7 +355,8 @@ void ESPMegaDisplay::setVolume(int value) {
/**
* @brief Restarts the display.
*/
void ESPMegaDisplay::reset() {
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
sendStopBytes();
@ -272,68 +364,22 @@ void ESPMegaDisplay::reset() {
sendStopBytes();
}
/**
* @brief Registers a callback function that will be called when a push event is received.
* @param callback The callback function.
*/
void ESPMegaDisplay::registerPushCallback(std::function<void(uint8_t, uint8_t)> callback) {
this->pushCallback = callback;
}
/**
* @brief Registers a callback function that will be called when a pop event is received.
* @param callback The callback function.
*/
void ESPMegaDisplay::registerPopCallback(std::function<void(uint8_t, uint8_t)> callback) {
this->popCallback = callback;
}
/**
* @brief Registers a callback function that will be called when a page change event is received.
* @param callback The callback function.
*/
void ESPMegaDisplay::registerPageChangeCallback(std::function<void(uint8_t)> callback) {
this->pageChangeCallback = callback;
}
/**
* @brief Unregisters the push callback function.
*/
void ESPMegaDisplay::unregisterPushCallback() {
this->pushCallback = NULL;
}
/**
* @brief Unregisters the pop callback function.
*/
void ESPMegaDisplay::unregisterPopCallback() {
this->popCallback = NULL;
}
/**
* @brief Unregisters the page change callback function.
*/
void ESPMegaDisplay::unregisterPageChangeCallback() {
this->pageChangeCallback = NULL;
}
/**
* @brief Constructor for the ESPMegaDisplay class.
* @param displayAdapter The serial adapter connected to the display.
*/
ESPMegaDisplay::ESPMegaDisplay(HardwareSerial *displayAdapter) {
ESPMegaDisplay::ESPMegaDisplay(HardwareSerial *displayAdapter)
{
this->displayAdapter = displayAdapter;
this->currentPage = 0;
this->rx_buffer_index = 0;
this->pushCallback = NULL;
this->popCallback = NULL;
this->pageChangeCallback = NULL;
}
/**
* @brief Initializes the display.
*/
void ESPMegaDisplay::begin() {
void ESPMegaDisplay::begin()
{
this->displayAdapter->begin(115200);
this->displayAdapter->setTimeout(100);
this->displayAdapter->flush();
@ -343,8 +389,51 @@ void ESPMegaDisplay::begin() {
/**
* @brief The main loop function of the display.
*/
void ESPMegaDisplay::loop() {
void ESPMegaDisplay::loop()
{
// Check if there is data in the serial buffer
// If there is data, process the data
recieveSerialCommand();
}
}
/**
* @brief Registers a callback function for touch events.
* @param callback The callback function.
* @return The handle of the callback function.
*/
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;
}
/**
* @brief Deregisters a callback function for touch events.
* @param handle The handle of the callback function.
*/
void ESPMegaDisplay::deregisterTouchCallback(uint16_t handle)
{
touch_callbacks.erase(handle);
}
/**
* @brief Registers a callback function for page change events.
* @param callback The callback function.
* @return The handle of the callback function.
*/
uint16_t ESPMegaDisplay::registerPageChangeCallback(std::function<void(uint8_t)> callback)
{
uint16_t handle = page_change_callbacks.size();
page_change_callbacks[handle] = callback;
return handle;
}
/**
* @brief Deregisters a callback function for page change events.
* @param handle The handle of the callback function.
*/
void ESPMegaDisplay::deregisterPageChangeCallback(uint16_t handle)
{
page_change_callbacks.erase(handle);
}