XIAO ESP32C6 using MicroPython - the external Wakeup Pin is broken after DEEPSLEEP

To test the failure using Thonny:

XIAO Hardware setup:

  • Connect a 10K resister between 3v3 and D2.
  • Connect a N/O switch between GND and D2.
  • Connect your favorite serial monitor to D6 (and ground if necessary).

Install the ESP32_GENERIC_C6-20250911-v1.26.1.bin from MicroPython Downloads.

Copy the following program “main.py” to Thonny and save to ESP32C6 as main.py.

"""
    Just report the reset_cause.
"""
from micropython import const
import os
import sys
import time
import machine
from machine import Pin, UART

LED_PIN = const(15) 


def main():
    reset_cause = machine.reset_cause()
    # match reset_cause:
    if reset_cause == machine.PWRON_RESET:
        print('reset_cause = PWRON_RESET')
    elif reset_cause == machine.HARD_RESET:
        print('reset_cause = HARD_RESET')
    elif reset_cause == machine.WDT_RESET:
        print('reset_cause = WDT_RESET')
    elif reset_cause == machine.DEEPSLEEP_RESET:
        print('reset_cause = DEEPSLEEP_RESET')
    elif reset_cause == machine.SOFT_RESET:
        print('reset_cause = SOFT_RESET')
    else:
        print('reset_cause = Unknown_RESET')
    
    
if __name__ == '__main__':
    # excpt_log = open('exception.txt', 'w')
    start = time.ticks_ms()
    try:
        led = Pin(LED_PIN, Pin.OUT, value=0)      # Turn LED(GPIO15) ON
        uart = UART(1, baudrate=115200, tx=Pin(6), rx=Pin(7))
        uart.init(115200, bits=8, parity=None, stop=1)
        os.dupterm(uart, 0)
        main()
        print(f'Runtime was {time.ticks_diff(time.ticks_ms(), start)} millisecs.')
    except Exception as e:
        sys.print_exception(e)   # , excpt_log)
    # excpt_log.close()
    led.value(1)                                  # Turn LED OFF
    

Copy the following program “deepsleep.py” to Thonny and run it.

# now MicroPython v1.26.1 on 2025-09-11; ESP32C6 module with ESP32C6
# was MicroPython v1.24.0-preview.407.g197becbdc on 2024-10-09;
"""
Function            Purpose
--------            -------
__main__            Setup
main                Verify the door_sw is working, init and enter deepsleep

"""
# from circuitpython import const
import os
import sys
import time
import esp32
import machine
from time import sleep, ticks_ms, ticks_diff
from machine import Pin, UART, ADC
from esp32 import wake_on_ext1, WAKEUP_ALL_LOW


LED_PIN = const(15)
WAKE_UP_PIN = const(2)


def main():
    global WAKE_UP_PIN
    door_sw = Pin(WAKE_UP_PIN, Pin.IN)         # pullup with 10K to 3v3
    print("OK for 10 seconds, operate the switch & verify the value changes.")
    for _ in range(20):
        print(f'Mailbox door_sw is {"Closed" if door_sw.value() else "Open"}.')
        sleep(0.5)
    for sec in range(604800):         # 7 days in seconds
        if door_sw.value():
            break
        minutes, seconds = divmod(sec, 60)
        hours, minutes = divmod(minutes, 60)
        days, hours = divmod(hours, 24)
        print(f'Waiting for door closure {days}:{hours:02}:{minutes:02}.{seconds:02}', end='\r')   
        sleep(1)
    esp32.wake_on_ext1(pins=[door_sw], level=esp32.WAKEUP_ALL_LOW)
    if door_sw.value():    # default (door_sw=closed=1) to DeepSleep
        print('\n Going into DeepSleep for maximum of 30 seconds.')
        sleep(1)
        machine.deepsleep(29 * 1000)
    print('\n Past deepsleep()')


if __name__ == '__main__':
    print("Entry to deepsleep.py(__main__)")
    led = Pin(LED_PIN, Pin.OUT, value=0)      # light the LED
    start = ticks_ms()
    excpt_log = open('exception.txt', 'w')
    try:
        uart = UART(1, baudrate=115200, tx=Pin(6), rx=Pin(7))
        uart.init(115200, bits=8, parity=None, stop=1)
        os.dupterm(uart, 0)
        main()
        print(f'Runtime = {ticks_diff(ticks_ms(), start)} millisecs.')
    except Exception as e:
        sys.print_exception(e)   # , excpt_log)
    excpt_log.close()
    led.value(1)        # turn the LED OFF (program end)

