From 3b319240c6d12afca15e4bedf5bfb2cb128c15a0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Oct 2026 21:20:32 -0500 Subject: [PATCH] [mipi_rgb] Keep the init sequence in flash (#20090) --- esphome/components/mipi_rgb/display.py | 3 ++- esphome/components/mipi_rgb/mipi_rgb.cpp | 16 ++++++++-------- esphome/components/mipi_rgb/mipi_rgb.h | 9 +++++++-- .../mipi_rgb/test_reset_sequence.py | 18 +++++++++++++++--- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/esphome/components/mipi_rgb/display.py b/esphome/components/mipi_rgb/display.py index 9cba56a976..71234c2f25 100644 --- a/esphome/components/mipi_rgb/display.py +++ b/esphome/components/mipi_rgb/display.py @@ -286,7 +286,8 @@ async def to_code(config: ConfigType) -> None: if CONF_SPI_ID in config: await spi.register_spi_device(var, config, write_only=True) sequence = model.get_sequence(config, add_reset=True) - cg.add(var.set_init_sequence(sequence)) + table = cg.shared_progmem_array("mipi_rgb_init_sequence", cg.uint8, sequence) + cg.add(var.set_init_sequence(table, len(sequence))) cg.add(var.set_color_mode(COLOR_ORDERS[config[CONF_COLOR_ORDER]])) cg.add(var.set_invert_colors(config[CONF_INVERT_COLORS])) diff --git a/esphome/components/mipi_rgb/mipi_rgb.cpp b/esphome/components/mipi_rgb/mipi_rgb.cpp index 034efb9c92..4928b6f667 100644 --- a/esphome/components/mipi_rgb/mipi_rgb.cpp +++ b/esphome/components/mipi_rgb/mipi_rgb.cpp @@ -78,27 +78,28 @@ void MipiRgbSpi::write_data_(uint8_t value) { void MipiRgbSpi::write_init_sequence_() { size_t index = 0; - auto &vec = this->init_sequence_; - while (index != vec.size()) { - if (vec.size() - index < 2) { + const uint8_t *seq = this->init_sequence_; + const size_t len = this->init_sequence_len_; + while (index != len) { + if (len - index < 2) { this->mark_failed(LOG_STR("Malformed init sequence")); return; } - uint8_t cmd = vec[index++]; - uint8_t x = vec[index++]; + uint8_t cmd = seq[index++]; + uint8_t x = seq[index++]; if (x == DELAY_FLAG) { ESP_LOGD(TAG, "Delay %dms", cmd); delay(cmd); } else { uint8_t num_args = x & 0x7F; - if (vec.size() - index < num_args) { + if (len - index < num_args) { this->mark_failed(LOG_STR("Malformed init sequence")); return; } if (cmd == SLEEP_OUT) { delay(120); // NOLINT } - const auto *ptr = vec.data() + index; + const auto *ptr = seq + index; char hex_buf[format_hex_pretty_size(MIPI_RGB_MAX_CMD_LOG_BYTES)]; ESP_LOGD(TAG, "Write command %02X, length %d, byte(s) %s", cmd, num_args, format_hex_pretty_to(hex_buf, ptr, num_args, '.')); @@ -111,7 +112,6 @@ void MipiRgbSpi::write_init_sequence_() { } } // this->spi_teardown(); // SPI not needed after this - this->init_sequence_.clear(); delay(10); } diff --git a/esphome/components/mipi_rgb/mipi_rgb.h b/esphome/components/mipi_rgb/mipi_rgb.h index f528943c1b..8b29e273ab 100644 --- a/esphome/components/mipi_rgb/mipi_rgb.h +++ b/esphome/components/mipi_rgb/mipi_rgb.h @@ -109,7 +109,10 @@ class MipiRgbSpi final : public MipiRgb, public: MipiRgbSpi(int width, int height) : MipiRgb(width, height) {} - void set_init_sequence(const std::vector &init_sequence) { this->init_sequence_ = init_sequence; } + void set_init_sequence(const uint8_t *init_sequence, size_t len) { + this->init_sequence_ = init_sequence; + this->init_sequence_len_ = len; + } void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; } void setup() override; @@ -120,7 +123,9 @@ class MipiRgbSpi final : public MipiRgb, void dump_config() override; GPIOPin *dc_pin_{nullptr}; - std::vector init_sequence_; + // Shared PROGMEM table + const uint8_t *init_sequence_{nullptr}; + size_t init_sequence_len_{0}; }; #endif diff --git a/tests/component_tests/mipi_rgb/test_reset_sequence.py b/tests/component_tests/mipi_rgb/test_reset_sequence.py index 50478361b1..b0fd3ed573 100644 --- a/tests/component_tests/mipi_rgb/test_reset_sequence.py +++ b/tests/component_tests/mipi_rgb/test_reset_sequence.py @@ -9,6 +9,7 @@ here. from collections.abc import Callable from pathlib import Path +import re # A model with no reset_pin default: SWRESET ({1, 0}) is prepended ahead of the # inherited ST7701S reset_delay ({50, 255}). @@ -54,6 +55,18 @@ display: """ +def _init_sequence(main_cpp: str, display_id: str) -> str: + """Return the shared PROGMEM init sequence table passed to a display, as rendered.""" + match = re.search(rf"{display_id}->set_init_sequence\((\w+), \d+\);", main_cpp) + assert match is not None + table = re.search( + rf"static constexpr uint8_t {match.group(1)}\[\] PROGMEM = (\{{[^;]*\}});", + main_cpp, + ) + assert table is not None + return table.group(1) + + def test_swreset_and_reset_delay_without_reset_pin( generate_main: Callable[[str | Path], str], tmp_path: Path, @@ -64,7 +77,7 @@ def test_swreset_and_reset_delay_without_reset_pin( main_cpp = generate_main(yaml_file) - assert "no_reset_display->set_init_sequence({1, 0, 50, 255," in main_cpp + assert _init_sequence(main_cpp, "no_reset_display").startswith("{1, 0, 50, 255,") def test_reset_delay_only_with_reset_pin( @@ -77,5 +90,4 @@ def test_reset_delay_only_with_reset_pin( main_cpp = generate_main(yaml_file) - assert "has_reset_display->set_init_sequence({50, 255," in main_cpp - assert "has_reset_display->set_init_sequence({1, 0," not in main_cpp + assert _init_sequence(main_cpp, "has_reset_display").startswith("{50, 255,")