Skip to content

Stop accelerometer thread when double-tap/wake-on-motion disabled at runtime#11025

Open
thebentern wants to merge 1 commit into
developfrom
claude/compassionate-tesla-de5f79
Open

Stop accelerometer thread when double-tap/wake-on-motion disabled at runtime#11025
thebentern wants to merge 1 commit into
developfrom
claude/compassionate-tesla-de5f79

Conversation

@thebentern

@thebentern thebentern commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Problem

double_tap_as_button_press (device config) and wake_on_tap_or_motion (display config) are applied live, without a reboot — the requiresReboot equality gates in handleSetConfig deliberately exclude these two fields. On the OFF→ON edge, each handler calls accelerometerThread->start() and sets enabled = true.

But there was no ON→OFF branch anywhere (grep 'accelerometerThread->' shows only calibrate, enabled = true, and start()). So:

  • Turning either flag off at runtime left the accelerometer thread running — polling I2C and drawing power — until the next reboot.
  • Because enabled stayed stuck true, a later OFF→ON edge became a no-op (the enabled == false guard blocked the re-start()), leaving the feature un-restartable without a reboot.

Fix

Add the symmetric ON→OFF branch to both handlers. When a flag transitions true→false and the other consumer of the shared thread is also off, call accelerometerThread->disable():

  • OSThread::disable() sets enabled = false and setInterval(INT32_MAX), halting runOnce() (no more I2C polling) without deleting the sensor. Since init() is idempotent, a later start() cleanly resumes — no leak, fully restartable.
  • Each branch checks the other flag first, so disabling one feature never stops the sensor while the other still needs it.

Chose the live disable() (symmetric with the existing live start()) over adding these fields to the requiresReboot gate, since the flags are intentionally applied live — a reboot gate would clash with the existing live-start.

Transition matrix (D = double-tap, W = wake-on-motion; only one config variant changes per setConfig):

Transition Other flag After fix
D off→on W off start()
D on→off W off disable() (off, enabled cleared)
D on→off W on skip — W still needs it
W off→on D off start()
W on→off D off disable()
W on→off D on skip — D still needs it

This is a cooperative OSThread scheduled on mainController in the same loop context as setConfig, so disable() is safe with no concurrency concern.

