Merge branch 'feature/advanced_roaming_support' into 'master'

Add advanced Wi-Fi roaming support through Wi-Fi app

Closes WIFI-6169, WIFI-4212, WIFIBUG-314, and IDFGH-10713

See merge request espressif/esp-idf!26126
pull/13550/head
Jiang Jiang Jian 2024-04-02 00:02:41 +08:00
commit d885808b9f
40 zmienionych plików z 1914 dodań i 165 usunięć

Wyświetl plik

@ -629,3 +629,4 @@ mainmenu "Espressif IoT Development Framework Configuration"
- CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
- CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL
- CONFIG_ESP_WIFI_EAP_TLS1_3
- CONFIG_ESP_WIFI_ENABLE_ROAMING_APP

Wyświetl plik

@ -37,7 +37,10 @@ if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED)
endif()
if(CONFIG_ESP_WIFI_NAN_ENABLE)
list(APPEND srcs "wifi_apps/src/nan_app.c")
list(APPEND srcs "wifi_apps/nan_app/src/nan_app.c")
endif()
if(CONFIG_ESP_WIFI_ENABLE_ROAMING_APP)
list(APPEND srcs "wifi_apps/roaming_app/src/roaming_app.c")
endif()
set(local_include_dirs include/local)
else()
@ -50,10 +53,13 @@ else()
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" "wifi_apps/include" ${local_include_dirs}
INCLUDE_DIRS "include" "wifi_apps/include" "wifi_apps/nan_app/include" ${local_include_dirs}
REQUIRES esp_event esp_phy esp_netif
PRIV_REQUIRES driver esptool_py esp_pm esp_timer nvs_flash
wpa_supplicant hal lwip esp_coex ${extra_priv_requires}
PRIV_INCLUDE_DIRS ../wpa_supplicant/src/ ../wpa_supplicant/esp_supplicant/src/
wifi_apps/roaming_app/include
LDFRAGMENTS "${ldfragments}")
if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED)

Wyświetl plik

@ -574,6 +574,26 @@ menu "Wi-Fi"
help
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
default n
select ESP_WIFI_SCAN_CACHE
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"
endmenu
config ESP_WIFI_DPP_SUPPORT
bool "Enable DPP support"
default n

Wyświetl plik

