Skip to content

TCA8418, T-Lora Pager, T-Deck Pro: Keyboard improvements#7989

Open
WillyJL wants to merge 28 commits into
meshtastic:developfrom
WillyJL:feat/keyboard-improvements
Open

TCA8418, T-Lora Pager, T-Deck Pro: Keyboard improvements#7989
WillyJL wants to merge 28 commits into
meshtastic:developfrom
WillyJL:feat/keyboard-improvements

Conversation

@WillyJL

@WillyJL WillyJL commented Sep 15, 2025

Copy link
Copy Markdown
Contributor
  • Refactored TCA8418 and derived keyboards:
    • KeyState enum and field moved to TCA8418Keyboard class (Nokia multitap keyboard) so classes derived from TCA8418KeyboardBase can handle key hold state their own way
    • Pass key parameter to release() allowing T-Lora Pager and T-Deck Pro to track multiple held keys at once
    • Standardize trigger() implementation into TCA8418KeyboardBase, and fix it to process all available FIFO key events correctly
    • Remove unused/unrelated code from T-Lora Pager and T-Deck Pro keyboards (eg multitap)
    • Make event maps const so they do not take up RAM
  • N-key rollover: Due to above changes, T-Lora Pager and T-Deck Pro can now handle multiple (probably all) keys being held at once, and register them correctly in order of key release, helps greatly with typing fast registering inputs correctly (starting pressing the next key before releasing the previous key doesn't invalidate the previous key anymore)
  • T-Lora Pager and T-Deck Pro Keyboard modifiers persist while held, even if a non-modifier key is pressed, like you would expect from a keyboard (eg can keep holding shift to type multiple keys in caps)
    • However can still press a single modifier on its own to make it apply to the next keystroke (eg for one handed use, press modifier then press key, can click another modifier to cancel the active modifier)
  • TCA8418 now uses KB_INT pin to trigger on interrupts, thus no more 300ms typing delay on T-Lora Pager and T-Deck Pro keyboards
    • I plan to bring this concept to other applicable i2c keyboards soon when this PR is merged
  • New InputPollable system in InputBroker that allows for polling inputs right after an interrupt so no events are missed, this is what TCA8418 now uses for interrupts
    • Basic idea is:
    • InputEvent sources inherit from InputPollable (like they did with Observable<const InputEvent *>
    • Define a pollOnce() method on the input source class, this will be run from userspace so it can do whatever
    • Inside pollOnce() add events with inputBroker->queueInputEvent(&event) (instead of this->notifyObservers(&event) with previous system)
    • During setup attach an interrupt for the device and in the interrupt handler call inputBroker->pollSoonRequestFromIsr(this) (this being the input event source class which inherits from InputPollable)
    • With this setup, when the interrupt is hit the class will be polled using pollOnce() by InputBroker as soon as the interrupt handler exits, bypassing cooperative multitasking delay of OSThread, providing much better responsiveness and avoiding missed inputs, and avoiding unnecessary periodic polling
    • After the input events are collected with inputBroker->queueInputEvent(&event) in pollOnce(), they will be processed by GUI and other modules on the main thread when possible via inputBroker->processInputEventQueue() which is shared among all input event sources that use this paradigm
    • Note that this relies on FreeRTOS, so it will not work on platforms that don't use FreeRTOS

Unfortunately I do not have a T-Deck Pro to test, but being so similar to the T-Lora Pager keyboard I'm guessing it should be fine.

I also have no way of testing regressions on TCA8418Keyboard, which AFAICT is for Nokia 5130 with multitap (click same key multiple times to cycle what character it produces); since it's multitap I did not add N-key rollover to it, I tried to keep it equivalent behavior to current, but can't test for regressions from the refactoring.

🤝 Attestations

  • I have tested that my proposed changes behave as described.
  • I have tested that my proposed changes do not cause any obvious regressions on the following devices:
    • Heltec (Lora32) V3
    • LilyGo T-Deck
    • LilyGo T-Beam
    • RAK WisBlock 4631
    • Seeed Studio T-1000E tracker card
    • Other (please specify below)
      • T-Lora Pager

- Pass key parameter to release(), allows tracking multiple held keys
- Move KeyState enum and member out of TCA8418KeyboardBase class
- Process all FIFO events in trigger(), remove overrides of trigger()
- Remove all unused/unrelated code from TDeckPro/TLoraPager Keyboard
- TDeckPro/TLoraPager Keyboard track held keys individually, can press
  multiple keys together and they are registered in order of release,
  helps greatly with typing fast registering inputs correctly
- TDeckPro/TLoraPager Keyboard modifiers persist while held, even if a
  non-modifier key is pressed, like you would expect from a keyboard
- Keep TCA8418Keyboard (multitap) handling one key at a time, wouldn't
  make much sense to handle multiple held keys on a multitap keyboard
- Make event maps const so they do not take up RAM
- Restore behavior of single modifier keypress influencing next keypress
- Holding modifier and clicking other keys/modifiers still combines them
  as you would expect from a real keyboard
- But for ease of use, can press just one modifier on its own to make it
  apply to the next keypress
@Szetya

Szetya commented Sep 15, 2025

Copy link
Copy Markdown
Contributor

Hi @WillyJL!

The "cardputer adv" keyboard is also TCA8418. There is no PR yet.
I shared the related files here
#7985 (comment)
Could you later complete the development on this keyboard file as well?
Thanks! 😇

@thebentern thebentern requested a review from mverch67 September 15, 2025 11:26
@WillyJL

WillyJL commented Sep 15, 2025

Copy link
Copy Markdown
Contributor Author

Hi @WillyJL!

The "cardputer adv" keyboard is also TCA8418. There is no PR yet.

I shared the related files here

#7985 (comment)

Could you later complete the development on this keyboard file as well?

Thanks! 😇

Of course! That was the next thing I wanted to try and figure out, since a YouTuber and friend of mine got a cardputer adv and noted how typing felt quite sluggish.

How should we go about it? Make a PR for cardputer adv first and once that's merged I update this PR for it? Or merge this first, and I'll provide the updated versions of the relevant files for cardputer adv for when that PR is opened?

@Szetya

Szetya commented Sep 15, 2025

Copy link
Copy Markdown
Contributor

Of course! That was the next thing I wanted to try and figure out, since a YouTuber and friend of mine got a cardputer adv and noted how typing felt quite sluggish.

How should we go about it? Make a PR for cardputer adv first and once that's merged I update this PR for it? Or merge this first, and I'll provide the updated versions of the relevant files for cardputer adv for when that PR is opened?

I am not a PR expert. I don't want to start being one either. I'm afraid I would do more harm than good. :)
It's too complicated for me, and I'm afraid I'd mess something up and then freeze up.
I am happy to share similar things, the first steps. Sometimes I come up with ideas myself.
If what I have shared is useful, feel free to use it for PR.
I don't think the cardputer is in progress yet.
Since the last modification to the TCA8418 keyboard was made by you, it will be easier for you to merge it.

@WillyJL

WillyJL commented Sep 15, 2025

Copy link
Copy Markdown
Contributor Author

@Szetya just to confirm, are you the person that uploaded "Meshtastic For Cardputer-Adv" to M5Burner? i could not find a source for that firmware on M5Burner, and since it seems to work mostly well it would make sense to go off from that code to get it merged into here. if it is you, and its based on the files you shared in #7985 (comment), then i can open a PR for you with you as commit author, and based on whatever PR gets merged first i will update the other.

@Szetya

Szetya commented Sep 15, 2025

Copy link
Copy Markdown
Contributor

@Szetya just to confirm, are you the person that uploaded "Meshtastic For Cardputer-Adv" to M5Burner? i could not find a source for that firmware on M5Burner, and since it seems to work mostly well it would make sense to go off from that code to get it merged into here. if it is you, and its based on the files you shared in #7985 (comment), then i can open a PR for you with you as commit author, and based on whatever PR gets merged first i will update the other.

No, I didn't upload it.
Since M5burner binary is not official, I would be happy if it were available here later.
Oh, I just noticed that some uppercase characters in the files are lowercase (e.g., n). 🙁

@WillyJL

WillyJL commented Sep 15, 2025

Copy link
Copy Markdown
Contributor Author

No, I didn't upload it.

interesting. so it would seem this "ozbk" guy on M5Burner is in breach of Meshtastic's GPLv3 license.

@Szetya

Szetya commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

No, I didn't upload it.

interesting. so it would seem this "ozbk" guy on M5Burner is in breach of Meshtastic's GPLv3 license.

I tried searching for the user on GitHub. I was unsuccessful, or perhaps I wasn't persistent enough. 😉
However, I found them on the m5 community page.

@mverch67 mverch67 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I tested with a T-Deck Pro. The key combinations work except backspace: Typing backspace 2x within a short time reacts like escape and goes back to the main screen. Typing it slowly with a larger pause deletes the previously typed characters as expected.

Edit: I noticed when typing quickly a longer text several typed characters are missing. I believe this wasn't the case before this PR.

@WillyJL

WillyJL commented Sep 16, 2025

Copy link
Copy Markdown
Contributor Author

Interesting, I can't replicate either of those on T-Lora Pager. I had that behavior, where pressing backspace multiple times quickly caused it to switch view (not quit the text input, but switch frame/tab in the ui to the clock one I think, and from there pressing backspace again kept deleting characters in the text input view even though I couldn't see it until all text was deleted where the next backspace removed the text input view from the tab list/switcher), before this PR, but not at all with these changes. Also, typing fast on it before I missed many inputs because I tend to click the next key before releasing the previous one, which with this PR is not a problem and registers correctly so I can type reliably much faster with this PR.