Reference to “door closure” is the switch you wired between GND and D2.

When you see the “OK for 10 seconds” message operate the switch and the output should from “closed” to “open”. When you see the “Going into DeepSleep” before 30 seconds elapse press the switch and Thonny is disconnect but the serial monitor will report a reset_cause of DEEPSLEEP_RESET. Now when you run the deepsleep.py again the Mailbox door_sw will show “Open” although it should be Closed because the voltage on D2 is 3v3.

I believe the wake_on_ext1 is making changes to D2 operation that the DEEPSLEEP should be restoring (and isn’t). Whether I’m right or not there is something that is freezing D2 from reading 3v3 after DEEPSLEEP has awaken. Whether DEEPSLEEP is awaken from D2 or the 30 second timeout, D2 is still frozen as if the switch was pressed and it isn’t.

I find I have to press the BOOT button on the XIAO to restore D2 operation.

Anyone have a workaround?

Thanks, Morris

Hi there,
Are you “imprint certain” :grin:
That’s not a strapping Pin ,so at boot all configs are off ?

HTH
GL :grin: PJ :victory_hand:

Try a different pin

The pin failing is GPIO02, a required rtc pin for external trigger.

What is “imprint certain”?

Regards, Morris

Hi there,

So , I see the Git Hub case open, and the Known condition your facing,
Unfortunately On ESP32-class chips, deep sleep + external wake can involve pad hold in the RTC domain. If the firmware sets up EXT1 wake incorrectly (and doesn’t fully restore the pad config), the pin can come back from deep sleep latched in its last level. That’s exactly what the GitHub issue describes for C6 with WAKEUP_ALL_LOW. MicroPython Documentation+1

From that GitHub issue, the only configuration that behaves correctly on C6 is:

  • Use WAKEUP_ANY_HIGH, not WAKEUP_ALL_LOW.
  • Use a pulldown instead of pullup (invert the logic). GitHub

So the practical workaround for this :

  1. Invert the hardware wiring
  • 10 kΩ pulldown from D2 to GND.
  • Button from D2 to 3V3 (so “door open/pressed” = HIGH).
  1. Change the MicroPython setup to:
from esp32 import wake_on_ext1, WAKEUP_ANY_HIGH
from machine import Pin

WAKE_UP_PIN = const(2)
door_sw = Pin(WAKE_UP_PIN, Pin.IN, Pin.PULL_DOWN)

# Wake if the pin goes HIGH
wake_on_ext1(pins=[door_sw], level=WAKEUP_ANY_HIGH)
machine.deepsleep(29_000)

This avoids the broken WAKEUP_ALL_LOW path on ESP32-C6 and, per the issue report, does not leave the pin stuck after wake.

Optionally, right after boot you can also be extra-defensive and clear any pin hold:

import esp32
esp32.gpio_deep_sleep_hold(False)

…but the real fix is the wake mode workaround above, plus waiting for MicroPython to address the bug upstream.

HTH
GL :slight_smile: PJ :v:

Thank you very much!

Best regards, morrs

Updated deepsleep.py to complement the D2 trigger, NOT each door_sw.value(), use WAKE_ANY_HIGH.

Updated main.py to add esp32.gpio_deep_sleep_hold(False) in DEEPSLEEP_RESET clause.

DeepSleep was successfully triggered with 3v3.

However the latch behavior remains, BOOT to restore D2 operation.

Regards, Morris

Correction:

MicroPython v1.26.1 on 2025-09-11; ESP32C6 module with ESP32C6
Type “help()” for more information.

import esp32
esp32.gpio_deep_sleep_hold(False)
Traceback (most recent call last):
File “”, line 1, in
AttributeError: ‘module’ object has no attribute ‘gpio_deep_sleep_hold’

Regards, Morris

Do you mean that after the device wakes up, the function on the D2 pin is no longer working?

We’ve seen a similar case before. You can try switching to another pin to test whether the issue is related to D2 specifically.