Skip to content

Wifi power management #10271

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

Merged
merged 5 commits into from
Apr 22, 2025
Merged

Wifi power management #10271

merged 5 commits into from
Apr 22, 2025

Conversation

dhalbert
Copy link
Collaborator

wifi.radio.listen_interval was inadvertently not added when #9476 was merged. Instead of fixing that directly, I've redone wifi power management so the same API can be used for both Espressif and CYW43 (Pico W et al).

The old cyw43 API is still available, but now there a property wifi.radio.power_management which can take values wifi.PowerManagement.{MIN,MAX,NONE}. On CYW43, these correspond to the "PERFORMANCE", "AGGRESSIVE", and "DISABLED" CYW43-specific values. The default value is MIN.

I also corrected the current CYW43 values, which hadn't yet incorporated the upstream fixes in georgerobotics/cyw43-driver#126.

Because of all these changes, it would be good to re-test these various power-savings modes, as discussed in #10170. @anecdata, when you have time, could you test build artifacts from this PR, against the performance tests you used in the past for these kinds of settings? Thank you. I will make it draft for now. If anyone else is interested in testing, that would be great?

I did wifi smoke tests (pings and HTTP and HTTPS fetches on ESP32-S3 and Pico W with all the different power-management settings.

@anecdata
Copy link
Member

anecdata commented Apr 20, 2025

Trying on ESP32-S2 QT Py & Pico W (3 of each to run each port with each power setting overnight). Not yet exercising the new get/set power APIs. Consistently get Hard fault: memory access or instruction error somewhere right after the wifi connection (after printing the IP address. Code runs w/o safemode on 9.2.7.

adafruit-circuitpython-adafruit_qtpy_esp32s2-en_US-20250420-main-PR10271-f8ed46a.uf2
Adafruit CircuitPython 10.0.0-alpha.2-22-gf8ed46adce on 2025-04-20; Adafruit QT Py ESP32S2 with ESP32S2

adafruit-circuitpython-raspberry_pi_pico_w-en_US-20250420-main-PR10271-f8ed46a.uf2
Adafruit CircuitPython 10.0.0-alpha.2-22-gf8ed46adce on 2025-04-20; Raspberry Pi Pico W with rp2040

code.py
import time
import os
import traceback
import supervisor
import microcontroller
import wifi
import socketpool
import adafruit_requests

TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool)

def connect():
    while not wifi.radio.connected:
        try:
            wifi.radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))
            print(f"{wifi.radio.ipv4_address}")
        except Exception as ex:
            traceback.print_exception(ex, ex, ex.__traceback__)
            time.sleep(5)

# uncomment one of the three as appropriate, or leave all commented for default MIN
# wifi.radio.power_management = wifi.PowerManagement.NONE
wifi.radio.power_management = wifi.PowerManagement.MIN
# wifi.radio.power_management = wifi.PowerManagement.MAX

# increase delay between requests each time
interval_sec = 0
increment_sec = 60
timer_sec = time.time() - interval_sec
while True:
    if time.time() - timer_sec > interval_sec:
        try:
            connect()
            print(f"Fetching URL...")
            with requests.get(TEXT_URL) as r:
                print(f"{interval_sec:>5} {wifi.radio.power_management} {r.status_code} {r.reason.decode()} {r.content}")
            interval_sec += increment_sec
            timer_sec = time.time()
        except Exception as ex:
            traceback.print_exception(ex, ex, ex.__traceback__)
            time.sleep(5)
            # supervisor.reload()
            # microcontroller.reset()

(edited to match proper API)

@dhalbert
Copy link
Collaborator Author

The crashes @anecdata is seeing are totally unrelated to this PR. I am seeing the same crashes on the tip of main right now, so main is busted in some way.

@anecdata
Copy link
Member

anecdata commented Apr 22, 2025

Testing now on all six boards. If it behaves like it used to (on Pico W), the more aggressive power management settings may cause the wifi connection to drop unrecoverable [Errno 2] No such file/directory as the time between requests increases. But, the field values for each setting are quite different than they used to be, so they may all work. I'll update tomorrow, once they have run long enough to confirm whether wifi stays connected at each power management level (it used to fail between 5-10 minutes on Pico W with increased power management).

Interim Update: Both ports, at each power management level, have run so far with 20-minute intervals between requests, with no wifi disconnections. So it's looking good for no regressions. There are a number of requests exceptions along the way (more on espressif than on raspberrypi):

Traceback (most recent call last):
  File "adafruit_requests.py", line 658, in request
OSError: [Errno 116] ETIMEDOUT

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "code.py", line 37, in <module>
  File "adafruit_requests.py", line 711, in get
  File "adafruit_requests.py", line 671, in request
OutOfRetries: Repeated socket failures

but code.py retries recover, and wifi stays connected.

I've repurposed the raspberrypi wifi.PowerManagement.NONE board to try to reproduce the original 2022 issue by forcing values of cyw43.set_power_management(). It's possible some other pico-sdk changes have fixed that. It's also possible that network differences account for differences in outcomes.

I have not done any actual power usage testing.

Copy link
Member

@anecdata anecdata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original issue with more aggressive power management on Pico W in #6958 was [Errno 2] No such file/directory, typically after about a 5 minute interval between network transactions, requiring a reset to recover. The wifi connection to the AP was not lost.

I have not seen that behavior on either espressif or raspberrypi port, using any of the new power management settings (NONE, MIN, MAX) running overnight, with intervals between requests of 40 minutes and more. Also no wifi disconnections at all.

Trying to reproduce the original issue on Pico W, using the deprecated cyw43 power management values of 0x111022 and 0xa11c82:

  • 0xa11c82: A single test ran with 20-minute interval between requests without issue.

  • 0x111022: One test got repeated gaierror: (-2, 'Name or service not known') after hitting the 22 minute interval between requests. Not sure if that is related. A second test ran fine with over 40 minute interval between requests.

In any case, no regressions were found with the new values, indicating to me that this is a relatively safe change. Users can turn off power management if the default isn't getting them the results they want (or change it to MAX). And advanced users on Pico W can tweak the detailed values using cyw43 if they wish.

@dhalbert
Copy link
Collaborator Author

@anecdata thank you for the testing! I'm marking this ready for review now.

@dhalbert dhalbert marked this pull request as ready for review April 22, 2025 19:21
@dhalbert dhalbert requested a review from tannewt April 22, 2025 19:21
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thank you.

@tannewt tannewt merged commit ba69a59 into adafruit:main Apr 22, 2025
615 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.

wifi.radio.listen_interval not available
3 participants