Skip to content

Commit ba69a59

Browse files
authored
Merge pull request #10271 from dhalbert/wifi-power-management
Wifi power management
2 parents dd81542 + 7d65991 commit ba69a59

File tree

13 files changed

+243
-73
lines changed

13 files changed

+243
-73
lines changed

ports/espressif/common-hal/wifi/Radio.c

+35-14
Original file line numberDiff line numberDiff line change
@@ -139,23 +139,44 @@ void common_hal_wifi_radio_set_tx_power(wifi_radio_obj_t *self, const mp_float_t
139139
esp_wifi_set_max_tx_power(tx_power * 4.0f);
140140
}
141141

142-
mp_int_t common_hal_wifi_radio_get_listen_interval(wifi_radio_obj_t *self) {
143-
wifi_config_t *config = &self->sta_config;
144-
return config->sta.listen_interval;
142+
wifi_power_management_t common_hal_wifi_radio_get_power_management(wifi_radio_obj_t *self) {
143+
wifi_ps_type_t ps;
144+
esp_err_t ret = esp_wifi_get_ps(&ps);
145+
if (ret == ESP_OK) {
146+
switch (ps) {
147+
case WIFI_PS_MIN_MODEM:
148+
return POWER_MANAGEMENT_MIN;
149+
case WIFI_PS_MAX_MODEM:
150+
return POWER_MANAGEMENT_MAX;
151+
case WIFI_PS_NONE:
152+
return POWER_MANAGEMENT_NONE;
153+
}
154+
}
155+
return POWER_MANAGEMENT_MIN;
145156
}
146157

147-
void common_hal_wifi_radio_set_listen_interval(wifi_radio_obj_t *self, const mp_int_t listen_interval) {
148-
wifi_config_t *config = &self->sta_config;
149-
config->sta.listen_interval = listen_interval;
150-
if (listen_interval == 1) {
151-
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
152-
} else if (listen_interval > 1) {
153-
esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
154-
} else {
155-
esp_wifi_set_ps(WIFI_PS_NONE);
156-
}
157158

158-
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
159+
void common_hal_wifi_radio_set_power_management(wifi_radio_obj_t *self, wifi_power_management_t power_management) {
160+
switch (power_management) {
161+
case POWER_MANAGEMENT_MIN:
162+
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
163+
break;
164+
case POWER_MANAGEMENT_MAX: {
165+
// listen_interval is only used in this case.
166+
wifi_config_t *config = &self->sta_config;
167+
// This is a typical value seen in various examples.
168+
config->sta.listen_interval = 3;
169+
esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
170+
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
171+
}
172+
break;
173+
case POWER_MANAGEMENT_NONE:
174+
esp_wifi_set_ps(WIFI_PS_NONE);
175+
break;
176+
case POWER_MANAGEMENT_UNKNOWN:
177+
// This should be prevented in shared-bindings.
178+
break;
179+
}
159180
}
160181

161182
mp_obj_t common_hal_wifi_radio_get_mac_address_ap(wifi_radio_obj_t *self) {

ports/espressif/mpconfigport.mk

-3
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ CIRCUITPY_FULL_BUILD ?= 1
4040
# If SSL is enabled, it's mbedtls
4141
CIRCUITPY_SSL_MBEDTLS = 1
4242

43-
# Wifi Power Save
44-
CIRCUITPY_WIFI_RADIO_SETTABLE_LISTEN_INTERVAL = 1
45-
4643
# Never use our copy of MBEDTLS
4744
CIRCUITPY_HASHLIB_MBEDTLS_ONLY = 0
4845

ports/raspberrypi/bindings/cyw43/__init__.c

+17-9
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#include "lib/cyw43-driver/src/cyw43.h"
1818

19-
static int power_management_value = PM_DISABLED;
19+
static uint32_t power_management_value = CONST_CYW43_DEFAULT_PM;
2020

2121
void cyw43_enter_deep_sleep(void) {
2222
#define WL_REG_ON 23
@@ -43,14 +43,23 @@ MP_DEFINE_CONST_OBJ_TYPE(
4343
print, shared_bindings_microcontroller_pin_print
4444
);
4545

46+
uint32_t cyw43_get_power_management_value() {
47+
return power_management_value;
48+
}
49+
50+
void cyw43_set_power_management_value(uint32_t value) {
51+
power_management_value = value;
52+
bindings_cyw43_wifi_enforce_pm();
53+
}
54+
4655
//| PM_STANDARD: int
47-
//| """The standard power management mode"""
56+
//| """The default power management mode; same as PM_PERFORMANCE"""
4857
//| PM_AGGRESSIVE: int
4958
//| """Aggressive power management mode for optimal power usage at the cost of performance"""
5059
//| PM_PERFORMANCE: int
5160
//| """Performance power management mode where more power is used to increase performance"""
5261
//| PM_DISABLED: int
53-
//| """Disable power management and always use highest power mode. CircuitPython sets this value at reset time, because it provides the best connectivity reliability."""
62+
//| """Disable power management and always use highest power mode."""
5463
//|
5564
//|
5665
//| def set_power_management(value: int) -> None:
@@ -85,8 +94,7 @@ MP_DEFINE_CONST_OBJ_TYPE(
8594
//|
8695
static mp_obj_t cyw43_set_power_management(const mp_obj_t value_in) {
8796
mp_int_t value = mp_obj_get_int(value_in);
88-
power_management_value = value;
89-
bindings_cyw43_wifi_enforce_pm();
97+
cyw43_set_power_management_value(value);
9098
return mp_const_none;
9199
}
92100
static MP_DEFINE_CONST_FUN_OBJ_1(cyw43_set_power_management_obj, cyw43_set_power_management);
@@ -126,10 +134,10 @@ static const mp_rom_map_elem_t cyw43_module_globals_table[] = {
126134
{ MP_ROM_QSTR(MP_QSTR_CywPin), MP_ROM_PTR(&cyw43_pin_type) },
127135
{ MP_ROM_QSTR(MP_QSTR_set_power_management), &cyw43_set_power_management_obj },
128136
{ MP_ROM_QSTR(MP_QSTR_get_power_management), &cyw43_get_power_management_obj },
129-
{ MP_ROM_QSTR(MP_QSTR_PM_STANDARD), MP_ROM_INT(PM_STANDARD) },
130-
{ MP_ROM_QSTR(MP_QSTR_PM_AGGRESSIVE), MP_ROM_INT(PM_AGGRESSIVE) },
131-
{ MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(PM_PERFORMANCE) },
132-
{ MP_ROM_QSTR(MP_QSTR_PM_DISABLED), MP_ROM_INT(PM_DISABLED) },
137+
{ MP_ROM_QSTR(MP_QSTR_PM_STANDARD), MP_ROM_INT(CONST_CYW43_DEFAULT_PM) },
138+
{ MP_ROM_QSTR(MP_QSTR_PM_AGGRESSIVE), MP_ROM_INT(CONST_CYW43_AGGRESSIVE_PM) },
139+
{ MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(CONST_CYW43_PERFORMANCE_PM) },
140+
{ MP_ROM_QSTR(MP_QSTR_PM_DISABLED), MP_ROM_INT(CONST_CYW43_NONE_PM) },
133141
};
134142

135143
static MP_DEFINE_CONST_DICT(cyw43_module_globals, cyw43_module_globals_table);

ports/raspberrypi/bindings/cyw43/__init__.h

+13-9
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,25 @@ const mcu_pin_obj_t *validate_obj_is_free_pin_including_cyw43(mp_obj_t obj, qstr
1616
const mcu_pin_obj_t *validate_obj_is_free_pin_or_gpio29(mp_obj_t obj, qstr arg_name);
1717
const mcu_pin_obj_t *validate_obj_is_pin_including_cyw43(mp_obj_t obj, qstr arg_name);
1818

19-
#define CONSTANT_CYW43_PM_VALUE(pm_mode, pm2_sleep_ret_ms, li_beacon_period, li_dtim_period, li_assoc) \
19+
// This is equivalent to the code in cyw43.h, except that the values are computed at compile time.
20+
// A `CONST_` prefix has been added to the computation function (expressed as a macro) and the values.
21+
22+
#define CONST_cyw43_pm_value(pm_mode, pm2_sleep_ret_ms, li_beacon_period, li_dtim_period, li_assoc) \
2023
(li_assoc << 20 | /* listen interval sent to ap */ \
2124
li_dtim_period << 16 | \
2225
li_beacon_period << 12 | \
2326
(pm2_sleep_ret_ms / 10) << 4 | /* cyw43_ll_wifi_pm multiplies this by 10 */ \
2427
pm_mode /* CYW43_PM2_POWERSAVE_MODE etc */)
2528

26-
// CYW43_DEFAULT_PM (except a compile-time constant)
27-
#define PM_STANDARD CONSTANT_CYW43_PM_VALUE(CYW43_PM2_POWERSAVE_MODE, 200, 1, 1, 10)
28-
// CYW43_AGGRESSIVE_PM (except a compile-time constant)
29-
#define PM_AGGRESSIVE CONSTANT_CYW43_PM_VALUE(CYW43_PM2_POWERSAVE_MODE, 2000, 1, 1, 10)
30-
// CYW43_PERFORMANCE_PM (except a compile-time constant)
31-
#define PM_PERFORMANCE CONSTANT_CYW43_PM_VALUE(CYW43_PM2_POWERSAVE_MODE, 20, 1, 1, 1)
32-
// The 0xa11140 magic value
33-
#define PM_DISABLED CONSTANT_CYW43_PM_VALUE(CYW43_NO_POWERSAVE_MODE, 200, 1, 1, 10)
29+
#define CONST_CYW43_DEFAULT_PM (CONST_CYW43_PERFORMANCE_PM)
30+
31+
#define CONST_CYW43_NONE_PM (CONST_cyw43_pm_value(CYW43_NO_POWERSAVE_MODE, 10, 0, 0, 0))
32+
33+
#define CONST_CYW43_AGGRESSIVE_PM (CONST_cyw43_pm_value(CYW43_PM1_POWERSAVE_MODE, 10, 0, 0, 0))
34+
35+
#define CONST_CYW43_PERFORMANCE_PM (CONST_cyw43_pm_value(CYW43_PM2_POWERSAVE_MODE, 200, 1, 1, 10))
3436

37+
extern uint32_t cyw43_get_power_management_value(void);
38+
extern void cyw43_set_power_management_value(uint32_t value);
3539
extern void bindings_cyw43_wifi_enforce_pm(void);
3640
void cyw43_enter_deep_sleep(void);

ports/raspberrypi/common-hal/wifi/Radio.c

+36
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// SPDX-License-Identifier: MIT
66

77
#include "supervisor/port.h"
8+
#include "shared-bindings/wifi/PowerManagement.h"
89
#include "shared-bindings/wifi/Radio.h"
910
#include "shared-bindings/wifi/Network.h"
1011

@@ -107,6 +108,41 @@ void common_hal_wifi_radio_set_tx_power(wifi_radio_obj_t *self, const mp_float_t
107108
cyw43_ioctl(&cyw43_state, CYW43_IOCTL_SET_VAR, 9 + 4, buf, CYW43_ITF_AP);
108109
}
109110

111+
wifi_power_management_t common_hal_wifi_radio_get_power_management(wifi_radio_obj_t *self) {
112+
uint32_t pm_value = cyw43_get_power_management_value();
113+
114+
switch (pm_value) {
115+
case CONST_CYW43_PERFORMANCE_PM:
116+
return POWER_MANAGEMENT_MIN;
117+
case CONST_CYW43_AGGRESSIVE_PM:
118+
return POWER_MANAGEMENT_MAX;
119+
case CONST_CYW43_NONE_PM:
120+
return POWER_MANAGEMENT_NONE;
121+
default:
122+
return POWER_MANAGEMENT_UNKNOWN;
123+
}
124+
}
125+
126+
127+
void common_hal_wifi_radio_set_power_management(wifi_radio_obj_t *self, wifi_power_management_t power_management) {
128+
uint32_t pm_setting = CONST_CYW43_DEFAULT_PM;
129+
switch (power_management) {
130+
case POWER_MANAGEMENT_MIN:
131+
pm_setting = CONST_CYW43_PERFORMANCE_PM;
132+
break;
133+
case POWER_MANAGEMENT_MAX:
134+
pm_setting = CONST_CYW43_AGGRESSIVE_PM;
135+
break;
136+
case POWER_MANAGEMENT_NONE:
137+
pm_setting = CONST_CYW43_NONE_PM;
138+
break;
139+
default:
140+
// Should not get here.
141+
break;
142+
}
143+
cyw43_set_power_management_value(pm_setting);
144+
}
145+
110146
mp_obj_t common_hal_wifi_radio_get_mac_address_ap(wifi_radio_obj_t *self) {
111147
return common_hal_wifi_radio_get_mac_address(self);
112148
}

ports/zephyr-cp/common-hal/wifi/Radio.c

+38-4
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,44 @@ void common_hal_wifi_radio_set_tx_power(wifi_radio_obj_t *self, const mp_float_t
137137
// esp_wifi_set_max_tx_power(tx_power * 4.0f);
138138
}
139139

140-
mp_int_t common_hal_wifi_radio_get_listen_interval(wifi_radio_obj_t *self) {
141-
// wifi_config_t *config = &self->sta_config;
142-
// return config->sta.listen_interval;
143-
return 0;
140+
wifi_power_management_t common_hal_wifi_radio_get_power_management(wifi_radio_obj_t *self) {
141+
// wifi_ps_type_t ps;
142+
// esp_err_t ret = esp_wifi_get_ps(&ps);
143+
// if (ret == ESP_OK) {
144+
// switch (ps) {
145+
// case WIFI_PS_MIN_MODEM:
146+
// return POWER_MANAGEMENT_MIN;
147+
// case WIFI_PS_MAX_MODEM:
148+
// return POWER_MANAGEMENT_MAX;
149+
// case WIFI_PS_NONE:
150+
// return POWER_MANAGEMENT_NONE;
151+
// }
152+
// }
153+
return POWER_MANAGEMENT_UNKNOWN;
154+
}
155+
156+
157+
void common_hal_wifi_radio_set_power_management(wifi_radio_obj_t *self, wifi_power_management_t power_management) {
158+
// switch (power_management) {
159+
// case POWER_MANAGEMENT_MIN:
160+
// esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
161+
// break;
162+
// case POWER_MANAGEMENT_MAX: {
163+
// // listen_interval is only used in this case.
164+
// wifi_config_t *config = &self->sta_config;
165+
// // This is a typical value seen in various examples.
166+
// config->sta.listen_interval = 3;
167+
// esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
168+
// esp_wifi_set_config(ESP_IF_WIFI_STA, config);
169+
// }
170+
// break;
171+
// case POWER_MANAGEMENT_NONE:
172+
// esp_wifi_set_ps(WIFI_PS_NONE);
173+
// break;
174+
// case POWER_MANAGEMENT_UNKNOWN:
175+
// // This should be prevented in shared-bindings.
176+
// break;
177+
// }
144178
}
145179

146180
void common_hal_wifi_radio_set_listen_interval(wifi_radio_obj_t *self, const mp_int_t listen_interval) {

py/circuitpy_defns.mk

+1
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ $(filter $(SRC_PATTERNS), \
620620
supervisor/StatusBar.c \
621621
wifi/AuthMode.c \
622622
wifi/Packet.c \
623+
wifi/PowerManagement.c \
623624
)
624625

625626
ifeq ($(CIRCUITPY_BLEIO_HCI),1)

py/circuitpy_mpconfig.mk

-3
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,6 @@ CFLAGS += -DCIRCUITPY_WEB_WORKFLOW=$(CIRCUITPY_WEB_WORKFLOW)
707707
CIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS?= 1
708708
CFLAGS += -DCIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS=$(CIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS)
709709

710-
CIRCUITPY_WIFI_RADIO_SETTABLE_LISTEN_INTERVAL?= 0
711-
CFLAGS += -DCIRCUITPY_WIFI_RADIO_SETTABLE_LISTEN_INTERVAL=$(CIRCUITPY_WIFI_RADIO_SETTABLE_LISTEN_INTERVAL)
712-
713710
# tinyusb port tailored configuration
714711
CIRCUITPY_TUSB_MEM_ALIGN ?= 4
715712
CFLAGS += -DCIRCUITPY_TUSB_MEM_ALIGN=$(CIRCUITPY_TUSB_MEM_ALIGN)
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include "py/enum.h"
8+
9+
#include "shared-bindings/wifi/PowerManagement.h"
10+
11+
//| class PowerManagement:
12+
//| """Power-saving options for wifi
13+
//|
14+
//| .. note:: On boards using the CYW43 radio module, the choices below correspond to the
15+
//| power management values defined in the `cyw43` module:
16+
//| `PowerManagement.MIN` is the same as `cyw43.PM_PERFORMANCE`, `PowerManagement.MAX`
17+
//| is the same as `cyw43.PM_AGGRESSIVE`, and `PowerManagement.NONE` is the same as
18+
//| `cyw43.PM_DISABLED`. If a custom value was set with `cyw43.set_power_management()`
19+
//| not corresponding to one of these three values, then `PowerManagement.UNKNOWN` will be returned.
20+
//| """
21+
//|
22+
//| MIN: PowerManagement
23+
//| """Minimum power management (default). The WiFi station wakes up to receive a beacon every DTIM period.
24+
//| The DTIM period is set by the access point."""
25+
//| MAX: PowerManagement
26+
//| """Maximum power management, at the expense of some performance. The WiFi station wakes up less often than `MIN`."""
27+
//| NONE: PowerManagement
28+
//| """No power management: the WiFi station does not sleep."""
29+
//| UNKNOWN: PowerManagement
30+
//| """Power management setting cannot be determined."""
31+
//|
32+
33+
// In order of the enum type.
34+
MAKE_ENUM_VALUE(wifi_power_management_type, power_management, NONE, POWER_MANAGEMENT_NONE);
35+
MAKE_ENUM_VALUE(wifi_power_management_type, power_management, MIN, POWER_MANAGEMENT_MIN);
36+
MAKE_ENUM_VALUE(wifi_power_management_type, power_management, MAX, POWER_MANAGEMENT_MAX);
37+
MAKE_ENUM_VALUE(wifi_power_management_type, power_management, UNKNOWN, POWER_MANAGEMENT_UNKNOWN);
38+
39+
MAKE_ENUM_MAP(wifi_power_management) {
40+
MAKE_ENUM_MAP_ENTRY(power_management, NONE),
41+
MAKE_ENUM_MAP_ENTRY(power_management, MIN),
42+
MAKE_ENUM_MAP_ENTRY(power_management, MAX),
43+
MAKE_ENUM_MAP_ENTRY(power_management, UNKNOWN),
44+
};
45+
46+
static MP_DEFINE_CONST_DICT(wifi_power_management_locals_dict, wifi_power_management_locals_table);
47+
48+
MAKE_PRINTER(wifi, wifi_power_management);
49+
50+
MAKE_ENUM_TYPE(wifi, PowerManagement, wifi_power_management);
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
9+
#include "py/enum.h"
10+
11+
typedef enum {
12+
POWER_MANAGEMENT_NONE = 0,
13+
POWER_MANAGEMENT_MIN = 1,
14+
POWER_MANAGEMENT_MAX = 2,
15+
// Value can't be determined.
16+
POWER_MANAGEMENT_UNKNOWN = 3,
17+
} wifi_power_management_t;
18+
19+
extern const mp_obj_type_t wifi_power_management_type;

0 commit comments

Comments
 (0)