Skip to content

espressif: release TouchAlarm's claimed pin(s) on reset - #11691

Merged
dhalbert merged 1 commit into
adafruit:mainfrom
mmabey:fix-touchalarm-pin-release
Oct 30, 2026
Merged

dhalbert merged 1 commit into
adafruit:mainfrom
mmabey:fix-touchalarm-pin-release

Conversation

@mmabey

@mmabey mmabey commented Oct 30, 2026

Copy link
Copy Markdown

Summary

alarm_touch_touchalarm_reset() clears its own internal state but never releases the GPIO claim(s) taken in common_hal_alarm_touch_touchalarm_construct() via claim_pin() - unlike alarm_pin_pinalarm_reset(), which loops over its claimed pins and calls reset_pin_number() for each.

light_sleep_until_alarms() returning is a plain function return within the same running VM, not a VM-reset boundary, so reset_all_pins() never runs to clean this up either. The result: after any light sleep that included a TouchAlarm, the pin stays marked "in use" until a real reboot - so a later attempt to claim the same pin (e.g. a fresh touchio.TouchIn) incorrectly raises "<pin> in use", even though the alarm itself is done with it.

Fix mirrors alarm_pin_pinalarm_reset()'s existing pin-release loop.

Testing

Confirmed on real ESP32-S3 hardware:

  • Light sleep (alarm.light_sleep_until_alarms) with a TouchAlarm alongside a PinAlarm: repeated touch-triggered and motion-triggered wakes, each rebuilding a fresh touchio.TouchIn on the same pin afterward with no error. This is the path the bug actually affected, and where the fix was verified - before the fix, the very first wake failed with ValueError: IO9 in use.
  • Deep sleep (alarm.exit_and_deep_sleep_until_alarms) with a TouchAlarm as the sole wake source: tap-to-wake confirmed working. This path reboots the chip entirely, so reset_all_pins() runs regardless of this fix - it doesn't exercise the bug, but confirms TouchAlarm itself works correctly for deep-sleep wake.

Not yet tested: deep sleep combining a TouchAlarm with a second alarm type (a motion PinAlarm) at the same time - only the touch-only deep-sleep variant has been exercised so far.

Ran pre-commit against the changed file - formatting/whitespace/codespell checks pass. The Translations hook failed locally due to a missing polib package in my environment, unrelated to this change (no new translatable strings are added).

alarm_touch_touchalarm_reset() cleared its own internal state but never
released the GPIO claim(s) taken in
common_hal_alarm_touch_touchalarm_construct() via claim_pin() - unlike
alarm_pin_pinalarm_reset(), which loops over its claimed pins and calls
reset_pin_number() for each.

light_sleep_until_alarms() returning is a plain function return within
the same running VM, not a VM-reset boundary, so reset_all_pins() never
runs to clean this up either. The result: after any light sleep that
included a TouchAlarm, the pin stays marked "in use" until a real
reboot, so a later attempt to claim the same pin (e.g. a fresh
touchio.TouchIn) incorrectly raises "<pin> in use" even though the
alarm is done with it.

Observed on hardware (ESP32-S3): a touch pad used as a light-sleep wake
source, rebuilt as a touchio.TouchIn after every wake, failed on the
very first wake with this error.

Mirrors alarm_pin_pinalarm_reset()'s existing pin-release loop.

@dhalbert dhalbert 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.

Great, thank you!

@dhalbert
dhalbert merged commit a1790f7 into adafruit:main Oct 30, 2026
287 checks passed
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