I'll reread the T-Deck Pro code in case I missed something but other than that debugging will be difficult for me as I don't have the device

@WillyJL

WillyJL commented Sep 16, 2025

Copy link
Copy Markdown
Contributor Author

No, I didn't upload it.

interesting. so it would seem this "ozbk" guy on M5Burner is in breach of Meshtastic's GPLv3 license.

I tried searching for the user on GitHub. I was unsuccessful, or perhaps I wasn't persistent enough. 😉

However, I found them on the m5 community page.

I asked in the Meshtastic discord, someone asked then in the M5Stack discord, they said:

There is no open source code yet, we are still optimizing

Which seems scummy to me. Anyway, I have built the code you shared in your issue and had my friend sn0ren test it with his cardputer adv (I don't have one), he said it works so I'll open a PR for it shortly with you as commit author so there's proper support without some third party gatekept build

@Szetya

Szetya commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

I tested with a T-Deck Pro. The key combinations work except backspace: Typing backspace 2x within a short time reacts like escape and goes back to the main screen. Typing it slowly with a larger pause deletes the previously typed characters as expected.

I experienced this behavior with the backspace key just yesterday when using the original files (on the cardputer). I can repeat it over and over again. At first, I thought it was a feature.

@Szetya

Szetya commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

No, I didn't upload it.

interesting. so it would seem this "ozbk" guy on M5Burner is in breach of Meshtastic's GPLv3 license.

I tried searching for the user on GitHub. I was unsuccessful, or perhaps I wasn't persistent enough. 😉
However, I found them on the m5 community page.

I asked in the Meshtastic discord, someone asked then in the M5Stack discord, they said:

There is no open source code yet, we are still optimizing

Which seems scummy to me. Anyway, I have built the code you shared in your issue and had my friend sn0ren test it with his cardputer adv (I don't have one), he said it works so I'll open a PR for it shortly with you as commit author so there's proper support without some third party gatekept build

We need to agree on which display driver to use. I don't want to decide that myself.
I couldn't fix the sound, so there will definitely be more work to do on that.
@mverch67 You have experience with sound. Maybe you could help.
Thank you for everything! 🤝

@mverch67

Copy link
Copy Markdown
Collaborator

@WillyJL: this is 2.7.7 alpha compared to the PR branch, quick typing test on the T-Deck Pro. Some characters get eaten.

2.7.7

IMG_0579.mp4

PR:

IMG_0578.mp4

@WillyJL

WillyJL commented Sep 16, 2025

Copy link
Copy Markdown
Contributor Author

@mverch67 thanks for the videos. The fact the screen refreshes slower makes me think it might be related. The TCA8418 has a 10 event FIFO, so I'm guessing the issue here is that there are more than 10 key press/releases before the main loop polls it again due to the gui sleeping (I'm assuming) longer to not redraw as fast on the epaper display, and it would make sense since it's at about 4-5 typed keys (so ~10 key events) not being drawn yet that it starts losing the last key not yet registered.

If my conclusion is correct, I see 2 ways to handle it. One is similar to the other PR I made, have a freertos thread that gets woken up by the interrupt and submit into queue, so the inputs don't depend on the main loop. Other is changing how display delays drawing, if my assumption of it sleeping longer is the case, to not sleep but instead just no-op and keep track of the last time to drew to the screen, so it can draw less often without sleeping on the main loop.

@mverch67

mverch67 commented Sep 16, 2025

Copy link
Copy Markdown
Collaborator

I have added the two logs. Indeed, there is a screen refresh pushing in when the characters get lost:
2-7-7.txt
PR.txt

Edit: log not related to the videos... the text displayed now on the PR version is: "this is ck t test"

@WillyJL WillyJL mentioned this pull request Sep 16, 2025
10 tasks
@WillyJL

WillyJL commented Sep 17, 2025

Copy link
Copy Markdown
Contributor Author

If my conclusion is correct, I see 2 ways to handle it. One is similar to the other PR I made, have a freertos thread that gets woken up by the interrupt and submit into queue, so the inputs don't depend on the main loop.

im thinking of ways to implement this fix, one way of course would be to do essentially the same thing i did for the rotary encoder PR, but that means 2 background threads doing nothing until an interrupt happens...

i was thinking maybe its worth standardizing this? eg move this freertos thread to input broker and have an interrupt-safe method in input broker to give it a class with its own method to run ASAP in the freertos thread that would then queue events into a input broker queue and input broker would process them on the main thread in loop().

for example:

  • interface InputPollable with virtual pollOnce() method
  • KbI2cBase inherits from InputPollable and in its pollOnce() method it does the usual TCAKeyboard.trigger() but instead of this->notifyObservers(&e) it would do inputBroker->queueEvent(&e)
  • in the interrupt handler put inputBroker->pollSoonFromIsr(this) (this being KbI2cBase, since it inherits InputPollable)
  • inputBroker->pollSoonFromIsr(InputPollable*) will add this instance to a queue and wake up a freertos thread internal to input broker so as soon as ISR context is exited, this thread runs to poll these inputs
  • somewhere in loop() add code to process events that were queued with inputBroker->queueEvent(&e) on the freertos thread

(names arent great can always change them, but i hope you get the idea)

this way gpio can be polled as soon as something happens to register the events so they are not lost (like they are here on tdeck pro), while handling them later when system is ready, and other input systems can use this model too without spawning each their own freertos thread and queue... @mverch67 if you too think this is a good idea i will implement it and update #7986 to use it too

one concern i have tho is im not sure what the state of freertos is on nrf52 based devices, is it possible to use freertos on them?

@mverch67

Copy link
Copy Markdown
Collaborator

@WillyJL
Your suggestions with the inputBroker / InputPollable sounds interesting. As with the queue the actual event time gets lost I assume that events need to be tagged with a time stamp so that long press / double press could be distinguished?

Regarding nrf52 I found this from last year which indicates that freeRTOS is available for the nordic platform. But care must be taken about the code size as nrf52 firmware is at its flash size limits.

@WillyJL

WillyJL commented Sep 19, 2025

Copy link
Copy Markdown
Contributor Author

As with the queue the actual event time gets lost I assume that events need to be tagged with a time stamp so that long press / double press could be distinguished?

i dont think it should: the way inputs reach the UI is the same as before, they are processed on the main loop, so for example if a screen refresh takes long the inputs will still be processed after they happen. this system however makes sure the inputs are recorded when they happen, even if the system is not ready to process them, so they do not get lost. AFAICT things like long press are handled in input sources, like TCA8418Keyboard class, which will emit different input events based on timing. this does not change, actually it would mean they are more responsive because it does not poll inputs with 300ms interval, but as soon a the hardware reports that an event has happened, so the logic in TCA8418Keyboard class to calculate time between inputs will be even more precise now. a concrete example of this is what you mentioned with pressing backspace 2 times quickly, for me on develop branch it often switches tab/frame instead of deleting 2 characters, while with this PR it always registers as deleting 2 characters, my guess is because it records the inputs right as they happen, not delayed by polling, thus the input timing is more accurate, even if they are processed by the UI later anyway.

as for FreeRTOS on nrf52, i noticed src/freertosinc.h which includes FreeRTOS headers if FreeRTOS is available for the given platform and sets the HAS_FREE_RTOS define, which is the case for esp32, nrf52 and rp2040. i added appropriate #if checks for HAS_FREE_RTOS and MESHTASTIC_EXCLUDE_INPUTBROKER so this new helper is only compiled in where appropriate. i tried building targets tlora-pager (esp32s3), rak4631 (nrf52840), rak3172 (stm32), rak11310 (rp2040) and pico2 (rp2350), they all compile fine (besides current issues with building on develop branch), and it works very well on T-Lora Pager. i will push shortly when develop branch is fixed up so i can merge it here.

unfortunately i do not have any other device on different architectures so i cannot test those without freertos besides "it compiles", but i assume its fine since i dont think there is any overlap between devices without freertos and devices with these input modules that im porting to a interrupt + freertos task + queue approach.

@WillyJL WillyJL marked this pull request as ready for review September 24, 2025 01:12
@github-actions github-actions Bot added the Stale Issues that will be closed if not triaged. label Dec 12, 2025
@github-actions github-actions Bot closed this Dec 20, 2025
@WillyJL

WillyJL commented Dec 20, 2025

Copy link
Copy Markdown
Contributor Author

@mverch67 is it possible to reopen this? Or should I make a new PR?

@thebentern thebentern reopened this Dec 20, 2025
@github-actions github-actions Bot removed the Stale Issues that will be closed if not triaged. label Dec 24, 2025
@caveman99 caveman99 added enhancement New feature or request hardware-support Hardware related: new devices or modules, problems specific to hardware labels Feb 5, 2026
@caveman99 caveman99 mentioned this pull request Feb 5, 2026
@github-actions github-actions Bot added the Stale Issues that will be closed if not triaged. label Mar 25, 2026
@github-actions github-actions Bot closed this Apr 2, 2026
@WillyJL

WillyJL commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

sigh

@mverch67 mverch67 reopened this Apr 2, 2026
@github-actions github-actions Bot removed the Stale Issues that will be closed if not triaged. label Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request hardware-support Hardware related: new devices or modules, problems specific to hardware

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants