Skip to content

Conversation

@clydebarrow
Copy link
Contributor

@clydebarrow clydebarrow commented Nov 13, 2025

What does this implement/fix?

Add support for the SSD1677 display controller, currently in monochrome only, with support
for partial refresh.

Also add a model for the Waveshare 4.26" e-paper with Seeed EE04 driver board.

Other changes:

  • Add transform and rotation for all epaper_spi
  • Update test_card to show better on monochrome displays

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code quality improvements to existing code or addition of tests
  • Other

Related issue or feature (if applicable):
Quick Camera Image 2025-11-10 at 10 03 22 PM

  • fixes

Pull request in esphome-docs with documentation (if applicable):

Test Environment

  • ESP32
  • ESP32 IDF
  • ESP8266
  • RP2040
  • BK72xx
  • RTL87xx
  • nRF52840

Example entry for config.yaml:

# Example config.yaml

display:
  - platform: epaper_spi
    model: seeed-ee04-mono-4.26
    full_update_every: 10

Checklist:

  • The code change is tested and works locally.
  • Tests have been added to verify that the new code works (under tests/ folder).

If user exposed functionality or configuration variables are added/changed:

@github-actions
Copy link
Contributor

To use the changes from this PR as an external component, add the following to your ESPHome configuration YAML file:

external_components:
  - source: github://pr#11887
    components: [display, epaper_spi]
    refresh: 1h

(Added by the PR bot)

@codecov-commenter
Copy link

codecov-commenter commented Nov 13, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.47%. Comparing base (fdc7ae7) to head (29b1656).

Additional details and impacted files
@@            Coverage Diff             @@
##              dev   #11887      +/-   ##
==========================================
+ Coverage   72.44%   72.47%   +0.02%     
==========================================
  Files          53       53              
  Lines       11150    11150              
  Branches     1509     1509              
==========================================
+ Hits         8078     8081       +3     
+ Misses       2677     2675       -2     
+ Partials      395      394       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 14, 2025

Memory Impact Analysis

Components: epaper_spi
Platform: esp32-s3-idf

Metric Target Branch This PR Change
RAM 13,988 bytes 14,012 bytes 📈 🔸 +24 bytes (+0.17%)
Flash 297,871 bytes 299,535 bytes 📈 🔸 +1,664 bytes (+0.56%)
📊 Component Memory Breakdown
Component Target Flash PR Flash Change
[esphome]epaper_spi 2,504 bytes 3,866 bytes 📈 🚨 +1,362 bytes (+54.39%)
app_framework 1,364 bytes 1,668 bytes 📈 +304 bytes (+22.29%)
[esphome]display 2,168 bytes 1,937 bytes 📉 🎉 -231 bytes (-10.65%)
esp32_sdk 44 bytes 60 bytes 📈 +16 bytes (+36.36%)
rom_functions 6,983 bytes 6,987 bytes 📈 +4 bytes (+0.06%)
[esphome]core 7,615 bytes 7,619 bytes 📈 🔸 +4 bytes (+0.05%)
🔍 Symbol-Level Changes (click to expand)

Changed Symbols

Symbol Target Size PR Size Change
setup() 796 bytes 1,079 bytes 📈 +283 bytes (+35.55%)
esphome::epaper_spi::EPaperBase::process_state_() 249 bytes 367 bytes 📈 +118 bytes (+47.39%)
esphome::epaper_spi::EPaperBase::dump_config() 190 bytes 290 bytes 📈 +100 bytes (+52.63%)
esphome::epaper_spi::EPaperSpectraE6::transfer_data() 234 bytes 174 bytes 📉 -60 bytes (-25.64%)
esphome::epaper_spi::EPaperBase::set_state_(esphome::epaper_spi::EPaperState, unsigned short) 103 bytes 118 bytes 📈 +15 bytes (+14.56%)
esphome::epaper_spi::EPaperBase::initialise_() 153 bytes 139 bytes 📉 -14 bytes (-9.15%)
esphome::epaper_spi::EPaperBase::update() 53 bytes 62 bytes 📈 +9 bytes (+16.98%)
esphome::display::Display::test_card() 817 bytes 809 bytes 📉 -8 bytes (-0.98%)
esphome::App 188 bytes 192 bytes 📈 +4 bytes (+2.13%)
vtable for esphome::epaper_spi::EPaperSpectraE6 156 bytes 152 bytes 📉 -4 bytes (-2.56%)

New Symbols (top 15)

