Skip to content

T-Watch S3 Power button managment#9855

Merged
jp-bennett merged 21 commits into
meshtastic:developfrom
Emanuele-Mb:develop
Apr 24, 2026
Merged

T-Watch S3 Power button managment#9855
jp-bennett merged 21 commits into
meshtastic:developfrom
Emanuele-Mb:develop

Conversation

@Emanuele-Mb

@Emanuele-Mb Emanuele-Mb commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Hello there
I started working on this easy and simple modification because the only way to wake the t-watch s3 from light sleep status was tapping the screen and the button didn't do anything.
I noticed by looking at the circuit that the button is connected not to a pin of the main esp32s3 board, but directly and only to the PMU. So I made it so that when you press the button the PMU raises an interrupt that resets the counter of the run_once() function in the power class, making it possible to add further modifications to it if needed (for example in the future I might add something on the long press).
To prevent any impact on other devices, I've enclosed the changes within the #ifdef T_WATCH_S3 macro.

In the process of doing this I noticed that the PMU interrupt pin wasn't defined on the variant.h file, so I defined it (info obtained via official documentation of the watch).

🤝 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 (T-Watch S3)

Added interrupt handling for the Power/Corona button on T-Watch S3, I use it to control screen state.
@CLAassistant

CLAassistant commented Mar 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions github-actions Bot added the hardware-support Hardware related: new devices or modules, problems specific to hardware label Mar 7, 2026
@thebentern thebentern requested a review from Copilot March 9, 2026 22:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Enable the T-Watch S3 “Power/Corona” button to wake/trigger logic via the PMU interrupt line, without affecting other boards (guarded by T_WATCH_S3).

Changes:

  • Define the PMU interrupt GPIO for the T-Watch S3 variant.
  • Attach a PMU IRQ interrupt in Power::setup() to wake/schedule the power task immediately.
  • Handle PMU short-press events in Power::runOnce() to toggle the display on/off.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
variants/esp32s3/t-watch-s3/variant.h Adds a macro identifying the PMU IRQ GPIO used by the watch.
src/Power.cpp Wires PMU IRQ into the power task scheduling and adds short-press behavior for screen toggling.

Comment thread src/Power.cpp Outdated
Comment thread variants/esp32s3/t-watch-s3/variant.h
Comment thread src/Power.cpp Outdated
@jp-bennett jp-bennett self-assigned this Apr 19, 2026
@jp-bennett

Copy link
Copy Markdown
Collaborator

Finally got a chance to test this, and Bravo! Really adds something that was missing to the T-watch-s3, and I'll look at re-using this idea for other devices. Thanks!

@jp-bennett

Copy link
Copy Markdown
Collaborator

There is a bit of trouble in paradise:

INFO  | ??:??:?? 245 [PowerFSM] setup LORA_DIO1 (GPIO09) with wakeup by gpio interrupt
Guru Meditation Error: Core  1 panic'ed (Interrupt wdt timeout on CPU1). 

Core  1 register dump:
PC      : 0x40384fef  PS      : 0x00060534  A0      : 0x803758e9  A1      : 0x3fc9d4e0  
  #0  0x40384fef in xTimerPendFunctionCallFromISR at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:1114

A2      : 0x420a5e84  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x3fc9d51c  
  #0  0x420a5e84 in pmu_deferred_handler(void*, unsigned int) at :?

A6      : 0x00000000  A7      : 0x00000003  A8      : 0x3fcaf488  A9      : 0x3fc9d4b0  
A10     : 0x3fcaf38c  A11     : 0x3fc9d4e0  A12     : 0x3fc9d51c  A13     : 0x00000000  
A14     : 0x3fcaf38c  A15     : 0x3fcaf3d8  SAR     : 0x0000000b  EXCCAUSE: 0x00000006  
EXCVADDR: 0x00000000  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x00000000  
Core  1 was running in ISR context:
EPC1    : 0x4217c87f  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x40384fef
  #0  0x4217c87f in uart_hal_write_txfifo at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/hal/uart_hal_iram.c:35



