ESP Wi-Fi Service

[English]

简介

Wi-Fi Service 把设备联网流程中的凭据存储、配网交互、自动连接、网络选择和质量探测统一到一套 ESP Service 服务接口中。使用它之后,应用不需要分别维护凭据存储、SoftAP/Web 配网、BluFi 配网、断线重连和多 AP 选择逻辑,可以更快构建稳定、便于现场运维的联网产品。

功能清单

  • Profile 管理:维护多组 Wi-Fi 凭据,支持新增、更新、启用、禁用、删除和清理,配网通道与连接选择共享同一个 profile manager

  • 可插拔存储:复用 ESP Config Manager 的 NVS、文件系统、双分区 raw flash 或自定义存储适配层,并支持加密回调保护已保存凭据

  • 多通道配网:HTTP SoftAP/Web UI、BluFi,以及应用自定义配网流程,所有通道写入同一份共享 profile

  • 自动启动策略:服务启动时根据是否存在已启用配置,自动进入连接选择流程或启动已配置的配网流程

  • 智能选择与切换:按用户优先级、信号质量、历史连通性和临时黑名单选择更合适的 AP,并在断线或链路退化后重新评估

  • 网络质量探测:支持连通性、延迟和吞吐退化判断,处理“已连接但业务不可用”的场景

  • 可选 MCP 工具支持:同时启用 CONFIG_ESP_MCP_ENABLECONFIG_WIFI_SERVICE_MCP_ENABLE 后,可通过 ESP Service 的 MCP 服务器远程查询状态、管理 profile 和触发配网/连接

技术拆解

Profile 管理与存储

每个 Wi-Fi 凭据保存为一个 esp_wifi_service_profile_t(SSID、密码、优先级、启用标志),由 esp_wifi_service_profile_mgr_t 统一管理并持久化。profile manager 的存储层直接复用 ESP Config Manager 的存储适配接口,创建时需要先准备好一个 esp_config_storage_t 句柄:

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);

同一个 profile_manager 句柄要同时传给 Wi-Fi Service 和每个配网通道的配置,这样配网写入的凭据才能被连接选择逻辑立即看到。

配网通道

服务启动时如果存在至少一个已启用的 profile,就直接进入连接选择流程;否则启动 prov_list 中配置的所有配网通道。内置提供 HTTP SoftAP/Web UI 和 BluFi 两种通道,也可以由应用自行实现自定义配网流程写入同一个 profile manager:

  • HTTP 通道启动 SoftAP、DNS captive portal、HTTP server 和默认或自定义 Web UI,凭据通过 POST /prov/profiles 提交

  • BluFi 通道通过手机侧蓝牙发送网络信息,凭据同样落到共享 profile manager,依赖 CONFIG_WIFI_SERVICE_PROV_BLUFI_ENABLE 及底层 BLUFI 协议栈

  • 两种通道都可以并行启用,配网结束后统一由 selector 逻辑接管连接

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);

选择与切换

selector 在多网络环境下自动决策连接哪个 AP:重新评估时先扫描周围 AP,只保留匹配已保存且已启用 profile 的候选项,再按用户优先级、信号质量、最近一次可正常访问的记录,以及临时黑名单排序,而不是简单选择信号最强的 AP。

        flowchart TD
    Init[服务初始化] --> Check{存在已启用配置}
    Check -- 是 --> Selector[启动 selector]
    Check -- 否 --> Prov[启动配网通道]
    Prov --> Save[接收凭据并保存]
    Save --> Selector
    Selector --> Scan[扫描候选 AP 并排序]
    Scan --> Decide{需要切换}
    Decide -- 否 --> Keep[保持当前连接]
    Decide -- 是 --> Switch[切换或故障转移]
    Switch --> Probe[连接后进行质量探测]
    Probe --> Decide2{探测失败或退化}
    Decide2 -- 否 --> Keep
    Decide2 -- 是 --> Scan
    

没有找到可用候选网络时,selector 按内置退避表(1000, 5000, 10000, 20000, 30000 ms)重试扫描,可通过 selector_policy.retry 覆盖;应用也可以调用 esp_wifi_service_request_connect() 跳过扫描和重新评估,直接连接一个已保存的 SSID,适合命令行工具或远程管理场景。

网络质量探测

已连接 Wi-Fi 但业务不可用是常见的现场问题:设备已经拿到 IP,但云端接口访问失败、延迟过高或吞吐不足。质量探测覆盖连通性检查(访问指定 URL 判断外部连通性)和延迟/吞吐检查(统计请求耗时和实际吞吐),在连续失败或持续退化后才触发处理,避免因为一次偶发抖动就切换网络;确认退化后,当前 BSSID 会被临时加入黑名单,交由 selector 重新选择候选网络。

应用示例

  • examples 目录下的示例演示 NVS profile 存储、HTTP 配网、自定义 selector 策略的最小接入流程,完整代码可参考组件仓库。

FAQ

Q1:max_connect_retry 设为 0 会一直重试吗?

会。0 表示保持原有行为、持续重试;设为非 0 值后,连续连接失败达到该次数会停止自动重新评估,直到调用 esp_wifi_service_request_connect()esp_wifi_service_request_reeval() 才会重新计数。

API 参考

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.

备注

The caller is responsible for creating the profile manager before calling this function and destroying it after esp_wifi_service_destroy returns. The same handle should be passed to each provisioning config so that all components share one profile store.

参数:
  • cfg[in] Required configuration; profile_manager must be a valid, initialised handle

  • out_service[out] Created service handle

返回:

  • 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.

参数:

service[in] Service handle

返回:

  • 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.

参数:
  • service[in] Service handle

  • manager_out[out] Profile manager handle owned by service

返回:

  • 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.

备注

Selector, provisioning transports, and application code should share this agent so scan requests can be coalesced and receive the same driver scan records.

参数:
  • service[in] Service handle

  • scan_handle_out[out] Scan handle owned by service

返回:

  • 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.

参数:

service[in] Service handle

返回:

  • 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.

参数:

service[in] Service handle

返回:

  • 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.

参数:
  • service[in] Service handle

  • running_out[out] True if any provisioning instance is running

返回:

  • 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.

备注

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.

参数:
  • 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

返回:

  • ESP_OK On success

  • ESP_ERR_INVALID_ARG Invalid argument

  • ESP_ERR_TIMEOUT wait_sec is non-zero and STA did not get IP in time

  • Others 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.

备注

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.

参数:

service[in] Service handle

返回:

  • 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

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

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.

参数:
  • cfg[in] Configuration; cfg->storage from ::esp_config_storage_init_nvs (or related) and must outlive the profile manager

  • out_handle[out] Profile manager handle

返回:

  • ESP_OK On success

  • ESP_ERR_INVALID_ARG Invalid configuration; max_profiles must be greater than 0

  • ESP_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.

参数:

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.

参数:
  • handle[in] Profile manager handle

  • count_out[out] Number of profiles

返回:

  • 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.

备注

The callback is invoked under the internal lock; it must not call any profile manager API that acquires the same lock.

参数:
  • handle[in] Profile manager handle

  • callback[in] Called for each profile; return false to stop early

  • user_ctx[in] Forwarded to every callback invocation

返回:

  • 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.

参数:
  • handle[in] Profile manager handle

  • ssid[in] SSID to match

  • profile_out[out] Profile content

返回:

  • 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.

参数:
  • handle[in] Profile manager handle

  • profile[in] Profile to add or update; caller retains ownership

返回:

  • 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.

参数:
  • handle[in] Profile manager handle

  • ssid[in] NUL-terminated SSID

  • enabled[in] True to enable

返回:

  • 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.

参数:
  • handle[in] Profile manager handle

  • ssid[in] NUL-terminated SSID; pass NULL or empty string to clear

返回:

  • 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.

参数:
  • 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

返回:

  • 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.

参数:
  • handle[in] Profile manager handle

  • ssid[in] SSID to remove

返回:

  • 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.

参数:

handle[in] Profile manager handle

返回:

  • 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)

Public Members

uint8_t flags

::ESP_WIFI_SERVICE_PROFILE_FLAG_*

uint8_t priority

Higher value = stronger user preference

char ssid[ESP_WIFI_SERVICE_PROFILE_SSID_MAX_LEN + 1]

NUL-terminated SSID

char password[ESP_WIFI_SERVICE_PROFILE_PASS_MAX_LEN + 1]

NUL-terminated password

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

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.