[nextion] Fix unbounded queue growth and OOM crash when display sends no data (#17553)

This commit is contained in:
Keith Burzinski
2026-07-14 11:56:03 +12:00
committed by GitHub
parent f1e4726f4e
commit ca77cc585c
2 changed files with 28 additions and 8 deletions
+22 -6
View File
@@ -1,6 +1,7 @@
#include "nextion.h"
#include <cinttypes>
#include <new>
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
@@ -354,6 +355,7 @@ void Nextion::loop() {
this->process_serial_(); // Receive serial data
this->process_nextion_commands_(); // Process nextion return commands
this->purge_stale_queue_entries_(); // Drop expired entries even when the display sends no data
if (!this->connection_state_.nextion_reports_is_setup_) {
if (this->started_ms_ == 0)
@@ -902,6 +904,11 @@ void Nextion::process_nextion_commands_() {
this->command_data_.erase(0, to_process_length + DELIMITER_SIZE + 1);
}
ESP_LOGN(TAG, "Loop end");
this->process_serial_();
} // Nextion::process_nextion_commands_()
void Nextion::purge_stale_queue_entries_() {
const uint32_t ms = App.get_loop_component_start_time();
if (this->max_q_age_ms_ > 0 && !this->nextion_queue_.empty() &&
@@ -927,10 +934,7 @@ void Nextion::process_nextion_commands_() {
}
}
}
ESP_LOGN(TAG, "Loop end");
// App.feed_wdt(); Remove before master merge
this->process_serial_();
} // Nextion::process_nextion_commands_()
}
void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) {
this->set_nextion_sensor_state(static_cast<NextionQueueType>(queue_type), name, state);
@@ -1101,7 +1105,13 @@ void Nextion::add_no_result_to_queue_(const std::string &variable_name) {
new (nextion_queue) nextion::NextionQueue();
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
nextion_queue->component = new nextion::NextionComponentBase;
nextion_queue->component = new (std::nothrow) nextion::NextionComponentBase;
if (nextion_queue->component == nullptr) {
ESP_LOGW(TAG, "Component alloc failed");
nextion_queue->~NextionQueue();
allocator.deallocate(nextion_queue, 1);
return;
}
nextion_queue->component->set_variable_name(variable_name);
nextion_queue->queue_time = App.get_loop_component_start_time();
@@ -1157,7 +1167,13 @@ void Nextion::add_no_result_to_queue_with_pending_command_(const std::string &va
}
new (nextion_queue) nextion::NextionQueue();
nextion_queue->component = new nextion::NextionComponentBase;
nextion_queue->component = new (std::nothrow) nextion::NextionComponentBase;
if (nextion_queue->component == nullptr) {
ESP_LOGW(TAG, "Component alloc failed");
nextion_queue->~NextionQueue();
allocator.deallocate(nextion_queue, 1);
return;
}
nextion_queue->component->set_variable_name(variable_name);
nextion_queue->queue_time = App.get_loop_component_start_time();
nextion_queue->pending_command = command; // Store command for retry
+4
View File
@@ -1486,6 +1486,10 @@ class Nextion final : public NextionBase, public PollingComponent, public uart::
void process_nextion_commands_();
void process_serial_();
/// Drop queue entries older than max_q_age_ms_. Called from loop() so it also runs when the
/// display sends no data at all (disconnected or asleep), which would otherwise grow the queue
/// without bound.
void purge_stale_queue_entries_();
uint16_t touch_sleep_timeout_ = 0;
uint8_t wake_up_page_ = 255;
#ifdef USE_NEXTION_CONF_START_UP_PAGE