Skip to content

feat: add T-Lora Pager (TFT 480x222) support#285

Closed
ndoo wants to merge 9 commits intomeshtastic:masterfrom
Meshtastic-Malaysia:feature/tlora-pager-tft
Closed

feat: add T-Lora Pager (TFT 480x222) support#285
ndoo wants to merge 9 commits intomeshtastic:masterfrom
Meshtastic-Malaysia:feature/tlora-pager-tft

Conversation

@ndoo
Copy link

@ndoo ndoo commented Mar 7, 2026

Hardware

LilyGo T-Lora Pager — ESP32-S3 based LoRa device with:

  • ST7796 480×222 TFT display (landscape, ~2.16:1 aspect ratio)
  • TCA8418 I²C key matrix controller driving a 4×10 physical keyboard (31 usable keys with Shift/Sym modifiers)
  • Quadrature rotary encoder (scroll wheel + button)
  • I²C-only keyboard — no GPIO interrupt line available for display wake

Product page: https://lilygo.cc/products/t-lora-pager

What's added

Commit Description
EEZ Studio project studio/480x222/TFT480x222.eez-project derived from the 320x240 project; dimensions updated to 480×222
Generated UI assets generated/ui_480x222/ — screens, styles, fonts and images exported from EEZ Studio
Thread-safe display wake DisplayDriver::requestWake() static flag, integrated into LGFXDriver::task_handler() for both dimming and blank-screen powersave paths; also fixes stale lv_disp_trig_activity()lv_display_trigger_activity() (LVGL 9 API)
Themes guard Extends Themes.cpp compile guard to include VIEW_480x222 alongside VIEW_320x240
RotaryEncoderInputDriver New INPUTDRIVER_ROTARY_TYPE input driver wrapping the mverch67 RotaryEncoder library; activated via INPUTDRIVER_ROTARY_UP/DOWN/BTN pin macros; sits alongside the existing INPUTDRIVER_ENCODER_TYPE path
TLoraPagerKeyboardInputDriver::readKeyboard() Full TCA8418 FIFO key reading with sticky Shift/Sym modifiers; wakes display on any keypress via DisplayDriver::requestWake(); backspace outside a textarea sends LV_KEY_ESC
TFTView_480x222 Complete MeshtasticView port for the 480×222 display — node list, messaging, map, settings, signal scanner, trace route and packet log

Design notes

  • Display wake without GPIO: The TCA8418 communicates over I²C and cannot trigger a light-sleep GPIO wake. DisplayDriver::requestWake() provides a polling-based alternative that any input handler can call from any context.
  • Keyboard differences from Kate-hey reference: Removed I2CKeyboardInputDriver callback infrastructure (setNavigateHomeCallback, setScrollCallback, setAltIndicatorCallback) — not needed here; wake is handled via requestWake() and backspace-outside-textarea maps directly to LV_KEY_ESC.
  • Rotary encoder: Uses INPUTDRIVER_ROTARY_TYPE define (distinct from the existing INPUTDRIVER_ENCODER_TYPE) to avoid conflicts on devices that use a different encoder library.

Status

🚧 Draft — hardware testing in progress. UI layout and input handling are functional in simulation; on-device validation of the rotary encoder and full keyboard matrix is ongoing.

Testing

  • Keyboard: all keys, Shift modifier, Sym modifier, backspace in/out of textarea
  • Rotary encoder: scroll, button press
  • Display wake from powersave via keyboard keypress
  • Display wake from powersave via rotary encoder
  • All screens: node list, messaging, map, settings, signal scanner, trace route, packet log

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Congratulations for your first pull request

@ndoo
Copy link
Author

ndoo commented Mar 7, 2026

Please help me label with AI if there is an AI label.

@ndoo ndoo force-pushed the feature/tlora-pager-tft branch 5 times, most recently from cc8c1ce to b3abe7c Compare March 7, 2026 10:14
ndoo and others added 5 commits March 7, 2026 18:26
Derived from the 320x240 project with the following changes:

Settings:
- Display dimensions: 320x240 -> 480x222 (ST7796, T-Lora Pager landscape)
- Output folder: generated/ui_320x240 -> generated/ui_480x222
- LVGL version: 9.0 -> 9.2.2
- Build: fontExportMode/imageExportMode=source, embedFonts=true

Layout (MainScreen, 36 widgets adjusted, hierarchy unchanged):
- ButtonPanel: width 12%->8%
  8% of 480px ~= 12% of 320px in pixels; shrinking the percentage
  reclaims horizontal space for content on the wider display.
- Content panels (16): left 12%->8%, width 88%->92%
  Right edge stays at 100%; gains 4% more content area.
- Title bar panels (17): left 12%->8%, width 80%->84%
  Right edge kept at 92% (= 100%-8%) to match the left margin,
  keeping the title visually centered. The BatteryPanel (19% wide,
  TOP_RIGHT aligned) intentionally overlaps from the right so titles
  remain centered regardless of whether battery percentage is shown.
