Fix external RTC detection on Port A and the sleep wakeup handling - #290
Merged
lovyan03 merged 3 commits intoJul 30, 2026
Merged
Conversation
RTC_Class::begin() passed the given I2C bus to the RX8130 instances but not to the PCF8563 fallback, whose third parameter defaults to In_I2C. As a result cfg.external_rtc had no effect for UNIT-RTC on Port A, except on boards where the internal bus and Port A are the same pins (M5Grey).
…set (m5stack#205 m5stack#285) deepSleep() enabled EXT1 wakeup unconditionally on targets without EXT0 support, wrapped in ESP_ERROR_CHECK. On M5PaperS3 the touch INT pin is GPIO48, which is not an RTC IO, so deepSleep(us, true) aborted instead of sleeping. The pin is now checked with rtc_gpio_is_valid_gpio() and a warning is logged when it cannot be used; lightSleep() already handled this case via gpio wakeup. The EXT1 path also used _wakeupPin for the pin mask while the wait loop used wpin, so the M5PaperMono override (GPIO4) was not applied to the mask. In addition, a warning is logged when neither a timer nor a wakeup pin is requested, which is the deepSleep(0, false) case that silently sleeps forever.
deepSleep() and lightSleep() took micro_seconds = 0 as "sleep without a timer wakeup", so the default arguments (0, true) meant "sleep until the wakeup pin is triggered". As a result a caller that computed a duration and accidentally got 0 slept until an external reset, which is hard to tell apart from a freeze. The intent "no timer wakeup" now has its own value, Power.sleep_no_timer, which is also the new default argument, so calls without arguments behave as before. An explicit 0 no longer sleeps and logs a warning instead; for deepSleep the check runs before the display is put to sleep, so the caller resumes with the display still on. lightSleep now also tracks whether a wakeup pin was actually enabled, and warns when neither a timer nor a pin wakeup source is in effect. BREAKING: code that passed a literal 0 to sleep indefinitely has to pass Power.sleep_no_timer instead.
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent fixes found while going through the open issues. Each one is a separate commit.
1. External RTC on Port A is never detected (#266)
RTC_Class::begin()passes the givenI2C_Class*to the RX8130 instances, but not to the PCF8563 fallback, whose third parameter defaults to&In_I2C. Withcfg.external_rtc = truethe callM5.Rtc.begin(&M5.Ex_I2C)therefore probes the internal bus, and UNIT-RTC on Port A is never found.The argument was present in v0.2.10 and was dropped in e35a905 (first released in v0.2.11), which matches the reporter's version bisection - including why M5Grey is the one board that still works: its internal bus is G21/G22, the same pins as Port A.
2. deepSleep() aborts on M5PaperS3 when touch_wakeup is requested (#205)
On targets without EXT0 support,
deepSleep()enabled EXT1 wakeup unconditionally insideESP_ERROR_CHECK. The PaperS3 touch INT pin is GPIO48, which is not an RTC IO, soesp_sleep_enable_ext1_wakeup_io()returnsESP_ERR_INVALID_ARGand the call aborts instead of sleeping.lightSleep()already handled this case withgpio_wakeup_enable().The pin is now validated with
rtc_gpio_is_valid_gpio(), and a warning is logged when it cannot be used as a deep sleep wakeup source.The same block also built the EXT1 pin mask from
_wakeupPinwhile the wait loop usedwpin, so the M5PaperMono override (GPIO4) never reached the mask; both usewpinnow.3. A sleep duration of zero silently means "sleep forever" (#285)
micro_seconds = 0meant "no timer wakeup", and 0 was also the default argument - sodeepSleep()with no arguments means "sleep until the wakeup pin triggers". A caller that computes a duration and accidentally ends up with 0 sleeps until an external reset, which is hard to tell apart from a freeze.That intent now has its own value:
0no longer sleeps and logs a warning instead. FordeepSleep()the check runs beforeM5.Display.sleep(), so the caller resumes with the display still on;esp_sleep_enable_timer_wakeup()(about 580,000 years, which would be rejected) - it keeps taking the "disable the timer wakeup source" path;lightSleep()now also tracks whether a wakeup pin was actually enabled, and warns when neither a timer nor a pin wakeup source is in effect.This is a breaking change for code that passes a literal
0in order to sleep indefinitely: such code needsM5.Power.sleep_no_timerinstead. In public GitHub code the no-argument form outnumbers explicit0by roughly 97 to 11, and the direction of the change is the safe one - the device stays awake and logs why, rather than never waking up.Verification
Builds cleanly with PlatformIO for
esp32(EXT0 path),esp32s3(EXT1 path) and the PC build (M5UNIFIED_PC_BUILD).Not verified on hardware yet -
deepSleep()on PaperS3 and UNIT-RTC on Port A would be the two worth checking.