adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.02k stars 1.19k forks source link

Excessive amperage in deep sleep on ESP32-S3 #9464

Open dy0x opened 1 month ago

dy0x commented 1 month ago

CircuitPython version

Adafruit CircuitPython 9.1.1 on 2024-07-23; ProS3 with ESP32S3

Code/REPL

import alarm
import board
import time
from microcontroller import watchdog as w
from watchdog import WatchDogMode

# Watchdog and Supervisor
w.timeout = 300
w.mode = WatchDogMode.RESET

print("test")
time.sleep(1)

w.feed()

wakeup_period = time.monotonic() + int(30)
time_alarm = alarm.time.TimeAlarm(monotonic_time=wakeup_period)
alarm.exit_and_deep_sleep_until_alarms(time_alarm)

Behavior

ESP32-S3 consumes ~28mA in deep sleep on 9.1.0/9.1.1 but only ~26µA on 9.0.5

Auto-reload is off.
code.py output:
Wi-Fi: off | BLE:Off | code.py | 9.1.1test
Wi-Fi: off | BLE:Off | Done | 9.1.1
Code done running.

Press any key to enter the REPL. Use CTRL-D to reload.
Pretending to deep sleep until alarm, CTRL-C or file write.

Description

In terms of functionality, everything runs as expected. However, I noticed after updating from 9.0.5 to 9.1.1, my code consumes substantially more power in deep sleep. In CircuitPython 9.1.0 and 9.1.1, the provided code runs on ESP32-S3 in deep sleep at ~28mA. Whereas if I run this code using CircuitPython 9.0.5, it deep sleeps at ~26µA.

Additional information

Things I have tried:

UnexpectedMaker commented 1 month ago

Maybe something is keeping LDO2 enabled? That shouldn't cause 28mA but might cause a few mA. Can you confirm there is no voltage on the 3V3_2 pin when it's sleeping?

dy0x commented 1 month ago

Yeah, seems like there is a voltage on 3V3_2 during sleep.

UnexpectedMaker commented 1 month ago

And if you pull IO17 LOW before entering deep sleep, does it keep it low (LDO2 off)?

dy0x commented 1 month ago

No, it only stays low until deep sleep starts, and then it goes back to ~3.3V

UnexpectedMaker commented 1 month ago

Ok, that shouldn't really be possible at a HW level as the LDO enable is controlled by a logical AND and requires both Io17 and VDD_SPI to be high, and VDD_SPI is shut down during deep sleep.

Just to be sure - you're not testing with power from USB right? Is it being powered from a battery?

dy0x commented 1 month ago

Yep, I'm testing on two separate boards. One with alkaline batteries, the other connected to a power profiler on 3V3_1.

Also slight correction, the 3V3_2 reads about 2.8V in deep sleep.

UnexpectedMaker commented 1 month ago

I have no idea what has changed under the hood in CP for 9.1, so I'll need to make some enquiries. I know there's a way in the board configs to tell CP not to touch certain IO, but that should not be needed as it was working correctly before 9.1.

@dhalbert Can you think of anything off the top of your head (apart from possible IDF level changes) that could have changed the behaviour on the IO when going into deep sleep? Maybe some other bug fix that might have caused some knock-on effect?

dhalbert commented 1 month ago

We updated to ESP-IDF 5.2.2. Also #9324 was merged.

UnexpectedMaker commented 1 month ago

We updated to ESP-IDF 5.2.2. Also #9324 was merged.

Ok, so the only way to fix this now for my boards is to add something like this to board.c ?

void board_deinit(void) {
  // Turn off LDO2
  gpio_set_direction(17, GPIO_MODE_DEF_OUTPUT);
  gpio_set_level(17, false);
}

Will that even survive the rest of the CP code that sets everything to INPUT_PULL when going to sleep?

Didn't there used to be a way to tell CP not to touch certain IO when going to sleep?

dhalbert commented 1 month ago

I haven't yet, but I think looking carefully at the code in #9324 would be good.

Yes, we had "never reset" certain pins, and "never ever reset" certain pins. Some of that logic has been simplified. A code comparison between 9.0.5 and 9.1.1 would be helpful, or a bisect.

tannewt commented 1 month ago

Didn't there used to be a way to tell CP not to touch certain IO when going to sleep?

alarm.exit_and_deep_sleep_until_alarms() has a preserve_dios kwarg that you can give DigitalInOuts to be preserved.