@ -164,15 +164,21 @@ typedef struct {
cause station to disconnect from AP and are not recommended. */
} wifi_scan_time_t;
typedef struct {
uint16_t ghz_2_channels; /**< Represents 2.4 GHz channels */
uint32_t ghz_5_channels; /**< Represents 5 GHz channels */
} wifi_scan_channel_bitmap_t;
/** @brief Parameters for an SSID scan. */
typedef struct {
uint8_t *ssid; /**< SSID of AP */
uint8_t *bssid; /**< MAC address of AP */
uint8_t channel; /**< channel, scan the specific channel */
bool show_hidden; /**< enable to scan AP whose SSID is hidden */
wifi_scan_type_t scan_type; /**< scan type, active or passive */
wifi_scan_time_t scan_time; /**< scan time per channel */
uint8_t home_chan_dwell_time;/**< time spent at home channel between scanning consecutive channels.*/
uint8_t *ssid; /**< SSID of AP */
uint8_t *bssid; /**< MAC address of AP */
uint8_t channel; /**< channel, scan the specific channel */
bool show_hidden; /**< enable to scan AP whose SSID is hidden */
wifi_scan_type_t scan_type; /**< scan type, active or passive */
wifi_scan_time_t scan_time; /**< scan time per channel */
uint8_t home_chan_dwell_time; /**< time spent at home channel between scanning consecutive channels. */
wifi_scan_channel_bitmap_t channel_bitmap; /**< Channel bitmap for setting specific channels to be scanned. For 2.4ghz channels set ghz_2_channels from BIT(1) to BIT(14) from LSB to MSB order to indicate channels to be scanned. Currently scanning in 5ghz channels is not supported. Please note that the 'channel' parameter above needs to be set to 0 to allow scanning by bitmap. */
} wifi_scan_config_t;
typedef enum {
@ -247,7 +253,7 @@ typedef enum {
typedef struct {
int8_t rssi; /**< The minimum rssi to accept in the fast scan mode */
wifi_auth_mode_t authmode; /**< The weakest authmode to accept in the fast scan mode
Note: Incase this value is not set and password is set as per WPA2 standards(password len >= 8), it will be defaulted to WPA2 and device won't connect to deprecated WEP/WPA networks. Please set authmode threshold as WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK to connect to WEP/WPA networks */
Note: In case this value is not set and password is set as per WPA2 standards(password len >= 8), it will be defaulted to WPA2 and device won't connect to deprecated WEP/WPA networks. Please set authmode threshold as WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK to connect to WEP/WPA networks */
}wifi_scan_threshold_t;
typedef enum {
@ -269,8 +275,8 @@ typedef enum {
/** Configuration structure for Protected Management Frame */
typedef struct {
bool capable; /**< Deprecated variable. Device will always connect in PMF mode if other device also advertizes PMF capability. */
bool required; /**< Advertizes that Protected Management Frame is required. Device will not associate to non-PMF capable devices. */
bool capable; /**< Deprecated variable. Device will always connect in PMF mode if other device also advertises PMF capability. */
bool required; /**< Advertises that Protected Management Frame is required. Device will not associate to non-PMF capable devices. */
} wifi_pmf_config_t;
/** Configuration for SAE PWE derivation */
@ -318,8 +324,8 @@ typedef struct {
wifi_scan_threshold_t threshold; /**< When scan_threshold is set, only APs which have an auth mode that is more secure than the selected auth mode and a signal stronger than the minimum RSSI will be used. */
wifi_pmf_config_t pmf_cfg; /**< Configuration for Protected Management Frame. Will be advertised in RSN Capabilities in RSN IE. */
uint32_t rm_enabled:1; /**< Whether Radio Measurements are enabled for the connection */
uint32_t btm_enabled:1; /**< Whether BSS Transition Management is enabled for the connection */
uint32_t mbo_enabled:1; /**< Whether MBO is enabled for the connection */
uint32_t btm_enabled:1; /**< Whether BSS Transition Management is enabled for the connection. Note that when btm is enabled, the application itself should not set specific bssid (i.e using bssid_set and bssid in this config)or channel to connect to. This defeats the purpose of a BTM supported network, and hence if btm is supported and a specific bssid or channel is set in this config, it will be cleared from the config at the first disconnection or connection so that the device can roam to other BSS. It is recommended not to set BSSID when BTM is enabled. */
uint32_t mbo_enabled:1; /**< Whether MBO is enabled for the connection. Note that when mbo is enabled, the application itself should not set specific bssid (i.e using bssid_set and bssid in this config)or channel to connect to. This defeats the purpose of a MBO supported network, and hence if btm is supported and a specific bssid or channel is set in this config, it will be cleared from the config at the first disconnection or connection so that the device can roam to other BSS. It is recommended not to set BSSID when MBO is enabled. Enabling mbo here, automatically enables btm and rm above.*/
uint32_t ft_enabled:1; /**< Whether FT is enabled for the connection */
uint32_t owe_enabled:1; /**< Whether OWE is enabled for the connection */
uint32_t transition_disable:1; /**< Whether to enable transition disable feature */
@ -327,7 +333,7 @@ typedef struct {
wifi_sae_pwe_method_t sae_pwe_h2e; /**< Configuration for SAE PWE derivation method */
wifi_sae_pk_mode_t sae_pk_mode; /**< Configuration for SAE-PK (Public Key) Authentication method */
uint8_t failure_retry_cnt; /**< Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config.
Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. */
Note: Enabling this may cause connection time to increase in case best AP doesn't behave properly. */
uint32_t he_dcm_set:1; /**< Whether DCM max.constellation for transmission and reception is set. */
uint32_t he_dcm_max_constellation_tx:2; /**< Indicate the max.constellation for DCM in TB PPDU the STA supported. 0: not supported. 1: BPSK, 2: QPSK, 3: 16-QAM. The default value is 3. */
uint32_t he_dcm_max_constellation_rx:2; /**< Indicate the max.constellation for DCM in both Data field and HE-SIG-B field the STA supported. 0: not supported. 1: BPSK, 2: QPSK, 3: 16-QAM. The default value is 3. */
@ -441,7 +447,7 @@ typedef struct {
typedef enum {
WIFI_PKT_MGMT, /**< Management frame, indicates 'buf' argument is wifi_promiscuous_pkt_t */
WIFI_PKT_CTRL, /**< Control frame, indicates 'buf' argument is wifi_promiscuous_pkt_t */
WIFI_PKT_DATA, /**< Data frame, indiciates 'buf' argument is wifi_promiscuous_pkt_t */
WIFI_PKT_DATA, /**< Data frame, indicates 'buf' argument is wifi_promiscuous_pkt_t */
WIFI_PKT_MISC, /**< Other type, such as MIMO etc. 'buf' argument is wifi_promiscuous_pkt_t but the payload is zero length. */
} wifi_promiscuous_pkt_type_t;
@ -565,10 +571,10 @@ typedef struct {
#define ESP_WIFI_NDP_ROLE_INITIATOR 1
#define ESP_WIFI_NDP_ROLE_RESPONDER 2
#define ESP_WIFI_MAX_SVC_NAME_LEN 256
#define ESP_WIFI_MAX_FILTER_LEN 256
#define ESP_WIFI_MAX_SVC_INFO_LEN 64
#define ESP_WIFI_MAX_SVC_NAME_LEN 256
#define ESP_WIFI_MAX_FILTER_LEN 256
#define ESP_WIFI_MAX_SVC_INFO_LEN 64
#define ESP_WIFI_MAX_NEIGHBOR_REP_LEN 64
/**
* @brief NAN Services types
*
@ -781,6 +787,8 @@ typedef enum {
WIFI_EVENT_NDP_TERMINATED, /**< NAN Datapath terminated indication */
WIFI_EVENT_HOME_CHANNEL_CHANGE, /**< WiFi home channel changedoesn't occur when scanning */
WIFI_EVENT_STA_NEIGHBOR_REP, /**< Received Neighbor Report response */
WIFI_EVENT_MAX, /**< Invalid WiFi event ID */
} wifi_event_t;
@ -1009,6 +1017,12 @@ typedef struct {
uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */
} wifi_event_ndp_terminated_t;
/** Argument structure for WIFI_EVENT_STA_NEIGHBOR_REP event */
typedef struct {
uint8_t report[ESP_WIFI_MAX_NEIGHBOR_REP_LEN]; /**< Neighbor Report received from the AP*/
uint16_t report_len; /**< Length of the report*/
} wifi_event_neighbor_report_t;
#ifdef __cplusplus
}
#endif

@ -1 +1 @@
Subproject commit 5ae2e01957e3d1e38f5e423a25bd241d3a6ef339
Subproject commit 1b1ab64e64b69d25e58e740b8bf556519b1730f8

Wyświetl plik

@ -533,7 +533,7 @@ static void nan_app_action_ndp_confirm(void *arg, esp_event_base_t event_base, i
}
if (nan_find_ndl(evt->ndp_id, NULL) == NULL) {
/* As ndl isn't found, timeout has occured for NDP response and datapath request is rejected */
/* As ndl isn't found, timeout has occurred for NDP response and datapath request is rejected */
goto done;
}
if (evt->status == NDP_STATUS_REJECTED) {

Wyświetl plik

@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_wifi_types.h"
#include "utils/common.h"
#include <sys/time.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SUPPLICANT_CANDIDATE_LIST_EXPIRY 10
/* Global Roaming Configuration */
#define ROAMING_BACKOFF_TIME CONFIG_ESP_WIFI_ROAMING_BACKOFF_TIME
/* Low RSSI based roaming configuration */
#define LOW_RSSI_ROAMING_ENABLED CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_ROAMING
#if LOW_RSSI_ROAMING_ENABLED
#define ROAMING_LOW_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD
#define RSSI_THRESHOLD_REDUCTION_OFFSET CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_OFFSET
#endif /*LOW_RSSI_ROAMING_ENABLED*/
/* Periodic Scan based Roaming configuration */
#define PERIODIC_SCAN_MONITORING CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
#if PERIODIC_SCAN_MONITORING
#define SCAN_MONITOR_INTERVAL CONFIG_ESP_WIFI_ROAMING_SCAN_MONITOR_INTERVAL
#define SCAN_MONITOR_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_THRESHOLD
#define SCAN_ROAM_RSSI_DIFF CONFIG_ESP_WIFI_ROAMING_SCAN_ROAM_RSSI_DIFF
#endif /* PERIODIC_SCAN_MONITORING */
/* Scan configuration */
#define SCAN_TIME_MIN_DURATION CONFIG_ESP_WIFI_ROAMING_SCAN_MIN_SCAN_TIME
#define SCAN_TIME_MAX_DURATION CONFIG_ESP_WIFI_ROAMING_SCAN_MAX_SCAN_TIME
#define HOME_CHANNEL_DWELL_TIME CONFIG_ESP_WIFI_ROAMING_HOME_CHANNEL_DWELL_TIME
#define SCAN_PREFERRED_CHAN_LIST CONFIG_ESP_WIFI_ROAMING_SCAN_CHAN_LIST
#define DEFAULT_PREFERRED_SCAN_CHAN_LIST "None"
#define SCAN_RESULTS_USABILITY_WINDOW CONFIG_ESP_WIFI_ROAMING_SCAN_EXPIRY_WINDOW
#define MAX_CANDIDATE_COUNT CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES
/* Legacy roaming configuration */
#define LEGACY_ROAM_ENABLED CONFIG_ESP_WIFI_ROAMING_LEGACY_ROAMING
#define BSS_TM_RETRY_COUNT CONFIG_ESP_WIFI_NETWORK_ASSISTED_ROAMING_RETRY_COUNT
/* Network Assisted Roaming */
#define NETWORK_ASSISTED_ROAMING_ENABLED CONFIG_ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM
/* Periodic RRM configuration */
#define PERIODIC_RRM_MONITORING CONFIG_ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING
#if PERIODIC_RRM_MONITORING
#define RRM_MONITOR_TIME CONFIG_ESP_WIFI_ROAMING_RRM_MONITOR_TIME
#define RRM_MONITOR_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD
#endif /*PERIODIC_RRM_MONITORING*/
#define MAX_SCAN_CHAN_LIST_COUNT 14
#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;
wifi_ap_record_t ap_records[MAX_CANDIDATE_COUNT];
};
struct cand_bss {
uint8_t channel;
uint8_t bssid[ETH_ALEN];
};
struct roaming_app {
wifi_scan_config_t scan_params;
bool scan_ongoing;
int8_t current_rssi_threshold;
char *btm_neighbor_list;
struct timeval last_roamed_time;
wifi_ap_record_t ap_info;
struct scanned_ap_info scanned_aps;
bool btm_support;
bool rrm_support;
#if LOW_RSSI_ROAMING_ENABLED
int8_t current_low_rssi_threshold;
#endif
#if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED
uint8_t btm_attempt;
#endif
#if LEGACY_ROAM_ENABLED
bool force_roam_ongoing;
#endif
#if PERIODIC_RRM_MONITORING
bool periodic_rrm_active;
bool rrm_request_active;
#endif
#if PERIODIC_SCAN_MONITORING
bool periodic_scan_active;
#endif
};
void init_roaming_app(void);
void deinit_roaming_app(void);
#if PERIODIC_RRM_MONITORING
void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx);
#endif /*PERIODIC_RRM_MONITORING*/
#if PERIODIC_SCAN_MONITORING
void roaming_app_periodic_scan_internal_handler(void *data, void *ctx);
#endif /*PERIODIC_SCAN_ROAM_MONITORING*/
void roaming_app_trigger_roam_internal_handler(void *data, void *ctx);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,176 @@
# Visible if ESP_WIFI_ENABLE_ROAMING_APP enabled in components/esp_wifi/Kconfig
menu "Roaming triggers"
config ESP_WIFI_ROAMING_LOW_RSSI_ROAMING
bool "Use Low RSSI to trigger roaming."
default y
help
Enable to use a RSSI threshold to trigger roaming.
config ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD
depends on ESP_WIFI_ROAMING_LOW_RSSI_ROAMING
int "WiFi RSSI threshold to trigger roaming"
range -99 -30
default -60
help
WiFi RSSI threshold to trigger roaming value in dBm (-99 to -1). Values under -30 dbm
might lead to a flood of low rssi events. This interferes with normal functioning and
TX/Rx performance.
config ESP_WIFI_ROAMING_LOW_RSSI_OFFSET
depends on ESP_WIFI_ROAMING_LOW_RSSI_ROAMING
int "Offset by which to reset the RSSI Threshold after attempt to roam."
range 0 99
default 5
help
Decide the offset by which to decrease the Low RSSI threshold set by ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD
after each failed attempt to roam. This allows for the station to keep scanning for better AP's after
the Low RSSI threshold is reached in a stepped manner, rather than only attempting to roam the first time
the current AP's RSSI breaches the set RSSI threshold.
Setting 0 here may cause station to be flooded with low rssi events,
therefore that's not recommended to be kept.
config ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
bool "Conduct periodic scans to check if a better AP is available"
default y
help
Conduct periodic scans periodically to check if a better AP is available.
config ESP_WIFI_ROAMING_PERIODIC_SCAN_THRESHOLD
int "Threshold at which to begin periodic scanning for a better AP."
depends on ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
range -99 -1
default -50
help
Threshold at which the station will begin scanning to find an AP with better RSSI.
config ESP_WIFI_ROAMING_SCAN_MONITOR_INTERVAL
int "Time intervals (in seconds) at which station will initiate a scan"
depends on ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
range 1 1500
default 30
help
Intervals at which station will periodically scan to check if better AP is available
config ESP_WIFI_ROAMING_SCAN_ROAM_RSSI_DIFF
int "RSSI difference b/w current AP and candidate AP to initiate connection"
depends on ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR
default 15
range 0 99
help
Minimum RSSI difference b/w current AP and a potential roaming candidate AP
to trigger a roaming attempt.
endmenu #"Roaming triggers"
menu "Roaming Methods"
config ESP_WIFI_ROAMING_LEGACY_ROAMING
bool "Support Legacy roaming approach"
default y
help
Roaming between APs that do not support 802.11kv.
This will allow station to roam even when connection is not BTM supported,
by forcefully disconnecting from current AP and connecting to better AP.
config ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM
bool "Support Network Assisted roaming using 802.11kv"
depends on ESP_WIFI_11KV_SUPPORT
default y
help
Roaming between APs using network assisted Roaming.
This involves BSS Transition Management mechanisms outlined in 802.11v.
Note that this moves the responsibility to the AP's network, and hence isn't
guaranteed to cause the station to attempt to roam each time.
config ESP_WIFI_NETWORK_ASSISTED_ROAMING_RETRY_COUNT
int "Retry count after which to switch to legacy roaming"
depends on ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM
depends on ESP_WIFI_ROAMING_LEGACY_ROAMING
range 1 5
default 2
help
Retry threshold after which the station should stop using Network Assisted
roaming methods and start using legacy roaming instead.
endmenu #"Roaming Methods"
menu "Scan Configuration"
config ESP_WIFI_ROAMING_SCAN_MIN_SCAN_TIME
int "Minimum duration (in milliseconds) of station's per channel active scan"
default 10
range 0 120
help
Minimum duration of active scanning per channel in milliseconds.
config ESP_WIFI_ROAMING_SCAN_MAX_SCAN_TIME
int "Maximum duration (in milliseconds) of station's per channel active scan time"
default 70
range 30 120
help
Maximum duration of active scanning per channel in milliseconds.
config ESP_WIFI_ROAMING_HOME_CHANNEL_DWELL_TIME
int "Home channel dwell time scanning between consecutive channels"
default 30
range 30 150
help
If connected, duration for which the station will return to it's home channel for Tx/Rx of
frames stored in buffers between scanning on consecutive channels.
config ESP_WIFI_ROAMING_SCAN_CHAN_LIST
string "Preferred channel list for scanning"
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.
config ESP_WIFI_ROAMING_SCAN_EXPIRY_WINDOW
int "Scan results expiry window (in seconds)"
default 10
range 5 20
help
Duration for which the results from the most recent scans can be used
by the roaming app for determining the roaming candidates.
config ESP_WIFI_ROAMING_MAX_CANDIDATES
int "Max Candidates in the network"
default 3
range 3 20
help
Max candidates that can be considered while scanning as a part of the
network at one time.
endmenu #"Scan Configuration"
config ESP_WIFI_ROAMING_BACKOFF_TIME
int "Default time to wait between subsequent roaming attempts."
default 15
range 0 120
help
Time to wait (in seconds) by station before registering for the RSSI event again
or start continuous montoring to find better AP.
config ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING
bool "Send periodic neighbor report request to AP for internal list updation"
depends on ESP_WIFI_11KV_SUPPORT
default y
help
This option will enable station to keep sending RRM neighbor list request to AP and
update its internal list.
config ESP_WIFI_ROAMING_RRM_MONITOR_TIME
int "Time interval (in seconds) between neighbor report requests to an AP"
depends on ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING
default 60
range 0 1500
help
Enable this to send periodic neighbor report requests to the AP.
These neighbor report requests provide information about other APs in the same managed
network. This information is used for more intelligent roaming.
config ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD
int "Threshold for sending periodic neighbor report requests"
depends on ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING
default -20
range -99 0
help
The RSSI threshold beyond which we start sending periodic neighbor report requests.

Wyświetl plik

@ -0,0 +1,92 @@
**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.
**How to use:**
To enable the roaming app in the menuconfig, please navigate to Component Settings --> Wi-Fi and then enable “Advanced support for Wi-Fi Roaming”
**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.
**Roaming Triggers: (Roaming Module Settings --> Roaming Triggers)**
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.
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.
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”)
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.**
**Roaming Methods**
Currently 2 methods of roaming are supported by the roaming app. (Roaming App Settings --> Roaming Methods)
1) Network Assisted Roaming:
Enabled by “Support Network Assisted Roaming using 802.11kv”, 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.
2) Legacy Roaming approach.
Enabled by “Support Legacy roaming approach”, this is designed for scenarios where there is no support for 802.11v amongst the APs of the network. In this scenario, upon determining the better AP to connect to, the station forcibly will disconnect and attempt to connect to the AP with the better AP.
Please note that if both approaches are enabled, there is an additional configuration (“Retry count after which to switch to legacy roaming”) that allows developers to decide how many times should the station attempt to roam using the Network Assisted roaming methods before switching to using the legacy approach.
**Please note that at least one of the two roaming methods needs to be selected.**
**Scan configuration**
The scan configuration allows for configuring the parameters for the scans that are conducted in various scenarios during the functioning of the roaming app. Please note that all scans conducted by the roaming app are active scans.
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.
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.
**Backoff Time:**
Successive roaming attempts by multiple roaming triggers simultaneously could lead to performance degradation, hence a backoff time can be configured (“Default time to wait between subsequent roaming attempts”). A roaming attempt can be made using any method only once the configured backoff time has passed since the last roaming attempt.
**Periodic Neighbor Report Requests:**
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.
**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.
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)
Enterprise <--> Enterprise.
Also note that this module would also prevent roaming to an AP that does not clear the authmode threshold set by wifi_config_t.sta.
4) As a general guideline for configuring the values of the roaming app it would be helpful to understand the trade-off between roaming and general performance of the overall application. Roaming includes processes such as scanning, disconnecting, connecting, etc which take up time away from other tasks of application. Deciding the several configurations in this module needs to be done while prioritising between being connected to the best AP, seamless transfer between APs and performance based on the requirements of the end application. For example, setting the thresholds of processes outlined above to a lower value or reducing the intervals between the aforementioned periodic tasks would make the roaming more aggressive while interrupting the general operation of the whole application more frequently.

