Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP32-S3 simulated deep sleep does not activate when USB re-connected after running on battery #9898

Open
samblenny opened this issue Dec 18, 2024 · 3 comments

Comments

@samblenny
Copy link

samblenny commented Dec 18, 2024

CircuitPython version

Adafruit CircuitPython 9.2.1 on 2024-11-20; Adafruit Metro ESP32S3 with ESP32S3

Code/REPL

from alarm import exit_and_deep_sleep_until_alarms
from alarm.time import TimeAlarm
from time import monotonic

def main():
    # This will run each time the board wakes from deep sleep.
    print("I woke up")
    seconds = 8
    ta = TimeAlarm(monotonic_time=monotonic() + seconds)
    exit_and_deep_sleep_until_alarms(ta)

main()

Behavior

Steps to reproduce the problem:

  1. Run the code above as code.py on a Metro ESP32-S3 with charged battery pack connected (I suspect this might act the same for any S2 or S3 board with battery power, but I haven't tried it)
  2. In macOS, eject the CIRCUITPY drive (unmount for linux, etc)
  3. Unplug the USB cable, leaving the board powered by its battery
  4. Plug the USB port back in

After re-connecting the USB cable, the CIRCUITPY never shows up, and the USB serial device never shows up.

Description

If I reset the board, everything goes back to normal with CIRCUITPY and USB serial. But, I really don't want to reset the board because I'm trying to make a low cost data logger that uses alarm.sleep_memory instead of an SD card. A reset will clear the sleep memory, erasing the measurements.

Assuming I can get it working, my intended use case is:

  1. Plug in the board to charge the battery, set the clock, and start recording measurements
  2. Unplug the board, seal it up in a case, and move it to the location I want to monitor
  3. Wait a while
  4. Get the board, open the case, plug it back in, and dump the data over the serial port

Additional information

No response

@samblenny samblenny added the bug label Dec 18, 2024
@samblenny
Copy link
Author

TL;DR: I suspect there are two related problems: 1) a series of deep sleeps may not allow enough non-sleeping time for USB to enumerate, and 2) CircuitPython's mechanism for simulated deep sleeping does not seem to reliably re-activate when the board has been real deep sleeping on battery power and then gets re-connected to a USB port.

Long version... Using the code below for code.py lets me jumper A3 to GND with a wire to get a loop of ESP32 light sleep. With the jumper disconnected, it does a series of deep sleeps like the original reproducing code posted above.

  1. If I unplug the USB cable while the jumper is connected (light sleep loop), then plug it back in while still in light sleep mode, CIRCUITPY shows up normally.
  2. If I unplug the USB cable during the light sleep loop, then remove the jumper to switch to deep sleep mode, then plug the cable back in, CIRCUITPY never shows up.
  3. If I unplug USB in either of deep sleep or light sleep mode, ensure I'm in deep sleep mode, then plug the cable back in, CIRCUITPY does not appear. But, if I then remove the jumper on A3 to switch to the light sleep loop, CIRCUITPY shows up again.
  4. If I add a time.sleep(5) before the deep sleep call, things get chaotic. CIRCUITPY eventually mounts, but it quickly disappears and macOS complains about the disk not being ejected properly. The appear/disappear cycle continues repeatedly until I put the A3 jumper back to disable deep sleep. It looks like, in that case, CircuitPython isn't switching back to simulated deep sleep, but instead is continuing to do real deep sleeps while CIRCUITPY is mounted.
  5. If I connect the USB cable while deep sleeping (no A3 jumper), then add the jumper to switch to light sleep, CIRCUITPY shows up fine after the last deep sleep alarm fires. I can also connect to the USB serial REPL. But, if I then wait a while and remove the jumper, CIRCUITPY immediately disappears and the serial session disconnects (no simulated deep sleep messages). It looks like a switch back to simulated deep sleep is not happening.
from alarm import light_sleep_until_alarms, exit_and_deep_sleep_until_alarms
from alarm.time import TimeAlarm
import board
from digitalio import DigitalInOut, Direction, Pull
import time

def main():
    # This will run each time the board wakes from deep sleep.
    print("start of main()")

    # If A3 is jumpered to GND, light sleep until jumper is removed
    a3_jumper = DigitalInOut(board.A3)
    a3_jumper.direction = Direction.INPUT
    a3_jumper.pull = Pull.UP
    first = True
    if not a3_jumper.value:
        while not a3_jumper.value:
            ta = TimeAlarm(monotonic_time=time.monotonic() + 1)
            light_sleep_until_alarms(ta)
            if first:
                first = False
                print("waiting while A3 is grounded...")
        print("done")

    # When jumper is not grounded, do the deep sleep thing
    # uncommenting the time.sleep() line below on macOS causes
    #   a repeating loop of CIRCUITPY appearing, then disappearing,
    #   with the OS complaining about  "Disk not ejected properly"
    # time.sleep(5)
    seconds = 8
    ta = TimeAlarm(monotonic_time=time.monotonic() + seconds)
    exit_and_deep_sleep_until_alarms(ta)

main()

@samblenny samblenny changed the title Unplugging ESP32-S3 USB during deep sleep glitches USB stack ESP32-S3 simulated deep sleep does not activate when USB re-connected after running on battery Dec 18, 2024
@dhalbert
Copy link
Collaborator

I had a discord discussion with a user who was having similar issues. I think we may have recently changed the way we check for the presence of a USB connection, which is making deciding whether to do real or fake sleep more difficult. It would be worth doing some tests with older versions, and doing a bisect as needed.

@samblenny
Copy link
Author

samblenny commented Dec 23, 2024

FWIW, I just came across this note in the docs for alarm.exit_and_deep_sleep_until_alarms():

If CircuitPython goes into a true deep sleep, and USB or BLE is reconnected, the next deep sleep will still be a true deep sleep. You must do a hard reset or power-cycle to exit a true deep sleep loop.

So, I guess the issue I encountered here is actually documented behavior, though it would be nice if USB could be made to work again somehow without a reset. For the moment, I've been able to get my data loggers working by dedicating a GPIO pin to serve as a USB mode jumper. I added an alarm.pin so that I can ground the USB mode pin to interrupt deep sleep and prevent further deep sleeps. It works reasonably well.

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

No branches or pull requests

3 participants