Skip to content

Station always connects to the first AP found, not the strongest one (mesh / multi-AP networks) #452

Description

@ft972

Environment

  • ESP-IDF v5.5.4, target esp32s3
  • Board: Waveshare ESP32-S3-Touch-LCD-1.9 (WS19Touch variant)
  • App version 2.0.3.2

What happens

In a network where several access points share one SSID — a mesh, or just two
APs with the same name — the controller regularly ends up on a weak AP while a
much stronger one is in range. It then stays there.

Why

wifi_config.c builds the station config with only the auth mode set:

wifi_config_t wifi_config = {
    .sta = {
        .threshold.authmode = WIFI_AUTH_WPA2_PSK,
    },
};

Every other field is therefore zero, and for scan_method the zero value is
WIFI_FAST_SCAN. From esp_wifi_types_generic.h:

typedef enum {
    WIFI_FAST_SCAN = 0,          /**< Do fast scan, scan will end after find SSID match AP */
    WIFI_ALL_CHANNEL_SCAN,       /**< All channel scan, scan will end after scan all the channel */
} wifi_scan_method_t;

The scan stops at the first matching AP, so whichever one answers first
wins, regardless of signal strength.

The sort order is not the problem — sort_method is zero too, and that is
WIFI_CONNECT_AP_BY_SIGNAL, which is exactly what you want. It simply never has
anything left to sort, because the fast scan already stopped.

Suggested fix — one line

wifi_config_t wifi_config = {
    .sta = {
        .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        .scan_method = WIFI_ALL_CHANNEL_SCAN,
    },
};

What it costs

An all-channel scan runs at most WIFI_ACTIVE_SCAN_MAX_DEFAULT_TIME (120 ms)
per channel. On 2.4 GHz with 13 channels that is ~1.6 s worst case, once per
connect. With a single AP the result is the same as before, only slower to get
there — that is the trade-off, and it may be worth making it a Kconfig option
rather than the default if boot time matters to you.

A bonus that comes with it

failure_retry_cnt (retries at one AP before moving to the next) is documented
in the same header as only working with WIFI_ALL_CHANNEL_SCAN. It is currently
unusable for that reason.

What this does not fix

Only the choice made while connecting. Once associated, the station stays on
that AP until the link drops, however bad it gets. Steering by the AP would need
802.11k/v (CONFIG_ESP_WIFI_11KV_SUPPORT), which is not enabled in the current
sdkconfigs — that is a separate and much bigger decision.

Verified on hardware

Built and flashed on 2026-08-05 on the board above. Before the change the
controller regularly associated with a weak AP in a mesh; with
WIFI_ALL_CHANNEL_SCAN it picks the strongest one. Connection setup still works
normally, and the image does not grow — scan_method is an initialiser value,
not extra code.

What was not measured is throughput, and it should not be expected to change:
this affects which AP gets chosen at association time, not the speed of a
connection that is already up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions