From 4b5a07323804c91927b36d0d6555ecf41a5ec77e Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Sun, 13 Sep 2026 16:07:10 -0700 Subject: [PATCH] wifi: allow 5 GHz channels in start_ap() The channel was clamped to 1..13, so dual-band radios could not run a 5 GHz access point. Accept 1..165, matching start_scanning_networks(), and let each port reject what its radio does not support: - espressif: raise on esp_wifi_set_config() failure. An invalid channel raises ValueError. If this call turned AP mode on, turn it off again. - raspberrypi: CYW43 is 2.4 GHz only; keep its bound at 13. The IDF further limits 5 GHz by country and station band. With the default world-safe country only 36-64 are allowed while disconnected. Fixes #11338 Co-Authored-By: Claude Fable 5.1 --- ports/espressif/common-hal/wifi/Radio.c | 14 +++++++++++++- ports/raspberrypi/common-hal/wifi/Radio.c | 3 +++ shared-bindings/wifi/Radio.c | 9 +++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ports/espressif/common-hal/wifi/Radio.c b/ports/espressif/common-hal/wifi/Radio.c index cc92a41705a..78ce251def4 100644 --- a/ports/espressif/common-hal/wifi/Radio.c +++ b/ports/espressif/common-hal/wifi/Radio.c @@ -242,6 +242,7 @@ void common_hal_wifi_radio_stop_station(wifi_radio_obj_t *self) { } void common_hal_wifi_radio_start_ap(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, uint32_t authmode, uint8_t max_connections) { + bool was_ap = self->ap_mode; set_mode_ap(self, true); uint8_t esp_authmode = 0; @@ -275,7 +276,18 @@ void common_hal_wifi_radio_start_ap(wifi_radio_obj_t *self, uint8_t *ssid, size_ config->ap.max_connection = max_connections; - esp_wifi_set_config(WIFI_IF_AP, config); + esp_err_t result = esp_wifi_set_config(WIFI_IF_AP, config); + if (result != ESP_OK) { + if (!was_ap) { + set_mode_ap(self, false); + } + // The IDF returns ESP_ERR_INVALID_ARG for a channel this radio or its + // country setting cannot use (wifi_ap_config_t.channel). + if (result == ESP_ERR_INVALID_ARG) { + mp_arg_error_invalid(MP_QSTR_channel); + } + raise_esp_error(result); + } // Wait a few ms for the AP to start. Empirically, this takes < 3ms on ESP32, and < 1ms on other chips. for (size_t ms = 0; ms < 10; ms++) { if (common_hal_wifi_radio_get_ap_active(self)) { diff --git a/ports/raspberrypi/common-hal/wifi/Radio.c b/ports/raspberrypi/common-hal/wifi/Radio.c index 73e7794ecc2..f7f382cae79 100644 --- a/ports/raspberrypi/common-hal/wifi/Radio.c +++ b/ports/raspberrypi/common-hal/wifi/Radio.c @@ -196,6 +196,9 @@ void common_hal_wifi_radio_start_ap(wifi_radio_obj_t *self, uint8_t *ssid, size_ mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); } + // The CYW43 is 2.4 GHz only. + mp_arg_validate_int_max(channel, 13, MP_QSTR_channel); + /* TODO: If the AP is stopped once it cannot be restarted. * This means that if if the user does: * diff --git a/shared-bindings/wifi/Radio.c b/shared-bindings/wifi/Radio.c index 9dffaad995d..c9aafc903ba 100644 --- a/shared-bindings/wifi/Radio.c +++ b/shared-bindings/wifi/Radio.c @@ -342,6 +342,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); //| //| If ``channel`` is given, the access point will use that channel unless //| a station is already operating on a different channel. +//| On dual-band radios, 5 GHz channels (36 and above) may also be given. +//| A channel the radio does not support raises `ValueError`. //| //| If ``authmode`` is not None, the access point will use the given authentication modes. //| If a non-empty password is given, ``authmode`` must not include ``OPEN``. @@ -363,7 +365,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); //| //| .. note:: //| -//| In the raspberrypi port (RP2040 CYW43), ``max_connections`` is ignored. +//| In the raspberrypi port (RP2040 CYW43), ``max_connections`` is ignored +//| and only 2.4 GHz channels (1-13) are accepted. //| """ //| ... //| @@ -403,7 +406,9 @@ static mp_obj_t wifi_radio_start_ap(size_t n_args, const mp_obj_t *pos_args, mp_ } } - mp_int_t channel = mp_arg_validate_int_range(args[ARG_channel].u_int, 1, 13, MP_QSTR_channel); + // 165 is the highest channel in the scan pattern. Channels the radio + // doesn't support are rejected by the port rather than here. + mp_int_t channel = mp_arg_validate_int_range(args[ARG_channel].u_int, 1, 165, MP_QSTR_channel); if (authmode == AUTHMODE_OPEN && password.len > 0) { mp_raise_ValueError(MP_ERROR_TEXT("AuthMode.OPEN is not used with password"));