Wyświetl plik

@ -0,0 +1,822 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wnm.h"
#include "esp_rrm.h"
#include "esp_mbo.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "nvs_flash.h"
#include <sys/time.h>
#include "regex.h"
#include <stdio.h>
#include "esp_roaming.h"
#include "utils/common.h"
#include "esp_wifi_driver.h"
#include "utils/eloop.h"
#include "rom/ets_sys.h"
#include "common/ieee802_11_defs.h"
static struct roaming_app g_roaming_app;
typedef void (* scan_done_cb_t)(void *arg, ETS_STATUS status);
extern int esp_wifi_promiscuous_scan_start(wifi_scan_config_t *config, scan_done_cb_t cb);
static void *scan_results_lock = NULL;
#define ROAM_SCAN_RESULTS_LOCK() os_mutex_lock(scan_results_lock)
#define ROAM_SCAN_RESULTS_UNLOCK() os_mutex_unlock(scan_results_lock)
static void *neighbor_list_lock = NULL;
#define ROAM_NEIGHBOR_LIST_LOCK() os_mutex_lock(neighbor_list_lock)
#define ROAM_NEIGHBOR_LIST_UNLOCK() os_mutex_unlock(neighbor_list_lock)
static int wifi_post_roam_event(struct cand_bss *bss);
static void determine_best_ap(int8_t rssi_threshold);
static const char *ROAMING_TAG = "ROAM";
static inline long time_diff_sec(struct timeval *a, struct timeval *b)
{
return (a->tv_sec - b->tv_sec);
}
static void roaming_app_get_ap_info(wifi_ap_record_t *ap_info)
{
esp_wifi_sta_get_ap_info(ap_info);
#if LOW_RSSI_ROAMING_ENABLED
/*
* If the current rssi is below the configured rssi threshold for
* low rssi based roaming and the current rssi threshold is below that,
* we should reset the rssi threshold back to the configured rssi threshold */
if ((ap_info->rssi > ROAMING_LOW_RSSI_THRESHOLD) && (g_roaming_app.current_low_rssi_threshold < ROAMING_LOW_RSSI_THRESHOLD)) {
g_roaming_app.current_low_rssi_threshold = ROAMING_LOW_RSSI_THRESHOLD;
esp_wifi_set_rssi_threshold(ROAMING_LOW_RSSI_THRESHOLD);
ESP_LOGD(ROAMING_TAG, "Reset the low rssi threshold back to %d", ROAMING_LOW_RSSI_THRESHOLD);
}
#endif /*LOW_RSSI_ROAMING_ENABLED*/
}
#if LEGACY_ROAM_ENABLED
static void legacy_roam_clear_bssid_flag(void)
{
wifi_config_t *config = {0};
config = os_zalloc(sizeof(wifi_config_t));
if (!config) {
ESP_LOGE(ROAMING_TAG, "failed to allocate memory");
return;
}
esp_wifi_get_config(WIFI_IF_STA, config);
if (config->sta.bssid_set) {
config->sta.bssid_set = 0;
esp_wifi_set_config(WIFI_IF_STA, config);
}
os_free(config);
ESP_LOGD(ROAMING_TAG, "cleared bssid flag");
}
#endif /*LEGACY_ROAM_ENABLED*/
static int8_t initialize_roaming_event(void)
{
#if LEGACY_ROAM_ENABLED
/*
* Resetting the Bssid param as it is possible that a previous force
* roam has set config to connect to a specific bssid and now further
* roaming attempts using BTM could lead to a spiral of connecting to
* the previous AP */
if (g_roaming_app.force_roam_ongoing) {
legacy_roam_clear_bssid_flag();
}
#endif /*LEGACY_ROAM_ENABLED*/
return ESP_OK;
}
#if PERIODIC_RRM_MONITORING
static void init_periodic_rrm_event(void)
{
if (!neighbor_list_lock) {
neighbor_list_lock = os_recursive_mutex_create();
if (!neighbor_list_lock) {
ESP_LOGE(ROAMING_TAG, "%s: failed to create roaming neighbor list lock", __func__);
}
}
ESP_LOGV(ROAMING_TAG, "Initialised Periodic RRM Monitoring event!");
g_roaming_app.periodic_rrm_active = true;
if (eloop_register_timeout(RRM_MONITOR_TIME, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) {
ESP_LOGE(ROAMING_TAG, "Could not register periodic neighbor report event.");
}
}
#endif /*PERIODIC_RRM_MONITORING*/
#if PERIODIC_SCAN_MONITORING
static void init_periodic_scan_roam_event(void)
{
ESP_LOGV(ROAMING_TAG, "Initialised Periodic Scan Roam event!");
g_roaming_app.periodic_scan_active = true;
if (eloop_register_timeout(SCAN_MONITOR_INTERVAL, 0, roaming_app_periodic_scan_internal_handler, NULL, NULL)) {
ESP_LOGE(ROAMING_TAG, "Could not register periodic scan monitoring event");
}
}
#endif /*PERIODIC_SCAN_ROAM_MONITORING*/
static void roaming_app_disconnected_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
#if PERIODIC_RRM_MONITORING
g_roaming_app.periodic_rrm_active = false;
#endif /*PERIODIC_RRM_MONITORING*/
#if PERIODIC_SCAN_MONITORING
g_roaming_app.periodic_scan_active = false;
#endif /*PERIODIC_SCAN_MONITORING*/
wifi_event_sta_disconnected_t *disconn = event_data;
ESP_LOGD(ROAMING_TAG, "station got disconnected reason=%d", disconn->reason);
if (disconn->reason == WIFI_REASON_ROAMING) {
ESP_LOGD(ROAMING_TAG, "station roaming, do nothing");
} else {
#if LEGACY_ROAM_ENABLED
/*
* Resetting the Bssid param as it is possible that a previous force
* roam has set config to connect to a specific bssid and now further
* roaming attempts using BTM could lead to a spiral of connecting to
* the previous AP */
if (g_roaming_app.force_roam_ongoing) {
legacy_roam_clear_bssid_flag();
}
#endif /*LEGACY_ROAM_ENABLED*/
esp_wifi_connect();
}
}
static void roaming_app_connected_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
#if LOW_RSSI_ROAMING_ENABLED
roaming_app_get_ap_info(&g_roaming_app.ap_info);
g_roaming_app.scan_params.ssid = g_roaming_app.ap_info.ssid;
if (g_roaming_app.ap_info.rssi < ROAMING_LOW_RSSI_THRESHOLD) {
/* To ensure that the threshold is set to one offset below the current AP RSSI
* in case, the AP is already below the RSSI threshold */
g_roaming_app.current_low_rssi_threshold = g_roaming_app.ap_info.rssi - RSSI_THRESHOLD_REDUCTION_OFFSET;
} else {
g_roaming_app.current_low_rssi_threshold = ROAMING_LOW_RSSI_THRESHOLD;
}
ESP_LOGD(ROAMING_TAG, "setting rssi threshold as %d", g_roaming_app.current_low_rssi_threshold);
esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold);
#endif /*LOW_RSSI_ROAMING_ENABLED*/
g_roaming_app.rrm_support = esp_rrm_is_rrm_supported_connection();
g_roaming_app.btm_support = esp_wnm_is_btm_supported_connection();
ESP_LOGD(ROAMING_TAG, "Station connected, RRM %ssupported, BTM %ssupported",
g_roaming_app.rrm_support ? " " : "not ",
g_roaming_app.btm_support ? " " : "not ");
gettimeofday(&g_roaming_app.last_roamed_time, NULL);
if (!initialize_roaming_event()) {
#if PERIODIC_RRM_MONITORING
if (g_roaming_app.rrm_support) {
init_periodic_rrm_event();
}
#endif /*PERIODIC_RRM_MONITORING*/
#if PERIODIC_SCAN_MONITORING
init_periodic_scan_roam_event();
#endif /*PERIODIC_SCAN_ROAM_MONITORING*/
ESP_LOGD(ROAMING_TAG, "Initialised initialise roaming events!");
} else {
ESP_LOGE(ROAMING_TAG, "Failed to Initialise roaming events");
}
#if LEGACY_ROAM_ENABLED
g_roaming_app.force_roam_ongoing = true;
#endif /*LEGACY_ROAM_ENABLED*/
}
#define MAX_NEIGHBOR_LEN 512
#if PERIODIC_RRM_MONITORING
static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
{
size_t len = 0;
const uint8_t *data;
int ret = 0;
/*
* Neighbor Report element (IEEE P802.11-REVmc/D5.0)
* BSSID[6]
* BSSID Information[4]
* Operating Class[1]
* Channel Number[1]
* PHY Type[1]
* Optional Subelements[variable]
*/
#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
if (!report || report_len == 0) {
ESP_LOGE(ROAMING_TAG, "RRM neighbor report is not valid");
return NULL;
}
char *buf = os_calloc(1, MAX_NEIGHBOR_LEN);
if (!buf) {
ESP_LOGE(ROAMING_TAG, "get_btm_list : Memory alloc failed for buf");
return NULL;
}
data = report;
while (report_len >= 2 + NR_IE_MIN_LEN) {
const uint8_t *nr;
uint8_t nr_len = data[1];
const uint8_t *pos = data, *end;
if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
nr_len < NR_IE_MIN_LEN) {
ESP_LOGD(ROAMING_TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%u",
data[0], nr_len);
ret = -1;
goto cleanup;
}
if (2U + nr_len > report_len) {
ESP_LOGD(ROAMING_TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
data[0], report_len, nr_len);
ret = -1;
goto cleanup;
}
pos += 2;
end = pos + nr_len;
nr = pos;
pos += NR_IE_MIN_LEN;
while (end - pos > 2) {
uint8_t s_len;
s_len = *pos++;
if (s_len > end - pos) {
ret = -1;
goto cleanup;
}
pos += s_len;
}
ESP_LOGD(ROAMING_TAG, "RMM neighbor report bssid=" MACSTR
" info=0x%" PRIx32 " op_class=%u chan=%u phy_type=%u",
MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
nr[ETH_ALEN + 6]);
/* neighbor start */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
/* bssid */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, MACSTR, MAC2STR(nr));
/* , */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* bssid info */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "0x%04" PRIx32 "", WPA_GET_LE32(nr + ETH_ALEN));
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* operating class */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 4]);
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* channel number */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 5]);
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ",");
/* phy type */
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 6]);
/* optional elements, skip */
data = end;
report_len -= 2 + nr_len;
}
cleanup:
if (ret < 0) {
os_free(buf);
buf = NULL;
}
return buf;
}
static void roaming_app_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
if (!g_roaming_app.rrm_request_active) {
ESP_LOGV(ROAMING_TAG, "Not the response for our Neighbor Report Request");
return;
}
g_roaming_app.rrm_request_active = false;
if (!event_data) {
ESP_LOGE(ROAMING_TAG, "No data received for neighbor report");
return;
}
wifi_event_neighbor_report_t *neighbor_report_event = (wifi_event_neighbor_report_t*)event_data;
ESP_LOGD(ROAMING_TAG, "Received cb for Neighbor Report Request");
uint8_t *pos = (uint8_t *)neighbor_report_event->report;
if (!pos) {
ESP_LOGE(ROAMING_TAG, "Neighbor report is empty");
return;
}
uint8_t report_len = neighbor_report_event->report_len;
/* dump report info */
ESP_LOGD(ROAMING_TAG, "rrm: neighbor report len=%d", report_len);
ESP_LOG_BUFFER_HEXDUMP(ROAMING_TAG, pos, report_len, ESP_LOG_DEBUG);
ROAM_NEIGHBOR_LIST_LOCK();
if (g_roaming_app.btm_neighbor_list) {
os_free(g_roaming_app.btm_neighbor_list);
g_roaming_app.btm_neighbor_list = NULL;
}
/* create neighbor list */
g_roaming_app.btm_neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1);
ROAM_NEIGHBOR_LIST_UNLOCK();
}
#endif /*PERIODIC_RRM_MONITORING*/
#if LOW_RSSI_ROAMING_ENABLED
static void roaming_app_rssi_low_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
wifi_event_bss_rssi_low_t *event = event_data;
ESP_LOGI(ROAMING_TAG, "%s:bss rssi is=%ld", __func__, event->rssi);
roaming_app_get_ap_info(&g_roaming_app.ap_info);
determine_best_ap(0);
g_roaming_app.current_low_rssi_threshold -= RSSI_THRESHOLD_REDUCTION_OFFSET;
ESP_LOGD(ROAMING_TAG, "Resetting RSSI Threshold to %d", g_roaming_app.current_low_rssi_threshold);
esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold);
}
#endif
#if NETWORK_ASSISTED_ROAMING_ENABLED
static void trigger_network_assisted_roam(void)
{
#if PERIODIC_RRM_MONITORING
ROAM_NEIGHBOR_LIST_LOCK();
#endif /*PERIODIC_RRM_MONITORING*/
if (esp_wnm_send_bss_transition_mgmt_query(REASON_RSSI, g_roaming_app.btm_neighbor_list, 1) < 0) {
ESP_LOGD(ROAMING_TAG, "failed to send btm query");
}
#if PERIODIC_RRM_MONITORING
ROAM_NEIGHBOR_LIST_UNLOCK();
#endif /*PERIODIC_RRM_MONITORING*/
ESP_LOGD(ROAMING_TAG, "Sent BTM Query");
gettimeofday(&g_roaming_app.last_roamed_time, NULL);
g_roaming_app.btm_attempt++;
}
#endif /*NETWORK_ASSISTED_ROAMING*/
#if LEGACY_ROAM_ENABLED
static void trigger_legacy_roam(struct cand_bss *bss)
{
wifi_config_t wifi_cfg = {0};
esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg);
wifi_cfg.sta.channel = bss->channel;
wifi_cfg.sta.bssid_set = true;
os_memcpy(wifi_cfg.sta.bssid, bss->bssid, ETH_ALEN);
esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC);
esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg);
esp_wifi_connect();
ESP_LOGI(ROAMING_TAG, "Disconnecting and connecting to "MACSTR" on account of better rssi",MAC2STR(bss->bssid));
gettimeofday(&g_roaming_app.last_roamed_time, NULL);
g_roaming_app.force_roam_ongoing = true;
}
#endif /*LEGACY_ROAM_ENABLED*/
void roaming_app_trigger_roam(struct cand_bss *bss)
{
struct timeval now;
gettimeofday(&now, NULL);
ESP_LOGD(ROAMING_TAG,"Processing trigger roaming request.");
if (time_diff_sec(&now, &g_roaming_app.last_roamed_time) < ROAMING_BACKOFF_TIME ) {
ESP_LOGD(ROAMING_TAG,"Ignoring request as time difference to last request is %ld",time_diff_sec(&now, &g_roaming_app.last_roamed_time));
goto free_bss;
}
#if NETWORK_ASSISTED_ROAMING_ENABLED
if (g_roaming_app.btm_support) {
#if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED
if (g_roaming_app.btm_attempt <= BSS_TM_RETRY_COUNT) {
#endif
trigger_network_assisted_roam();
goto free_bss;
#if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED
} else {
ESP_LOGD(ROAMING_TAG, "Not Sending BTM query as this method has failed too many times.");
g_roaming_app.btm_attempt = 0;
}
#endif
}
#endif /*NETWORK_ASSISTED_ROAMING_ENABLED*/
#if LEGACY_ROAM_ENABLED
trigger_legacy_roam(bss);
#endif /*LEGACY_ROAM_ENABLED*/
free_bss :
os_free(bss);
}
void roaming_app_trigger_roam_internal_handler(void *ctx, void *data)
{
if (!data) {
ESP_LOGE(ROAMING_TAG, "No data received for roaming event");
} else {
roaming_app_trigger_roam((struct cand_bss *)data);
}
}
static int wifi_post_roam_event(struct cand_bss *bss)
{
if (bss) {
struct cand_bss *cand_bss = (struct cand_bss *)os_zalloc(sizeof(struct cand_bss));
if (!cand_bss) {
ESP_LOGE(ROAMING_TAG, "Cannot allocate data for roaming event");
return -1;
}
os_memcpy(cand_bss, bss, sizeof(struct cand_bss));
/* trigger the roaming event */
if (eloop_register_timeout(0, 0, roaming_app_trigger_roam_internal_handler, NULL, (void*)cand_bss)) {
ESP_LOGE(ROAMING_TAG, "Could not register roaming event.");
os_free(cand_bss);
return -1;
}
} else {
ESP_LOGE(ROAMING_TAG, "Cannot trigger roaming event without any candidate APs");
return -1;
}
return 0;
}
void print_ap_records(struct scanned_ap_info *ap_info)
{
ESP_LOGD(ROAMING_TAG, "Scanned AP List");
for (int i = 0; i < ap_info->current_count ; i++) {
ESP_LOGD(ROAMING_TAG, "%d. ssid : %s bssid :"MACSTR" channel : %d rssi : %d authmode : %d", i,
ap_info->ap_records[i].ssid,MAC2STR(ap_info->ap_records[i].bssid),
ap_info->ap_records[i].primary,ap_info->ap_records[i].rssi, ap_info->ap_records[i].authmode);
}
}
#if PERIODIC_RRM_MONITORING
static void periodic_rrm_request(struct timeval *now)
{
roaming_app_get_ap_info(&g_roaming_app.ap_info);
if (esp_rrm_is_rrm_supported_connection() && (g_roaming_app.ap_info.rssi < RRM_MONITOR_RSSI_THRESHOLD)) {
if (esp_rrm_send_neighbor_report_request() < 0) {
ESP_LOGE(ROAMING_TAG, "failed to send neighbor report request");
return;
}
g_roaming_app.rrm_request_active = true;
}
}
#else
static void periodic_rrm_request(struct timeval *now) { }
#endif
static bool candidate_security_match(wifi_ap_record_t candidate)
{
wifi_auth_mode_t curr_auth = g_roaming_app.ap_info.authmode;
wifi_auth_mode_t cand_auth = candidate.authmode;
ESP_LOGV(ROAMING_TAG, "Cand authmode : %d, Current Authmode : %d", cand_auth, curr_auth);
if (cand_auth == curr_auth) {
ESP_LOGV(ROAMING_TAG, "Authmode matched!");
return true;
}
wifi_config_t wifi_cfg = {0};
esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg);
if (wifi_cfg.sta.owe_enabled && OWE_COMPATIBLE(curr_auth, cand_auth)) {
/*
* OWE <--> Open allowed if threshold is Open
*/
if (wifi_cfg.sta.threshold.authmode == WIFI_AUTH_OPEN) {
ESP_LOGV(ROAMING_TAG, "transition between OWE and open permitted");
return true;
} else {
ESP_LOGV(ROAMING_TAG, "transition between OWE and open not permitted");
return false;
}
} else if (wifi_cfg.sta.threshold.authmode > cand_auth) {
/* If the authmode of the candidate AP is less than our threshold, it
* will fail during connection*/
ESP_LOGV(ROAMING_TAG, "Authmode threshold failure %d -> %d", wifi_cfg.sta.threshold.authmode, cand_auth);
return false;
} else if (PSK_COMPATIBLE(curr_auth, cand_auth)) {
/*
* PSK based authmodes are compatible with each other for roaming
*/
ESP_LOGV(ROAMING_TAG, "Roaming between a PSK APs");
return true;
}
return false;
}
static bool candidate_profile_match(wifi_ap_record_t candidate)
{
return candidate_security_match(candidate);
}
/* Remember to always call this function with the ROAM_SCAN_RESULTS_LOCK */
static void parse_scan_results_and_roam(void)
{
int8_t rssi_threshold = g_roaming_app.current_rssi_threshold;
uint8_t best_rssi_diff = rssi_threshold;
struct cand_bss *best_ap = NULL;
int8_t rssi_diff = 0;
uint8_t i;
int8_t best_ap_index = -1;
wifi_ap_record_t ap_info;
roaming_app_get_ap_info(&ap_info);
for (i = 0; i < g_roaming_app.scanned_aps.current_count; i++) {
rssi_diff = g_roaming_app.scanned_aps.ap_records[i].rssi - ap_info.rssi;
ESP_LOGD(ROAMING_TAG, "The difference between ("MACSTR", "MACSTR") with rssi (%d,%d) is : %d while the threshold is %d and the best rssi diff yet is %d, thecand_auth is %d",
MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid),MAC2STR(ap_info.bssid),
g_roaming_app.scanned_aps.ap_records[i].rssi, ap_info.rssi,
rssi_diff, rssi_threshold, best_rssi_diff, g_roaming_app.scanned_aps.ap_records[i].authmode);
if ((memcmp(g_roaming_app.scanned_aps.ap_records[i].bssid, ap_info.bssid, ETH_ALEN) != 0) &&
candidate_profile_match(g_roaming_app.scanned_aps.ap_records[i]) && rssi_diff > best_rssi_diff ) {
best_rssi_diff = rssi_diff;
best_ap_index = i;
}
}
if (best_ap_index >= 0) {
best_ap = (struct cand_bss*)os_zalloc(sizeof(struct cand_bss));
if (!best_ap) {
ESP_LOGE(ROAMING_TAG,"Memory Allocation for candidate bss failed");
return;
}
os_memcpy(best_ap->bssid,g_roaming_app.scanned_aps.ap_records[best_ap_index].bssid , ETH_ALEN);
best_ap->channel = g_roaming_app.scanned_aps.ap_records[best_ap_index].primary;
}
if (best_ap) {
ESP_LOGI(ROAMING_TAG,"Found a better AP "MACSTR" at channel %d", MAC2STR(best_ap->bssid), best_ap->channel);
if (wifi_post_roam_event(best_ap)) {
ESP_LOGE(ROAMING_TAG, "Posting of roaming event failed");
}
os_free(best_ap);
} else {
ESP_LOGI(ROAMING_TAG, "Could not find a better AP with the threshold set to %d", g_roaming_app.current_rssi_threshold + 1);
}
}
static void scan_done_event_handler(void *arg, ETS_STATUS status)
{
if (status == ETS_OK) {
ROAM_SCAN_RESULTS_LOCK();
ESP_LOGD(ROAMING_TAG, "Scan Done properly");
g_roaming_app.scanned_aps.current_count = MAX_CANDIDATE_COUNT;
esp_wifi_scan_get_ap_records(&g_roaming_app.scanned_aps.current_count, g_roaming_app.scanned_aps.ap_records);
print_ap_records(&g_roaming_app.scanned_aps);
parse_scan_results_and_roam();
g_roaming_app.scan_ongoing = false;
ROAM_SCAN_RESULTS_UNLOCK();
} else {
ESP_LOGD(ROAMING_TAG, "Scan Done with error %d ", status);
}
}
static void conduct_scan(void)
{
/* Update scan time in global structure */
gettimeofday(&g_roaming_app.scanned_aps.time, NULL);
/* Issue scan */
os_memset(&g_roaming_app.scanned_aps, 0, sizeof(struct scanned_ap_info));
if (esp_wifi_promiscuous_scan_start(&g_roaming_app.scan_params, scan_done_event_handler) < 0) {
ESP_LOGE(ROAMING_TAG, "failed to issue scan");
return;
}
ESP_LOGI(ROAMING_TAG, "Issued Scan");
}
static void determine_best_ap(int8_t rssi_threshold)
{
struct timeval now;
gettimeofday(&now, NULL);
ROAM_SCAN_RESULTS_LOCK();
/* If the scan results are recent enough or a scan is already ongoing we should not trigger a new scan */
if (!g_roaming_app.scan_ongoing) {
g_roaming_app.scan_ongoing = true;
g_roaming_app.current_rssi_threshold = rssi_threshold;
if (time_diff_sec(&now,&g_roaming_app.scanned_aps.time) > SCAN_RESULTS_USABILITY_WINDOW) {
conduct_scan();
} else {
parse_scan_results_and_roam();
g_roaming_app.scan_ongoing = false;
}
} else if(rssi_threshold < g_roaming_app.current_rssi_threshold) {
g_roaming_app.current_rssi_threshold = rssi_threshold;
}
ROAM_SCAN_RESULTS_UNLOCK();
}
#if PERIODIC_SCAN_MONITORING
static void periodic_scan_roam(struct timeval *now)
{
#if NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED
/*
* In case we support only network assisted roaming, there is no need to scan
* if we currently have over 10secs to go for backoff time to expire
* as the results produced by a scan at this time would not be used by
* supplicant to build candidate lists.
* */
if (time_diff_sec(now, &g_roaming_app.last_roamed_time) < ROAMING_BACKOFF_TIME - SUPPLICANT_CANDIDATE_LIST_EXPIRY) {
return;
}
#endif /*NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED*/
/* If the current RSSI is not worse than the configured threshold
* for station initiated roam, then do not trigger roam */
roaming_app_get_ap_info(&g_roaming_app.ap_info);
if (g_roaming_app.ap_info.rssi > SCAN_MONITOR_RSSI_THRESHOLD) {
return;
}
determine_best_ap(SCAN_ROAM_RSSI_DIFF - 1);
}
#endif /*PERIODIC_SCAN_MONITORING*/
#if PERIODIC_RRM_MONITORING
void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx)
{
struct timeval now;
if (g_roaming_app.periodic_rrm_active) {
ESP_LOGD(ROAMING_TAG,"Triggered periodic rrm event!");
gettimeofday(&now, NULL);
/* This will be done every RRM_MONITOR_TIME */
periodic_rrm_request(&now);
if (eloop_register_timeout(RRM_MONITOR_TIME, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) {
ESP_LOGE(ROAMING_TAG,"Could not register periodic neighbor report request event.");
}
}
}
#endif /*PERIODIC_RRM_MONITORING*/
#if PERIODIC_SCAN_MONITORING
void roaming_app_periodic_scan_internal_handler(void *data, void *ctx)
{
struct timeval now;
if (g_roaming_app.periodic_scan_active) {
ESP_LOGD(ROAMING_TAG,"Started the periodic scan roam event!");
gettimeofday(&now, NULL);
/* This will be done every SCAN_MONITOR_INTERVAL */
periodic_scan_roam(&now);
if (eloop_register_timeout(SCAN_MONITOR_INTERVAL, 0, roaming_app_periodic_scan_internal_handler, NULL, NULL)) {
ESP_LOGE(ROAMING_TAG,"Could not register periodic scan event.");
}
}
}
#endif /*PERIODIC_SCAN_ROAM_MONITORING*/
static bool validate_scan_chan_list(const char* scan_chan_list)
{
regex_t regex;
const char* pattern = "^[0-9]+(,[0-9]+)*$";
uint8_t ret = regcomp(&regex, pattern, REG_EXTENDED);
if (ret != 0) {
ESP_LOGE(ROAMING_TAG, "Regex compilation failed.");
return false;
}
ret = regexec(&regex, scan_chan_list, 0, NULL, 0);
regfree(&regex);
if (ret == REG_NOMATCH) {
return false;
}
/* Check for consecutive commas or comma at start/end */
if (strstr(scan_chan_list, ",,") != NULL || scan_chan_list[0] == ',' || scan_chan_list[strlen(scan_chan_list) - 1] == ',') {
return false;
}
return true;
}
static int8_t parse_scan_chan_list(void)
{
int8_t ret = 0;
char *scan_chan_string = NULL;
if (validate_scan_chan_list(SCAN_PREFERRED_CHAN_LIST) == false) {
ESP_LOGE(ROAMING_TAG, "scan chan list validation failed.");
ret = -1;
goto cleanup;
}
scan_chan_string = (char *)os_zalloc(sizeof(char) * strlen(SCAN_PREFERRED_CHAN_LIST) + 1);
if (scan_chan_string == NULL) {
ESP_LOGE(ROAMING_TAG, "Memory allocation failed.");
ret = -1;
goto cleanup;
}
strlcpy(scan_chan_string, SCAN_PREFERRED_CHAN_LIST, strlen(SCAN_PREFERRED_CHAN_LIST) + 1);
char* token;
token = strsep(&scan_chan_string, ",");
g_roaming_app.scan_params.channel_bitmap.ghz_2_channels = 0;
while (token != NULL) {
uint8_t channel = atoi(token);
/* Check if the number is within the required range */
if (channel >= 1 && channel <= 14) {
/* Check if the number is already present in the array */
g_roaming_app.scan_params.channel_bitmap.ghz_2_channels |= (1 << channel);
} else {
ESP_LOGE(ROAMING_TAG, "Channel out of range: %d", channel);
ret = -1;
goto cleanup;
}
token = strsep(&scan_chan_string, ",");
}
cleanup:
if (scan_chan_string) {
os_free(scan_chan_string);
}
return ret;
}
esp_err_t init_scan_params(void)
{
if (!scan_results_lock) {
scan_results_lock = os_recursive_mutex_create();
if (!scan_results_lock) {
ESP_LOGE(ROAMING_TAG, "%s: failed to create scan results lock", __func__);
return ESP_FAIL;
}
}
if (strcmp(DEFAULT_PREFERRED_SCAN_CHAN_LIST, SCAN_PREFERRED_CHAN_LIST)) {
ESP_ERROR_CHECK(parse_scan_chan_list());
}
g_roaming_app.scan_params.scan_type = 0;
g_roaming_app.scan_params.scan_time.active.min = SCAN_TIME_MIN_DURATION;
g_roaming_app.scan_params.scan_time.active.max = SCAN_TIME_MAX_DURATION;
g_roaming_app.scan_params.home_chan_dwell_time = HOME_CHANNEL_DWELL_TIME;
gettimeofday(&g_roaming_app.scanned_aps.time, NULL);
return ESP_OK;
}
void init_roaming_app(void)
{
#if !LOW_RSSI_ROAMING_ENABLED && !PERIODIC_SCAN_MONITORING
ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized");
return;
#endif
#if !NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED
ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized");
return;
#endif
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, roaming_app_connected_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, roaming_app_disconnected_event_handler, NULL));
#if LOW_RSSI_ROAMING_ENABLED
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,
&roaming_app_rssi_low_handler, NULL));
#endif /*ROAMING_LOW_RSSI_THRESHOLD*/
ESP_ERROR_CHECK(init_scan_params());
#if PERIODIC_RRM_MONITORING
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP,
&roaming_app_neighbor_report_recv_handler, NULL));
#endif /*PERIODIC_RRM_MONITORING*/
ESP_LOGI(ROAMING_TAG, "Roaming app initialization done");
}
void deinit_roaming_app(void)
{
#if !LOW_RSSI_ROAMING_ENABLED && !PERIODIC_SCAN_MONITORING
ESP_LOGE(ROAMING_TAG, "No roaming trigger enabled. Roaming app cannot be de-initialized");
return;
#endif
#if !NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED
ESP_LOGE(ROAMING_TAG, "No roaming trigger enabled. Roaming app cannot be de-initialized");
return;
#endif
/* Unregister Event handlers */
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, roaming_app_connected_event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, roaming_app_disconnected_event_handler));
#if LOW_RSSI_ROAMING_ENABLED
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,
&roaming_app_rssi_low_handler));
#endif /*ROAMING_LOW_RSSI_THRESHOLD*/
#if PERIODIC_RRM_MONITORING
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP,
&roaming_app_neighbor_report_recv_handler));
#endif /*PERIODIC_RRM_MONITORING*/
/* Disabling the periodic scan and RRM events */
#if PERIODIC_RRM_MONITORING
g_roaming_app.periodic_rrm_active = false;
#endif /*PERIODIC_RRM_MONITORING*/
#if PERIODIC_SCAN_MONITORING
g_roaming_app.periodic_scan_active = false;
#endif /*PERIODIC_SCAN_MONITORING*/
os_mutex_delete(neighbor_list_lock);
neighbor_list_lock = NULL;
os_mutex_delete(scan_results_lock);
scan_results_lock = NULL;
}

