Skip to content

T5S3 e-paper: grayscale driver with per pixel doses as default - #722

Merged
KenVanHoeylandt merged 7 commits into
TactilityProject:mainfrom
AdaSzi:t5s3-epd-state-machine
Oct 28, 2026
Merged

KenVanHoeylandt merged 7 commits into
TactilityProject:mainfrom
AdaSzi:t5s3-epd-state-machine

Conversation

@AdaSzi

@AdaSzi AdaSzi commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Replaces the waveform of the grayscale driver lilygo,t5s3-display with a per pixel dose of frames, using the method of InkBoy (only the method, no code). The device lilygo-t5-epd47-s3 now uses this driver. The black and white driver stays available as commented out lines in the devicetree. User data is now stored on the SD card, like on the T-Deck.

How it works

  • A pixel whose shade changes is driven light for 15 frames, which erases what it showed, then rests for 0 to 3 frames and is driven dark for 1 to 8 frames. That gives 16 shades, and every gray is reached the same way, so moving content leaves almost no trails.
  • Frames of 40 ms cover the whole screen, so all changes show at once. The next frame is encoded on the other core.
  • refresh() flashes the panel and draws the content again. The driver also does that by itself when the content changed without touches, at most every 10 minutes.
  • A frame that stalls (flash writes, WiFi connecting) makes the grays it interrupted start again.

License
The epdiy waveform and the update logic derived from it are removed. Only the board signalling (t5s3_epd_board.cpp/.h) stays LGPL.

Testing
LILYGO T5 4.7 S3 V2.4: boot, launcher, Satellite Tracker with a gray map and moving markers. The shades and trails were measured with a camera on the panel.

Summary by CodeRabbit

  • New Features
    • Added a black-and-white display option with nonblocking updates and dithered gray levels.
    • The grayscale display now supports 16 shades and nonblocking updates, with automatic refreshes after content has been idle. Automatic refreshes occur no more than once every 10 minutes.
    • Refreshing the display flashes the panel and redraws existing content.
    • User data is now stored on the SD card instead of internal storage.
  • Documentation
    • Updated display guidance to describe both display options and their refresh behavior.

- Split the board signalling into t5s3_epd_board.cpp, shared by both drivers
- lilygo,t5s3-display (default): draw_bitmap() no longer waits for the panel. Changes are shown at once with a black and white dither, each changed area gets its 16 gray levels 3 s after its last change. Only changed pixels are driven, refresh() no longer blocks touch input
- lilygo,t5s3-display-bw (new): black and white pixel state machine like the PaperS3 driver
…veform

- lilygo,t5s3-display draws 7 shades with a dose of frames per pixel, in the manner of InkBoy
- Frames of 40 ms cover the whole screen, so all changes show at once, the next frame is encoded on the other core
- Every gray is reached the same way (15 light frames, a frame of rest, then its dark frames), so moving content leaves almost no trails
- A frame that stalls makes the grays it interrupted start again
- Only pixels whose shade changes are driven. refresh() flashes and draws the content again, the driver also does that when the content changed without touches (at most every 10 minutes)
- The epdiy waveform and the update logic derived from epdiy are removed, only the board signalling stays LGPL
- Doses, frame counts and shade brightness were measured with a camera on the panel
- Grays rest for 0 to 3 frames before their dark frames, each frame of rest makes a gray a little darker
- That gives 16 shades close to each other, so the dither between neighbouring shades is hardly visible
- AUTO_REFRESH_INTERVAL_MS is renamed to MINIMAL_AUTO_REFRESH_INTERVAL_MS
- lilygo-t5-epd47-s3 uses lilygo,t5s3-display, the black and white driver stays in the devicetree as commented out lines in case someone prefers it
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 63da1f43-874e-48b5-ab04-202488a39432
Commits

Reviewing files that changed from the base of the PR and between 9c2480c and ba7c89f.

Files selected for processing: 1
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_display.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Walkthrough

The module replaces its previous EPD implementation with shared board-signalling code and separate grayscale and black-and-white display drivers. The grayscale driver renders 16 shades through timed frames. The black-and-white driver dithers grayscale input to 1-bit targets. Both drivers use the board-level panel interface. The device-tree bindings, driver registration, build settings, and README are updated for the two-driver configuration. The device's user-data location changes from internal storage to SD.

Priority: Normal

Merge Risk: Minimal · up to ba7c8

The reviewed display-task synchronization issue is resolved, with no remaining concrete merge-blocking risk identified.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the main change: a grayscale driver for the T5S3 e-paper panel that uses per-pixel frame doses as the default. The changeset adds and rewires the grayscale display driver (t5s3_display…
Docstring Coverage Passed Docstring coverage is inconclusive: files from earlier reviews still need to be rechecked within the check's analysis limits. Not blocking.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the main change: a grayscale driver for the T5S3 e-paper panel that uses per-pixel frame doses as the default. The changeset adds and rewires the grayscale display driver (t5s3_display.cpp), replaces the old fast/quality epdiy engine, and switches the device to this driver, which matches the title. The title is concise and specific, and it does not list files. Minor note: the title does not mention the new black-and-white driver or the SD-card storage change, but those are secondary.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 62358706-5ac5-400d-97fb-28e45c714803
Commits

Reviewing files that changed from the base of the PR and between a26581f and f50a185.

Files selected for processing: 16
  • Devices/lilygo-t5-epd47-s3/lilygo,t5-epd47-s3.dts
  • Drivers/lilygo-t5s3-epd-module/CMakeLists.txt
  • Drivers/lilygo-t5s3-epd-module/README.md
  • Drivers/lilygo-t5s3-epd-module/bindings/lilygo,t5s3-display-bw.yaml
  • Drivers/lilygo-t5s3-epd-module/bindings/lilygo,t5s3-display.yaml
  • Drivers/lilygo-t5s3-epd-module/include/bindings/t5s3_display_bw.h
  • Drivers/lilygo-t5s3-epd-module/include/drivers/t5s3_display_bw.h
  • Drivers/lilygo-t5s3-epd-module/source/module.cpp
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_display.cpp
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_display_bw.cpp
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd.cpp
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd.h
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd_board.cpp
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd_board.h
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd_rows.h
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd_waveform.h
Files with no reviewable changes: 4
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd_waveform.h
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd.cpp
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd.h
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_epd_rows.h

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

- Black is driven for 18 frames, until grays drawn with it end their light frames. Before, a black line of 1 pixel on gray faded after a refresh.
- A pending restart of the doses runs on the display task before both cores encode, so it no longer races with the encoding of the upper half.
- Black is driven for 18 frames, until grays drawn with it end their light frames. Before, a black line of 1 pixel on gray faded after a refresh.
- A pending restart of the doses runs on the display task before both cores encode, so it no longer races with the encoding of the upper half.
- User data is stored on the SD card

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

Minor · Move the restart store after finish_helper() to remove the… · t5s3_display.cpp:202

Drivers/lilygo-t5s3-epd-module/source/t5s3_display.cpp:202
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Move the restart store after finish_helper() to remove the unsynchronized access.

display_task writes restart at line 600 during the active frame. helper_task reads and clears the same field at lines 385-386 for the job it started at line 585. Nothing orders those accesses, so this is a data race on a non-atomic bool. If the helper task is preempted between its read and its clear, the store from the display task can be overwritten and that restart request is lost.

Publish the flag after finish_helper() returns. The helper's clear then happens before the display task's store, and the next start_helper() orders the store before the next read. The helper still applies the restart at the next job, as it does now.

🐛 Suggested fix
-        if (late_us > (encoding_late ? ENCODING_STALL_US : STALL_US) || frame_us > FRAME_PERIOD_US + STALL_US) {
-            internal->restart = true;
-            LOG_W(TAG, "Frame %d us late and %d us long, doses start again", static_cast<int>(late_us), static_cast<int>(frame_us));
-        }
         const Encoded next = finish_helper(internal);
+        if (late_us > (encoding_late ? ENCODING_STALL_US : STALL_US) || frame_us > FRAME_PERIOD_US + STALL_US) {
+            internal->restart = true;
+            LOG_W(TAG, "Frame %d us late and %d us long, doses start again", static_cast<int>(late_us), static_cast<int>(frame_us));
+        }
         encoding_late = esp_timer_get_time() > next_frame_us;

Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 700b6d37-d882-4d09-86e5-cede191ae86e
Commits

Reviewing files that changed from the base of the PR and between f50a185 and 9c2480c.

Files selected for processing: 3
  • Devices/lilygo-t5-epd47-s3/device.properties
  • Drivers/lilygo-t5s3-epd-module/README.md
  • Drivers/lilygo-t5s3-epd-module/source/t5s3_display.cpp
Files skipped from review as they are similar to previous changes: 1
  • Drivers/lilygo-t5s3-epd-module/README.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

The display task set the restart flag while the helper task could read and clear it. It is now set after finish_helper(), so the helper sees it with its next job.
@KenVanHoeylandt
KenVanHoeylandt merged commit bf902be into TactilityProject:main Oct 28, 2026
78 checks passed
@KenVanHoeylandt

Copy link
Copy Markdown
Contributor

Thank you!

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