[nextion] Adds a command pacer with command_spacing attribute (#7948)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Edward Firmo 2025-05-05 10:08:16 +02:00 committed by GitHub
parent 6f35d0ac88
commit 8bbc509b0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 120 additions and 21 deletions

View File

@ -7,28 +7,29 @@ from esphome.const import CONF_BACKGROUND_COLOR, CONF_FOREGROUND_COLOR, CONF_VIS
from . import CONF_NEXTION_ID, Nextion
CONF_VARIABLE_NAME = "variable_name"
CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch"
CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color"
CONF_COMMAND_SPACING = "command_spacing"
CONF_COMPONENT_NAME = "component_name"
CONF_WAVE_CHANNEL_ID = "wave_channel_id"
CONF_WAVE_MAX_VALUE = "wave_max_value"
CONF_PRECISION = "precision"
CONF_WAVEFORM_SEND_LAST_VALUE = "waveform_send_last_value"
CONF_TFT_URL = "tft_url"
CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start"
CONF_FONT_ID = "font_id"
CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow"
CONF_ON_PAGE = "on_page"
CONF_ON_SETUP = "on_setup"
CONF_ON_SLEEP = "on_sleep"
CONF_ON_WAKE = "on_wake"
CONF_ON_SETUP = "on_setup"
CONF_ON_PAGE = "on_page"
CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow"
CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout"
CONF_WAKE_UP_PAGE = "wake_up_page"
CONF_START_UP_PAGE = "start_up_page"
CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch"
CONF_WAVE_MAX_LENGTH = "wave_max_length"
CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color"
CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
CONF_FONT_ID = "font_id"
CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start"
CONF_PRECISION = "precision"
CONF_SKIP_CONNECTION_HANDSHAKE = "skip_connection_handshake"
CONF_START_UP_PAGE = "start_up_page"
CONF_TFT_URL = "tft_url"
CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout"
CONF_VARIABLE_NAME = "variable_name"
CONF_WAKE_UP_PAGE = "wake_up_page"
CONF_WAVE_CHANNEL_ID = "wave_channel_id"
CONF_WAVE_MAX_LENGTH = "wave_max_length"
CONF_WAVE_MAX_VALUE = "wave_max_value"
CONF_WAVEFORM_SEND_LAST_VALUE = "waveform_send_last_value"
def NextionName(value):

View File

@ -9,16 +9,17 @@ from esphome.const import (
CONF_ON_TOUCH,
CONF_TRIGGER_ID,
)
from esphome.core import CORE
from esphome.core import CORE, TimePeriod
from . import Nextion, nextion_ns, nextion_ref
from .base_component import (
CONF_AUTO_WAKE_ON_TOUCH,
CONF_COMMAND_SPACING,
CONF_EXIT_REPARSE_ON_START,
CONF_ON_BUFFER_OVERFLOW,
CONF_ON_PAGE,
CONF_ON_SETUP,
CONF_ON_SLEEP,
CONF_ON_PAGE,
CONF_ON_WAKE,
CONF_SKIP_CONNECTION_HANDSHAKE,
CONF_START_UP_PAGE,
@ -88,6 +89,10 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean,
cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean,
cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean,
cv.Optional(CONF_COMMAND_SPACING): cv.All(
cv.positive_time_period_milliseconds,
cv.Range(max=TimePeriod(milliseconds=255)),
),
}
)
.extend(cv.polling_component_schema("5s"))
@ -120,6 +125,10 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await uart.register_uart_device(var, config)
if command_spacing := config.get(CONF_COMMAND_SPACING):
cg.add_define("USE_NEXTION_COMMAND_SPACING")
cg.add(var.set_command_spacing(command_spacing.total_milliseconds))
if CONF_BRIGHTNESS in config:
cg.add(var.set_brightness(config[CONF_BRIGHTNESS]))

View File

@ -31,11 +31,22 @@ bool Nextion::send_command_(const std::string &command) {
return false;
}
#ifdef USE_NEXTION_COMMAND_SPACING
if (!this->ignore_is_setup_ && !this->command_pacer_.can_send()) {
return false;
}
#endif // USE_NEXTION_COMMAND_SPACING
ESP_LOGN(TAG, "send_command %s", command.c_str());
this->write_str(command.c_str());
const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF};
this->write_array(to_send, sizeof(to_send));
#ifdef USE_NEXTION_COMMAND_SPACING
this->command_pacer_.mark_sent();
#endif // USE_NEXTION_COMMAND_SPACING
return true;
}
@ -158,6 +169,10 @@ void Nextion::dump_config() {
if (this->start_up_page_ != -1) {
ESP_LOGCONFIG(TAG, " Start Up Page: %" PRId16, this->start_up_page_);
}
#ifdef USE_NEXTION_COMMAND_SPACING
ESP_LOGCONFIG(TAG, " Command spacing: %" PRIu8 "ms", this->command_pacer_.get_spacing());
#endif // USE_NEXTION_COMMAND_SPACING
}
float Nextion::get_setup_priority() const { return setup_priority::DATA; }
@ -312,6 +327,11 @@ bool Nextion::remove_from_q_(bool report_empty) {
}
NextionQueue *nb = this->nextion_queue_.front();
if (!nb || !nb->component) {
ESP_LOGE(TAG, "Invalid queue entry!");
this->nextion_queue_.pop_front();
return false;
}
NextionComponentBase *component = nb->component;
ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str());
@ -341,6 +361,12 @@ void Nextion::process_nextion_commands_() {
return;
}
#ifdef USE_NEXTION_COMMAND_SPACING
if (!this->command_pacer_.can_send()) {
return; // Will try again in next loop iteration
}
#endif
size_t to_process_length = 0;
std::string to_process;
@ -380,7 +406,9 @@ void Nextion::process_nextion_commands_() {
this->setup_callback_.call();
}
}
#ifdef USE_NEXTION_COMMAND_SPACING
this->command_pacer_.mark_sent(); // Here is where we should mark the command as sent
#endif
break;
case 0x02: // invalid Component ID or name was used
ESP_LOGW(TAG, "Nextion reported component ID or name invalid!");
@ -524,6 +552,11 @@ void Nextion::process_nextion_commands_() {
}
NextionQueue *nb = this->nextion_queue_.front();
if (!nb || !nb->component) {
ESP_LOGE(TAG, "Invalid queue entry!");
this->nextion_queue_.pop_front();
return;
}
NextionComponentBase *component = nb->component;
if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) {
@ -564,6 +597,11 @@ void Nextion::process_nextion_commands_() {
}
NextionQueue *nb = this->nextion_queue_.front();
if (!nb || !nb->component) {
ESP_LOGE(TAG, "Invalid queue entry!");
this->nextion_queue_.pop_front();
return;
}
NextionComponentBase *component = nb->component;
if (component->get_queue_type() != NextionQueueType::SENSOR &&

View File

@ -35,8 +35,54 @@ using nextion_writer_t = std::function<void(Nextion &)>;
static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
#ifdef USE_NEXTION_COMMAND_SPACING
class NextionCommandPacer {
public:
/**
* @brief Creates command pacer with initial spacing
* @param initial_spacing Initial time between commands in milliseconds
*/
explicit NextionCommandPacer(uint8_t initial_spacing = 0) : spacing_ms_(initial_spacing) {}
/**
* @brief Set the minimum time between commands
* @param spacing_ms Spacing in milliseconds
*/
void set_spacing(uint8_t spacing_ms) { spacing_ms_ = spacing_ms; }
/**
* @brief Get current command spacing
* @return Current spacing in milliseconds
*/
uint8_t get_spacing() const { return spacing_ms_; }
/**
* @brief Check if enough time has passed to send next command
* @return true if enough time has passed since last command
*/
bool can_send() const { return (millis() - last_command_time_) >= spacing_ms_; }
/**
* @brief Mark a command as sent, updating the timing
*/
void mark_sent() { last_command_time_ = millis(); }
private:
uint8_t spacing_ms_;
uint32_t last_command_time_{0};
};
#endif // USE_NEXTION_COMMAND_SPACING
class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice {
public:
#ifdef USE_NEXTION_COMMAND_SPACING
/**
* @brief Set the command spacing for the display
* @param spacing_ms Time in milliseconds between commands
*/
void set_command_spacing(uint32_t spacing_ms) { this->command_pacer_.set_spacing(spacing_ms); }
#endif // USE_NEXTION_COMMAND_SPACING
/**
* Set the text of a component to a static string.
* @param component The component name.
@ -1227,6 +1273,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
bool is_connected() { return this->is_connected_; }
protected:
#ifdef USE_NEXTION_COMMAND_SPACING
NextionCommandPacer command_pacer_{0};
#endif // USE_NEXTION_COMMAND_SPACING
std::deque<NextionQueue *> nextion_queue_;
std::deque<NextionQueue *> waveform_queue_;
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag);
@ -1360,5 +1409,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
uint32_t started_ms_ = 0;
bool sent_setup_commands_ = false;
};
} // namespace nextion
} // namespace esphome

View File

@ -280,6 +280,7 @@ display:
- platform: nextion
id: main_lcd
update_interval: 5s
command_spacing: 5ms
on_sleep:
then:
lambda: 'ESP_LOGD("display","Display went to sleep");'