Wyświetl plik

@ -226,6 +226,7 @@ idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src}
"${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}"
INCLUDE_DIRS include port/include esp_supplicant/include
PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto
../esp_wifi/wifi_apps/roaming_app/include
LDFRAGMENTS ${linker_fragments}
PRIV_REQUIRES mbedtls esp_timer esp_wifi)

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -27,6 +27,8 @@ typedef void (*neighbor_rep_request_cb)(void *ctx, const uint8_t *report, size_t
/**
* @brief Send Radio measurement neighbor report request to connected AP
*
* @deprecated This function is deprecated and will be removed in the future.
* Please use 'esp_rrm_send_neighbor_report_request'
* @param cb: callback function for neighbor report
* @param cb_ctx: callback context
*
@ -35,8 +37,18 @@ typedef void (*neighbor_rep_request_cb)(void *ctx, const uint8_t *report, size_t
* - -1: AP does not support RRM
* - -2: station not connected to AP
*/
__attribute__((deprecated("Use 'esp_rrm_send_neighbor_report_request' instead")))
int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
void *cb_ctx);
void *cb_ctx);
/**
* @brief Send Radio measurement neighbor report request to connected AP
* @return
* - 0: success
* - -1: AP does not support RRM
* - -2: station not connected to AP
*/
int esp_rrm_send_neighbor_report_request(void);
/**
* @brief Check RRM capability of connected AP

Wyświetl plik

@ -23,6 +23,9 @@
#include "rsn_supp/wpa_i.h"
#include "rsn_supp/wpa.h"
#include "esp_private/wifi.h"
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
#include "esp_roaming.h"
#endif
/* Utility Functions */
esp_err_t esp_supplicant_str_to_mac(const char *str, uint8_t dest[6])
@ -162,7 +165,7 @@ static void btm_rrm_task(void *pvParameters)
}
#endif /* CONFIG_SUPPLICANT_TASK */
static void clear_bssid_flag(struct wpa_supplicant *wpa_s)
static void clear_bssid_flag_and_channel(struct wpa_supplicant *wpa_s)
{
wifi_config_t *config;
@ -178,7 +181,8 @@ static void clear_bssid_flag(struct wpa_supplicant *wpa_s)
}
esp_wifi_get_config(WIFI_IF_STA, config);
if (config->sta.bssid_set) {
if (config->sta.bssid_set || config->sta.channel) {
config->sta.channel = 0;
config->sta.bssid_set = 0;
esp_wifi_set_config(WIFI_IF_STA, config);
}
@ -299,7 +303,7 @@ static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
#ifdef CONFIG_MBO
bool mbo_bss_profile_match(u8 *bssid)
{
/* Incase supplicant wants drivers to skip this BSS, return false */
/* In case supplicant wants drivers to skip this BSS, return false */
struct wpa_bss *bss = wpa_bss_get_bssid(&g_wpa_supp, bssid);
if (!bss) {
return true;
@ -418,6 +422,9 @@ void esp_supplicant_common_deinit(void)
}
s_supplicant_task_init_done = false;
#endif /* CONFIG_SUPPLICANT_TASK */
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
deinit_roaming_app();
#endif
#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */
}
@ -439,11 +446,11 @@ void supplicant_sta_conn_handler(uint8_t *bssid)
/* Register for mgmt frames */
register_mgmt_frames(wpa_s);
/* clear set bssid flag */
clear_bssid_flag(wpa_s);
clear_bssid_flag_and_channel(wpa_s);
#endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */
}
void supplicant_sta_disconn_handler(void)
void supplicant_sta_disconn_handler(uint8_t reason_code)
{
#if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R)
struct wpa_supplicant *wpa_s = &g_wpa_supp;
@ -451,6 +458,12 @@ void supplicant_sta_disconn_handler(void)
#ifdef CONFIG_IEEE80211KV
wpas_rrm_reset(wpa_s);
wpas_clear_beacon_rep_data(wpa_s);
/* Not clearing in case of roaming disconnect as BTM induced connection
* itself sets a specific bssid and channel to connect to before disconnection.
* Subsequent connections or disconnections will clear this flag */
if (reason_code != WIFI_REASON_ROAMING) {
clear_bssid_flag_and_channel(wpa_s);
}
#endif /* CONFIG_IEEE80211KV */
if (wpa_s->current_bss) {
wpa_s->current_bss = NULL;
@ -477,6 +490,7 @@ bool esp_rrm_is_rrm_supported_connection(void)
return true;
}
/*This function has been deprecated in favour of esp_rrm_send_neighbor_report_request*/
int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
void *cb_ctx)
{
@ -503,6 +517,49 @@ int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
return wpas_rrm_send_neighbor_rep_request(wpa_s, &wpa_ssid, 0, 0, cb, cb_ctx);
}
void neighbor_report_recvd_cb(void *ctx, const uint8_t *report, size_t report_len) {
if (report == NULL) {
wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, NULL, 0, 0);
return;
}
if (report_len > ESP_WIFI_MAX_NEIGHBOR_REP_LEN) {
wpa_printf(MSG_ERROR, "RRM: Neighbor report too large (>%d bytes), hence not reporting", ESP_WIFI_MAX_NEIGHBOR_REP_LEN);
return;
}
wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", report[0]);
wifi_event_neighbor_report_t neighbor_report_event = {0};
os_memcpy(neighbor_report_event.report, report, report_len);
neighbor_report_event.report_len = report_len;
esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, &neighbor_report_event, sizeof(wifi_event_neighbor_report_t), 0);
}
int esp_rrm_send_neighbor_report_request(void)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
struct wpa_ssid_value wpa_ssid = {0};
struct wifi_ssid *ssid;
if (!wpa_s->current_bss) {
wpa_printf(MSG_ERROR, "STA not associated, return");
return -2;
}
if (!(wpa_s->rrm_ie[0] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
wpa_printf(MSG_ERROR,
"RRM: No network support for Neighbor Report.");
return -1;
}
ssid = esp_wifi_sta_get_prof_ssid_internal();
os_memcpy(wpa_ssid.ssid, ssid->ssid, ssid->len);
wpa_ssid.ssid_len = ssid->len;
return wpas_rrm_send_neighbor_rep_request(wpa_s, &wpa_ssid, 0, 0, neighbor_report_recvd_cb, NULL);
}
bool esp_wnm_is_btm_supported_connection(void)
{
struct wpa_supplicant *wpa_s = &g_wpa_supp;
@ -561,6 +618,7 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
/* We only support roaming in same ESS, therefore only bssid setting is needed */
os_memcpy(config->sta.bssid, bss->bssid, ETH_ALEN);
config->sta.bssid_set = 1;
config->sta.channel = bss->channel;
/* supplicant connect will only be called in case of bss transition(roaming) */
esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC);
esp_wifi_set_config(WIFI_IF_STA, config);
@ -680,6 +738,16 @@ static uint8_t get_extended_caps_ie(uint8_t *ie, size_t len)
return ext_caps_ie_len + 2;
}
#else /* CONFIG_IEEE80211KV */
bool esp_rrm_is_rrm_supported_connection(void)
{
return false;
}
bool esp_wnm_is_btm_supported_connection(void)
{
return false;
}
#endif /* CONFIG_IEEE80211KV */
void esp_set_scan_ie(void)
@ -847,12 +915,16 @@ int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data)
}
#endif /* CONFIG_SUPPLICANT_TASK */
#else /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */
int esp_rrm_send_neighbor_report_request(void)
{
return -1;
}
int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
void *cb_ctx)
{
return -1;
}
int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
const char *btm_candidates,
int cand_list)

