fix(esp_wifi): Fixed some issues in roaming app found using static analysis

This commit is contained in:
Kapil Gupta
2026-05-06 13:20:54 +05:30
committed by Jiang Jiang Jian
parent c00874869b
commit b585c0b364
14 changed files with 1737 additions and 292 deletions

View File

@@ -852,6 +852,5 @@ mainmenu "Espressif IoT Development Framework Configuration"
- CONFIG_SPIRAM_SPEED_120M && CONFIG_SPIRAM_MODE_OCT
- CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
- CONFIG_ESP_WIFI_EAP_TLS1_3
- CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
- CONFIG_USB_HOST_EXT_PORT_RESET_ATTEMPTS
- CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION

View File

@@ -710,19 +710,13 @@ menu "Wi-Fi"
Select this option to enable WiFi Multiband operation certification support.
config ESP_WIFI_ENABLE_ROAMING_APP
bool "Advanced support for Wi-Fi Roaming (Experimental)"
depends on IDF_EXPERIMENTAL_FEATURES
bool "Advanced support for Wi-Fi Roaming"
default n
help
Enable Espressif's roaming app to allow for efficient Wi-Fi roaming.
This includes configurable periodic environment scans, maintaining a cache of the
best APs, handling low rssi events etc.
Risk Warning
Please note that this feature is still experimental and enabling this potentially can
lead to unpredictable scanning, connection and roaming attempts.
We are still working on tuning and optimising this feature to ensure reliable and stable use.
menu "Configure roaming App"
depends on ESP_WIFI_ENABLE_ROAMING_APP
rsource "wifi_apps/roaming_app/src/Kconfig.roaming"

View File

@@ -689,19 +689,13 @@ config WIFI_RMT_MBO_SUPPORT
Select this option to enable WiFi Multiband operation certification support.
config WIFI_RMT_ENABLE_ROAMING_APP
bool "Advanced support for Wi-Fi Roaming (Experimental)"
depends on IDF_EXPERIMENTAL_FEATURES
bool "Advanced support for Wi-Fi Roaming"
default n
help
Enable Espressif's roaming app to allow for efficient Wi-Fi roaming.
This includes configurable periodic environment scans, maintaining a cache of the
best APs, handling low rssi events etc.
Risk Warning
Please note that this feature is still experimental and enabling this potentially can
lead to unpredictable scanning, connection and roaming attempts.
We are still working on tuning and optimising this feature to ensure reliable and stable use.
menu "Configure roaming App"
depends on WIFI_RMT_ENABLE_ROAMING_APP
rsource "wifi_apps/roaming_app/src/Kconfig.roaming"

View File

@@ -360,7 +360,6 @@ endif
if WIFI_RMT_ENABLE_ROAMING_APP
config ESP_WIFI_ENABLE_ROAMING_APP # ignore: multiple-definition
bool
depends on IDF_EXPERIMENTAL_FEATURES
default WIFI_RMT_ENABLE_ROAMING_APP
endif

View File

@@ -43,3 +43,4 @@ CONFIG_WPA_MBO_SUPPORT CONFIG_ESP_WIFI_MBO_SUPPORT
CONFIG_WPA_DPP_SUPPORT CONFIG_ESP_WIFI_DPP_SUPPORT
CONFIG_WPA_11R_SUPPORT CONFIG_ESP_WIFI_11R_SUPPORT
CONFIG_WPA_WPS_SOFTAP_REGISTRAR CONFIG_ESP_WIFI_WPS_SOFTAP_REGISTRAR
CONFIG_ESP_WIFI_NETWORK_ASSISTED_ROAMING_IP_RENEW_SKIP CONFIG_ESP_WIFI_ROAMING_IP_RENEW_SKIP

View File

