ESP Wi-Fi Service
Introduction
Wi-Fi Service unifies credential storage, provisioning interaction, automatic connection, network selection, and quality probing in a device’s networking workflow into a single set of ESP Service service interfaces. With it, applications no longer need to separately maintain credential storage, SoftAP/Web provisioning, BluFi provisioning, reconnection, and multi-AP selection logic, making it possible to build stable, field-serviceable networked products more quickly.
Feature List
Profile management: maintains multiple sets of Wi-Fi credentials, supporting addition, update, enabling, disabling, deletion, and cleanup; provisioning channels and connection selection share the same profile manager
Pluggable storage: reuses the NVS, file system, dual-partition raw flash, or custom storage adapter layers from ESP Config Manager, and supports encryption callbacks to protect saved credentials
Multi-channel provisioning: HTTP SoftAP/Web UI, BluFi, and application-defined custom provisioning flows, all of which write to the same shared profile
Automatic startup strategy: when the service starts, it automatically enters the connection selection flow or starts the configured provisioning flow, depending on whether an enabled profile exists
Intelligent selection and switching: selects a more suitable AP based on user priority, signal quality, historical connectivity, and a temporary blacklist, and re-evaluates after disconnection or link degradation
Network quality probing: supports connectivity, latency, and throughput degradation detection to handle the “connected but the service is unusable” scenario
Optional MCP tool support: once both
CONFIG_ESP_MCP_ENABLEandCONFIG_WIFI_SERVICE_MCP_ENABLEare enabled, the MCP server from ESP Service can be used to remotely query status, manage profiles, and trigger provisioning/connection
Technical Deep Dive
Profile Management and Storage
Each Wi-Fi credential is stored as an esp_wifi_service_profile_t(SSID, password, priority, enable flag), which is centrally managed and persisted by esp_wifi_service_profile_mgr_t. The storage layer of the profile manager directly reuses the storage adapter interface from ESP Config Manager; an esp_config_storage_t handle must be prepared before creation:
esp_config_storage_nvs_t nvs_cfg = {
.nvs_namespace = "wifi_store",
.key_primary = "profile_p",
.key_backup = "profile_b",
};
esp_config_storage_t profile_store = NULL;
esp_config_storage_init_nvs(&nvs_cfg, &profile_store);
esp_wifi_service_profile_mgr_cfg_t profile_cfg = {
.max_profiles = 8,
.storage = profile_store,
};
esp_wifi_service_profile_mgr_t profile_manager = NULL;
esp_wifi_service_profile_mgr_init(&profile_cfg, &profile_manager);
The same profile_manager handle must be passed to both the Wi-Fi Service and the configuration of each provisioning channel, so that credentials written by provisioning are immediately visible to the connection selection logic.
Provisioning Channels
If at least one enabled profile exists when the service starts, it goes directly into the connection selection flow; otherwise, it starts all the provisioning channels configured in prov_list. HTTP SoftAP/Web UI and BluFi are provided as built-in channels, and applications can also implement a custom provisioning flow that writes to the same profile manager:
The HTTP channel starts a SoftAP, DNS captive portal, HTTP server, and a default or custom Web UI; credentials are submitted via
POST /prov/profilesThe BluFi channel sends network information via Bluetooth from the phone side; credentials also land in the shared profile manager, and this channel depends on
CONFIG_WIFI_SERVICE_PROV_BLUFI_ENABLEand the underlying BluFi protocol stackBoth channels can be enabled in parallel; once provisioning finishes, the selector logic takes over the connection
esp_wifi_service_prov_t *http_agent = NULL;
esp_wifi_service_prov_http_config_t http_cfg = {
.name = "http",
.port = 80,
.profile_manager = profile_manager,
.default_priority = 10,
};
esp_wifi_service_prov_http_create(&http_cfg, &http_agent);
esp_wifi_service_config_t cfg = {
.name = "wifi_service",
.profile_manager = profile_manager,
.prov_list = &http_agent,
.prov_num = 1,
};
esp_wifi_service_t *svc = NULL;
esp_wifi_service_create(&cfg, &svc);
esp_service_start((esp_service_t *)svc);
Selection and Switching
In multi-network environments, the selector automatically decides which AP to connect to: during re-evaluation, it first scans the surrounding APs, keeps only the candidates that match a saved and enabled profile, and then ranks them by user priority, signal quality, the most recent record of successful access, and a temporary blacklist, rather than simply choosing the AP with the strongest signal.
flowchart TD
Init[Service initialization] --> Check{Enabled profile exists}
Check -- Yes --> Selector[Start selector]
Check -- No --> Prov[Start provisioning channel]
Prov --> Save[Receive and save credentials]
Save --> Selector
Selector --> Scan[Scan and rank candidate APs]
Scan --> Decide{Switch needed}
Decide -- No --> Keep[Keep current connection]
Decide -- Yes --> Switch[Switch or fail over]
Switch --> Probe[Probe quality after connecting]
Probe --> Decide2{Probe failed or degraded}
Decide2 -- No --> Keep
Decide2 -- Yes --> Scan
When no usable candidate network is found, the selector retries scanning according to a built-in backoff table (1000, 5000, 10000, 20000, 30000 ms), which can be overridden via selector_policy.retry; applications can also call esp_wifi_service_request_connect() to skip scanning and re-evaluation and connect directly to a saved SSID, which is suitable for command-line tools or remote management scenarios.
Network Quality Probing
Being connected to Wi-Fi while the service remains unusable is a common field issue: the device has obtained an IP address, but access to cloud endpoints fails, latency is too high, or throughput is insufficient. Quality probing covers connectivity checks (accessing a specified URL to determine external connectivity) and latency/throughput checks (measuring request duration and actual throughput); handling is triggered only after consecutive failures or sustained degradation, to avoid switching networks because of a single transient fluctuation. Once degradation is confirmed, the current BSSID is temporarily added to the blacklist, and the selector re-selects a candidate network.
Application Examples
The example under the
examplesdirectory demonstrates the minimal integration flow for NVS profile storage, HTTP provisioning, and a custom selector policy; refer to the component repository for the complete code.
FAQ
Q1: Does setting max_connect_retry to 0 cause it to retry forever?
Yes.0 preserves the original behavior of retrying continuously; when set to a non-zero value, automatic re-evaluation stops once consecutive connection failures reach that count, and the counter only resets after esp_wifi_service_request_connect() or esp_wifi_service_request_reeval() is called.
API Reference
Header File
Functions
-
esp_err_t esp_wifi_service_create(const esp_wifi_service_config_t *cfg, esp_wifi_service_t **out_service)
Create Wi-Fi service instance.
Note
The caller is responsible for creating the profile manager before calling this function and destroying it after
esp_wifi_service_destroyreturns. The same handle should be passed to each provisioning config so that all components share one profile store.- Parameters:
cfg – [in] Required configuration;
profile_managermust be a valid, initialised handleout_service – [out] Created service handle
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid argument
ESP_ERR_NO_MEM Out of memory
-
esp_err_t esp_wifi_service_destroy(esp_wifi_service_t *service)
Destroy the service.
- Parameters:
service – [in] Service handle
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid argument
-
esp_err_t esp_wifi_service_get_profile_manager(esp_wifi_service_t *service, esp_wifi_service_profile_mgr_t *manager_out)
Get profile manager handle owned by service.
- Parameters:
service – [in] Service handle
manager_out – [out] Profile manager handle owned by service
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL argument
-
esp_err_t esp_wifi_service_get_scan_handle(esp_wifi_service_t *service, esp_wifi_service_scan_handle_t *scan_handle_out)
Get the shared scan handle owned by service.
Note
Selector, provisioning transports, and application code should share this agent so scan requests can be coalesced and receive the same driver scan records.
- Parameters:
service – [in] Service handle
scan_handle_out – [out] Scan handle owned by service
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL argument
-
esp_err_t esp_wifi_service_start_provisioning(esp_wifi_service_t *service)
Start all provisioning instances.
- Parameters:
service – [in] Service handle
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid argument
Others Provisioning-specific error
-
esp_err_t esp_wifi_service_stop_provisioning(esp_wifi_service_t *service)
Stop all started provisioning instances.
- Parameters:
service – [in] Service handle
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid argument
Others Provisioning-specific error
-
esp_err_t esp_wifi_service_is_provisioning_running(esp_wifi_service_t *service, bool *running_out)
Query provisioning running state.
- Parameters:
service – [in] Service handle
running_out – [out] True if any provisioning instance is running
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid argument
-
esp_err_t esp_wifi_service_request_connect(esp_wifi_service_t *service, char *ssid, char *password, uint8_t prio, uint32_t wait_sec)
Save a Wi-Fi profile and request connection to the specified SSID.
Note
This API stores or updates the profile as enabled, stops provisioning, starts the selector if needed, and asks the selector to connect to the specified saved profile directly without running a scan/re-evaluation cycle.
- Parameters:
service – [in] Service handle
ssid – [in] NUL-terminated SSID
password – [in] NUL-terminated password; NULL is treated as an empty password
prio – [in] Profile priority, 0 to ::ESP_WIFI_SERVICE_PROFILE_PRIORITY_MAX
wait_sec – [in] Seconds to wait for STA got IP; 0 returns after the request is accepted
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid argument
ESP_ERR_TIMEOUT
wait_secis non-zero and STA did not get IP in timeOthers From profile manager or selector
-
esp_err_t esp_wifi_service_request_reeval(esp_wifi_service_t *service)
Request one Wi-Fi selector re-evaluation cycle.
Note
This API is asynchronous. It returns after the request is accepted, not after scan results are evaluated or a connection decision is completed. If the selector is already running and no re-evaluation is in flight, this API attempts to start a Wi-Fi scan before returning. If a scan/re-evaluation is already in flight, the request is marked pending and runs after the current cycle finishes. If the selector is not running but saved profiles exist, the service starts the selector and schedules evaluation.
- Parameters:
service – [in] Service handle
- Returns:
ESP_OK On success or when the request is throttled
ESP_ERR_INVALID_ARG service is NULL
ESP_ERR_NOT_FOUND No Wi-Fi profile is available to evaluate
Others Selector-specific error
Structures
-
struct esp_wifi_service_config_t
Wi-Fi service configuration.
Public Members
-
const char *name
Service instance name for service manager
-
esp_wifi_service_profile_mgr_t profile_manager
Required profile manager handle; created and owned by the caller
-
esp_wifi_service_prov_t *prov_list
Provisioning handle array owned by application
-
size_t prov_num
Number of entries in prov_list
-
const esp_wifi_service_selector_cfg_t *selector_policy
Selector policy; NULL uses selector built-in defaults
-
const char *name
Type Definitions
-
typedef struct esp_wifi_service esp_wifi_service_t
Opaque Wi-Fi service handle.
Enumerations
-
enum esp_wifi_service_event_t
Wi-Fi service event identifiers.
Values:
-
enumerator ESP_WIFI_SERVICE_EVENT_CONNECTED
Wi-Fi service reports connected
-
enumerator ESP_WIFI_SERVICE_EVENT_DISCONNECTED
Wi-Fi service reports disconnected
-
enumerator ESP_WIFI_SERVICE_EVENT_PROV_STARTED
Provisioning transport started
-
enumerator ESP_WIFI_SERVICE_EVENT_PROV_STOPPED
Provisioning transport stopped
-
enumerator ESP_WIFI_SERVICE_EVENT_PROV_PEER_CONNECTED
Provisioning peer connected
-
enumerator ESP_WIFI_SERVICE_EVENT_PROV_PEER_DISCONNECTED
Provisioning peer disconnected
-
enumerator ESP_WIFI_SERVICE_EVENT_PROV_CREDENTIAL_RECEIVED
Provisioning credential received
-
enumerator ESP_WIFI_SERVICE_EVENT_PROV_CUSTOM_DATA_RECEIVED
Provisioning custom data received
-
enumerator ESP_WIFI_SERVICE_EVENT_PROV_ERROR
Provisioning runtime error
-
enumerator ESP_WIFI_SERVICE_EVENT_STA_CONFIG
STA config can be adjusted before connect
-
enumerator ESP_WIFI_SERVICE_EVENT_STA_GOT_IP
Wi-Fi service reports station got IP
-
enumerator ESP_WIFI_SERVICE_EVENT_STA_LOST_IP
Wi-Fi service reports station lost IP
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_CANDIDATE
Selector candidate chosen from scan result
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_SWITCHING
Selector decided to switch/connect
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_SWITCH_FAILED
Selector switch/connect failed
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_BLACKLISTED
Selector blacklisted one BSSID
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_ACCESS_FAILED
Selector probe access check failed
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_LATENCY_DEGRADED
Selector latency check degraded
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_THROUGHPUT_DEGRADED
Selector throughput check degraded
-
enumerator ESP_WIFI_SERVICE_EVENT_SELECTOR_RSSI_LOW
Selector RSSI check is below threshold
-
enumerator ESP_WIFI_SERVICE_EVENT_CONNECTED
Header File
Functions
-
esp_err_t esp_wifi_service_profile_mgr_init(const esp_wifi_service_profile_mgr_cfg_t *cfg, esp_wifi_service_profile_mgr_t *out_handle)
Create profile manager and esp_config_manager handle.
- Parameters:
cfg – [in] Configuration;
cfg->storagefrom ::esp_config_storage_init_nvs (or related) and must outlive the profile managerout_handle – [out] Profile manager handle
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid configuration;
max_profilesmust be greater than 0ESP_ERR_NO_MEM Allocation failure
Others From esp_config_manager
-
void esp_wifi_service_profile_mgr_deinit(esp_wifi_service_profile_mgr_t handle)
Destroy wifi_profile instance.
- Parameters:
handle – [in] Profile manager handle
-
esp_err_t esp_wifi_service_profile_mgr_count(esp_wifi_service_profile_mgr_t handle, uint8_t *count_out)
Get number of profiles.
- Parameters:
handle – [in] Profile manager handle
count_out – [out] Number of profiles
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL argument
-
esp_err_t esp_wifi_service_profile_mgr_foreach(esp_wifi_service_profile_mgr_t handle, bool (*callback)(const esp_wifi_service_profile_t *profile, void *user_ctx), void *user_ctx)
Iterate all stored profiles in insertion order.
Note
The callback is invoked under the internal lock; it must not call any profile manager API that acquires the same lock.
- Parameters:
handle – [in] Profile manager handle
callback – [in] Called for each profile; return
falseto stop earlyuser_ctx – [in] Forwarded to every callback invocation
- Returns:
ESP_OK On success (including early stop by callback)
ESP_ERR_INVALID_ARG NULL argument
-
esp_err_t esp_wifi_service_profile_mgr_get(esp_wifi_service_profile_mgr_t handle, const char *ssid, esp_wifi_service_profile_t *profile_out)
Get profile by SSID.
- Parameters:
handle – [in] Profile manager handle
ssid – [in] SSID to match
profile_out – [out] Profile content
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid SSID or NULL handle
ESP_ERR_NOT_FOUND SSID not in store
Others From load/save
-
esp_err_t esp_wifi_service_profile_mgr_add(esp_wifi_service_profile_mgr_t handle, esp_wifi_service_profile_t *profile)
Append or replace by SSID: add credentials, or update if SSID exists.
- Parameters:
handle – [in] Profile manager handle
profile – [in] Profile to add or update; caller retains ownership
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG Invalid SSID/password/priority or NULL argument
ESP_ERR_NO_MEM Profile table full on add
Others From load/save
-
esp_err_t esp_wifi_service_profile_mgr_set_enabled(esp_wifi_service_profile_mgr_t handle, const char *ssid, bool enabled)
Set profile enabled flag by SSID.
- Parameters:
handle – [in] Profile manager handle
ssid – [in] NUL-terminated SSID
enabled – [in] True to enable
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL argument
ESP_ERR_NOT_FOUND SSID not in store
Others From load/save
-
esp_err_t esp_wifi_service_profile_mgr_set_last_working(esp_wifi_service_profile_mgr_t handle, const char *ssid)
Record the last successfully connected profile by SSID.
- Parameters:
handle – [in] Profile manager handle
ssid – [in] NUL-terminated SSID; pass NULL or empty string to clear
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL handle
ESP_ERR_NOT_FOUND Non-empty SSID not in store
Others From load/save
-
esp_err_t esp_wifi_service_profile_mgr_get_last_working(esp_wifi_service_profile_mgr_t handle, char *ssid, size_t ssid_len)
Get the SSID of the last successfully connected profile.
- Parameters:
handle – [in] Profile manager handle
ssid – [out] Buffer to receive NUL-terminated SSID
ssid_len – [in] Size of ssid; must be at least ::ESP_WIFI_SERVICE_PROFILE_SSID_MAX_LEN + 1
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL argument or ssid_len too small
ESP_ERR_NOT_FOUND No last-working profile recorded
-
esp_err_t esp_wifi_service_profile_mgr_delete(esp_wifi_service_profile_mgr_t handle, const char *ssid)
Delete profile by SSID.
- Parameters:
handle – [in] Profile manager handle
ssid – [in] SSID to remove
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL argument
ESP_ERR_NOT_FOUND SSID not found
Others From load/save
-
esp_err_t esp_wifi_service_profile_mgr_clear_all(esp_wifi_service_profile_mgr_t handle)
Remove all profiles and reset store to defaults.
- Parameters:
handle – [in] Profile manager handle
- Returns:
ESP_OK On success
ESP_ERR_INVALID_ARG NULL handle
Others From load/save
Structures
-
struct esp_wifi_service_profile_t
One saved Wi-Fi profile (filled by provisioning or application)
-
struct esp_wifi_service_profile_mgr_cfg_t
Initialization parameters for ::esp_wifi_service_profile_mgr_init.
Public Members
-
uint8_t max_profiles
Maximum number of profiles to keep; must be greater than 0
-
esp_config_storage_t storage
From esp_config_storage_init_*; must outlive profile manager
-
const esp_config_crypto_ops_t *crypto
NULL: plaintext record except private metadata
-
size_t crypto_extra_size
Extra bytes crypto may add to the profile store
-
uint8_t max_profiles
Macros
-
ESP_WIFI_SERVICE_PROFILE_PRIORITY_MAX
-
ESP_WIFI_SERVICE_PROFILE_SSID_MAX_LEN
-
ESP_WIFI_SERVICE_PROFILE_PASS_MAX_LEN
-
ESP_WIFI_SERVICE_PROFILE_FLAG_ENABLED
Type Definitions
-
typedef void *esp_wifi_service_profile_mgr_t
Opaque handle to wifi_profile instance.