Wyświetl plik

@ -46,5 +46,5 @@ void esp_supplicant_unset_all_appie(void);
void esp_set_scan_ie(void);
void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool add_mdie);
void supplicant_sta_conn_handler(uint8_t* bssid);
void supplicant_sta_disconn_handler(void);
void supplicant_sta_disconn_handler(uint8_t reason_code);
#endif

Wyświetl plik

@ -162,11 +162,26 @@ int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
/* update rest of the frame */
os_memcpy(ptr, frame, len);
wpa_bss_update_scan_res(wpa_s, res, &now);
os_get_reltime(&wpa_s->last_scan);
os_free(res);
return 0;
}
#ifdef CONFIG_WNM
void get_scan_channel_bitmap(struct wpa_supplicant *wpa_s, wifi_scan_config_t *params) {
if (!wpa_s->wnm_num_neighbor_report) {
wpa_printf(MSG_DEBUG, "No Neighbor Report to gather scan channel list");
return;
}
params->channel_bitmap.ghz_2_channels = 0;
for (int i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
struct neighbor_report *nei;
nei = &wpa_s->wnm_neighbor_report_elements[i];
params->channel_bitmap.ghz_2_channels |= (1 << nei->channel_number);
}
}
#endif /*CONFIG_WNM*/
static int issue_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *scan_params)
{
@ -217,11 +232,18 @@ static int issue_scan(struct wpa_supplicant *wpa_s,
if (scan_params->channel) {
params->channel = scan_params->channel;
}
#ifdef CONFIG_WNM
else {
get_scan_channel_bitmap(wpa_s, params);
}
#endif /*CONFIG_WNM*/
if (scan_params->duration) {
params->scan_time.passive = scan_params->duration;
params->scan_time.active.min = scan_params->duration;
params->scan_time.active.max = scan_params->duration;
} else {
params->scan_time.active.min = SUPPLICANT_SCAN_ACTIVE_SCAN_MIN_DURATION;
params->scan_time.active.max = SUPPLICANT_SCAN_ACTIVE_SCAN_MAX_DURATION;
}
}

Wyświetl plik

@ -6,6 +6,10 @@
#ifndef ESP_SCAN_I_H
#define ESP_SCAN_I_H
#define SUPPLICANT_SCAN_ACTIVE_SCAN_MIN_DURATION 10
#define SUPPLICANT_SCAN_ACTIVE_SCAN_MAX_DURATION 70
void esp_scan_init(struct wpa_supplicant *wpa_s);
void esp_scan_deinit(struct wpa_supplicant *wpa_s);
int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,

Wyświetl plik

@ -39,6 +39,9 @@
#include "ap/sta_info.h"
#include "wps/wps_defs.h"
#include "wps/wps.h"
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
#include "esp_roaming.h"
#endif
#ifdef CONFIG_DPP
#include "common/dpp.h"
@ -301,7 +304,7 @@ static void wpa_sta_disconnected_cb(uint8_t reason_code)
owe_deinit();
#endif /* CONFIG_OWE_STA */
supplicant_sta_disconn_handler();
supplicant_sta_disconn_handler(reason_code);
}
#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT
@ -469,6 +472,10 @@ int esp_supplicant_init(void)
ret = esp_wifi_internal_wapi_init();
#endif
#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
init_roaming_app();
#endif
return ret;
}

Wyświetl plik

@ -805,6 +805,9 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
MAC2STR(nei->bssid));
return;
}
if (wpa_s->current_bss && (os_memcmp(nei->bssid, wpa_s->current_bss->bssid, ETH_ALEN) == 0)) {
continue;
}
if (nei->channel_number != chan) {
chan = nei->channel_number;
num_chan++;

Wyświetl plik

@ -479,3 +479,5 @@ void * __hide_aliasing_typecast(void *foo);
#define IANA_SECP384R1 20
#define IANA_SECP521R1 21
#endif /* COMMON_H */
#define GHZ_24_MAX_CHANNEL_COUNT 14

Wyświetl plik

@ -219,7 +219,7 @@ INPUT = \
$(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_rrm.h \
$(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h \
$(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_wps.h \
$(PROJECT_PATH)/components/esp_wifi/wifi_apps/include/esp_nan.h \
$(PROJECT_PATH)/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h \
$(PROJECT_PATH)/components/esp-tls/esp_tls_errors.h \
$(PROJECT_PATH)/components/esp-tls/esp_tls.h \
$(PROJECT_PATH)/components/fatfs/diskio/diskio_impl.h \
@ -310,7 +310,7 @@ INPUT = \
$(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_dpp.h \
$(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_supplicant_utils.h \
## Target specific headers are in seperate Doxyfile files
## Target specific headers are in separate Doxyfile files
@INCLUDE = $(PROJECT_PATH)/docs/doxygen/Doxyfile_$(IDF_TARGET)
## Get warnings for functions that have no documentation for their parameters or return value

Wyświetl plik

@ -15,7 +15,7 @@ The examples are grouped into sub-directories by category. Each category directo
* `getting_started` contains the basic examples for using Wi-Fi in STA and AP mode.
* `iperf` shows the common performance measurements for two ESP running this example.
* `power_save` contains a how to use power save mode of Wi-Fi.
* `roaming` contains a example using 802.11k and 802.11v APIs.
* `roaming` contains 2 examples that demonstrate roaming capabilities.
* `scan` shows how to scan for all the available APs.
* `smart_config` shows how ESP connects to a target AP with ESPTOUCH.
* `wifi_eap_fast` shows how ESP connects to AP with Wi-Fi enterprise encryption using the EAP-FAST method.

Wyświetl plik

@ -1,95 +1,16 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# Roaming Example
# Wi-Fi Roaming Examples
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This directory contains 2 example ESP-IDF projects that demonstrate the Wi-Fi Roaming capabilities. These are intended to demonstrate the Wi-Fi roaming functionality, and to provide code that you can copy and use in your own projects.
This example demonstrate a roaming example using 11k and 11v APIs.
# Example Layout
## How to use example
* `roaming_11kvr` demonstrates the use of API's that implement IEEE 802.11kvr mechanisms.
* `roaming_app` implements Espressif's roaming app that optimally combines legacy roaming and 802.11kv based
roaming for effective roaming.
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
# More
### Hardware Required
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for Power supply and programming
### Build and Flash
Build the project and flash it to the board, then run the monitor tool to view the serial output:
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html)
* [ESP-IDF Getting Started Guide on ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
* [ESP-IDF Getting Started Guide on ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/get-started/index.html)
## Example Output
All the logs are taken after debug enabled:
Beacon request:
```
I (11125) wpa: scan issued at time=7579442263
I (11125) wpa: BSS: Add new id 1 BSSID b6:fb:e4:4d:6e:22 SSID 'ESPTest' chan 1
I (11135) wpa: BSS: Add new id 2 BSSID d6:fb:e4:4d:6e:22 SSID 'ESPDedicated' chan 1
I (11165) wpa: BSS: Add new id 3 BSSID c6:fb:e4:4d:6e:22 SSID 'ESPGuestNetwork' chan 1
I (11185) wpa: BSS: Add new id 4 BSSID b4:fb:e4:4d:6e:22 SSID 'ESPIndia' chan 1
I (11245) wpa: BSS: Add new id 5 BSSID b8:27:eb:3b:4a:59 SSID 'wpa2_enterprise' chan 6
I (11365) wpa: BSS: Add new id 6 BSSID 38:94:ed:34:07:66 SSID 'Nighthawk' chan 6
I (11365) wpa: BSS: Add new id 7 BSSID 10:da:43:dc:99:20 SSID 'NETGEAR40' chan 6
I (12435) wpa: BSS: Add new id 8 BSSID c6:fb:e4:4d:7c:89 SSID 'ESPGuestNetwork' chan 11
I (12445) wpa: BSS: Add new id 9 BSSID d6:fb:e4:4d:7c:89 SSID 'ESPDedicated' chan 11
I (12455) wpa: BSS: Add new id 10 BSSID b6:fb:e4:4d:7c:89 SSID 'ESPTest' chan 11
I (12485) wpa: BSS: Add new id 11 BSSID b4:fb:e4:4d:7c:89 SSID 'ESPIndia' chan 11
I (12825) wpa: scan done received
I (12845) wpa: action frame sent
I (12845) wpa: action frame sent
```
Sta's moving from one AP to another on BTM request:
```
I (379479) wpa: WNM: RX action 7 from 50:3e:aa:26:35:42
I (379479) wpa: WNM: BSS Transition Management Request: dialog_token=1 request_mode=0x1 disassoc_timer=0 validity_interval=1
I (379489) wpa: WNM: Neighbor report tag 52
I (379499) wpa: WNM: Subelement id=3 len=1
I (379499) wpa: WNM: BSS Transition Candidate List
I (379509) wpa: 0: b4:e6:2d:eb:1d:76 info=0x0 op_class=81 chan=1 phy=7 pref=255
I (379519) wpa: WNM: Candidate list valid for 102 ms
I (379519) wpa: WNM: Scan only for a specific BSSID since there is only a single candidate b4:e6:2d:eb:1d:76
I (379539) wpa: scan issued at time=9979439869
I (379539) wpa: BSS: Add new id 12 BSSID b4:e6:2d:eb:1d:76 SSID 'roaming_test123' chan 6
I (381979) wpa: scan done received
I (381979) wpa: WNM: Process scan results for BSS Transition Management
I (381979) wpa: WNM: Found an acceptable preferred transition candidate BSS b4:e6:2d:eb:1d:76 (RSSI -7)
I (381989) wpa: WNM: Transition to BSS b4:e6:2d:eb:1d:76 based on BSS Transition Management Request after_new_scan=1)
I (381999) wpa: WNM: Sending successful BSS Transition Management Response
I (382009) wpa: WNM: Send BSS Transition Management Response to 50:3e:aa:26:35:42 dialog_token=1 status=0 reason=0 delay=0
I (382019) wpa: action frame sent
I (382029) wpa: WNM: Issuing connect
I (382029) wifi:state: run -> init (0)
I (382029) wifi:pm stop, total sleep time: 102136757 us / 109258805 us
I (382039) wifi:new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (382769) wifi:new:<6,1>, old:<6,0>, ap:<255,255>, sta:<6,1>, prof:1
I (384689) wifi:state: init -> auth (b0)
I (384699) wifi:state: auth -> assoc (0)
I (384709) wifi:state: assoc -> run (10)
I (384729) wifi:connected with roaming_test123, aid = 1, channel 6, 40U, bssid = b4:e6:2d:eb:1d:76
I (384729) wifi:security: WPA2-PSK, phy: bgn, rssi: -7
I (384729) wifi:pm start, type: 1
I (384789) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (385609) esp_netif_handlers: sta ip: 192.168.4.2, mask: 255.255.255.0, gw: 192.168.4.1
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.

Wyświetl plik

@ -0,0 +1,95 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# Roaming Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrate a roaming example using 11k and 11v APIs.
## How to use example
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
### Hardware Required
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for Power supply and programming
### Build and Flash
Build the project and flash it to the board, then run the monitor tool to view the serial output:
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html)
* [ESP-IDF Getting Started Guide on ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
* [ESP-IDF Getting Started Guide on ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/get-started/index.html)
## Example Output
All the logs are taken after debug enabled:
Beacon request:
```
I (11125) wpa: scan issued at time=7579442263
I (11125) wpa: BSS: Add new id 1 BSSID b6:fb:e4:4d:6e:22 SSID 'ESPTest' chan 1
I (11135) wpa: BSS: Add new id 2 BSSID d6:fb:e4:4d:6e:22 SSID 'ESPDedicated' chan 1
I (11165) wpa: BSS: Add new id 3 BSSID c6:fb:e4:4d:6e:22 SSID 'ESPGuestNetwork' chan 1
I (11185) wpa: BSS: Add new id 4 BSSID b4:fb:e4:4d:6e:22 SSID 'ESPIndia' chan 1
I (11245) wpa: BSS: Add new id 5 BSSID b8:27:eb:3b:4a:59 SSID 'wpa2_enterprise' chan 6
I (11365) wpa: BSS: Add new id 6 BSSID 38:94:ed:34:07:66 SSID 'Nighthawk' chan 6
I (11365) wpa: BSS: Add new id 7 BSSID 10:da:43:dc:99:20 SSID 'NETGEAR40' chan 6
I (12435) wpa: BSS: Add new id 8 BSSID c6:fb:e4:4d:7c:89 SSID 'ESPGuestNetwork' chan 11
I (12445) wpa: BSS: Add new id 9 BSSID d6:fb:e4:4d:7c:89 SSID 'ESPDedicated' chan 11
I (12455) wpa: BSS: Add new id 10 BSSID b6:fb:e4:4d:7c:89 SSID 'ESPTest' chan 11
I (12485) wpa: BSS: Add new id 11 BSSID b4:fb:e4:4d:7c:89 SSID 'ESPIndia' chan 11
I (12825) wpa: scan done received
I (12845) wpa: action frame sent
I (12845) wpa: action frame sent
```
Sta's moving from one AP to another on BTM request:
```
I (379479) wpa: WNM: RX action 7 from 50:3e:aa:26:35:42
I (379479) wpa: WNM: BSS Transition Management Request: dialog_token=1 request_mode=0x1 disassoc_timer=0 validity_interval=1
I (379489) wpa: WNM: Neighbor report tag 52
I (379499) wpa: WNM: Subelement id=3 len=1
I (379499) wpa: WNM: BSS Transition Candidate List
I (379509) wpa: 0: b4:e6:2d:eb:1d:76 info=0x0 op_class=81 chan=1 phy=7 pref=255
I (379519) wpa: WNM: Candidate list valid for 102 ms
I (379519) wpa: WNM: Scan only for a specific BSSID since there is only a single candidate b4:e6:2d:eb:1d:76
I (379539) wpa: scan issued at time=9979439869
I (379539) wpa: BSS: Add new id 12 BSSID b4:e6:2d:eb:1d:76 SSID 'roaming_test123' chan 6
I (381979) wpa: scan done received
I (381979) wpa: WNM: Process scan results for BSS Transition Management
I (381979) wpa: WNM: Found an acceptable preferred transition candidate BSS b4:e6:2d:eb:1d:76 (RSSI -7)
I (381989) wpa: WNM: Transition to BSS b4:e6:2d:eb:1d:76 based on BSS Transition Management Request after_new_scan=1)
I (381999) wpa: WNM: Sending successful BSS Transition Management Response
I (382009) wpa: WNM: Send BSS Transition Management Response to 50:3e:aa:26:35:42 dialog_token=1 status=0 reason=0 delay=0
I (382019) wpa: action frame sent
I (382029) wpa: WNM: Issuing connect
I (382029) wifi:state: run -> init (0)
I (382029) wifi:pm stop, total sleep time: 102136757 us / 109258805 us
I (382039) wifi:new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (382769) wifi:new:<6,1>, old:<6,0>, ap:<255,255>, sta:<6,1>, prof:1
I (384689) wifi:state: init -> auth (b0)
I (384699) wifi:state: auth -> assoc (0)
I (384709) wifi:state: assoc -> run (10)
I (384729) wifi:connected with roaming_test123, aid = 1, channel 6, 40U, bssid = b4:e6:2d:eb:1d:76
I (384729) wifi:security: WPA2-PSK, phy: bgn, rssi: -7
I (384729) wifi:pm start, type: 1
I (384789) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (385609) esp_netif_handlers: sta ip: 192.168.4.2, mask: 255.255.255.0, gw: 192.168.4.1
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

Wyświetl plik

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
@ -13,14 +18,14 @@
#include "esp_mac.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include <sys/time.h>
/* Configuration */
#define EXAMPLE_WIFI_SSID CONFIG_EXAMPLE_WIFI_SSID
#define EXAMPLE_WIFI_PASSWORD CONFIG_EXAMPLE_WIFI_PASSWORD
#define EXAMPLE_WIFI_RSSI_THRESHOLD CONFIG_EXAMPLE_WIFI_RSSI_THRESHOLD
/* rrm ctx */
int rrm_ctx = 0;
static bool g_neighbor_report_active;
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
@ -57,6 +62,8 @@ static void event_handler(void* arg, esp_event_base_t event_base,
#endif
if (esp_rrm_is_rrm_supported_connection()) {
ESP_LOGI(TAG,"RRM supported");
esp_rrm_send_neighbor_report_request();
g_neighbor_report_active = true;
} else {
ESP_LOGI(TAG,"RRM not supported");
}
@ -85,6 +92,7 @@ static void event_handler(void* arg, esp_event_base_t event_base,
#define ETH_ALEN 6
#endif
#define MAX_LCI_CIVIC_LEN 256 * 2 + 1
#define MAX_NEIGHBOR_LEN 512
#if 1
static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
@ -93,6 +101,8 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
const uint8_t *data;
int ret = 0;
char *lci = NULL;
char *civic = NULL;
/*
* Neighbor Report element (IEEE P802.11-REVmc/D5.0)
* BSSID[6]
@ -110,12 +120,24 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
}
char *buf = calloc(1, MAX_NEIGHBOR_LEN);
if (!buf) {
ESP_LOGE(TAG, "Memory allocation for neighbor list failed");
goto cleanup;
}
data = report;
while (report_len >= 2 + NR_IE_MIN_LEN) {
const uint8_t *nr;
char lci[256 * 2 + 1];
char civic[256 * 2 + 1];
lci = (char *)malloc(sizeof(char)*MAX_LCI_CIVIC_LEN);
if (!lci) {
ESP_LOGE(TAG, "Memory allocation for lci failed");
goto cleanup;
}
civic = (char *)malloc(sizeof(char)*MAX_LCI_CIVIC_LEN);
if (!civic) {
ESP_LOGE(TAG, "Memory allocation for civic failed");
goto cleanup;
}
uint8_t nr_len = data[1];
const uint8_t *pos = data, *end;
@ -172,7 +194,7 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
pos += s_len;
}
ESP_LOGI(TAG, "RMM neigbor report bssid=" MACSTR
ESP_LOGI(TAG, "RMM neighbor report bssid=" MACSTR
" info=0x%" PRIx32 " op_class=%u chan=%u phy_type=%u%s%s%s%s",
MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
@ -201,9 +223,25 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
data = end;
report_len -= 2 + nr_len;
if (lci) {
free(lci);
lci = NULL;
}
if (civic) {
free(civic);
civic = NULL;
}
}
cleanup:
if (lci) {
free(lci);
}
if (civic) {
free(civic);
}
if (ret < 0) {
free(buf);
buf = NULL;
@ -274,46 +312,50 @@ char * get_tmp_neighbor_list(uint8_t *report, size_t report_len)
return buf;
}
#endif
void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len)
static void esp_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data)
{
int *val = ctx;
uint8_t *pos = (uint8_t *)report;
int cand_list = 0;
if (!g_neighbor_report_active) {
ESP_LOGV(TAG,"Neighbor report received but not triggered by us");
return;
}
if (!event_data) {
ESP_LOGE(TAG, "No event data received for neighbor report");
return;
}
g_neighbor_report_active = false;
uint8_t cand_list = 0;
wifi_event_neighbor_report_t *neighbor_report_event = (wifi_event_neighbor_report_t*)event_data;
uint8_t *pos = (uint8_t *)neighbor_report_event->report;
char * neighbor_list = NULL;
if (!pos) {
ESP_LOGE(TAG, "Neighbor report is empty");
return;
}
uint8_t report_len = neighbor_report_event->report_len;
/* dump report info */
ESP_LOGD(TAG, "rrm: neighbor report len=%d", report_len);
ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_DEBUG);
if (!report) {
ESP_LOGE(TAG, "report is null");
return;
}
if (*val != rrm_ctx) {
ESP_LOGE(TAG, "rrm_ctx value didn't match, not initiated by us");
return;
}
/* dump report info */
ESP_LOGI(TAG, "rrm: neighbor report len=%d", report_len);
ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_INFO);
/* create neighbor list */
char *neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1);
/* In case neighbor list is not present issue a scan and get the list from that */
if (!neighbor_list) {
/* issue scan */
wifi_scan_config_t params;
memset(&params, 0, sizeof(wifi_scan_config_t));
if (esp_wifi_scan_start(&params, true) < 0) {
goto cleanup;
}
/* cleanup from net802.11 */
esp_wifi_clear_ap_list();
cand_list = 1;
/* create neighbor list */
neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1);
/* In case neighbor list is not present issue a scan and get the list from that */
if (!neighbor_list) {
/* issue scan */
wifi_scan_config_t params;
memset(&params, 0, sizeof(wifi_scan_config_t));
if (esp_wifi_scan_start(&params, true) < 0) {
goto cleanup;
}
/* cleanup from net802.11 */
esp_wifi_clear_ap_list();
cand_list = 1;
}
/* send AP btm query, this will cause STA to roam as well */
esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list);
cleanup:
if (neighbor_list)
free(neighbor_list);
}
#if EXAMPLE_WIFI_RSSI_THRESHOLD
@ -325,13 +367,16 @@ static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base,
ESP_LOGI(TAG, "%s:bss rssi is=%d", __func__, event->rssi);
/* Lets check channel conditions */
rrm_ctx++;
if (esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx) < 0) {
if (esp_rrm_send_neighbor_report_request() < 0) {
/* failed to send neighbor report request */
ESP_LOGI(TAG, "failed to send neighbor report request");
if (esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0) < 0) {
ESP_LOGI(TAG, "failed to send btm query");
}
} else {
g_neighbor_report_active = true;
}
}
#endif
@ -378,6 +423,8 @@ static void initialise_wifi(void)
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP,
&esp_neighbor_report_recv_handler, NULL));
#if EXAMPLE_WIFI_RSSI_THRESHOLD
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,
&esp_bss_rssi_low_handler, NULL));

Wyświetl plik

@ -0,0 +1,6 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(wifi_roaming_app)

Wyświetl plik

@ -0,0 +1,83 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# Wi-Fi Station Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example shows how to use the Wi-Fi Roaming App functionality of the Wi-Fi driver of ESP for efficient roaming between compatible APs.
## How to use example
### Configure the project
Open the project configuration menu (`idf.py menuconfig`).
In the `Example Configuration` menu:
* Set the Wi-Fi configuration.
* Set `WiFi SSID`.
* Set `WiFi Password`.
* To better understand how to configure the app in Components --> Wi-Fi --> Roaming App settings,
please go read the README at components/esp_wifi/wifi_apps/roaming_app/src.
Optional: If you need, change the other options according to your requirements.
### Build and Flash
Build the project and flash it to the board, then run the monitor tool to view the serial output:
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html)
* [ESP-IDF Getting Started Guide on ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
* [ESP-IDF Getting Started Guide on ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/get-started/index.html)
## Example Output
Note that the output, in particular the order of the output, may vary depending on the environment.
Console output if station roams between APs successfully:
```
I (827) wifi roaming app: wifi_init_sta finished.
I (837) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:5
I (837) wifi:state: init -> auth (b0)
I (847) wifi:state: auth -> assoc (0)
I (857) wifi:Association refused temporarily, comeback time 1000 (TUs)
I (1877) wifi:state: assoc -> assoc (0)
I (1917) wifi:state: assoc -> run (10)
I (2997) wifi:connected with roaming_setup, aid = 1, channel 1, BW20, bssid = de:bd:5b:93:55:99
I (2997) wifi:security: WPA2-PSK, phy: bgn, rssi: -58
I (3007) wifi:pm start, type: 1
I (3087) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (10467) ROAM: roaming_app_rssi_low_handler:bss rssi is=-61
I (10467) ROAM: Issued Scan
I (10857) ROAM: Could not find a better AP with the threshold set to 1
I (63017) ROAM: Issued Scan
I (63347) ROAM: Found a better AP 7c:50:79:06:18:46 at channel 9
I (63347) wifi:state: run -> init (cc0)
I (63347) wifi:pm stop, total sleep time: 54401305 us / 60339946 us
I (63347) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:5
E (63357) ROAM: Neighbor report is empty
I (63357) wifi roaming app: station disconnected during roaming
I (63377) wifi roaming app: connect to the AP fail
I (63377) ROAM: Disconnecting and connecting to 7c:50:79:06:18:46 on account of better rssi
I (63497) wifi:new:<9,0>, old:<1,0>, ap:<255,255>, sta:<9,0>, prof:5
I (63837) wifi:state: init -> auth (b0)
I (63847) wifi:state: auth -> assoc (0)
I (63887) wifi:Association refused temporarily, comeback time 1000 (TUs)
I (64907) wifi:state: assoc -> assoc (0)
I (64977) wifi:state: assoc -> run (10)
I (65007) wifi:connected with roaming_setup, aid = 1, channel 9, BW20, bssid = 7c:50:79:06:18:46
I (65007) wifi:security: WPA2-PSK, phy: bgn, rssi: -32
I (65027) wifi:pm start, type: 1
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

Wyświetl plik

@ -0,0 +1,2 @@
idf_component_register(SRCS "roaming_app_example.c"
INCLUDE_DIRS ".")

Wyświetl plik

@ -0,0 +1,14 @@
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
endmenu

Wyświetl plik

@ -0,0 +1,149 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* WiFi Roaming App Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
/* The examples use WiFi configuration that you can set via project configuration menu
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define MAXIMUM_RETRY 5
const char *TAG = "wifi roaming app";
static int s_retry_num = 0;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
wifi_event_sta_disconnected_t *disconn = event_data;
if (disconn->reason == WIFI_REASON_ROAMING) {
ESP_LOGI(TAG, "station disconnected during roaming");
} else {
if (s_retry_num < MAXIMUM_RETRY) {
ESP_LOGI(TAG, "station disconnected with reason %d", disconn->reason);
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
}
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
.btm_enabled = 1,
.rm_enabled = 1,
.mbo_enabled = 1,
.ft_enabled = 1,
}
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
}

Wyświetl plik

@ -0,0 +1,9 @@
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_ESP_WIFI_11KV_SUPPORT=y
CONFIG_ESP_WIFI_SCAN_CACHE=y
CONFIG_ESP_WIFI_MBO_SUPPORT=y
CONFIG_ESP_WIFI_ENABLE_ROAMING_APP=y
CONFIG_ESP_WIFI_11R_SUPPORT=y

Wyświetl plik

@ -23,7 +23,8 @@ Open the project configuration menu (`idf.py menuconfig`).
In the `Example Configuration` menu:
* Set the Example configuration.
* Use `Max size of scan list` to set the maximum nunber of access points in the list.
* Use `Max size of scan list` to set the maximum number of access points in the list.
* Use 'Scan Channel list' to list specific channels you wish to scan. For eg. 1,6,9,11. By Default all channels will be scanned.
### Build and Flash
@ -47,7 +48,7 @@ As you run the example, you will see the following log:
I (443) wifi:wifi firmware version: 6bff005
I (443) wifi:wifi certification version: v7.0
I (443) wifi:config NVS flash: enabled
I (443) wifi:config nano formating: disabled
I (443) wifi:config nano formatting: disabled
I (453) wifi:Init data frame dynamic rx buffer num: 32
I (453) wifi:Init management frame dynamic rx buffer num: 32
I (463) wifi:Init management short buffer num: 32

Wyświetl plik

@ -7,4 +7,11 @@ menu "Example Configuration"
help
The size of array that will be used to retrieve the list of access points.
config EXAMPLE_USE_SCAN_CHANNEL_BITMAP
bool "Scan only non overlapping channels using Channel bitmap"
default 0
help
Enable this to scan only the non overlapping channels i.e 1,6,11 by mentioning a channel bitmap
in scan config. If you wish to scan a different set of specific channels, please edit the channel_list
array in scan.c. Channels for a 2.4 ghz network range should range from 1-14.
endmenu

Wyświetl plik

@ -17,9 +17,16 @@
#include "esp_log.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "regex.h"
#define DEFAULT_SCAN_LIST_SIZE CONFIG_EXAMPLE_SCAN_LIST_SIZE
#ifdef CONFIG_EXAMPLE_USE_SCAN_CHANNEL_BITMAP
#define USE_CHANNEL_BTIMAP 1
#define CHANNEL_LIST_SIZE 3
static uint8_t channel_list[CHANNEL_LIST_SIZE] = {1, 6, 11};
#endif /*CONFIG_EXAMPLE_USE_SCAN_CHANNEL_BITMAP*/
static const char *TAG = "scan";
static void print_auth_mode(int authmode)
@ -133,6 +140,17 @@ static void print_cipher_type(int pairwise_cipher, int group_cipher)
}
}
#ifdef USE_CHANNEL_BTIMAP
static void array_2_channel_bitmap(const uint8_t channel_list[], const uint8_t channel_list_size, wifi_scan_config_t *scan_config) {
for(uint8_t i = 0; i < channel_list_size; i++) {
uint8_t channel = channel_list[i];
scan_config->channel_bitmap.ghz_2_channels |= (1 << channel);
}
}
#endif /*USE_CHANNEL_BTIMAP*/
/* Initialize Wi-Fi as sta and set scan method */
static void wifi_scan(void)
{
@ -149,9 +167,23 @@ static void wifi_scan(void)
uint16_t ap_count = 0;
memset(ap_info, 0, sizeof(ap_info));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
#ifdef USE_CHANNEL_BTIMAP
wifi_scan_config_t *scan_config = (wifi_scan_config_t *)calloc(1,sizeof(wifi_scan_config_t));
if (!scan_config) {
ESP_LOGE(TAG, "Memory Allocation for scan config failed!");
return;
}
array_2_channel_bitmap(channel_list, CHANNEL_LIST_SIZE, scan_config);
esp_wifi_scan_start(scan_config, true);
#else
esp_wifi_scan_start(NULL, true);
#endif /*USE_CHANNEL_BTIMAP*/
ESP_LOGI(TAG, "Max AP number ap_info can hold = %u", number);
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
@ -165,7 +197,6 @@ static void wifi_scan(void)
}
ESP_LOGI(TAG, "Channel \t\t%d", ap_info[i].primary);
}
}
void app_main(void)

Wyświetl plik

@ -1189,7 +1189,6 @@ examples/wifi/getting_started/softAP/main/softap_example_main.c
examples/wifi/getting_started/station/main/station_example_main.c
examples/wifi/iperf/main/iperf_example_main.c
examples/wifi/power_save/main/power_save.c
examples/wifi/roaming/main/roaming_example.c
examples/wifi/scan/main/scan.c
examples/wifi/smart_config/main/smartconfig_main.c
examples/wifi/wifi_easy_connect/dpp-enrollee/main/dpp_enrollee_main.c