Skip to content

EPD / OLED / LED panels: init without clear keeps the image on the panel - #375

Merged
lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:keep_display_on_init
Oct 23, 2026
Merged

lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:keep_display_on_init

Conversation

@ainyan03

@ainyan03 ainyan03 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #370.

Problem

With M5Unified cfg.clear_display = false, the image that was on the panel before a reset or a deep sleep is wiped during M5.begin():

  • CoreInk / AirQ with the GDEW0154D67 panel: LGFX_Device::init_impl() re-applies the invert setting after Panel::init(). Panel_GDEW0154D67::setInvert() marked the whole frame dirty while the init sequence was pending, so the endWrite() of init_impl() refreshed the whole panel with the (white) host buffer.
  • CoreMatrix (TM1680): the same setInvert() call pushed the cleared host buffer to the LEDs (and Panel_TM1680::init() did so a second time), so every begin() turned the matrix off.
  • PaperMono (SSD1677): same pattern as the GDEW0154D67 (setInvert() marks the whole frame dirty unconditionally).

Change

  • Panel_GDEW0154D67::setInvert() / Panel_SSD1677::setInvert(): while the init sequence is pending, applying the current value does not mark the frame dirty. The first display() after a reset sends the whole frame anyway.
  • Panel_GDEW0154D67::display(): the frame sent by the init sequence is no longer inverted. With the inverted frame, the flip draw that follows ended inverted (white and black swapped) whenever that was the first display() after a reset; it did not show before because init() itself refreshed the panel.
  • Panel_TM1680: init() no longer pushes the cleared buffer, and setInvert() pushes only when the value changes. The init commands do not touch the LED RAM, so the image stays on the LEDs until the first draw. On a cold power-up the LED RAM is undefined until the first push, which now comes with the clear() of a normal init instead of inside init(); the gap is a few I2C bytes.
  • Panel_1bitOLED (SSD1306 / SH110x / SSD1309 units) and Panel_M5UnitGlass: init() clears the host buffer (nothing is pushed). These panels relied on the clear() of a normal init to initialize the buffer; with an init without clear, the first partial drawing would have sent whatever the allocator left in the page.

The host buffer cannot be read back from these panels, so the first drawing after an init without clear still replaces the whole panel with the new buffer (white / off outside the drawn area). What is fixed is that begin() itself no longer wipes the panel.

The M5Unified side (external displays following cfg.clear_display) is m5stack/M5Unified#416.

Test

  • CoreInk (GDEW0154D67) and CoreInk (GDEW0154M09), Arduino core 3.3.x: a checkerboard drawn by a normal boot stays on the panel through esp_restart(), esp_deep_sleep() and M5.Power.deepSleep() followed by M5.begin() with clear_display = false (before: the D67 unit turned white then black during begin()). A normal boot with clear_display = true still clears and draws correctly. On the D67 the first drawing after an init without clear comes out with the right polarity and without ghosting of the previous image, in epd_quality (the default) and in epd_fast; before the change that drawing ended inverted in epd_quality.
  • CoreMatrix (TM1680), ESP-IDF 5.5: the pattern stays on the LEDs through esp_restart() + M5.begin() with clear_display = false (before: all LEDs off).
  • PaperMono (SSD1677): code change only, not tested on hardware.
  • Build: ESP32 / S3 / C3 / C5 / C6 / C61 / H2 / P4 with Arduino core 2.x / 3.x and ESP-IDF 5.1 to 6.1. CI on the fork passed.

With M5Unified cfg.clear_display = false (LGFX init_without_reset(false)),
the image on the panel was wiped during initialization (m5stack#370):

- GDEW0154D67 / SSD1677: LGFX_Device::init_impl() re-applies the invert
  setting after Panel::init(), and setInvert() marked the whole frame dirty
  while the init sequence was pending, so the endWrite() of init_impl()
  refreshed the panel with the (white) host buffer. Re-applying the current
  value no longer marks the frame dirty; a changed value is still shown.
- GDEW0154D67: the frame sent by the init sequence is no longer inverted.
  With the inverted frame, the flip draw that follows ended inverted
  whenever that was the first display() after a reset (not visible before,
  because init() itself refreshed the panel).
- TM1680: init() no longer pushes the cleared buffer, and setInvert()
  pushes only when the value changes. The init commands do not touch the
  LED RAM, so the image stays on the LEDs until the first draw.
- Panel_1bitOLED / Panel_M5UnitGlass: init() clears the host buffer (and
  the tile dirty flags of the Unit GLASS) without pushing it. They relied on
  the clear() of a normal init; with an init without clear, the first
  partial drawing would have sent whatever the allocator left there.

The host buffer cannot be read back from these panels, so the first drawing
after an init without clear still replaces the whole panel with the new
buffer. What changes is that the initialization itself no longer wipes it.
@lovyan03
lovyan03 merged commit 7867a2f into m5stack:develop Oct 23, 2026
25 checks passed
@ainyan03
ainyan03 deleted the keep_display_on_init branch October 23, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants