Skip to content

fix: dynamically assign button pin to prevent false interrupts#10742

Open
LN4CY wants to merge 2 commits into
meshtastic:developfrom
LN4CY:fix-dynamic-button-pin
Open

fix: dynamically assign button pin to prevent false interrupts#10742
LN4CY wants to merge 2 commits into
meshtastic:developfrom
LN4CY:fix-dynamic-button-pin

Conversation

@LN4CY

@LN4CY LN4CY commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

This PR resolves the false interrupt and random wake issues caused by floating button pins on variants where the button is not physically wired (such as rak3401_1watt), while fully restoring button functionality when a GPIO is explicitly assigned via the Meshtastic configuration.

Changes Made:

  • Removed hardcoded #ifdef BUTTON_PIN guards in InputBroker.cpp, PowerFSM.cpp, sleep.cpp, and main-esp32.cpp in favor of #if HAS_BUTTON.
  • Refactored logic to dynamically resolve _btnPin using the following priority:
    1. config.device.button_gpio (if explicitly assigned at runtime)
    2. USERPREFS_BUTTON_PIN (if defined in userprefs)
    3. BUTTON_PIN (if defined in the variant header)
  • If no valid pin is resolved (evaluates to 0xFF), the firmware skips configuring the button pull-ups, skips initializing ButtonThread, and ignores digital reads during sleep, avoiding any false positives on floating pins.

?? 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: RAK3401 1-Watt
    • Other: RAK WisMesh Tap

Fixes #10574

Summary by CodeRabbit

  • Bug Fixes
    • Improved button detection after waking from sleep.
    • Added support for selecting the configured button GPIO at runtime.
    • Corrected button pull-up and wake settings across light and deep sleep modes.
    • Prevented button wake handling when no valid button pin is configured.
    • Improved compatibility across supported ESP32 variants and button configurations.

@github-actions github-actions Bot added the bugfix Pull request that fixes bugs label Jun 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Note

Building this pull request… the flash button, badges and supported-board
list will appear here automatically once CI finishes.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Button GPIO selection now supports runtime device configuration with compile-time fallbacks across input initialization, sleep wake configuration, and idle-state wake event detection.

Changes

Button GPIO handling

Layer / File(s) Summary
Resolve and initialize the button GPIO
src/input/InputBroker.cpp
Button setup derives the GPIO from USERPREFS_BUTTON_PIN or BUTTON_PIN, overrides it with config.device.button_gpio, applies platform-specific pull-up configuration, and creates UserButtonThread only for a valid pin.
Apply the selected GPIO to sleep wake paths
src/sleep.cpp, src/platform/esp32/main-esp32.cpp
Deep and light sleep configure holds, pull-ups, wake masks, and wake interrupts using the selected button GPIO, including guarded external wake setup.
Map wake input to FSM events
src/PowerFSM.cpp
Idle wake handling reads the resolved GPIO and emits either EVENT_PRESS or EVENT_WAKE_TIMER when no valid pin is available.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: jp-bennett, caveman99, thebentern

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: dynamically selecting the button pin to prevent false interrupts.
Description check ✅ Passed The description covers the bug, summarizes the implementation, links the issue, and includes attestations/testing.
Linked Issues check ✅ Passed The code restores user-button handling when GPIO 31 is assigned and keeps the fix centered on the linked issue's goal.
Out of Scope Changes check ✅ Passed All changes stay within button-pin resolution and sleep/input handling needed for the reported false interrupt fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/input/InputBroker.cpp`:
- Around line 170-178: Duplicate button GPIO resolution should be centralized in
an inline helper such as getResolvedButtonPin(), preserving the
USERPREFS_BUTTON_PIN, BUTTON_PIN, and config.device.button_gpio precedence. Add
the helper in the shared configuration or utility location, then replace the
duplicated blocks in src/input/InputBroker.cpp lines 170-178 and 329-337,
src/sleep.cpp lines 313-321, 440-448, and 485-493,
src/platform/esp32/main-esp32.cpp lines 355-363, and src/PowerFSM.cpp lines
180-188 with calls to it.

In `@src/platform/esp32/main-esp32.cpp`:
- Around line 367-369: Validate the resolved button GPIO before use, preferably
in the shared pin-resolution helper, using the platform’s GPIO validity check or
an equivalent bounds check. Apply this to the wake-mask shift in
main-esp32.cpp#L367-L369, the digitalRead path in PowerFSM.cpp#L189, and
OneButton instantiation in InputBroker.cpp#L348-L349; invalid pins must be
excluded so they cannot cause undefined shifts or phantom button events.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 32fa657f-7831-4f92-ba75-b84721911335

📥 Commits

Reviewing files that changed from the base of the PR and between de0380c and 75a378d.

📒 Files selected for processing (4)
  • src/PowerFSM.cpp
  • src/input/InputBroker.cpp
  • src/platform/esp32/main-esp32.cpp
  • src/sleep.cpp

Comment thread src/input/InputBroker.cpp
Comment on lines +170 to +178
uint32_t _btnPin = 0xFF;
#if defined(USERPREFS_BUTTON_PIN)
_btnPin = USERPREFS_BUTTON_PIN;
#elif defined(BUTTON_PIN)
_btnPin = BUTTON_PIN;
#endif
if (config.device.button_gpio != 0) {
_btnPin = config.device.button_gpio;
}

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract duplicated button pin resolution logic into a helper function.

The exact block of logic that dynamically resolves the button GPIO from USERPREFS_BUTTON_PIN, BUTTON_PIN, and config.device.button_gpio is duplicated 7 times across the codebase. This violates the DRY principle and increases maintenance overhead.

Please extract this into a centralized inline helper function (e.g., getResolvedButtonPin() in configuration.h or utils.h) and replace these duplicate blocks with a single function call.

  • src/input/InputBroker.cpp#L170-L178: Replace the duplicated 9-line block with a call to the new helper function.
  • src/input/InputBroker.cpp#L329-L337: Replace the duplicated block with the helper call.
  • src/sleep.cpp#L313-L321: Replace the duplicated block with the helper call.
  • src/sleep.cpp#L440-L448: Replace the duplicated block with the helper call.
  • src/sleep.cpp#L485-L493: Replace the duplicated block with the helper call.
  • src/platform/esp32/main-esp32.cpp#L355-L363: Replace the duplicated block with the helper call.
  • src/PowerFSM.cpp#L180-L188: Replace the duplicated block with the helper call.
📍 Affects 4 files
  • src/input/InputBroker.cpp#L170-L178 (this comment)
  • src/input/InputBroker.cpp#L329-L337
  • src/sleep.cpp#L313-L321
  • src/sleep.cpp#L440-L448
  • src/sleep.cpp#L485-L493
  • src/platform/esp32/main-esp32.cpp#L355-L363
  • src/PowerFSM.cpp#L180-L188
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/input/InputBroker.cpp` around lines 170 - 178, Duplicate button GPIO
resolution should be centralized in an inline helper such as
getResolvedButtonPin(), preserving the USERPREFS_BUTTON_PIN, BUTTON_PIN, and
config.device.button_gpio precedence. Add the helper in the shared configuration
or utility location, then replace the duplicated blocks in
src/input/InputBroker.cpp lines 170-178 and 329-337, src/sleep.cpp lines
313-321, 440-448, and 485-493, src/platform/esp32/main-esp32.cpp lines 355-363,
and src/PowerFSM.cpp lines 180-188 with calls to it.