Symbol Size
esphome::epaper_spi::EPaperSSD1677::transfer_data() 401 bytes
esphome::epaper_spi::EPaperBase::EPaperBase(char const*, unsigned short, unsigned short, unsigned...esphome::epaper_spi::EPaperBase::EPaperBase(char const*, unsigned short, unsigned short, unsigned char const*, unsigned int, esphome::display::DisplayType)
186 bytes
esphome::epaper_spi::EPaperBase::draw_pixel_at(int, int, esphome::Color) 157 bytes
vtable for esphome::epaper_spi::EPaperBase 152 bytes
vtable for esphome::epaper_spi::EPaperSSD1677 152 bytes
esphome::epaper_spi::EPaperBase::rotate_coordinates_(int&, int&) const 119 bytes
esphome::epaper_spi::EPaperSpectraE6::draw_pixel_at(int, int, esphome::Color) 109 bytes
esphome::epaper_spi::EPaperBase::fill(esphome::Color) 58 bytes
esphome::epaper_spi::EPaperSSD1677::refresh_screen(bool) 52 bytes
esphome::epaper_spi::EPaperBase::reset() 41 bytes
esphome::epaper_spi::EPaperSpectraE6::refresh_screen(bool) 34 bytes
esphome::epaper_spi::EPaperSSD1677::deep_sleep() 28 bytes
esphome::epaper_spi::EPaperSSD1677::reset() 26 bytes
esphome::epaper_spi::EPaperBase::get_width() 22 bytes
esphome::epaper_spi::EPaperBase::get_height() 22 bytes
9 more new symbols... Total: 1,630 bytes

Removed Symbols (top 15)

Symbol Size
esphome::epaper_spi::EPaperSpectraE6::EPaperSpectraE6(char const*, unsigned short, unsigned short...esphome::epaper_spi::EPaperSpectraE6::EPaperSpectraE6(char const*, unsigned short, unsigned short, unsigned char const*, unsigned int)
174 bytes
esphome::display::DisplayBuffer::draw_pixel_at(int, int, esphome::Color) 144 bytes
esphome::epaper_spi::EPaperSpectraE6::draw_absolute_pixel_internal(int, int, esphome::Color) 100 bytes
esphome::epaper_spi::EPaperBase::reset_() const 41 bytes
esphome::display::DisplayBuffer::get_width() 38 bytes
esphome::display::DisplayBuffer::get_height() 34 bytes
esphome::epaper_spi::EPaperSpectraE6::refresh_screen() 34 bytes
esphome::epaper_spi::EPaperBase::wait_for_idle_(bool) 25 bytes
esphome::epaper_spi::EPaperBase::get_width_controller() 17 bytes
esphome::display::Display::set_rotation(esphome::display::DisplayRotation) 7 bytes

Note: This analysis measures static RAM and Flash usage only (compile-time allocation).
Dynamic memory (heap) cannot be measured automatically.
⚠️ You must test this PR on a real device to measure free heap and ensure no runtime memory issues.

This analysis runs automatically when components change. Memory usage is measured from a representative test configuration.

@esphome esphome bot added has-tests and removed needs-tests labels Nov 16, 2025
@clydebarrow clydebarrow marked this pull request as ready for review November 16, 2025 22:57
Copilot AI review requested due to automatic review settings November 16, 2025 22:57
@clydebarrow clydebarrow requested a review from a team as a code owner November 16, 2025 22:57
@esphome esphome bot added code-quality and removed needs-docs labels Nov 16, 2025
Copilot finished reviewing on behalf of clydebarrow November 16, 2025 23:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for the SSD1677 e-paper display controller with monochrome and partial refresh capabilities, along with a specific configuration for the Waveshare 4.26" e-paper display. It also introduces transform and rotation support for all epaper_spi displays and improves the test card rendering for monochrome displays.

  • Implements new SSD1677 display driver with partial refresh support
  • Adds transform/rotation capabilities (mirror X/Y, swap XY) to all epaper_spi displays
  • Refactors test card to work properly on both color and monochrome displays

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
esphome/components/epaper_spi/models/ssd1677.py Adds Python model definition for SSD1677 controller and Seeed EE04 4.26" display
esphome/components/epaper_spi/epaper_spi_ssd1677.h Header file for SSD1677 display controller class
esphome/components/epaper_spi/epaper_spi_ssd1677.cpp Implementation of SSD1677 display operations including transfer_data, refresh, and deep_sleep
esphome/components/epaper_spi/epaper_spi.h Adds transform constants, updates base class with rotation support and partial refresh capability
esphome/components/epaper_spi/epaper_spi.cpp Implements coordinate rotation, updates state machine for partial refresh, adds transform handling
esphome/components/epaper_spi/display.py Adds Python schema for rotation and transform options, configures transform based on rotation
esphome/components/epaper_spi/models/init.py Adds get_available_transforms method to EpaperModel base class
esphome/components/epaper_spi/epaper_spi_spectra_e6.h Updates method signature for refresh_screen to accept partial parameter
esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp Updates SpectraE6 implementation for new refresh_screen signature and draw_pixel_at method
esphome/components/display/display.cpp Refactors test_card to support monochrome displays by moving common rendering outside color-only block
tests/components/epaper_spi/test.esp32-s3-idf.yaml Adds test configuration for new Seeed EE04 4.26" display

@dhoeben
Copy link

dhoeben commented Nov 18, 2025

When using:

external_components:
  - source: github://pr#11887
    components: [display, epaper_spi]
    refresh: 1h

I get this error:

ERROR Unable to import component epaper_spi.display:
Traceback (most recent call last):
  File "/home/douwehoeben/.local/lib/python3.13/site-packages/esphome/loader.py", line 199, in _lookup_module
    module = importlib.import_module(f"esphome.components.{domain}")
  File "/usr/lib64/python3.13/importlib/__init__.py", line 88, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/home/douwehoeben/esphome/Thermostat/esp32-thermostat/.esphome/external_components/31cf842e/esphome/components/epaper_spi/display.py", line 8, in <module>
    from esphome.components.mipi import flatten_sequence, map_sequence
ImportError: cannot import name 'flatten_sequence' from 'esphome.components.mipi' (/home/douwehoeben/.local/lib/python3.13/site-packages/esphome/components/mipi/__init__.py)
Failed config

Platform not found: 'display.epaper_spi'

The error does not display when using the default model: 7.3in-spectra-e6.
My code to call this:

display:
  - platform: epaper_spi
    model: SSD1677
    id: epd

    cs_pin: !secret pin_epd_cs
    dc_pin: !secret pin_epd_dc
    busy_pin: !secret pin_epd_busy
    reset_pin: !secret pin_epd_res
    spi_id: spi_epd
    
    update_interval: never
    full_update_every: 50 

    rotation: 180
    lambda: |-

Hope you can help.

@clydebarrow
Copy link
Contributor Author

Either build using the dev or beta esphome, or add mipi to the component list in the external components config.

@dhoeben
Copy link

dhoeben commented Nov 18, 2025

Either build using the dev or beta esphome, or add mipi to the component list in the external components config.

Thanks, that worked.

New error:

In file included from src/esphome/components/epaper_spi/epaper_spi.cpp:1:
src/esphome/components/epaper_spi/epaper_spi.h: In member function 'virtual void esphome::epaper_spi::EPaperBase::fill(esphome::Color)':
src/esphome/components/epaper_spi/epaper_spi.h:82:19: error: 'class esphome::split_buffer::SplitBuffer' has no member named 'fill'
   82 |     this->buffer_.fill(pixel_color);
      |                   ^~~~
src/esphome/components/epaper_spi/epaper_spi.cpp: In member function 'virtual void esphome::epaper_spi::EPaperBase::draw_pixel_at(int, int, esphome::Color)':
src/esphome/components/epaper_spi/epaper_spi.cpp:322:18: error: 'clamp_at_most' was not declared in this scope
  322 |   this->x_low_ = clamp_at_most(this->x_low_, x);
      |                  ^~~~~~~~~~~~~
src/esphome/components/epaper_spi/epaper_spi.cpp:323:19: error: 'clamp_at_least' was not declared in this scope
  323 |   this->x_high_ = clamp_at_least(this->x_high_, x + 1);
      |                   ^~~~~~~~~~~~~~
*** [.pioenvs/esp-thermostat/src/esphome/components/epaper_spi/epaper_spi.cpp.o] Error 1
In file included from src/esphome/components/epaper_spi/epaper_spi_spectra_e6.h:3,
                 from src/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp:1:
src/esphome/components/epaper_spi/epaper_spi.h: In member function 'virtual void esphome::epaper_spi::EPaperBase::fill(esphome::Color)':
src/esphome/components/epaper_spi/epaper_spi.h:82:19: error: 'class esphome::split_buffer::SplitBuffer' has no member named 'fill'
   82 |     this->buffer_.fill(pixel_color);
      |                   ^~~~
src/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp: In member function 'virtual void esphome::epaper_spi::EPaperSpectraE6::fill(esphome::Color)':
src/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp:103:17: error: 'class esphome::split_buffer::SplitBuffer' has no member named 'fill'
  103 |   this->buffer_.fill(pixel_color + (pixel_color << 4));
      |                 ^~~~
*** [.pioenvs/esp-thermostat/src/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp.o] Error 1
In file included from src/esphome/components/epaper_spi/epaper_spi_ssd1677.h:3,
                 from src/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp:1:
src/esphome/components/epaper_spi/epaper_spi.h: In member function 'virtual void esphome::epaper_spi::EPaperBase::fill(esphome::Color)':
src/esphome/components/epaper_spi/epaper_spi.h:82:19: error: 'class esphome::split_buffer::SplitBuffer' has no member named 'fill'
   82 |     this->buffer_.fill(pixel_color);
      |                   ^~~~
src/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp: In member function 'virtual bool esphome::epaper_spi::EPaperSSD1677::transfer_data()':
src/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp:59:3: error: 'FixedVector' was not declared in this scope
   59 |   FixedVector<uint8_t> bytes_to_send{};
      |   ^~~~~~~~~~~
src/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp:59:22: error: expected primary-expression before '>' token
   59 |   FixedVector<uint8_t> bytes_to_send{};
      |                      ^
src/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp:59:24: error: 'bytes_to_send' was not declared in this scope
   59 |   FixedVector<uint8_t> bytes_to_send{};
      |                        ^~~~~~~~~~~~~
*** [.pioenvs/esp-thermostat/src/esphome/components/epaper_spi/epaper_spi_ssd1677.cpp.o] Error 1

@clydebarrow
Copy link
Contributor Author

You will need to use dev or beta then, there are core changes that can't be pulled in with external components.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants