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_LOGI(TAG, "Putting display into deep sleep mode...");
ESP_LOGV(TAG, "Putting display into deep sleep mode...");
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;
}
{
@@ -111,7 +111,7 @@ esp_err_t EInkDisplayHandler::refresh_display() {
} else {
// 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_);
err = transaction_guard.begin(pdMS_TO_TICKS(10000));
if (err != ESP_OK) {
@@ -123,7 +123,7 @@ esp_err_t EInkDisplayHandler::refresh_display() {
}
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
if (err != ESP_OK) {
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;
}
ESP_LOGI(TAG, "Refresh complete");
ESP_LOGV(TAG, "Refresh complete");
return ESP_OK;
}
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;
@@ -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
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
epd_handler_.wait_for_idle();
@@ -229,13 +229,13 @@ esp_err_t EInkDisplayHandler::full_write(const uint8_t* framebuffer, const bool
refresh_area_.reset();
memcpy(old_buffer_, draw_buffer_, DISPLAY_BUFFER_SIZE);
ESP_LOGI(TAG, "Full refresh complete");
ESP_LOGV(TAG, "Full refresh complete");
return ESP_OK;
}
// 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_LOGI(TAG, "Starting partial refresh (0.3 seconds)...");
ESP_LOGV(TAG, "Starting partial refresh (0.3 seconds)...");
esp_err_t err = ESP_OK;
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);
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;
}
@@ -273,7 +273,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
RefreshArea area = refresh_area_;
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_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;
}
@@ -348,9 +348,9 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
static_cast<uint8_t>(area.y2 & 0xFF),
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);
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[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
@@ -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
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);
err = epd_handler_.transfer_spi_data(partial_buffer, partial_buffer_size, transaction_guard.transaction_id(), true); // Inverted for partial refresh
if (err != ESP_OK) {
@@ -403,7 +403,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
return err;
}
}
ESP_LOGI(TAG, "Partial refresh complete");
ESP_LOGV(TAG, "Partial refresh complete");
err = deep_sleep_display();
if (err != ESP_OK) {
@@ -412,7 +412,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
}
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();
if (err != ESP_OK) {
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_++;
}
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);
force_full_refresh_ = true;
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_LOGI(TAG, "Clearing display to all white...");
ESP_LOGV(TAG, "Clearing display to all white...");
esp_err_t err = full_write(white_data, false);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear display: %s", esp_err_to_name(err));
return err;
}
ESP_LOGI(TAG, "Display cleared to all white");
ESP_LOGV(TAG, "Display cleared to all white");
return ESP_OK;
}
@@ -478,7 +478,7 @@ void EInkDisplayHandler::request_full_refresh(void) {
if (guard.take(pdMS_TO_TICKS(100))) {
force_full_refresh_ = true;
partial_refresh_count_ = 0;
ESP_LOGI(TAG, "Full refresh requested");
ESP_LOGV(TAG, "Full refresh requested");
} else {
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) {
// Indicate that display is ready
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;
}
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;
@@ -544,7 +544,7 @@ esp_err_t EInkDisplayHandler::init_display_pins_(void) {
}
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;
@@ -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
// Check BUSY pin with detailed logging
ESP_LOGI(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, "Waiting for EPD to be ready after power on...");
ESP_LOGV(TAG, "BUSY pin level after power on: %d (0=BUSY, 1=FREE)", gpio_get_level(PIN_BUSY));
epd_handler_.wait_for_idle();
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)
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;
// 1. Hardware Reset
@@ -676,12 +676,12 @@ esp_err_t EInkDisplayHandler::epd_init_partial_internal_(uint32_t transaction_id
return err;
}
ESP_LOGI(TAG, "EPD partial init (internal) complete");
ESP_LOGV(TAG, "EPD partial init (internal) complete");
return ESP_OK;
}
esp_err_t EInkDisplayHandler::init_touch_() {
ESP_LOGI(TAG, "Initializing touch...");
ESP_LOGV(TAG, "Initializing touch...");
esp_err_t err;
// 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));
return err;
}
ESP_LOGI("DisplayHandler", "I2C driver installed");
ESP_LOGV("DisplayHandler", "I2C driver installed");
// 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 = {};
// temporarily disable -Wmissing-field-initializers, as ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG macro does not set all fields
#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_);
if (err == ESP_OK && tp_handle_ != nullptr) {
ESP_LOGI("DisplayHandler", "GT911 touch controller initialized successfully");
ESP_LOGV(TAG, "GT911 touch controller initialized successfully");
} 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;
}
return err;
}
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;
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;
}
ESP_LOGI(TAG, "Display SRAM restored successfully");
ESP_LOGV(TAG, "Display SRAM restored successfully");
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
}
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);
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 (initial_level == BUSY_INACTIVE_LEVEL) {
ESP_LOGI(TAG, "Display already ready (BUSY pin = 1)");
ESP_LOGV(TAG, "Display already ready (BUSY pin = 1)");
return;
}
while (gpio_get_level(PIN_BUSY) != BUSY_INACTIVE_LEVEL) {
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_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_);
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;
}
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;
}
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_);
esp_err_t err =
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;
}
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;
}
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();
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_);
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;
}
}
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;
}
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
spi_transaction_t t {};
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) {
ESP_LOGE(TAG, "Failed to send data 0x%02X", cmd);
} 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;
}
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
spi_transaction_t t = { };
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) {
ESP_LOGE(TAG, "Failed to send data 0x%02X", data);
} 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;
}
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_);
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);
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 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
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));
}
}
@@ -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);
}
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;
}
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) {
ESP_LOGE(TAG, "begin_transaction_: transaction mutex timeout");
return ESP_ERR_TIMEOUT;
}
out_id = ++spi_transaction_id;
ESP_LOGI(TAG, "begin_transaction_: transaction mutex obtained");
ESP_LOGV(TAG, "begin_transaction_: transaction mutex obtained");
return ESP_OK;
}
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) {
ESP_LOGE(TAG, "end_transaction_: failed to release transaction mutex");
return ESP_FAIL;
}
ESP_LOGI(TAG, "end_transaction_: transaction mutex released");
ESP_LOGV(TAG, "end_transaction_: transaction mutex released");
return ESP_OK;
}

View File

@@ -137,6 +137,7 @@ void IotDisBridge::poll_task_(void* param) {
while (!bridge->stop_polling_) {
ESP_LOGI(TAG, "Polling for status update...");
bridge->poll_status_();
ESP_LOGI(TAG, "poll_status_() returned");
// Yield to allow display updates to complete
taskYIELD();
@@ -150,6 +151,7 @@ void IotDisBridge::poll_task_(void* param) {
}
ESP_LOGI(TAG, "Polling task stopped");
bridge->poll_task_handle_ = nullptr;
vTaskDelete(nullptr);
}
@@ -176,38 +178,28 @@ void IotDisBridge::poll_status_() {
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_) {
on_status_update_callback_(event_data, status_event_user_data_);
}
// VoiceState new_state = VoiceState::UNKNOWN;
// if (push_message == MUTED_RESPONSE) {
// new_state = VoiceState::MUTED;
// } 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
// }
// Got push update, skip polling
if (event_data.state != StatusUpdateEventData::VoiceState::UNKNOWN) {
return;
}
}
// Send STATUS command for polling
ESP_LOGI(TAG, "Sending STATUS command for polling");
err = udp_client_.send_command(STATUS_COMMAND);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to send STATUS command");
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 (on_status_update_callback_) {
@@ -221,12 +213,15 @@ void IotDisBridge::poll_status_() {
}
// Wait for response to STATUS command
ESP_LOGI(TAG, "Waiting for response to STATUS command (timeout: %dms)", RESPONSE_TIMEOUT_MS);
std::string response;
err = udp_client_.receive_response(response, RESPONSE_TIMEOUT_MS);
ESP_LOGI(TAG, "Received response from STATUS command: err=%d", err);
if (err == ESP_OK) {
// Success - reset failure counter
consecutive_failures_ = 0;
ESP_LOGI(TAG, "STATUS response: %s", response.c_str());
StatusUpdateEventData event_data {
.state = StatusUpdateEventData::VoiceState::UNKNOWN
@@ -236,15 +231,19 @@ void IotDisBridge::poll_status_() {
} else if (response == UNMUTED_RESPONSE) {
event_data.state = StatusUpdateEventData::VoiceState::UNMUTED;
}
ESP_LOGI(TAG, "Invoking status update callback with state: %d", event_data.state);
if (on_status_update_callback_) {
on_status_update_callback_(event_data, status_event_user_data_);
ESP_LOGI(TAG, "Status update callback returned");
}
} else {
// Timeout or error
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) {
ESP_LOGW(TAG, "Max failures reached, sending ERROR state");
if (on_status_update_callback_) {
on_status_update_callback_(
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:
static constexpr int POLL_INTERVAL_MS = 2000;
static constexpr int ERROR_POLL_INTERVAL_MS = 5000;
static constexpr int POLL_INTERVAL_MS = 10000;
static constexpr int ERROR_POLL_INTERVAL_MS = 20000;
static constexpr int RESPONSE_TIMEOUT_MS = 1000;
static constexpr int MAX_FAILURES_BEFORE_ERROR = 3;

View File

@@ -2,6 +2,7 @@
#include "ui/apps/iotdis/app.h"
#include "ui/interaction_handler.h"
#include "esp_log.h"
#include "esp_lvgl_port.h"
static const char* TAG = "MainUI";
@@ -28,6 +29,15 @@ esp_err_t MainUI::deinit(void) {
}
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
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);
@@ -101,6 +111,7 @@ void MainUI::create_ui_(lv_obj_t* parent) {
lv_obj_center(settings_icon);
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) {
@@ -124,6 +135,11 @@ void MainUI::update_status(VoiceState state) {
return;
}
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) {
ESP_LOGW(TAG, "Failed to acquire LVGL lock for status update");
return;
}
switch (state) {
case VoiceState::MUTED:
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);
break;
}
lvgl_port_unlock();
}
void MainUI::show_error_notification(bool show) {
if (error_notification_) {
if (show) {
lv_obj_clear_flag(error_notification_, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(error_notification_, LV_OBJ_FLAG_HIDDEN);
}
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) {
lv_obj_clear_flag(error_notification_, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(error_notification_, LV_OBJ_FLAG_HIDDEN);
}
lvgl_port_unlock();
}
void MainUI::update_config_prompt(bool configured) {
if (config_prompt_) {
if (configured) {
lv_obj_add_flag(config_prompt_, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_clear_flag(config_prompt_, LV_OBJ_FLAG_HIDDEN);
}
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) {
lv_obj_add_flag(config_prompt_, LV_OBJ_FLAG_HIDDEN);
} else {
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) {
ESP_LOGI(TAG, "on_status_update_ called with state: %d", data.state);
// Update state in thread-safe manner
if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) {
current_state_ = data.state;
xSemaphoreGive(state_mutex_);
ESP_LOGI(TAG, "State updated in mutex");
}
// Update UI
ESP_LOGI(TAG, "Calling update_ui_()");
update_ui_();
ESP_LOGI(TAG, "on_status_update_ complete");
}
void MainUIHandler::update_ui_() {
ESP_LOGI(TAG, "update_ui_ called");
if (main_ui_) {
StatusUpdateEventData::VoiceState state;
if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) {
@@ -126,6 +133,8 @@ void MainUIHandler::update_ui_() {
state = StatusUpdateEventData::VoiceState::UNKNOWN;
}
ESP_LOGI(TAG, "Converting state: %d", state);
// Convert to MainUI VoiceState
VoiceState ui_state;
switch (state) {
@@ -143,8 +152,14 @@ void MainUIHandler::update_ui_() {
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);
ESP_LOGI(TAG, "main_ui_->update_status() returned");
}
ESP_LOGI(TAG, "update_ui_ complete");
}
// ============================================================================