Comment on lines +367 to +369
if (_btnPin != 0xFF) {
gpioMask |= (1ULL << _btnPin);
}

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.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Missing validation for user-configured button GPIO leads to undefined behavior and phantom presses.

The dynamically resolved button pin (which can be overridden by the user via config.device.button_gpio protobuf payload) is not validated before use. If an invalid or out-of-bounds pin (e.g., >= 64) is provided, it triggers severe functional and safety failures across the system. The shared root cause is the lack of a bounds check (e.g., _btnPin < 64 or ESP-IDF's GPIO_IS_VALID_GPIO(_btnPin)) for the resolved pin.

  • src/platform/esp32/main-esp32.cpp#L367-L369: The operation 1ULL << _btnPin results in undefined behavior (shifting beyond the width of a 64-bit integer) if the pin is >= 64. This can corrupt the wake mask or crash the firmware upon entering deep sleep. Guard this with a validity check.
  • src/PowerFSM.cpp#L189-L189: digitalRead on an invalid pin gracefully returns 0 (LOW), which evaluates !digitalRead permanently to true. This creates an infinite wake loop flooded with phantom EVENT_PRESS transitions. Guard the read with a validity check.
  • src/input/InputBroker.cpp#L348-L349: Instantiating the thread with an invalid pin will cause OneButton's internal digitalRead to constantly return LOW, resulting in continuous phantom holds/clicks inside the thread. Guard the instantiation with a validity check.

If you consolidate the pin resolution into a single helper (as suggested separately), adding the validity check inside that helper will safely fix all these sites at once.

📍 Affects 3 files
  • src/platform/esp32/main-esp32.cpp#L367-L369 (this comment)
  • src/PowerFSM.cpp#L189-L189
  • src/input/InputBroker.cpp#L348-L349
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/platform/esp32/main-esp32.cpp` around lines 367 - 369, Validate the
resolved button GPIO before use, preferably in the shared pin-resolution helper,
using the platform’s GPIO validity check or an equivalent bounds check. Apply
this to the wake-mask shift in main-esp32.cpp#L367-L369, the digitalRead path in
PowerFSM.cpp#L189, and OneButton instantiation in InputBroker.cpp#L348-L349;
invalid pins must be excluded so they cannot cause undefined shifts or phantom
button events.

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

Labels

bugfix Pull request that fixes bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Add user button for Rak 1 Watt radio

1 participant