Testing

  • Compile-verified for the rak4631 (nRF52840) target, where the accelerometer block is active (the native/portduino test build #if-excludes this region). AdminModule.cpp compiled cleanly.
  • Low severity: power/behavior, not a crash. No hardware required.

Summary by CodeRabbit

  • Bug Fixes
    • Improved power management by disabling motion detection when tap- and motion-based wake features are turned off.
    • Prevented unnecessary accelerometer activity after related wake settings are disabled.

…runtime

double_tap_as_button_press and wake_on_tap_or_motion are applied live: the
OFF->ON edge calls accelerometerThread->start(), but there was no ON->OFF
branch, so turning either flag off left the sensor thread running (polling
I2C, drawing power) until reboot. Worse, because enabled stayed true, a later
OFF->ON edge was a no-op (the enabled==false guard blocked re-start), leaving
the feature un-restartable without a reboot.

Add the symmetric ON->OFF branch in both handlers. When a flag goes true->off
and the other consumer of the shared thread is also off, call
accelerometerThread->disable() (stops runOnce polling and clears enabled so a
later re-enable can start() again). Each branch checks the other flag first so
disabling one feature never stops the sensor while the other still needs it.
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

AdminModule::handleSetConfig() now disables the accelerometer thread when disabling either wake-related setting leaves no remaining accelerometer-dependent behavior enabled.

Changes

Accelerometer configuration

Layer / File(s) Summary
Disable unused accelerometer for wake behaviors
src/modules/AdminModule.cpp
Disabling double_tap_as_button_press or wake_on_tap_or_motion now disables an enabled accelerometer thread when the other behavior is also disabled.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: nomdetom, jp-bennett, h3lix1

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the runtime accelerometer-thread shutdown for double-tap and wake-on-motion settings.
Description check ✅ Passed The description covers the problem, fix, and testing, and is mostly aligned with the template despite missing the attestation checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/compassionate-tesla-de5f79

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.

Caution

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

⚠️ Outside diff range comments (1)
src/modules/AdminModule.cpp (1)

791-803: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Null-pointer dereference on devices without an accelerometer.

accelerometerThread is only allocated if compatible hardware is detected at runtime. If an admin configuration update toggling these settings is received on a node lacking an accelerometer, dereferencing accelerometerThread->enabled will cause a null-pointer crash. Both the new disable() paths and the adjacent existing start() paths need to be guarded against a null pointer.

  • src/modules/AdminModule.cpp#L791-L803: Add a null check before accessing accelerometerThread for the device configuration toggle.
  • src/modules/AdminModule.cpp#L902-L913: Add a null check before accessing accelerometerThread for the display configuration toggle.
🛡️ Proposed fixes

For src/modules/AdminModule.cpp#L791-L803:

-        if (config.device.double_tap_as_button_press == false && c.payload_variant.device.double_tap_as_button_press == true &&
-            accelerometerThread->enabled == false) {
+        if (config.device.double_tap_as_button_press == false && c.payload_variant.device.double_tap_as_button_press == true &&
+            accelerometerThread && accelerometerThread->enabled == false) {
             config.device.double_tap_as_button_press = c.payload_variant.device.double_tap_as_button_press;
             accelerometerThread->enabled = true;
             accelerometerThread->start();
         }
         // Turning double-tap off: stop the accelerometer thread, but only if wake-on-motion doesn't still need it.
         // disable() clears enabled so a later re-enable can restart it.
-        else if (config.device.double_tap_as_button_press == true &&
-                 c.payload_variant.device.double_tap_as_button_press == false && config.display.wake_on_tap_or_motion == false &&
-                 accelerometerThread->enabled == true) {
+        else if (config.device.double_tap_as_button_press == true &&
+                 c.payload_variant.device.double_tap_as_button_press == false && config.display.wake_on_tap_or_motion == false &&
+                 accelerometerThread && accelerometerThread->enabled == true) {
             accelerometerThread->disable();
         }

For src/modules/AdminModule.cpp#L902-L913:

-        if (config.display.wake_on_tap_or_motion == false && c.payload_variant.display.wake_on_tap_or_motion == true &&
-            accelerometerThread->enabled == false) {
+        if (config.display.wake_on_tap_or_motion == false && c.payload_variant.display.wake_on_tap_or_motion == true &&
+            accelerometerThread && accelerometerThread->enabled == false) {
             config.display.wake_on_tap_or_motion = c.payload_variant.display.wake_on_tap_or_motion;
             accelerometerThread->enabled = true;
             accelerometerThread->start();
         }
         // Turning wake-on-motion off: stop the accelerometer thread, but only if double-tap doesn't still need it.
         // disable() clears enabled so a later re-enable can restart it.
-        else if (config.display.wake_on_tap_or_motion == true && c.payload_variant.display.wake_on_tap_or_motion == false &&
-                 config.device.double_tap_as_button_press == false && accelerometerThread->enabled == true) {
+        else if (config.display.wake_on_tap_or_motion == true && c.payload_variant.display.wake_on_tap_or_motion == false &&
+                 config.device.double_tap_as_button_press == false && accelerometerThread && accelerometerThread->enabled == true) {
             accelerometerThread->disable();
         }
🤖 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/modules/AdminModule.cpp` around lines 791 - 803, Guard every
accelerometerThread access in the device configuration toggle at
src/modules/AdminModule.cpp lines 791-803 and the display configuration toggle
at src/modules/AdminModule.cpp lines 902-913 with a null check, including
enabled checks, start(), and disable(). Preserve the existing toggle conditions
while ensuring updates on devices without an accelerometer do not dereference
the null pointer.
🤖 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.

Outside diff comments:
In `@src/modules/AdminModule.cpp`:
- Around line 791-803: Guard every accelerometerThread access in the device
configuration toggle at src/modules/AdminModule.cpp lines 791-803 and the
display configuration toggle at src/modules/AdminModule.cpp lines 902-913 with a
null check, including enabled checks, start(), and disable(). Preserve the
existing toggle conditions while ensuring updates on devices without an
accelerometer do not dereference the null pointer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e4bc40e5-52c8-4c59-acb2-135433be5a7c

📥 Commits

Reviewing files that changed from the base of the PR and between 53a6b5e and d6caa4c.

📒 Files selected for processing (1)
  • src/modules/AdminModule.cpp

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (29)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Pocket V3 rak_wismesh_pocket nrf52840
RAK WisMesh Repeater Mini V2 rak_wismesh_repeater_mini nrf52840
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-15. Updated for d6caa4c.

@jp-bennett

Copy link
Copy Markdown
Collaborator

Does this break the compass, particularly on devices that are 6+3 axis?

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