Backtrace: 0x40384fec:0x3fc9d4e0 0x403758e6:0x3fc9d510 0x42016eb9:0x3fc9d540 0x40375931:0x3fc9d560 0x40375956:0x3fc9d580 0x4037a0fd:0x3fc9d5a0 0x40375992:0x3fcebd40 0x403854d2:0x3fcebd50 0x420dfd8e:0x3fcebd70 0x42045cd7:0x3fcebdc0 0x420a05e7:0x3fcebde0 0x420047bb:0x3fcebe00 0x420827ba:0x3fcebe20 0x4209db7a:0x3fcebe40 0x42081a61:0x3fcebe60 0x4201eb61:0x3fcebeb0
  #0  0x40384fec in xTimerPendFunctionCallFromISR at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:1114
  #1  0x3fc9d4e0 in port_IntStack at ??:?
  #2  0x403758e6 in pmu_isr_handler() at :?
  #3  0x3fc9d510 in port_IntStack at ??:?
  #4  0x42016eb9 in __onPinInterrupt at :?
  #5  0x3fc9d540 in port_IntStack at ??:?
  #6  0x40375931 in gpio_isr_loop at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/gpio.c:417
  #7  0x3fc9d560 in port_IntStack at ??:?
  #8  0x40375956 in gpio_intr_service at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/gpio.c:434
  #9  0x3fc9d580 in port_IntStack at ??:?
  #10 0x4037a0fd in _xt_lowint1 at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/xtensa_vectors.S:1118
  #11 0x3fc9d5a0 in port_IntStackTop at ??:?
  #12 0x40375992 in gpio_ll_clear_intr_status_high at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/hal/esp32s3/include/hal/gpio_ll.h:150
      (inlined by) gpio_intr_service at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/gpio.c:444
  #13 0x403854d2 in vPortClearInterruptMaskFromISR at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/include/freertos/portmacro.h:571
      (inlined by) vPortExitCritical at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/port.c:332
  #14 0x420dfd8e in esp_light_sleep_start at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_hw_support/sleep_modes.c:894
  #15 0x42045cd7 in doLightSleep(unsigned long long) at ??:?
  #16 0x420a05e7 in _ZL6lsIdlev$lto_priv$1443 at ??:?
  #17 0x420047bb in _ZN3Fsm11run_machineEv$constprop$1157 at ??:?
  #18 0x420827ba in concurrency::PowerFSMThread::runOnce() at ??:?
  #19 0x4209db7a in concurrency::OSThread::run() at ??:?
  #20 0x42081a61 in loop() at ??:?
  #21 0x4201eb61 in loopTask(void*) at :?



Core  0 register dump:
PC      : 0x420179d3  PS      : 0x00060134  A0      : 0x8209daf4  A1      : 0x3fcf6370  
  #0  0x420179d3 in millis at ??:?

A2      : 0x00060123  A3      : 0x00000000  A4      : 0x00060120  A5      : 0x3fcf63e0  
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x00000000  A9      : 0x3fcf6350  
A10     : 0x0003ceae  A11     : 0x00000000  A12     : 0x00000000  A13     : 0x00000000  
A14     : 0x00000001  A15     : 0x3fcf5920  SAR     : 0x0000000a  EXCCAUSE: 0x00000006  
EXCVADDR: 0x00000000  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x00000000  


Backtrace: 0x420179d0:0x3fcf6370 0x4209daf1:0x3fcf6390 0x420a5e91:0x3fcf63b0 0x40384eed:0x3fcf63d0
  #0  0x420179d0 in millis at ??:?
  #1  0x4209daf1 in concurrency::OSThread::setIntervalFromNow(unsigned long) at ??:?
  #2  0x420a5e91 in pmu_deferred_handler(void*, unsigned int) at :?
  #3  0x40384eed in prvProcessReceivedCommands at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:801
      (inlined by) prvTimerTask at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:600

This code currently crashes when the crown is pressed during sleep.

@Emanuele-Mb

Copy link
Copy Markdown
Contributor Author

There is a bit of trouble in paradise:

INFO  | ??:??:?? 245 [PowerFSM] setup LORA_DIO1 (GPIO09) with wakeup by gpio interrupt
Guru Meditation Error: Core  1 panic'ed (Interrupt wdt timeout on CPU1). 

Core  1 register dump:
PC      : 0x40384fef  PS      : 0x00060534  A0      : 0x803758e9  A1      : 0x3fc9d4e0  
  #0  0x40384fef in xTimerPendFunctionCallFromISR at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:1114

A2      : 0x420a5e84  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x3fc9d51c  
  #0  0x420a5e84 in pmu_deferred_handler(void*, unsigned int) at :?

A6      : 0x00000000  A7      : 0x00000003  A8      : 0x3fcaf488  A9      : 0x3fc9d4b0  
A10     : 0x3fcaf38c  A11     : 0x3fc9d4e0  A12     : 0x3fc9d51c  A13     : 0x00000000  
A14     : 0x3fcaf38c  A15     : 0x3fcaf3d8  SAR     : 0x0000000b  EXCCAUSE: 0x00000006  
EXCVADDR: 0x00000000  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x00000000  
Core  1 was running in ISR context:
EPC1    : 0x4217c87f  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x40384fef
  #0  0x4217c87f in uart_hal_write_txfifo at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/hal/uart_hal_iram.c:35



Backtrace: 0x40384fec:0x3fc9d4e0 0x403758e6:0x3fc9d510 0x42016eb9:0x3fc9d540 0x40375931:0x3fc9d560 0x40375956:0x3fc9d580 0x4037a0fd:0x3fc9d5a0 0x40375992:0x3fcebd40 0x403854d2:0x3fcebd50 0x420dfd8e:0x3fcebd70 0x42045cd7:0x3fcebdc0 0x420a05e7:0x3fcebde0 0x420047bb:0x3fcebe00 0x420827ba:0x3fcebe20 0x4209db7a:0x3fcebe40 0x42081a61:0x3fcebe60 0x4201eb61:0x3fcebeb0
  #0  0x40384fec in xTimerPendFunctionCallFromISR at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:1114
  #1  0x3fc9d4e0 in port_IntStack at ??:?
  #2  0x403758e6 in pmu_isr_handler() at :?
  #3  0x3fc9d510 in port_IntStack at ??:?
  #4  0x42016eb9 in __onPinInterrupt at :?
  #5  0x3fc9d540 in port_IntStack at ??:?
  #6  0x40375931 in gpio_isr_loop at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/gpio.c:417
  #7  0x3fc9d560 in port_IntStack at ??:?
  #8  0x40375956 in gpio_intr_service at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/gpio.c:434
  #9  0x3fc9d580 in port_IntStack at ??:?
  #10 0x4037a0fd in _xt_lowint1 at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/xtensa_vectors.S:1118
  #11 0x3fc9d5a0 in port_IntStackTop at ??:?
  #12 0x40375992 in gpio_ll_clear_intr_status_high at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/hal/esp32s3/include/hal/gpio_ll.h:150
      (inlined by) gpio_intr_service at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/gpio.c:444
  #13 0x403854d2 in vPortClearInterruptMaskFromISR at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/include/freertos/portmacro.h:571
      (inlined by) vPortExitCritical at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port/xtensa/port.c:332
  #14 0x420dfd8e in esp_light_sleep_start at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_hw_support/sleep_modes.c:894
  #15 0x42045cd7 in doLightSleep(unsigned long long) at ??:?
  #16 0x420a05e7 in _ZL6lsIdlev$lto_priv$1443 at ??:?
  #17 0x420047bb in _ZN3Fsm11run_machineEv$constprop$1157 at ??:?
  #18 0x420827ba in concurrency::PowerFSMThread::runOnce() at ??:?
  #19 0x4209db7a in concurrency::OSThread::run() at ??:?
  #20 0x42081a61 in loop() at ??:?
  #21 0x4201eb61 in loopTask(void*) at :?



Core  0 register dump:
PC      : 0x420179d3  PS      : 0x00060134  A0      : 0x8209daf4  A1      : 0x3fcf6370  
  #0  0x420179d3 in millis at ??:?

A2      : 0x00060123  A3      : 0x00000000  A4      : 0x00060120  A5      : 0x3fcf63e0  
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x00000000  A9      : 0x3fcf6350  
A10     : 0x0003ceae  A11     : 0x00000000  A12     : 0x00000000  A13     : 0x00000000  
A14     : 0x00000001  A15     : 0x3fcf5920  SAR     : 0x0000000a  EXCCAUSE: 0x00000006  
EXCVADDR: 0x00000000  LBEG    : 0x40056f5c  LEND    : 0x40056f72  LCOUNT  : 0x00000000  


Backtrace: 0x420179d0:0x3fcf6370 0x4209daf1:0x3fcf6390 0x420a5e91:0x3fcf63b0 0x40384eed:0x3fcf63d0
  #0  0x420179d0 in millis at ??:?
  #1  0x4209daf1 in concurrency::OSThread::setIntervalFromNow(unsigned long) at ??:?
  #2  0x420a5e91 in pmu_deferred_handler(void*, unsigned int) at :?
  #3  0x40384eed in prvProcessReceivedCommands at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:801
      (inlined by) prvTimerTask at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/timers.c:600

This code currently crashes when the crown is pressed during sleep.

Thank you for testing my code, I will look into this.
It never happened to me probably because I deactivated deep sleep altogether from the app while using it.

@jp-bennett

Copy link
Copy Markdown
Collaborator

This code currently crashes when the crown is pressed during sleep.

Thank you for testing my code, I will look into this. It never happened to me probably because I deactivated deep sleep altogether from the app while using it.

I'm doing a little more testing, and I think this crash happens even without your code. Elsewhere in the code we unhook interrupts for sleep, and for whatever reason we're not doing so in the power function. I'll spend a bit more time on this today, and see if I can get it cleaned up.

@jp-bennett

Copy link
Copy Markdown
Collaborator

The bug fixed by #10231 was the cause of at least some of the crashing I was seeing. But it appears we also need #10230

@jp-bennett

Copy link
Copy Markdown
Collaborator

Once we get the upstream branches wrangled, this one is good to go.

@jp-bennett jp-bennett merged commit 924411d into meshtastic:develop Apr 24, 2026
78 checks passed
@Emanuele-Mb

Copy link
Copy Markdown
Contributor Author

Thank you @jp-bennett for your precious work and time, I didn't know about the input broker channel you used.
Also thank you for your patience in seeing my pull request merged.

mariotti pushed a commit to mariotti/firmware that referenced this pull request May 6, 2026
* PMU interrupt pin defined in t-watch s3

* Implement button control on T-Watch S3

Added interrupt handling for the Power/Corona button on T-Watch S3, I use it to control screen state.

* Reducing labels

* Reducing labels

* Updated the comment

* ISR is now IRAM-safe

Updated interrupt management not to cause random crashes.

* Trunk

* Simplify and use INPUT_BROKER_CANCEL

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

4 participants