Fix polling error

This commit is contained in:
GW_MC
2026-02-02 21:12:54 +08:00
parent e467951b8c
commit 1dff88ed1a
6 changed files with 141 additions and 93 deletions

View File

@@ -61,9 +61,9 @@ EInkDisplayHandler::~EInkDisplayHandler() {
} }
esp_err_t EInkDisplayHandler::deep_sleep_display(void) { esp_err_t EInkDisplayHandler::deep_sleep_display(void) {
ESP_LOGI(TAG, "Putting display into deep sleep mode..."); ESP_LOGV(TAG, "Putting display into deep sleep mode...");
if (is_deep_sleep_) { if (is_deep_sleep_) {
ESP_LOGI(TAG, "Display is already in deep sleep mode"); ESP_LOGW(TAG, "Display is already in deep sleep mode");
return ESP_OK; return ESP_OK;
} }
{ {
@@ -111,7 +111,7 @@ esp_err_t EInkDisplayHandler::refresh_display() {
} else { } else {
// refresh does not correctly work after recovering from deep sleep due to sram reset // refresh does not correctly work after recovering from deep sleep due to sram reset
{ {
ESP_LOGI(TAG, "Waiting for display to be idle..."); ESP_LOGV(TAG, "Waiting for display to be idle...");
TransactionGuard transaction_guard(this->epd_handler_); TransactionGuard transaction_guard(this->epd_handler_);
err = transaction_guard.begin(pdMS_TO_TICKS(10000)); err = transaction_guard.begin(pdMS_TO_TICKS(10000));
if (err != ESP_OK) { if (err != ESP_OK) {
@@ -123,7 +123,7 @@ esp_err_t EInkDisplayHandler::refresh_display() {
} }
epd_handler_.wait_for_idle(); epd_handler_.wait_for_idle();
ESP_LOGI(TAG, "Starting display refresh..."); ESP_LOGV(TAG, "Starting display refresh...");
err = epd_handler_.epd_write_cmd(0x92, transaction_guard.transaction_id()); // enter normal mode err = epd_handler_.epd_write_cmd(0x92, transaction_guard.transaction_id()); // enter normal mode
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to enter normal mode: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to enter normal mode: %s", esp_err_to_name(err));
@@ -149,12 +149,12 @@ esp_err_t EInkDisplayHandler::refresh_display() {
force_full_refresh_ = false; force_full_refresh_ = false;
} }
ESP_LOGI(TAG, "Refresh complete"); ESP_LOGV(TAG, "Refresh complete");
return ESP_OK; return ESP_OK;
} }
esp_err_t EInkDisplayHandler::full_write(const uint8_t* framebuffer, const bool white_basemap) { esp_err_t EInkDisplayHandler::full_write(const uint8_t* framebuffer, const bool white_basemap) {
ESP_LOGI(TAG, "Starting full refresh (3 seconds)..."); ESP_LOGV(TAG, "Starting full refresh (3 seconds)...");
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
@@ -214,7 +214,7 @@ esp_err_t EInkDisplayHandler::full_write(const uint8_t* framebuffer, const bool
} }
vTaskDelay(pdMS_TO_TICKS(MINIMUM_PIN_SETUP_DELAY_MS)); // at least 200us delay vTaskDelay(pdMS_TO_TICKS(MINIMUM_PIN_SETUP_DELAY_MS)); // at least 200us delay
ESP_LOGI(TAG, "Display refresh triggered, BUSY pin: %d", gpio_get_level(PIN_BUSY)); ESP_LOGV(TAG, "Display refresh triggered, BUSY pin: %d", gpio_get_level(PIN_BUSY));
// Wait for refresh to complete // Wait for refresh to complete
epd_handler_.wait_for_idle(); epd_handler_.wait_for_idle();
@@ -229,13 +229,13 @@ esp_err_t EInkDisplayHandler::full_write(const uint8_t* framebuffer, const bool
refresh_area_.reset(); refresh_area_.reset();
memcpy(old_buffer_, draw_buffer_, DISPLAY_BUFFER_SIZE); memcpy(old_buffer_, draw_buffer_, DISPLAY_BUFFER_SIZE);
ESP_LOGI(TAG, "Full refresh complete"); ESP_LOGV(TAG, "Full refresh complete");
return ESP_OK; return ESP_OK;
} }
// TODO: Partial refresh is inverted in color // TODO: Partial refresh is inverted in color
esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_framebuffer, const RefreshArea& incoming_area, const bool is_last_partial_update) { esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_framebuffer, const RefreshArea& incoming_area, const bool is_last_partial_update) {
ESP_LOGI(TAG, "Starting partial refresh (0.3 seconds)..."); ESP_LOGV(TAG, "Starting partial refresh (0.3 seconds)...");
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
write_to_buffer_(incoming_partial_framebuffer, incoming_area); write_to_buffer_(incoming_partial_framebuffer, incoming_area);
@@ -244,7 +244,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
refresh_area_.expand_to_include(incoming_area); refresh_area_.expand_to_include(incoming_area);
if (!is_last_partial_update) { if (!is_last_partial_update) {
ESP_LOGI(TAG, "Partial refresh skipped (not last partial update)"); ESP_LOGV(TAG, "Partial refresh skipped (not last partial update)");
return ESP_OK; return ESP_OK;
} }
@@ -273,7 +273,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
RefreshArea area = refresh_area_; RefreshArea area = refresh_area_;
if (area.x1 % 8 != 0 || area.x2 % 8 != 7) { if (area.x1 % 8 != 0 || area.x2 % 8 != 7) {
ESP_LOGE(TAG, "Partial refresh area x1 and x2 must be byte-aligned (x1 %% 8 == 0 and x2 %% 8 == 7)"); ESP_LOGE(TAG, "Partial refresh area x1 and x2 must be byte-aligned (x1 %% 8 == 0 and x2 %% 8 == 7)");
ESP_LOGI(TAG, "Given area: x1=%d, x2=%d", area.x1, area.x2); ESP_LOGV(TAG, "Given area: x1=%d, x2=%d", area.x1, area.x2);
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
@@ -348,9 +348,9 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
static_cast<uint8_t>(area.y2 & 0xFF), static_cast<uint8_t>(area.y2 & 0xFF),
0x01 // Gates scan both inside and outside of the partial window 0x01 // Gates scan both inside and outside of the partial window
}; };
ESP_LOGI(TAG, "Setting partial window: x1=%d, y1=%d, x2=%d, y2=%d", ESP_LOGV(TAG, "Setting partial window: x1=%d, y1=%d, x2=%d, y2=%d",
area.x1, area.y1, area.x2, area.y2); area.x1, area.y1, area.x2, area.y2);
ESP_LOGI(TAG, "Partial window data: %02X %02X %02X %02X %02X %02X %02X %02X", ESP_LOGV(TAG, "Partial window data: %02X %02X %02X %02X %02X %02X %02X %02X",
window_data[0], window_data[1], window_data[2], window_data[3], window_data[4], window_data[0], window_data[1], window_data[2], window_data[3], window_data[4],
window_data[5], window_data[6], window_data[7]); window_data[5], window_data[6], window_data[7]);
err = epd_handler_.epd_write_cmd_with_data(0x90, window_data, transaction_guard.transaction_id()); // Set partial window err = epd_handler_.epd_write_cmd_with_data(0x90, window_data, transaction_guard.transaction_id()); // Set partial window
@@ -370,7 +370,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
} }
// Send only the partial area data, not the full display buffer // Send only the partial area data, not the full display buffer
ESP_LOGI(TAG, "Sending new partial buffer: %zu bytes (area: %dx%d)", ESP_LOGV(TAG, "Sending new partial buffer: %zu bytes (area: %dx%d)",
partial_buffer_size, area_width_bytes * 8, area_height); partial_buffer_size, area_width_bytes * 8, area_height);
err = epd_handler_.transfer_spi_data(partial_buffer, partial_buffer_size, transaction_guard.transaction_id(), true); // Inverted for partial refresh err = epd_handler_.transfer_spi_data(partial_buffer, partial_buffer_size, transaction_guard.transaction_id(), true); // Inverted for partial refresh
if (err != ESP_OK) { if (err != ESP_OK) {
@@ -403,7 +403,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
return err; return err;
} }
} }
ESP_LOGI(TAG, "Partial refresh complete"); ESP_LOGV(TAG, "Partial refresh complete");
err = deep_sleep_display(); err = deep_sleep_display();
if (err != ESP_OK) { if (err != ESP_OK) {
@@ -412,7 +412,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
} }
if (force_full_refresh_) { if (force_full_refresh_) {
ESP_LOGI(TAG, "Full refresh already requested, skipping partial refresh count increment"); ESP_LOGV(TAG, "Full refresh already requested, skipping partial refresh count increment");
err = refresh_display(); err = refresh_display();
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to perform forced full refresh: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to perform forced full refresh: %s", esp_err_to_name(err));
@@ -432,7 +432,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
partial_refresh_count_++; partial_refresh_count_++;
} }
if (partial_refresh_count_ >= PARTIAL_REFRESH_THRESHOLD) { if (partial_refresh_count_ >= PARTIAL_REFRESH_THRESHOLD) {
ESP_LOGI(TAG, "Partial refresh count %u reached threshold %u, next refresh will be full", ESP_LOGV(TAG, "Partial refresh count %u reached threshold %u, next refresh will be full",
partial_refresh_count_, PARTIAL_REFRESH_THRESHOLD); partial_refresh_count_, PARTIAL_REFRESH_THRESHOLD);
force_full_refresh_ = true; force_full_refresh_ = true;
partial_refresh_count_ = 0; partial_refresh_count_ = 0;
@@ -447,14 +447,14 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
} }
esp_err_t EInkDisplayHandler::clear_display(void) { esp_err_t EInkDisplayHandler::clear_display(void) {
ESP_LOGI(TAG, "Clearing display to all white..."); ESP_LOGV(TAG, "Clearing display to all white...");
esp_err_t err = full_write(white_data, false); esp_err_t err = full_write(white_data, false);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear display: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to clear display: %s", esp_err_to_name(err));
return err; return err;
} }
ESP_LOGI(TAG, "Display cleared to all white"); ESP_LOGV(TAG, "Display cleared to all white");
return ESP_OK; return ESP_OK;
} }
@@ -478,7 +478,7 @@ void EInkDisplayHandler::request_full_refresh(void) {
if (guard.take(pdMS_TO_TICKS(100))) { if (guard.take(pdMS_TO_TICKS(100))) {
force_full_refresh_ = true; force_full_refresh_ = true;
partial_refresh_count_ = 0; partial_refresh_count_ = 0;
ESP_LOGI(TAG, "Full refresh requested"); ESP_LOGV(TAG, "Full refresh requested");
} else { } else {
ESP_LOGE(TAG, "Failed to take refresh mutex to request full refresh"); ESP_LOGE(TAG, "Failed to take refresh mutex to request full refresh");
} }
@@ -506,13 +506,13 @@ esp_err_t EInkDisplayHandler::init_devices(EventGroupHandle_t system_event_group
if (system_event_group != nullptr) { if (system_event_group != nullptr) {
// Indicate that display is ready // Indicate that display is ready
xEventGroupSetBits(system_event_group, DISPLAY_READY_BIT | TOUCH_CALIBRATED_BIT); xEventGroupSetBits(system_event_group, DISPLAY_READY_BIT | TOUCH_CALIBRATED_BIT);
ESP_LOGI(TAG, "Display marked as ready"); ESP_LOGV(TAG, "Display marked as ready");
} }
return ESP_OK; return ESP_OK;
} }
esp_err_t EInkDisplayHandler::init_display_pins_(void) { esp_err_t EInkDisplayHandler::init_display_pins_(void) {
ESP_LOGI(TAG, "Initializing E-Ink display handler..."); ESP_LOGV(TAG, "Initializing E-Ink display handler...");
esp_err_t ret; esp_err_t ret;
@@ -544,7 +544,7 @@ esp_err_t EInkDisplayHandler::init_display_pins_(void) {
} }
esp_err_t EInkDisplayHandler::epd_init_internal_(uint32_t transaction_id) { esp_err_t EInkDisplayHandler::epd_init_internal_(uint32_t transaction_id) {
ESP_LOGI(TAG, "Initializing EPD..."); ESP_LOGV(TAG, "Initializing EPD...");
esp_err_t err; esp_err_t err;
@@ -585,8 +585,8 @@ esp_err_t EInkDisplayHandler::epd_init_internal_(uint32_t transaction_id) {
vTaskDelay(pdMS_TO_TICKS(MINIMUM_POWER_ON_DELAY_MS)); // Wait for power on vTaskDelay(pdMS_TO_TICKS(MINIMUM_POWER_ON_DELAY_MS)); // Wait for power on
// Check BUSY pin with detailed logging // Check BUSY pin with detailed logging
ESP_LOGI(TAG, "Waiting for EPD to be ready after power on..."); ESP_LOGV(TAG, "Waiting for EPD to be ready after power on...");
ESP_LOGI(TAG, "BUSY pin level after power on: %d (0=BUSY, 1=FREE)", gpio_get_level(PIN_BUSY)); ESP_LOGV(TAG, "BUSY pin level after power on: %d (0=BUSY, 1=FREE)", gpio_get_level(PIN_BUSY));
epd_handler_.wait_for_idle(); epd_handler_.wait_for_idle();
std::vector<uint8_t> booster_data = { 0x27, 0x27, 0x18, 0x17 }; std::vector<uint8_t> booster_data = { 0x27, 0x27, 0x18, 0x17 };
@@ -618,7 +618,7 @@ esp_err_t EInkDisplayHandler::epd_init_internal_(uint32_t transaction_id) {
// Internal version that uses an existing transaction (no separate TransactionGuard) // Internal version that uses an existing transaction (no separate TransactionGuard)
esp_err_t EInkDisplayHandler::epd_init_partial_internal_(uint32_t transaction_id) { esp_err_t EInkDisplayHandler::epd_init_partial_internal_(uint32_t transaction_id) {
ESP_LOGI(TAG, "Initializing EPD for partial refresh (internal)..."); ESP_LOGV(TAG, "Initializing EPD for partial refresh (internal)...");
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
// 1. Hardware Reset // 1. Hardware Reset
@@ -676,12 +676,12 @@ esp_err_t EInkDisplayHandler::epd_init_partial_internal_(uint32_t transaction_id
return err; return err;
} }
ESP_LOGI(TAG, "EPD partial init (internal) complete"); ESP_LOGV(TAG, "EPD partial init (internal) complete");
return ESP_OK; return ESP_OK;
} }
esp_err_t EInkDisplayHandler::init_touch_() { esp_err_t EInkDisplayHandler::init_touch_() {
ESP_LOGI(TAG, "Initializing touch..."); ESP_LOGV(TAG, "Initializing touch...");
esp_err_t err; esp_err_t err;
// 1. Initialize I2C Bus // 1. Initialize I2C Bus
@@ -703,10 +703,10 @@ esp_err_t EInkDisplayHandler::init_touch_() {
ESP_LOGE(TAG, "Failed to install I2C driver: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to install I2C driver: %s", esp_err_to_name(err));
return err; return err;
} }
ESP_LOGI("DisplayHandler", "I2C driver installed"); ESP_LOGV("DisplayHandler", "I2C driver installed");
// 2. Initialize GT911 // 2. Initialize GT911
ESP_LOGI("DisplayHandler", "Initializing GT911 touch controller..."); ESP_LOGV("DisplayHandler", "Initializing GT911 touch controller...");
esp_lcd_panel_io_i2c_config_t tp_io_config = {}; esp_lcd_panel_io_i2c_config_t tp_io_config = {};
// temporarily disable -Wmissing-field-initializers, as ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG macro does not set all fields // temporarily disable -Wmissing-field-initializers, as ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG macro does not set all fields
#pragma GCC diagnostic push #pragma GCC diagnostic push
@@ -734,16 +734,16 @@ esp_err_t EInkDisplayHandler::init_touch_() {
err = esp_lcd_touch_new_i2c_gt911(tp_io_handle_, &tp_cfg, &tp_handle_); err = esp_lcd_touch_new_i2c_gt911(tp_io_handle_, &tp_cfg, &tp_handle_);
if (err == ESP_OK && tp_handle_ != nullptr) { if (err == ESP_OK && tp_handle_ != nullptr) {
ESP_LOGI("DisplayHandler", "GT911 touch controller initialized successfully"); ESP_LOGV(TAG, "GT911 touch controller initialized successfully");
} else { } else {
ESP_LOGE("DisplayHandler", "GT911 touch controller initialization failed: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "GT911 touch controller initialization failed: %s", esp_err_to_name(err));
tp_handle_ = nullptr; tp_handle_ = nullptr;
} }
return err; return err;
} }
esp_err_t EInkDisplayHandler::refresh_old_buffer_(uint32_t transaction_id) { esp_err_t EInkDisplayHandler::refresh_old_buffer_(uint32_t transaction_id) {
ESP_LOGI(TAG, "Refreshing display SRAM to restore state after wake..."); ESP_LOGV(TAG, "Refreshing display SRAM to restore state after wake...");
esp_err_t err; esp_err_t err;
err = epd_handler_.epd_write_cmd(0x92, transaction_id); // enter normal mode err = epd_handler_.epd_write_cmd(0x92, transaction_id); // enter normal mode
@@ -784,6 +784,6 @@ esp_err_t EInkDisplayHandler::refresh_old_buffer_(uint32_t transaction_id) {
return err; return err;
} }
ESP_LOGI(TAG, "Display SRAM restored successfully"); ESP_LOGV(TAG, "Display SRAM restored successfully");
return ESP_OK; return ESP_OK;
} }

View File

@@ -73,23 +73,23 @@ bool EPDHandler::is_busy(void) const {
return gpio_get_level(PIN_BUSY) != BUSY_ACTIVE_LEVEL; // BUSY is active LOW return gpio_get_level(PIN_BUSY) != BUSY_ACTIVE_LEVEL; // BUSY is active LOW
} }
void EPDHandler::wait_for_idle(void) const { void EPDHandler::wait_for_idle(void) const {
ESP_LOGI(TAG, "Waiting for display ready (BUSY pin)..."); ESP_LOGV(TAG, "Waiting for display ready (BUSY pin)...");
int initial_level = gpio_get_level(PIN_BUSY); int initial_level = gpio_get_level(PIN_BUSY);
ESP_LOGI(TAG, "Initial BUSY pin level: %d (0=BUSY, 1=FREE)", initial_level); ESP_LOGV(TAG, "Initial BUSY pin level: %d (0=BUSY, 1=FREE)", initial_level);
// If already free, no need to wait // If already free, no need to wait
if (initial_level == BUSY_INACTIVE_LEVEL) { if (initial_level == BUSY_INACTIVE_LEVEL) {
ESP_LOGI(TAG, "Display already ready (BUSY pin = 1)"); ESP_LOGV(TAG, "Display already ready (BUSY pin = 1)");
return; return;
} }
while (gpio_get_level(PIN_BUSY) != BUSY_INACTIVE_LEVEL) { while (gpio_get_level(PIN_BUSY) != BUSY_INACTIVE_LEVEL) {
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
} }
ESP_LOGI(TAG, "Display is now ready (BUSY pin = 1)"); ESP_LOGV(TAG, "Display is now ready (BUSY pin = 1)");
} }
esp_err_t EPDHandler::epd_write_cmd(const uint8_t cmd, uint32_t transaction_id) { esp_err_t EPDHandler::epd_write_cmd(const uint8_t cmd, uint32_t transaction_id) {
ESP_LOGI(TAG, "epd_write_cmd: waiting to send 0x%02X", cmd); ESP_LOGV(TAG, "epd_write_cmd: waiting to send 0x%02X", cmd);
SemaphoreGuard transaction_guard(spi_transaction_mutex_); SemaphoreGuard transaction_guard(spi_transaction_mutex_);
esp_err_t err = esp_err_t err =
@@ -106,12 +106,12 @@ esp_err_t EPDHandler::epd_write_cmd(const uint8_t cmd, uint32_t transaction_id)
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
err = dangerous_epd_write_cmd_without_lock_(cmd); err = dangerous_epd_write_cmd_without_lock_(cmd);
ESP_LOGI(TAG, "epd_write_cmd: 0x%02X done", cmd); ESP_LOGV(TAG, "epd_write_cmd: 0x%02X done", cmd);
return err; return err;
} }
esp_err_t EPDHandler::epd_write_data(const uint8_t data, uint32_t transaction_id) { esp_err_t EPDHandler::epd_write_data(const uint8_t data, uint32_t transaction_id) {
ESP_LOGI(TAG, "epd_write_data: waiting to send 0x%02X", data); ESP_LOGV(TAG, "epd_write_data: waiting to send 0x%02X", data);
SemaphoreGuard transaction_guard(spi_transaction_mutex_); SemaphoreGuard transaction_guard(spi_transaction_mutex_);
esp_err_t err = esp_err_t err =
wait_for_transaction_end_(pdMS_TO_TICKS(5000), transaction_id, transaction_guard); wait_for_transaction_end_(pdMS_TO_TICKS(5000), transaction_id, transaction_guard);
@@ -126,13 +126,13 @@ esp_err_t EPDHandler::epd_write_data(const uint8_t data, uint32_t transaction_id
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
err = dangerous_epd_write_data_without_lock_(data); err = dangerous_epd_write_data_without_lock_(data);
ESP_LOGI(TAG, "epd_write_data: 0x%02X done", data); ESP_LOGV(TAG, "epd_write_data: 0x%02X done", data);
return err; return err;
} }
esp_err_t EPDHandler::epd_write_cmd_with_data(const uint8_t cmd, std::vector<uint8_t>& data, uint32_t transaction_id) { esp_err_t EPDHandler::epd_write_cmd_with_data(const uint8_t cmd, std::vector<uint8_t>& data, uint32_t transaction_id) {
const size_t data_len = data.size(); const size_t data_len = data.size();
ESP_LOGI(TAG, "epd_write_cmd_with_data: waiting to send cmd 0x%02X with %u bytes of data", cmd, data_len); ESP_LOGV(TAG, "epd_write_cmd_with_data: waiting to send cmd 0x%02X with %u bytes of data", cmd, data_len);
SemaphoreGuard transaction_guard(spi_transaction_mutex_); SemaphoreGuard transaction_guard(spi_transaction_mutex_);
esp_err_t err = esp_err_t err =
@@ -158,13 +158,13 @@ esp_err_t EPDHandler::epd_write_cmd_with_data(const uint8_t cmd, std::vector<uin
return err; return err;
} }
} }
ESP_LOGI(TAG, "epd_write_cmd_with_data: cmd 0x%02X with %u bytes of data done", cmd, data_len); ESP_LOGV(TAG, "epd_write_cmd_with_data: cmd 0x%02X with %u bytes of data done", cmd, data_len);
return ESP_OK; return ESP_OK;
} }
esp_err_t EPDHandler::dangerous_epd_write_cmd_without_lock_(const uint8_t cmd) { esp_err_t EPDHandler::dangerous_epd_write_cmd_without_lock_(const uint8_t cmd) {
ESP_LOGI(TAG, "dangerous_epd_write_cmd_without_lock_: sending 0x%02X", cmd); ESP_LOGV(TAG, "dangerous_epd_write_cmd_without_lock_: sending 0x%02X", cmd);
gpio_set_level(PIN_DC, 0); // Command mode gpio_set_level(PIN_DC, 0); // Command mode
spi_transaction_t t {}; spi_transaction_t t {};
t.length = 8;t.tx_buffer = &cmd; t.length = 8;t.tx_buffer = &cmd;
@@ -172,13 +172,13 @@ esp_err_t EPDHandler::dangerous_epd_write_cmd_without_lock_(const uint8_t cmd) {
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send data 0x%02X", cmd); ESP_LOGE(TAG, "Failed to send data 0x%02X", cmd);
} else { } else {
ESP_LOGI(TAG, "dangerous_epd_write_cmd_without_lock_: 0x%02X sent", cmd); ESP_LOGV(TAG, "dangerous_epd_write_cmd_without_lock_: 0x%02X sent", cmd);
} }
return err; return err;
} }
esp_err_t EPDHandler::dangerous_epd_write_data_without_lock_(const uint8_t data) { esp_err_t EPDHandler::dangerous_epd_write_data_without_lock_(const uint8_t data) {
ESP_LOGI(TAG, "dangerous_epd_write_data_without_lock_: sending 0x%02X", data); ESP_LOGV(TAG, "dangerous_epd_write_data_without_lock_: sending 0x%02X", data);
gpio_set_level(PIN_DC, 1); // Data mode gpio_set_level(PIN_DC, 1); // Data mode
spi_transaction_t t = { }; spi_transaction_t t = { };
t.length = 8; t.tx_buffer = &data; t.length = 8; t.tx_buffer = &data;
@@ -186,13 +186,13 @@ esp_err_t EPDHandler::dangerous_epd_write_data_without_lock_(const uint8_t data)
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send data 0x%02X", data); ESP_LOGE(TAG, "Failed to send data 0x%02X", data);
} else { } else {
ESP_LOGI(TAG, "dangerous_epd_write_data_without_lock_: 0x%02X sent", data); ESP_LOGV(TAG, "dangerous_epd_write_data_without_lock_: 0x%02X sent", data);
} }
return err; return err;
} }
esp_err_t EPDHandler::transfer_spi_data(const uint8_t* data, const size_t& length, uint32_t transaction_id, bool inverted) { esp_err_t EPDHandler::transfer_spi_data(const uint8_t* data, const size_t& length, uint32_t transaction_id, bool inverted) {
ESP_LOGI(TAG, "transfer_spi_data: waiting to send %zu bytes of data", length); ESP_LOGV(TAG, "transfer_spi_data: waiting to send %zu bytes of data", length);
SemaphoreGuard transaction_guard(spi_transaction_mutex_); SemaphoreGuard transaction_guard(spi_transaction_mutex_);
esp_err_t err = esp_err_t err =
@@ -207,7 +207,7 @@ esp_err_t EPDHandler::transfer_spi_data(const uint8_t* data, const size_t& lengt
ESP_LOGE(TAG, "SPI mutex timeout for data transfer of %zu bytes", length); ESP_LOGE(TAG, "SPI mutex timeout for data transfer of %zu bytes", length);
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
ESP_LOGI(TAG, "transfer_spi_data: starting to send %zu bytes of data", length); ESP_LOGV(TAG, "transfer_spi_data: starting to send %zu bytes of data", length);
size_t offset = 0; size_t offset = 0;
size_t remaining = length; size_t remaining = length;
@@ -264,7 +264,7 @@ esp_err_t EPDHandler::transfer_spi_data(const uint8_t* data, const size_t& lengt
// Yield every 16KB to prevent watchdog timeout // Yield every 16KB to prevent watchdog timeout
if (offset % (16 * 1024) == 0) { if (offset % (16 * 1024) == 0) {
ESP_LOGI(TAG, "New data progress: %zu/%zu bytes sent, yielding...", offset, length); ESP_LOGV(TAG, "New data progress: %zu/%zu bytes sent, yielding...", offset, length);
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
} }
} }
@@ -274,30 +274,30 @@ esp_err_t EPDHandler::transfer_spi_data(const uint8_t* data, const size_t& lengt
heap_caps_free(temp_transfer_buffer); heap_caps_free(temp_transfer_buffer);
} }
ESP_LOGI(TAG, "transfer_spi_data: completed sending %zu bytes of data", length); ESP_LOGV(TAG, "transfer_spi_data: completed sending %zu bytes of data", length);
return ESP_OK; return ESP_OK;
} }
esp_err_t EPDHandler::begin_transaction_(TickType_t timeout, uint32_t& out_id) { esp_err_t EPDHandler::begin_transaction_(TickType_t timeout, uint32_t& out_id) {
ESP_LOGI(TAG, "begin_transaction_: waiting to obtain transaction mutex"); ESP_LOGV(TAG, "begin_transaction_: waiting to obtain transaction mutex");
if (xSemaphoreTake(spi_transaction_mutex_, timeout) != pdTRUE) { if (xSemaphoreTake(spi_transaction_mutex_, timeout) != pdTRUE) {
ESP_LOGE(TAG, "begin_transaction_: transaction mutex timeout"); ESP_LOGE(TAG, "begin_transaction_: transaction mutex timeout");
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
out_id = ++spi_transaction_id; out_id = ++spi_transaction_id;
ESP_LOGI(TAG, "begin_transaction_: transaction mutex obtained"); ESP_LOGV(TAG, "begin_transaction_: transaction mutex obtained");
return ESP_OK; return ESP_OK;
} }
esp_err_t EPDHandler::end_transaction_(void) { esp_err_t EPDHandler::end_transaction_(void) {
ESP_LOGI(TAG, "end_transaction_: releasing transaction mutex"); ESP_LOGV(TAG, "end_transaction_: releasing transaction mutex");
if (xSemaphoreGive(spi_transaction_mutex_) != pdTRUE) { if (xSemaphoreGive(spi_transaction_mutex_) != pdTRUE) {
ESP_LOGE(TAG, "end_transaction_: failed to release transaction mutex"); ESP_LOGE(TAG, "end_transaction_: failed to release transaction mutex");
return ESP_FAIL; return ESP_FAIL;
} }
ESP_LOGI(TAG, "end_transaction_: transaction mutex released"); ESP_LOGV(TAG, "end_transaction_: transaction mutex released");
return ESP_OK; return ESP_OK;
} }

View File

@@ -137,6 +137,7 @@ void IotDisBridge::poll_task_(void* param) {
while (!bridge->stop_polling_) { while (!bridge->stop_polling_) {
ESP_LOGI(TAG, "Polling for status update..."); ESP_LOGI(TAG, "Polling for status update...");
bridge->poll_status_(); bridge->poll_status_();
ESP_LOGI(TAG, "poll_status_() returned");
// Yield to allow display updates to complete // Yield to allow display updates to complete
taskYIELD(); taskYIELD();
@@ -150,6 +151,7 @@ void IotDisBridge::poll_task_(void* param) {
} }
ESP_LOGI(TAG, "Polling task stopped"); ESP_LOGI(TAG, "Polling task stopped");
bridge->poll_task_handle_ = nullptr;
vTaskDelete(nullptr); vTaskDelete(nullptr);
} }
@@ -176,38 +178,28 @@ void IotDisBridge::poll_status_() {
event_data.state = StatusUpdateEventData::VoiceState::UNMUTED; event_data.state = StatusUpdateEventData::VoiceState::UNMUTED;
} }
// Reset failure counter on successful push update
if (event_data.state != StatusUpdateEventData::VoiceState::UNKNOWN) {
consecutive_failures_ = 0;
}
if (on_status_update_callback_) { if (on_status_update_callback_) {
on_status_update_callback_(event_data, status_event_user_data_); on_status_update_callback_(event_data, status_event_user_data_);
} }
// VoiceState new_state = VoiceState::UNKNOWN; // Got push update, skip polling
// if (push_message == MUTED_RESPONSE) { if (event_data.state != StatusUpdateEventData::VoiceState::UNKNOWN) {
// new_state = VoiceState::MUTED; return;
// } else if (push_message == UNMUTED_RESPONSE) { }
// new_state = VoiceState::UNMUTED;
// }
// if (new_state != VoiceState::UNKNOWN) {
// consecutive_failures_ = 0;
// if (main_ui_ && current_page_ == Page::MAIN) {
// main_ui_->show_error_notification(false);
// }
// if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) {
// current_state_ = new_state;
// xSemaphoreGive(state_mutex_);
// }
// update_main_ui();
// return; // Got push update, skip polling
// }
} }
// Send STATUS command for polling // Send STATUS command for polling
ESP_LOGI(TAG, "Sending STATUS command for polling");
err = udp_client_.send_command(STATUS_COMMAND); err = udp_client_.send_command(STATUS_COMMAND);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to send STATUS command");
consecutive_failures_++; consecutive_failures_++;
ESP_LOGW(TAG, "Failed to send STATUS command for polling. Consecutive failures: %d",
consecutive_failures_);
if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) { if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) {
if (on_status_update_callback_) { if (on_status_update_callback_) {
@@ -221,12 +213,15 @@ void IotDisBridge::poll_status_() {
} }
// Wait for response to STATUS command // Wait for response to STATUS command
ESP_LOGI(TAG, "Waiting for response to STATUS command (timeout: %dms)", RESPONSE_TIMEOUT_MS);
std::string response; std::string response;
err = udp_client_.receive_response(response, RESPONSE_TIMEOUT_MS); err = udp_client_.receive_response(response, RESPONSE_TIMEOUT_MS);
ESP_LOGI(TAG, "Received response from STATUS command: err=%d", err);
if (err == ESP_OK) { if (err == ESP_OK) {
// Success - reset failure counter // Success - reset failure counter
consecutive_failures_ = 0; consecutive_failures_ = 0;
ESP_LOGI(TAG, "STATUS response: %s", response.c_str());
StatusUpdateEventData event_data { StatusUpdateEventData event_data {
.state = StatusUpdateEventData::VoiceState::UNKNOWN .state = StatusUpdateEventData::VoiceState::UNKNOWN
@@ -236,15 +231,19 @@ void IotDisBridge::poll_status_() {
} else if (response == UNMUTED_RESPONSE) { } else if (response == UNMUTED_RESPONSE) {
event_data.state = StatusUpdateEventData::VoiceState::UNMUTED; event_data.state = StatusUpdateEventData::VoiceState::UNMUTED;
} }
ESP_LOGI(TAG, "Invoking status update callback with state: %d", event_data.state);
if (on_status_update_callback_) { if (on_status_update_callback_) {
on_status_update_callback_(event_data, status_event_user_data_); on_status_update_callback_(event_data, status_event_user_data_);
ESP_LOGI(TAG, "Status update callback returned");
} }
} else { } else {
// Timeout or error // Timeout or error
consecutive_failures_++; consecutive_failures_++;
ESP_LOGW(TAG, "No response to STATUS (failures: %d)", consecutive_failures_); ESP_LOGW(TAG, "No response to STATUS (failures: %d, error: %d)", consecutive_failures_, err);
if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) { if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) {
ESP_LOGW(TAG, "Max failures reached, sending ERROR state");
if (on_status_update_callback_) { if (on_status_update_callback_) {
on_status_update_callback_( on_status_update_callback_(
StatusUpdateEventData { .state = StatusUpdateEventData::VoiceState::ERROR }, StatusUpdateEventData { .state = StatusUpdateEventData::VoiceState::ERROR },
@@ -253,4 +252,6 @@ void IotDisBridge::poll_status_() {
} }
} }
} }
ESP_LOGI(TAG, "poll_status_ complete");
} }

View File

@@ -45,8 +45,8 @@ public:
} }
private: private:
static constexpr int POLL_INTERVAL_MS = 2000; static constexpr int POLL_INTERVAL_MS = 10000;
static constexpr int ERROR_POLL_INTERVAL_MS = 5000; static constexpr int ERROR_POLL_INTERVAL_MS = 20000;
static constexpr int RESPONSE_TIMEOUT_MS = 1000; static constexpr int RESPONSE_TIMEOUT_MS = 1000;
static constexpr int MAX_FAILURES_BEFORE_ERROR = 3; static constexpr int MAX_FAILURES_BEFORE_ERROR = 3;

View File

@@ -2,6 +2,7 @@
#include "ui/apps/iotdis/app.h" #include "ui/apps/iotdis/app.h"
#include "ui/interaction_handler.h" #include "ui/interaction_handler.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_lvgl_port.h"
static const char* TAG = "MainUI"; static const char* TAG = "MainUI";
@@ -28,6 +29,15 @@ esp_err_t MainUI::deinit(void) {
} }
void MainUI::create_ui_(lv_obj_t* parent) { void MainUI::create_ui_(lv_obj_t* parent) {
if (!parent) {
ESP_LOGE(TAG, "Parent LVGL object is null");
return;
}
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) {
ESP_LOGE(TAG, "Failed to acquire LVGL lock for UI creation");
return;
}
// Set up main page with flex column layout // Set up main page with flex column layout
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(parent, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_flex_align(parent, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
@@ -101,6 +111,7 @@ void MainUI::create_ui_(lv_obj_t* parent) {
lv_obj_center(settings_icon); lv_obj_center(settings_icon);
ESP_LOGI(TAG, "Main UI created"); ESP_LOGI(TAG, "Main UI created");
lvgl_port_unlock();
} }
esp_err_t MainUI::register_on_settings_button_clicked(lv_event_cb_t cb, void* user_data) { esp_err_t MainUI::register_on_settings_button_clicked(lv_event_cb_t cb, void* user_data) {
@@ -124,6 +135,11 @@ void MainUI::update_status(VoiceState state) {
return; return;
} }
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) {
ESP_LOGW(TAG, "Failed to acquire LVGL lock for status update");
return;
}
switch (state) { switch (state) {
case VoiceState::MUTED: case VoiceState::MUTED:
lv_label_set_text(status_icon_label_, LV_SYMBOL_MUTE); lv_label_set_text(status_icon_label_, LV_SYMBOL_MUTE);
@@ -150,24 +166,40 @@ void MainUI::update_status(VoiceState state) {
lv_obj_set_style_text_color(status_icon_label_, lv_color_black(), 0); lv_obj_set_style_text_color(status_icon_label_, lv_color_black(), 0);
break; break;
} }
lvgl_port_unlock();
} }
void MainUI::show_error_notification(bool show) { void MainUI::show_error_notification(bool show) {
if (error_notification_) { if (!error_notification_) {
return;
}
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) {
ESP_LOGW(TAG, "Failed to acquire LVGL lock for error notification update");
return;
}
if (show) { if (show) {
lv_obj_clear_flag(error_notification_, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(error_notification_, LV_OBJ_FLAG_HIDDEN);
} else { } else {
lv_obj_add_flag(error_notification_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(error_notification_, LV_OBJ_FLAG_HIDDEN);
} }
} lvgl_port_unlock();
} }
void MainUI::update_config_prompt(bool configured) { void MainUI::update_config_prompt(bool configured) {
if (config_prompt_) { if (!config_prompt_) {
return;
}
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) {
ESP_LOGW(TAG, "Failed to acquire LVGL lock for config prompt update");
return;
}
if (configured) { if (configured) {
lv_obj_add_flag(config_prompt_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(config_prompt_, LV_OBJ_FLAG_HIDDEN);
} else { } else {
lv_obj_clear_flag(config_prompt_, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(config_prompt_, LV_OBJ_FLAG_HIDDEN);
} }
} lvgl_port_unlock();
} }

View File

@@ -106,17 +106,24 @@ void MainUIHandler::send_mute_command_() {
} }
void MainUIHandler::on_status_update_(StatusUpdateEventData data) { void MainUIHandler::on_status_update_(StatusUpdateEventData data) {
ESP_LOGI(TAG, "on_status_update_ called with state: %d", data.state);
// Update state in thread-safe manner // Update state in thread-safe manner
if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) { if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) {
current_state_ = data.state; current_state_ = data.state;
xSemaphoreGive(state_mutex_); xSemaphoreGive(state_mutex_);
ESP_LOGI(TAG, "State updated in mutex");
} }
// Update UI // Update UI
ESP_LOGI(TAG, "Calling update_ui_()");
update_ui_(); update_ui_();
ESP_LOGI(TAG, "on_status_update_ complete");
} }
void MainUIHandler::update_ui_() { void MainUIHandler::update_ui_() {
ESP_LOGI(TAG, "update_ui_ called");
if (main_ui_) { if (main_ui_) {
StatusUpdateEventData::VoiceState state; StatusUpdateEventData::VoiceState state;
if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) { if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) {
@@ -126,6 +133,8 @@ void MainUIHandler::update_ui_() {
state = StatusUpdateEventData::VoiceState::UNKNOWN; state = StatusUpdateEventData::VoiceState::UNKNOWN;
} }
ESP_LOGI(TAG, "Converting state: %d", state);
// Convert to MainUI VoiceState // Convert to MainUI VoiceState
VoiceState ui_state; VoiceState ui_state;
switch (state) { switch (state) {
@@ -143,8 +152,14 @@ void MainUIHandler::update_ui_() {
break; break;
} }
ESP_LOGI(TAG, "Calling main_ui_->update_status() with ui_state: %d", ui_state);
// Lock LVGL before calling UI functions from another task
main_ui_->update_status(ui_state); main_ui_->update_status(ui_state);
ESP_LOGI(TAG, "main_ui_->update_status() returned");
} }
ESP_LOGI(TAG, "update_ui_ complete");
} }
// ============================================================================ // ============================================================================