@@ -29,7 +29,7 @@ static esp_netif_t *s_wifi_netifs[MAX_WIFI_IFS] = { NULL };
static bool wifi_default_handlers_set = false;
static esp_err_t disconnect_and_destroy(esp_netif_t* esp_netif);
#ifdef CONFIG_ESP_WIFI_NETWORK_ASSISTED_ROAMING_IP_RENEW_SKIP
#ifdef CONFIG_ESP_WIFI_ROAMING_IP_RENEW_SKIP
static bool roaming_ongoing = false;
#endif
@@ -85,7 +85,7 @@ static void wifi_default_action_sta_stop(void *arg, esp_event_base_t base, int32
{
#ifdef CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
roam_disable_reconnect();
#ifdef CONFIG_ESP_WIFI_NETWORK_ASSISTED_ROAMING_IP_RENEW_SKIP
#ifdef CONFIG_ESP_WIFI_ROAMING_IP_RENEW_SKIP
roaming_ongoing = false;
#endif
#endif /* CONFIG_ESP_WIFI_ENABLE_ROAMING_APP */
@@ -98,7 +98,7 @@ static void wifi_default_action_sta_connected(void *arg, esp_event_base_t base,
{
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
roam_sta_connected();
#ifdef CONFIG_ESP_WIFI_NETWORK_ASSISTED_ROAMING_IP_RENEW_SKIP
#ifdef CONFIG_ESP_WIFI_ROAMING_IP_RENEW_SKIP
if (roaming_ongoing) {
/* IP stack is already in ready state */
roaming_ongoing = false;
@@ -127,7 +127,7 @@ static void wifi_default_action_sta_disconnected(void *arg, esp_event_base_t bas
{
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
roam_sta_disconnected(data);
#ifdef CONFIG_ESP_WIFI_NETWORK_ASSISTED_ROAMING_IP_RENEW_SKIP
#ifdef CONFIG_ESP_WIFI_ROAMING_IP_RENEW_SKIP
wifi_event_sta_disconnected_t *disconn = data;
if (disconn->reason == WIFI_REASON_ROAMING) {
roaming_ongoing = true;

View File

@@ -6,10 +6,15 @@
#pragma once
#include "esp_wifi.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ROAM_SCAN_FILTER_SSID_LEN (MAX_SSID_LEN + 1)
#define ROAM_SCAN_FILTER_BSSID_LEN 6
struct roam_config {
uint8_t backoff_time;
bool low_rssi_roam_trigger;
@@ -28,6 +33,9 @@ struct roam_config {
uint8_t rrm_monitor_time;
int8_t rrm_monitor_rssi_threshold;
wifi_scan_config_t scan_config;
uint8_t scan_filter_ssid[ROAM_SCAN_FILTER_SSID_LEN];
uint8_t scan_filter_bssid[ROAM_SCAN_FILTER_BSSID_LEN];
bool scan_filter_bssid_set;
};
void roam_init_app(void);
@@ -38,6 +46,10 @@ void roam_sta_connected(void);
void roam_sta_disconnected(void *disconn);
esp_err_t roam_get_config_params(struct roam_config *config);
esp_err_t roam_set_config_params(struct roam_config *config);
#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
esp_err_t esp_wifi_blacklist_add(const uint8_t *bssid);
esp_err_t esp_wifi_blacklist_remove(const uint8_t *bssid);
#endif
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
void esp_wifi_roaming_set_current_bssid(const uint8_t *bssid);

View File

@@ -90,13 +90,13 @@ menu "Roaming Methods"
Retry threshold after which the station should stop using Network Assisted
roaming methods and start using legacy roaming instead.
config ESP_WIFI_NETWORK_ASSISTED_ROAMING_IP_RENEW_SKIP
bool "Skip IP renew during BTM based roaming"
depends on ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM
config ESP_WIFI_ROAMING_IP_RENEW_SKIP
bool "Skip IP renew after roaming"
default n
help
Station will not ask for IP renew after a BTM based roaming. Before enabling please
make sure your network supports this.
Skip the default IP renew path when the Wi-Fi stack reports a roaming event.
Enable this only if your network preserves IP state across a roam, for example
during 802.11r or BTM-based roaming.
endmenu #"Roaming Methods"
@@ -129,7 +129,9 @@ menu "Scan Configuration"
default "None"
help
Channels your wireless network operates on to allow for faster scanning.
Specify the channels(between 1-14) in a comma separated manner.
Specify channels in a comma-separated manner. The list is used as a
channel filter, not as an ordered scan sequence. When 5 GHz support
is enabled, valid 5 GHz channels are also accepted.
config ESP_WIFI_ROAMING_SCAN_EXPIRY_WINDOW
int "Scan results expiry window (in seconds)"
@@ -214,12 +216,4 @@ menu "Blacklist Configuration"
help
Maximum number of roaming candidates to consider. This also defines the size of the blacklist.
config ESP_WIFI_ROAMING_PREVENT_DOWNGRADE
bool "Prevent security downgrade when roaming"
default n
help
If the currently connected AP sends a "transition disable" bit,
this option will make the roaming logic ignore less secure APs.
This helps prevent security downgrades when roaming in a mixed
security environment (e.g., WPA2/WPA3).
endmenu # "Blacklist Configuration"

View File

@@ -1,6 +1,8 @@
**Introduction**
The advanced Wi-Fi roaming app has been written with the intention of simplifying the process of developing applications that will function in a network environment that allows for roaming between the service areas of multiple compatible APs. It gathers basic approaches about how different roaming mechanisms and APIs can be integrated for an efficient solution to roaming and bundles into an easy to user yet highly configurable module.
The advanced Wi-Fi roaming app has been written with the intention of simplifying the process of developing applications that will function in a network environment that allows for roaming between the service areas of multiple compatible APs. It gathers basic approaches about how different roaming mechanisms and APIs can be integrated for an efficient solution to roaming and bundles into an easy to use yet highly configurable module.
For runtime flow and configuration details, see [ROAMING_FLOW.md](ROAMING_FLOW.md).
**How to use:**
@@ -9,26 +11,26 @@ To enable the roaming app in the menuconfig, please navigate to Component Settin
**Configuring the advanced Wi-Fi roaming app :**
After enabling the roaming app in the menuconfig, this roaming app can be configured to best suit your application requirements and the network environment. The configurations are classified into Roaming Triggers (better understood as “Under what conditions to roam?”), Roaming methods (better understood as “How to Roam), and then some additional configurations such as scanning parameters, backoff times, periodic neighbor report requests etc.
After enabling the roaming app in the menuconfig, this roaming app can be configured to best suit your application requirements and the network environment. The configurations are classified into Roaming Triggers (better understood as “Under what conditions to roam?”), Roaming methods (better understood as “How to roam?”), and then some additional configurations such as scanning parameters, backoff times, periodic neighbor report requests, blacklisting, etc.
**Roaming Triggers: (Roaming Module Settings --> Roaming Triggers)**
There are broadly two different Roaming triggers you can choose from:
There are broadly two different roaming triggers you can choose from:
1) Low RSSI triggered roaming :
If enabled, in this method the roaming app sets a Wi-Fi Threshold (configured by “Wi-Fi RSSI threshold to trigger roaming”), which when reached by the connection to the current AP, triggers a check for a better AP and if found will trigger roaming.
Every time the threshold is reached, a new threshold needs to be set at an even lower rssi threshold since if we fail to find a better AP the first time the RSSI threshold is reached there will be no further attempts to find better APs leading to possible disconnection as the only avenue for finding a better AP. The offset by which the next RSSI threshold can be set is decided by “Wi-Fi RSSI threshold to trigger roaming” which defaults to 5.
Every time the threshold is reached, a new threshold needs to be set at an even lower RSSI threshold since if we fail to find a better AP the first time the RSSI threshold is reached there will be no further attempts to find better APs, leading to possible disconnection as the only avenue for finding a better AP. The offset by which the next RSSI threshold can be set is decided by “Offset by which to reset the RSSI Threshold after attempt to roam” which defaults to 5.
Also please note that if the AP we connect to, upon connecting itself has a worse RSSI than the threshold set in the configuration, the new RSSI threshold is set to the current RSSI - offset. Additionally, the RSSI threshold gets reset to the configured value if the RSSI ever goes below the configured threshold by the offset amount.
Also please note that if the AP we connect to, upon connecting, itself has a worse RSSI than the threshold set in the configuration, the new RSSI threshold is set to the current RSSI - offset. Additionally, the RSSI threshold gets reset to the configured value if the RSSI later improves above the configured threshold.
2) Periodic Scan based roaming:
Unlike Low RSSI triggered roaming, which is a bit reactive to the changing network environment, periodic scanning-based roaming (enabled by “Conduct periodic scans to check if a better AP is available”) allows for a more active approach to ensuring that you are connected to the best AP in the network. You can also decide the threshold after which you want to conduct the periodic scans with a default of 20dbm. (Configured by “Threshold at which to begin periodic scanning for a better AP”)
Unlike Low RSSI triggered roaming, which is a bit reactive to the changing network environment, periodic scanning-based roaming (enabled by “Conduct periodic scans to check if a better AP is available”) allows for a more active approach to ensuring that you are connected to the best AP in the network. You can also decide the threshold after which you want to conduct the periodic scans with a default of -50 dBm. (Configured by “Threshold at which to begin periodic scanning for a better AP”)
The intervals at which this periodic scan will take place can also be configured through “Time intervals at which station will initiate a scan”. This defaults to 30 seconds.The RSSI difference between a candidate AP and the current AP, which can be considered as acceptable to initiate roaming can also be configured as the “RSSI difference b/w current AP and a candidate AP to initiate roaming”
The intervals at which this periodic scan will take place can also be configured through “Time intervals at which station will initiate a scan”. This defaults to 30 seconds. The RSSI difference between a candidate AP and the current AP, which can be considered as acceptable to initiate roaming, can also be configured as the “RSSI difference b/w current AP and a candidate AP to initiate roaming”
**Please note that at least one of the two Roaming Triggers needs to be enabled.**
@@ -39,9 +41,9 @@ Currently 2 methods of roaming are supported by the roaming app. (Roaming App Se
1) Network Assisted Roaming:
Enabled by “Support Network Assisted Roaming using 802.11v”, this method primarily uses the BSS transition Management mechanisms outlined in IEEE 802.11v. It uses Candidates received from neighbor report requests (if enabled, explained later.) and scanning results to Send BSS transition Management Queries to the AP it is currently associated to. Depending on the current radio environment and vendor implementation on the side of the AP, this could then lead to BSS Transition Management Requests and corresponding BSS Transition Management responses which could lead to a seamless transition from one AP to another. For a better understanding of the mechanisms involved and the general implementation please look up the IEEE 802.11v specification and upstream wpa_supplicants implementation.
Enabled by “Support Network Assisted Roaming using 802.11v”, this method primarily uses the BSS transition Management mechanisms outlined in IEEE 802.11v. It uses candidates received from neighbor report requests (if enabled, explained later) and scanning results to send BSS transition Management Queries to the AP it is currently associated to. Depending on the current radio environment and vendor implementation on the side of the AP, this could then lead to BSS Transition Management Requests and corresponding BSS Transition Management responses which could lead to a seamless transition from one AP to another. For a better understanding of the mechanisms involved and the general implementation please look up the IEEE 802.11v specification and upstream wpa_supplicants implementation.
Please note that for this to work as expected, the APs should support 802.11k & 802.11v and be setup in a network where they are aware of each other.
Please note that for this to work as expected, the APs should be setup in a network where they are aware of each other. If the network is expected to preserve IP configuration across a roaming event, `Skip IP renew after roaming` can also be enabled. This is relevant only when the roam keeps the same IP state, such as 802.11r or BTM-based roaming.
2) Legacy Roaming approach.
@@ -58,7 +60,7 @@ The scan configuration allows for configuring the parameters for the scans that
The minimum and maximum active scanning duration for each channel in milliseconds can be configured through “Minimum duration of active scan time for a station” & “Maximum duration of active scan time for a station”. If connected, the “Home channel dwell time between scanning consecutive channels” configuration decides for how long the station will return to the home channel for Tx of various frames in the buffer, and Rx of frames buffered by the AP.
Additionally, if channels of operation of the APs that the application designed will work with is known, you can provide this information as an ordered list of comma-separated channels. (configured using “Preferred channel list for scanning”) (for e.g. 1,6,9,11) Only those channels will be scanned in the order mentioned. Keeping in mind that network discovery/scanning is the process that takes up most of the time in roaming as well connecting to an AP, providing such a list could significantly increase the efficiency of the roaming app and process.
Additionally, if channels of operation of the APs that the application is designed to work with are known, you can provide this information as a comma-separated list of channels. (configured using “Preferred channel list for scanning”) (for e.g. 1,6,9,11) Only those channels will be scanned, but the list is treated as a filter and not as an ordered scan sequence. Keeping in mind that network discovery/scanning is the process that takes up most of the time in roaming as well as connecting to an AP, providing such a list could significantly increase the efficiency of the roaming app and process.
This module can trigger scans due to several reasons. The configuration “Scan results expiry window” decides the duration for which different modules will use the most recent scan results instead of triggering a new scan of their own.
@@ -73,17 +75,28 @@ Successive roaming attempts by multiple roaming triggers simultaneously could le
Neighbor Report requests are a part of the IEEE 802.11k specification, and hence the intended network would need to support and be setup in a way to support its mechanisms. Periodic neighbor report requests provide vital information about the network and other APs in the vicinity that are candidate APs of the same network. This can be enabled using “Send Periodic Neighbor Report requests for updating the internal list”. The frequency of these requests is controlled by “Time interval between Periodic Neighbor report Requests”. There can also be a RSSI threshold (“Threshold for sending periodic neighbor report requests”) after which you wish to consider sending these requests however this is set by default to -20.
**Blacklist and security behavior:**
`Enable BSSID blacklisting` allows the app to skip specific BSSIDs during candidate selection. Entries can be added manually through `esp_wifi_blacklist_add()` and `esp_wifi_blacklist_remove()`.
If `Enable automatic BSSID blacklisting` is enabled, a BSSID is blacklisted after repeated connection or authentication failures. Blacklisted entries are removed again after `Blacklist timeout (in seconds)`.
The roaming logic also honors AP-advertised transition disable policy while selecting candidates and prevents roaming to candidates that do not meet the authmode threshold set by `wifi_config_t.sta`.
**Notes :**
1) Advanced roaming support is disabled by default.
2) When enabling the advanced roaming support , it is expected that the bssid to connect to is not specifically set by the application. This would defeat the purpose of roaming between different APs of the network. Hence if the BSSID is set (in wifi_config_t.sta) , it will be unset at the first disconnection/connection.
2) When enabling the advanced roaming support, it is expected that the BSSID to connect to is not specifically set by the application. This would defeat the purpose of roaming between different APs of the network. The roaming app may temporarily set BSSID/channel hints for a directed reconnect, but these hints are cleared again so normal roaming can continue.
3) For roaming to work as expected, the APs between which the station is expected to roam must have the same or compatible authmode. These include :
3) For roaming to work as expected, the APs between which the station is expected to roam must have the same or compatible authmode. These include :
Open <--> OWE
PSK based authmodes <--> PSK based authmodes (Does not include WEP)
PSK based authmodes <--> PSK based authmodes (Does not include WEP)
`WAPI_PSK` <--> legacy WPA/WPA2 PSK authmodes
Enterprise <--> Enterprise.

View File

@@ -0,0 +1,201 @@
# Roaming App Flow
## Scope
This document describes the runtime control flow of the advanced Wi-Fi roaming app and identifies the configuration options that influence each stage. Each diagram focuses on one stage of the app, and the table below it lists the related configuration options.
## Terminology
- `AP`: Access Point.
- `BSSID`: MAC address of one AP radio.
- `Candidate AP`: A scanned AP that is still under consideration for roaming.
- `BTM`: Network-assisted roaming using IEEE 802.11v BSS Transition Management.
- `RRM / Neighbor Report`: Information about neighboring APs obtained using IEEE 802.11k.
- `Legacy roaming`: A directed reconnect initiated by the roaming app.
- `Blacklist`: A temporary deny-list of BSSIDs that the app should avoid.
## Initialization Preconditions
The roaming app starts only if both of the following are true:
- At least one roaming trigger is enabled.
- At least one roaming method is enabled.
If either condition is not satisfied, `roam_init_app()` returns without starting roaming support.
## 1. High-Level Flow
The following diagram summarizes the complete roaming flow before the later sections break it down by stage.
```mermaid
flowchart TD
A["roam_init_app()"] --> B{"Triggers and methods enabled?"}
B -- "No" --> X["Return without starting roaming support"]
B -- "Yes" --> C["Wait for station connection"]
C --> D["On connection, apply roaming configuration<br/>and start enabled monitors"]
D --> E{"Runtime event"}
E -- "Low RSSI or periodic scan" --> F["Find the best candidate AP"]
E -- "Periodic RRM timer" --> G["Refresh cached neighbor list for BTM"]
G --> E
E -- "Disconnect event" --> H{"Reconnect allowed?"}
H -- "No" --> I["No action"]
H -- "Yes" --> J{"After disconnect handling,<br/>is the disconnected BSSID blacklisted now?"}
J -- "No" --> K["Use default reconnect path"]
J -- "Yes" --> F
F --> L{"Reuse scan cache?"}
L -- "Yes" --> M["Evaluate scanned APs"]
L -- "No" --> N["Run a new scan and evaluate APs"]
M --> O{"Candidate selected?"}
N --> O
O -- "No, still connected" --> P["Stay on current AP"]
O -- "No, disconnected recovery" --> Q["Schedule retry with a fresh scan"]
O -- "Yes" --> R{"How to roam?"}
R -- "BTM query can be used" --> S["Send BTM query and wait for AP or network action"]
R -- "Directed reconnect is used" --> T["Start reconnect to the selected AP"]
R -- "No method available" --> U["No roam action"]
P --> E
Q --> E
S --> E
T --> E
K --> E
```
## 2. Initialization and Monitoring
```mermaid
flowchart TD
A["roam_init_app()"] --> B{"At least one trigger enabled?"}
B -- "No" --> X["Return without starting roaming support"]
B -- "Yes" --> C{"At least one roaming method enabled?"}
C -- "No" --> X
C -- "Yes" --> D["Load default configuration"]
D --> E["Register Wi-Fi event handlers"]
E --> F["Wait for station connection"]
F --> G["On connection: read AP state,<br/>refresh scan filters, apply runtime config,<br/>and arm enabled monitors"]
G --> H{"Runtime event"}
H -- "Low RSSI" --> I["Candidate discovery"]
H -- "Periodic scan timer" --> I
H -- "Periodic RRM timer" --> J["Send neighbor report request"]
J --> K["Update cached BTM neighbor list"]
K --> H
H -- "Disconnect event" --> L{"Reconnect allowed?"}
L -- "No" --> M["No action"]
L -- "Yes" --> N{"After disconnect handling,<br/>is the disconnected BSSID blacklisted now?"}
N -- "Yes" --> I
N -- "No" --> O["Default reconnect"]
```
### Configuration used in this stage
| Area | Configuration |
| --- | --- |
| Low RSSI trigger | `ESP_WIFI_ROAMING_LOW_RSSI_ROAMING`, `ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD`, `ESP_WIFI_ROAMING_LOW_RSSI_OFFSET` |
| Periodic scan trigger | `ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR`, `ESP_WIFI_ROAMING_PERIODIC_SCAN_THRESHOLD`, `ESP_WIFI_ROAMING_SCAN_MONITOR_INTERVAL`, `ESP_WIFI_ROAMING_SCAN_ROAM_RSSI_DIFF` |
| Periodic RRM | `ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING`, `ESP_WIFI_ROAMING_RRM_MONITOR_TIME`, `ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD` |
| Blacklist handling | `ESP_WIFI_ROAMING_BSSID_BLACKLIST`, `ESP_WIFI_ROAMING_AUTO_BLACKLISTING`, `ESP_WIFI_ROAMING_MAX_CONN_FAILURES`, `ESP_WIFI_ROAMING_BLACKLIST_TIMEOUT` |
| Runtime overrides | `roam_set_config_params()`, `scan_filter_ssid`, `scan_filter_bssid` |
## 3. Candidate Discovery and Selection
```mermaid
flowchart TD
A["determine_best_ap(threshold)"] --> A1{"Can the app act on a roam candidate now?"}
A1 -- "No" --> O["Stay on current AP"]
A1 -- "Yes" --> B{"Recent scan cache still valid?"}
B -- "Yes" --> C["Reuse cached scan results"]
B -- "No" --> D["Run a new scan"]
C --> E["Evaluate scanned APs"]
D --> E
E --> F{"Station connected?"}
F -- "Yes" --> G["Set baseline RSSI from current connection state"]
F -- "No" --> H["Use disconnected recovery rules"]
G --> I["Skip blacklisted BSSIDs"]
H --> I
I --> J["Apply security policy"]
J --> K["Compare RSSI delta with threshold"]
K --> L{"Candidate selected?"}
L -- "Yes" --> M{"Still able to act on this candidate?"}
M -- "Yes" --> Q["Post roam event"]
M -- "No" --> O
L -- "No" --> N{"Station connected?"}
N -- "Yes" --> O["Stay on current AP"]
N -- "No" --> P["Schedule retry and invalidate scan cache"]
```
### Configuration used in this stage
| Area | Configuration |
| --- | --- |
| Scan timing | `ESP_WIFI_ROAMING_SCAN_MIN_SCAN_TIME`, `ESP_WIFI_ROAMING_SCAN_MAX_SCAN_TIME`, `ESP_WIFI_ROAMING_HOME_CHANNEL_DWELL_TIME` |
| Scan filter and scope | `ESP_WIFI_ROAMING_SCAN_CHAN_LIST`, `scan_filter_ssid`, `scan_filter_bssid` |
| Scan cache reuse | `ESP_WIFI_ROAMING_SCAN_EXPIRY_WINDOW` |
| Blacklist filter | `ESP_WIFI_ROAMING_BSSID_BLACKLIST`, `ESP_WIFI_ROAMING_BLACKLIST_TIMEOUT`, `ESP_WIFI_ROAMING_MAX_CANDIDATES` |
| Security policy | `wifi_config_t.sta.threshold.authmode`, `wifi_config_t.sta.owe_enabled`, `wifi_config_t.sta.pmf_cfg.required`, `wifi_config_t.sta.sae_pwe_h2e`, `wifi_config_t.sta.sae_pk_mode` |
| Periodic scan threshold input | `ESP_WIFI_ROAMING_SCAN_ROAM_RSSI_DIFF` |
## 4. Roam Execution
```mermaid
flowchart TD
A["roaming_app_trigger_roam(candidate)"] --> B{"Station connected?"}
B -- "No" --> C["Start reconnect to selected AP"]
B -- "Yes" --> D{"Inside backoff window?"}
D -- "Yes" --> X["Ignore request"]
D -- "No" --> E{"BTM enabled and supported by current AP?"}
E -- "Yes" --> F{"BTM retries remaining?"}
F -- "Yes" --> G{"Submit BTM query?"}
G -- "Yes" --> H["Send BTM query with selected candidate<br/>and cached neighbor list"]
H --> I["Wait for AP or network action"]
G -- "No" --> J{"Legacy roaming enabled?"}
F -- "No" --> J
E -- "No" --> J
J -- "Yes" --> K["Start directed reconnect to selected AP"]
J -- "No" --> L["No roam action"]
```
### Configuration used in this stage
| Area | Configuration |
| --- | --- |
| Backoff | `ESP_WIFI_ROAMING_BACKOFF_TIME` |
| BTM path | `ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM`, `ESP_WIFI_NETWORK_ASSISTED_ROAMING_RETRY_COUNT` |
| Legacy path | `ESP_WIFI_ROAMING_LEGACY_ROAMING` |
| Post-roam IP behavior | `ESP_WIFI_ROAMING_IP_RENEW_SKIP` |
## 5. Configuration Summary by Functional Area
| Functional area | Configuration |
| --- | --- |
| Low RSSI roaming | `ESP_WIFI_ROAMING_LOW_RSSI_ROAMING`, `ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD`, `ESP_WIFI_ROAMING_LOW_RSSI_OFFSET` |
| Periodic scan roaming | `ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR`, `ESP_WIFI_ROAMING_PERIODIC_SCAN_THRESHOLD`, `ESP_WIFI_ROAMING_SCAN_MONITOR_INTERVAL`, `ESP_WIFI_ROAMING_SCAN_ROAM_RSSI_DIFF` |
| Network-assisted roaming | `ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM`, `ESP_WIFI_NETWORK_ASSISTED_ROAMING_RETRY_COUNT` |
| Post-roam IP behavior | `ESP_WIFI_ROAMING_IP_RENEW_SKIP` |
| Legacy roaming | `ESP_WIFI_ROAMING_LEGACY_ROAMING` |
| Periodic RRM / neighbor reports | `ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING`, `ESP_WIFI_ROAMING_RRM_MONITOR_TIME`, `ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD` |
| Scan behavior | `ESP_WIFI_ROAMING_SCAN_MIN_SCAN_TIME`, `ESP_WIFI_ROAMING_SCAN_MAX_SCAN_TIME`, `ESP_WIFI_ROAMING_HOME_CHANNEL_DWELL_TIME`, `ESP_WIFI_ROAMING_SCAN_CHAN_LIST`, `ESP_WIFI_ROAMING_SCAN_EXPIRY_WINDOW` |
| Blacklist behavior | `ESP_WIFI_ROAMING_BSSID_BLACKLIST`, `ESP_WIFI_ROAMING_AUTO_BLACKLISTING`, `ESP_WIFI_ROAMING_MAX_CONN_FAILURES`, `ESP_WIFI_ROAMING_BLACKLIST_TIMEOUT`, `ESP_WIFI_ROAMING_MAX_CANDIDATES` |
| Runtime scan filters | `roam_set_config_params()`, `scan_filter_ssid`, `scan_filter_bssid` |
| Candidate security policy | `wifi_config_t.sta.threshold.authmode`, `wifi_config_t.sta.owe_enabled`, `wifi_config_t.sta.pmf_cfg.required`, `wifi_config_t.sta.sae_pwe_h2e`, `wifi_config_t.sta.sae_pk_mode` |
## 6. Behavioral Notes
- A periodic RRM event does not directly trigger roaming. Its purpose is to refresh the cached neighbor list used by the BTM path.
- While connected, candidate ranking uses the stronger of two values: the RSSI from the current connection and the RSSI for the same BSSID in the latest scan results.
- After a disconnect, the app checks whether the disconnected BSSID is blacklisted. This covers both BSSIDs that were already blacklisted and BSSIDs that become blacklisted because the latest failure reached the configured threshold.
- After a disconnect, the app does not compare other APs against the failed AP's scan RSSI. This lets it choose any valid AP that is not blacklisted.
- Candidate security checks allow roaming only within supported compatibility groups: Open/OWE, Personal, and Enterprise. Transition-disable policy and station security requirements are still enforced.
- A BTM query is asynchronous. After the query is sent, the app waits for AP or network action. It does not immediately force legacy roaming in the same step.
- The reconnect paths start a connection attempt. Final success or failure is reported later through the normal Wi-Fi connection and disconnection events.
- If the station is already disconnected, the app can still reconnect to a selected AP even when legacy roaming is disabled.

View File

@@ -89,17 +89,6 @@ extern "C" {
#define MAX_NEIGHBOR_LEN 512
#define IS_PSK(authmode) \
(((authmode == WIFI_AUTH_WPA_PSK) || (authmode == WIFI_AUTH_WPA2_PSK) || \
(authmode == WIFI_AUTH_WPA_WPA2_PSK) || (authmode == WIFI_AUTH_WPA3_PSK) || \
(authmode == WIFI_AUTH_WPA2_WPA3_PSK) || (authmode == WIFI_AUTH_WAPI_PSK) ? 1 : 0))
#define OWE_COMPATIBLE(curr_auth, cand_auth) \
((((curr_auth == WIFI_AUTH_OPEN) || (curr_auth == WIFI_AUTH_OWE)) && ((cand_auth == WIFI_AUTH_OPEN) || (cand_auth == WIFI_AUTH_OWE)))? 1 : 0)
#define PSK_COMPATIBLE(curr_auth, cand_auth) \
((IS_PSK(curr_auth) && IS_PSK(cand_auth)) ? 1 : 0)
struct scanned_ap_info {
uint16_t current_count;
struct timeval time;
@@ -118,10 +107,14 @@ struct roam_bss_info {
struct roaming_app {
struct roam_config config;
bool app_active;
bool scan_ongoing;
bool sta_connected;
bool connect_hint_active;
int8_t current_rssi_threshold;
char *btm_neighbor_list;
struct timeval last_roamed_time;
struct timeval last_roam_attempt_time;
struct timeval last_roam_success_time;
struct scanned_ap_info scanned_aps;
struct roam_bss_info current_bss;
@@ -144,6 +137,7 @@ struct roaming_app {
#if CONFIG_ESP_WIFI_ROAMING_BSSID_BLACKLIST
struct blacklist_entry {
uint8_t bssid[ETH_ALEN];
bool manual;
#if CONFIG_ESP_WIFI_ROAMING_AUTO_BLACKLISTING
uint8_t failures;
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -939,6 +939,7 @@ enum phy_type {
PHY_TYPE_HT = 7,
PHY_TYPE_DMG = 8,
PHY_TYPE_VHT = 9,
PHY_TYPE_HE = 14,
};
/* IEEE P802.11-REVmc/D5.0, 9.4.2.37 - Neighbor Report element */

View File

@@ -1,4 +1,3 @@
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_ESP_WIFI_11KV_SUPPORT=y
CONFIG_ESP_WIFI_SCAN_CACHE=y