- TopLoraTxPanel: left 12%->8%, width 89%->88%
- HomePanel/HomeContainer: width 98%->100%

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
Generated from studio/480x222/TFT480x222.eez-project using EEZ Studio.
Contains screens, styles, fonts and images laid out for the ST7796
480x222 landscape display of the T-Lora Pager.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
Adds a static volatile wakeRequested flag to DisplayDriver so any input
handler can signal a wake from sleep without requiring a GPIO interrupt.
Integrates into LGFXDriver's task_handler for both the brightness-dimming
and blank-screen powersave paths. Also fixes a stale lv_disp_trig_activity()
call to the current LVGL 9 API lv_display_trigger_activity().

Needed for the T-Lora Pager TCA8418 keyboard which communicates over I2C
and cannot use GPIO light-sleep wake.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
Themes.cpp was previously only compiled for VIEW_320x240. The T-Lora
Pager's TFTView_480x222 uses the same theme system, so extend the guard
to include VIEW_480x222.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
Adds a new input driver wrapping the mverch67 RotaryEncoder library for
quadrature scroll wheel support on devices like the T-Lora Pager. The
driver registers an LVGL LV_INDEV_TYPE_ENCODER device and is polled each
task loop via DeviceGUI::task_handler().

Activated by defining INPUTDRIVER_ROTARY_TYPE alongside pin macros
INPUTDRIVER_ROTARY_UP, INPUTDRIVER_ROTARY_DOWN and optionally
INPUTDRIVER_ROTARY_BTN. Sits alongside the existing INPUTDRIVER_ENCODER_TYPE
path without conflict.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
@ndoo ndoo force-pushed the feature/tlora-pager-tft branch from b3abe7c to a0dbfd0 Compare March 7, 2026 10:27
ndoo and others added 4 commits March 7, 2026 18:56
Implements the previously stubbed readKeyboard() for the T-Lora Pager's
TCA8418 4x10 key matrix. Reads key events from the TCA8418 FIFO, handles
sticky shift/sym modifiers, maps keys to LVGL key codes, and calls
DisplayDriver::requestWake() on any keypress to wake the display from
power save.

Adds NavigationCallback infrastructure to I2CKeyboardInputDriver so that
backspace pressed outside a textarea calls a registered callback (e.g. to
focus the home button) rather than falling back to LV_KEY_ESC.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
Ports the complete MeshtasticView implementation for the T-Lora Pager's
480x222 ST7796 display. Includes all node list, messaging, map, settings,
signal scanner, trace route and packet log screens.

Registers the NavigationCallback so that backspace pressed outside a text
field focuses the home button, enabling proper back-key navigation across
all panels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
This file is not generated by EEZ Studio. It originated as Squareline
Studio generated code (see 320x240 counterpart) and must be manually
maintained alongside the EEZ project. It was never ported when the
480x222 EEZ project was set up, causing an undefined reference to
create_tabview_settings() at link time.

The file uses lv_pct() and parent-relative sizing throughout with no
hardcoded 320x240 pixel values, making it directly reusable for 480x222.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces onScreenSleep()/onScreenWake() virtual hooks on InputDriver.
LGFXDriver::task_handler() calls them at power-save transitions.

I2CKeyboardInputDriver overrides onScreenSleep/Wake to save and restore
keyboard backlight brightness. TLoraPagerKeyboardInputDriver::init()
registers itself as the InputDriver singleton so these hooks dispatch
correctly, and initialises the backlight PWM via initKeyboardBacklight().

Sym+Space (hold Sym, press Space) cycles keyboard backlight through
four brightness steps: off, dim, medium, full.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
@ndoo ndoo force-pushed the feature/tlora-pager-tft branch from a0dbfd0 to 67105e5 Compare March 7, 2026 11:11
@mverch67
Copy link
Collaborator

mverch67 commented Mar 7, 2026

This PR is a dead end as there is already work done on a plugin based UI for the T-LoRa Pager. Architecturally, it simply doesn't make any sense to copy&paste 7500 lines of code into a another class derived from MeshtasticView and claude should know that.

@ndoo
Copy link
Author

ndoo commented Mar 7, 2026

Would have been nice to have this context in the feature request @ #229

@ndoo
Copy link
Author

ndoo commented Mar 7, 2026

This PR is a dead end as there is already work done on a plugin based UI for the T-LoRa Pager. Architecturally, it simply doesn't make any sense to copy&paste 7500 lines of code into a another class derived from MeshtasticView and claude should know that.

I think it is not clear why this stub exists:
https://github.com/meshtastic/device-ui/blob/master/include/graphics/view/TFT/TFTView_480x222.h

The only way to avoid reimplementing is to extend from TFTView_320x240?

@mverch67
Copy link
Collaborator

mverch67 commented Mar 8, 2026

The 320x240 view is being refactored (into smaller plugins) to be used by other resolutions.

@ndoo
Copy link
Author

ndoo commented Mar 8, 2026

The 320x240 view is being refactored (into smaller plugins) to be used by other resolutions.

I see. Is the eez studio project for 480x222 any use to yourself or do you already have plans to rework it? I have been tightening up the layout for it, but I guess if you already have something in the works I shouldn't burn my time on this either.

@mverch67
Copy link
Collaborator

mverch67 commented Mar 8, 2026

I think it's not of any use as I already drew new views for T-LoRa Pager and T-Display Pro.
You don't burn any time, you get valuable knowledge by doing this ;-)

@ndoo
Copy link
Author

ndoo commented Mar 8, 2026 via email

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.

2 participants