junhui93/wifi_provisioner

1.3.3

Latest
uploaded 21 hours ago
Commercial-friendly WiFi provisioning component with captive portal fallback and persistent auto-reconnect for ESP-IDF

Readme

# WiFi Provisioner / WiFi 配网组件

**EN** — Commercial-friendly WiFi provisioning component for ESP-IDF with captive-portal fallback, persistent auto-reconnect, and TX power fallback for antenna impedance mismatch.

**中文** — 面向 ESP-IDF 的商用级 WiFi 配网组件,支持 Soft-AP 强制门户配网、断连持续自动重连、以及天线阻抗不匹配时的发射功率降级重试。

> **EN | Why this component?** The original `esp-idf-wifi-provisioner` is licensed under GPL-3.0, which requires the entire project to be open-sourced. This component is independently implemented under the **MIT license** based on the general concept of WiFi provisioning and ESP-IDF official documentation — safe for commercial and closed-source products.
>
> **中文 | 为什么选它?** 原始的 `esp-idf-wifi-provisioner` 采用 GPL-3.0 协议,要求整个工程开源。本组件基于 WiFi 配网的通用概念和 ESP-IDF 官方文档**独立实现**,采用 **MIT 协议**——可安全用于商用及闭源产品。

---

## Features / 功能特性

**EN**
- **STA auto-connect** from credentials stored in NVS
- **Soft-AP + captive portal** fallback when no credentials are stored or connection fails
- **WiFi network scan** with signal strength display in the captive portal
- **Credential verification** — submitted credentials are tested before being saved
- **Persistent auto-reconnect** — after a successful connection, if the link drops the component keeps reconnecting with exponential back-off
- **TX power fallback** — if the AP can be scanned but connection fails with signal-quality-related reason codes (e.g. handshake timeout), TX power is progressively lowered to work around antenna impedance mismatch (high VSWR)
- **Non-blocking API** — `wifi_prov_start()` returns immediately; all work happens in a background task
- **Configurable** via menuconfig or runtime `wifi_prov_config_t`
- **Event callbacks** for connect, disconnect, and portal start

**中文**
- **STA 自动连接**:从 NVS 读取已保存的凭证自动联网
- **Soft-AP + 强制门户回退**:无凭证或连接失败时自动开启配网门户
- **WiFi 扫描**:配网页面展示附近网络及信号强度
- **凭证校验**:用户提交的密码先尝试连接验证,成功才保存
- **断连持续重连**:连接成功后若链路断开,以指数退避持续重连
- **发射功率降级**:当能扫描到 AP 但连接失败且原因码为信号质量类(如握手超时),逐步降低发射功率以应对天线阻抗不匹配(高 VSWR)
- **非阻塞 API**:`wifi_prov_start()` 立即返回,所有工作在后台任务中进行
- **灵活配置**:支持 menuconfig 或运行时 `wifi_prov_config_t` 配置
- **事件回调**:连接、断开、门户启动三种回调

---

## Requirements / 运行环境

**EN**
- ESP-IDF >= 5.0
- Targets: ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6

**中文**
- ESP-IDF >= 5.0
- 支持目标:ESP32、ESP32-S2、ESP32-S3、ESP32-C3、ESP32-C6

---

## Installation / 安装

**EN** — Add the component to your project's `idf_component.yml`:

**中文** — 在工程的 `idf_component.yml` 中添加依赖:

```yaml
dependencies:
  junhui93/wifi_provisioner: "^1.3.0"
```

**EN** — Or copy the `wifi_provisioner` folder into your project's `components/` directory.

**中文** — 也可以将 `wifi_provisioner` 文件夹拷贝到工程的 `components/` 目录下。

---

## Quick Start / 快速开始

```c
#include "wifi_provisioner.h"

void app_main(void)
{
    /* EN: Initialise config with Kconfig defaults.
       中文: 用 Kconfig 默认值初始化配置。 */
    wifi_prov_config_t config = WIFI_PROV_DEFAULT_CONFIG();
    config.ap_ssid = "MyDevice-Setup";

    /* EN: Enable persistent auto-reconnect after disconnection.
       中文: 开启断连后持续自动重连。 */
    config.auto_reconnect          = true;
    config.reconnect_interval_ms   = 5000;    /* base retry interval / 基础重试间隔 */
    config.reconnect_max_delay_ms  = 60000;   /* cap back-off at 60 s / 退避上限 60 秒 */

    /* EN: TX power fallback is enabled by default. It lowers TX power
       on signal-quality-related connection failures to work around
       antenna impedance mismatch.
       中文: 发射功率降级默认开启,在信号质量类连接失败时
       逐步降低功率以应对天线阻抗不匹配。 */
    config.tx_power_fallback = true;
    config.tx_power_min      = 8;             /* 8 = 2.0 dBm, lowest valid / 最低合法值 2.0 dBm */

    /* EN: AP mode uses low TX power (8 dBm) so phones can discover the
       hotspot even with antenna impedance mismatch.
       中文: AP 模式使用低发射功率(8 dBm),即使天线阻抗不匹配
       手机也能发现热点。 */
    config.ap_tx_power        = 32;            /* 32 = 8 dBm */

    /* EN: STA auto-calibration tests each power level on first connect,
       measures TCP RTT, and saves the optimal level to NVS.
       中文: STA 自动校准在首次连接时测试每个功率级,测量 TCP RTT,
       将最优功率保存到 NVS。 */
    config.sta_power_autocal  = true;

    ESP_ERROR_CHECK(wifi_prov_start(&config));

    /* EN: wifi_prov_start() returns immediately. Use the on_connected
       callback or wifi_prov_wait_for_connection() to know when WiFi is ready.
       中文: wifi_prov_start() 立即返回,通过 on_connected 回调或
       wifi_prov_wait_for_connection() 获知 WiFi 就绪。 */

    while (1) {
        if (wifi_prov_is_connected()) {
            /* do your network work here / 在此执行业务逻辑 */
        }
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}
```

---

## Architecture / 架构设计

**EN** — The component uses a context-based architecture that differs from typical ESP-IDF examples:

| Module | Pattern | Description |
|---|---|---|
| `wifi_provisioner.c` | Background task + signal bits | `wifi_prov_start()` launches a worker task; connection-state tracking uses named event-group bits (`SIG_CONNECTED`, `SIG_STOP`) |
| `wifi_ap.c` | Builder + state struct | AP configuration is built by a separate `build_ap_config()` helper; state managed via `ap_state_t` struct |
| `wifi_sta.c` | Per-attempt context | Each connect call uses a stack-allocated `sta_conn_ctx_t` passed as the event-handler argument — no bare statics for per-attempt state |
| `nvs_store.c` | Simple wrapper | Thin NVS abstraction for credential and TX-power persistence |
| `http_server.c` | Async event-driven | HTTP server posts `WIFI_PROV_EVENT_CREDENTIALS_SET` on success |
| `dns_server.c` | UDP socket | Minimal DNS hijack for captive-portal detection |

**中文** — 组件采用基于上下文的架构,与典型 ESP-IDF 示例不同:

| 模块 | 模式 | 说明 |
|---|---|---|
| `wifi_provisioner.c` | 后台任务 + 信号位 | `wifi_prov_start()` 启动工作任务;连接状态用事件组命名位跟踪(`SIG_CONNECTED`、`SIG_STOP`) |
| `wifi_ap.c` | 构建器 + 状态结构体 | AP 配置由独立的 `build_ap_config()` 构建;状态通过 `ap_state_t` 结构体管理 |
| `wifi_sta.c` | 每次连接的上下文 | 每次连接使用栈上分配的 `sta_conn_ctx_t`,通过事件处理器参数传递——不用裸 static 变量 |
| `nvs_store.c` | 简单封装 | NVS 凭证和功率持久化的薄封装 |
| `http_server.c` | 异步事件驱动 | HTTP 服务器在验证成功后发送 `WIFI_PROV_EVENT_CREDENTIALS_SET` 事件 |
| `dns_server.c` | UDP socket | 强制门户检测的最小 DNS 劫持 |

---

## How It Works / 工作原理

**EN**
1. On `wifi_prov_start()`, the component reads the SSID and password stored in NVS.
2. If credentials exist, it tries to connect as a station (up to `max_retries` times).
3. On success, the device is online and auto-reconnect is armed.
4. On failure (or if no credentials are stored), the component starts a Soft-AP and a captive portal HTTP server.
5. The user connects to the AP, opens any URL (or `192.168.4.1`), selects a network, enters the password, and submits.
6. The component verifies the credentials by connecting; on success it saves them to NVS and switches to STA mode.

**中文**
1. 调用 `wifi_prov_start()` 后,组件从 NVS 读取已保存的 SSID 和密码。
2. 若存在凭证,尝试以 STA 模式连接(最多重试 `max_retries` 次)。
3. 连接成功后设备上线,同时启用断连自动重连。
4. 连接失败(或无凭证)时,组件启动 Soft-AP 和强制门户 HTTP 服务器。
5. 用户连接该 AP,打开任意网址(或直接访问 `192.168.4.1`),选择网络并输入密码提交。
6. 组件先尝试连接验证凭证,成功后将其保存到 NVS 并切换为 STA 模式。

---

## Persistent Auto-Reconnect / 断连持续重连

**EN** — After a successful connection, if the WiFi link drops, the component automatically attempts to reconnect:
- A single background task handles reconnection (no duplicate `esp_wifi_connect()` calls).
- **Exponential back-off**: `delay = min(interval × 2^attempts, max_delay_ms)`.
- Can be toggled at runtime with `wifi_prov_set_auto_reconnect(bool)`.
- An `on_disconnected` callback notifies the application with the disconnect reason.

**中文** — 连接成功后若 WiFi 链路断开,组件自动尝试重连:
- 单一后台任务处理重连(避免重复调用 `esp_wifi_connect()`)。
- **指数退避**:`delay = min(interval × 2^attempts, max_delay_ms)`。
- 可通过 `wifi_prov_set_auto_reconnect(bool)` 运行时开关。
- `on_disconnected` 回调通知应用层断开原因。

---

## TX Power Strategy / 发射功率策略

**EN** — The component uses a two-pronged TX power strategy to handle antenna design limitations:

**中文** — 组件采用双轨发射功率策略来应对天线设计限制:

### AP Mode: Fixed Low Power / AP 模式:固定低功率

**EN** — During provisioning, the AP (hotspot) mode uses a fixed low TX power (default 8 dBm). This ensures that even with antenna impedance mismatch, the phone can still discover the ESP32's hotspot. Since the user is physically close to the device during provisioning, low power is sufficient.

**中文** — 配网时 AP(热点)模式使用固定低发射功率(默认 8 dBm)。即使天线阻抗不匹配,手机也能发现 ESP32 的热点。因为配网时用户就在设备旁边,低功率足够。

### STA Mode: Auto-Calibration / STA 模式:自动校准

**EN** — On first connection (when no power is saved in NVS), the component runs an automatic calibration:
1. Iterates through all 6 power levels (20 → 0 dBm)
2. At each level: connects to the router, measures connection time + TCP RTT to gateway
3. Selects the power level with the best RTT (if RTT within 5 ms, prefers higher power for longer range)
4. Saves the optimal power to NVS — subsequent boots skip calibration

**中文** — 首次连接时(NVS 无保存功率时),组件执行自动校准:
1. 遍历全部 6 个功率级(20 → 0 dBm)
2. 每级:连接路由器,测量连接耗时 + 到网关的 TCP RTT
3. 选择 RTT 最优的功率级(RTT 在 5ms 内则偏好高功率,通信距离更远)
4. 最优功率保存到 NVS — 后续开机跳过校准

### Fallback: Step-Down on Failure / 兜底:逐级降功率

**EN** — If auto-calibration fails at all levels, or is disabled, the existing step-down mechanism remains: on signal-quality-related connection failures, TX power is progressively lowered.

**中文** — 如果自动校准全部失败或被禁用,保留原有逐级降功率机制:信号质量类连接失败时逐步降低发射功率。

### Power Ladder / 功率阶梯

**EN** — The ESP-IDF API `esp_wifi_set_max_tx_power()` only accepts values in **[8, 84]** (2.0 dBm – 21.0 dBm), and it must be called **after** `esp_wifi_start()`.

**中文** — ESP-IDF 的 `esp_wifi_set_max_tx_power()` 只接受 **[8, 84]**(2.0 dBm – 21.0 dBm)范围的值,且必须在 `esp_wifi_start()` **之后**调用。

| Step / 级 | Value / 值 | Power / 功率 |
|---|---|---|
| 0 | 80 | 20.0 dBm (default / 默认) |
| 1 | 64 | 16.0 dBm |
| 2 | 48 | 12.0 dBm |
| 3 | 32 | 8.0 dBm |
| 4 | 16 | 4.0 dBm |
| 5 | 8 | 2.0 dBm (minimum / 最小) |

### Triggered Reason Codes / 触发降级的失败码

**EN** — Only signal-quality-related reasons trigger the fallback. Known errors (wrong password, cipher mismatch, AP not found) do **not** trigger it.

**中文** — 只有信号质量类失败码触发降级。已知错误(密码错误、加密不匹配、AP 找不到)**不触发**。

| Reason Code | Name | Trigger / 触发 |
|---|---|---|
| 2 | AUTH_EXPIRE | ✅ Yes |
| 4 | ASSOC_EXPIRE | ✅ Yes |
| 15 | 4WAY_HANDSHAKE_TIMEOUT | ✅ Yes |
| 23 | 802_1X_AUTH_FAILED | ✅ Yes |
| 200 | BEACON_TIMEOUT | ✅ Yes |
| 203 | AUTH_FAIL | ✅ Yes |
| 204 | HANDSHAKE_TIMEOUT / ASSOC_FAIL | ✅ Yes |
| 205 | CONNECTION_FAIL | ✅ Yes |
| 201 | NO_AP_FOUND | ❌ No (scan issue / 扫描问题) |
| 14 | MIC_FAILURE | ❌ No (wrong password / 密码错误) |
| 18-22 | CIPHER / AKMP invalid | ❌ No (config mismatch / 配置不匹配) |

### Behaviour / 行为说明

**EN**
- Each power level retries up to `max_retries` times before stepping down.
- On success at a lowered power, the working level is **saved to NVS** so subsequent boots skip the failure cycle.
- All three connection paths are covered: initial connect, portal credential verification, and persistent reconnect.
- In the reconnect task, TX power is lowered after every 3 consecutive failures with impedance-related reasons.

**中文**
- 每个功率级重试 `max_retries` 次后才降一级。
- 在低功率下连接成功时,工作功率**保存到 NVS**,下次开机直接使用,跳过失败周期。
- 三个连接路径全覆盖:开机连接、门户凭证验证、断连重连。
- 重连任务中,每 3 次连续失败且原因为阻抗类时降一级功率。

---

## API Reference / API 参考

| Function | EN Description | 中文说明 |
|---|---|---|
| `wifi_prov_init()` | Initialise NVS, netif, event loop. Called automatically by `wifi_prov_start()`. | 初始化 NVS、网络接口、事件循环,`wifi_prov_start()` 会自动调用。 |
| `wifi_prov_start(config)` | Start the provisioner. Returns immediately; work runs in a background task. | 启动配网器,立即返回,工作在后台任务中运行。 |
| `wifi_prov_stop()` | Stop and release all resources. | 停止并释放所有资源。 |
| `wifi_prov_wait_for_connection(ticks)` | Block until connected (with timeout). Returns `ESP_OK` or `ESP_ERR_TIMEOUT`. | 阻塞等待连接(带超时),返回 `ESP_OK` 或 `ESP_ERR_TIMEOUT`。 |
| `wifi_prov_is_connected()` | Check if connected with a valid IP. | 查询是否已连接并获得有效 IP。 |
| `wifi_prov_get_ip_info(ip_info)` | Get current station IP information. | 获取当前 STA 的 IP 信息。 |
| `wifi_prov_erase_credentials()` | Erase stored WiFi credentials from NVS. | 清除 NVS 中保存的 WiFi 凭证。 |
| `wifi_prov_set_auto_reconnect(enable)` | Enable or disable persistent auto-reconnect at runtime. | 运行时开启或关闭断连持续重连。 |
| `wifi_prov_get_auto_reconnect()` | Query whether auto-reconnect is enabled. | 查询重连是否已开启。 |

---

## Configuration / 配置

**EN** — All parameters can be set via **menuconfig** (`Component config → WiFi Provisioner`) or by overriding fields in `wifi_prov_config_t` at runtime.

**中文** — 所有参数可通过 **menuconfig**(`Component config → WiFi Provisioner`)或运行时覆盖 `wifi_prov_config_t` 字段设置。

### Soft-AP

| Field / 字段 | Kconfig | Default / 默认值 |
|---|---|---|
| `ap_ssid` | `CONFIG_WIFI_PROV_AP_SSID` | `"ESP32-Setup"` |
| `ap_password` | `CONFIG_WIFI_PROV_AP_PASSWORD` | `""` (open / 开放) |
| `ap_channel` | `CONFIG_WIFI_PROV_AP_CHANNEL` | `1` |
| `ap_max_connections` | `CONFIG_WIFI_PROV_AP_MAX_CONNECTIONS` | `4` |

### STA Connection / STA 连接

| Field / 字段 | Kconfig | Default / 默认值 |
|---|---|---|
| `max_retries` | `CONFIG_WIFI_PROV_STA_MAX_RETRIES` | `5` |

### Auto-Reconnect / 自动重连

| Field / 字段 | Kconfig | Default / 默认值 |
|---|---|---|
| `auto_reconnect` | `CONFIG_WIFI_PROV_AUTO_RECONNECT` | `true` |
| `reconnect_interval_ms` | `CONFIG_WIFI_PROV_RECONNECT_INTERVAL_MS` | `5000` |
| `reconnect_max_delay_ms` | `CONFIG_WIFI_PROV_RECONNECT_MAX_DELAY_MS` | `60000` |

### TX Power / 发射功率

| Field / 字段 | Kconfig | Default / 默认值 |
|---|---|---|
| `tx_power_fallback` | `CONFIG_WIFI_PROV_TX_POWER_FALLBACK` | `true` |
| `tx_power_min` | `CONFIG_WIFI_PROV_TX_POWER_MIN` | `8` (2.0 dBm, valid range [8,84]) |
| `ap_tx_power` | `CONFIG_WIFI_PROV_AP_TX_POWER` | `32` (8.0 dBm, low power for AP mode) |
| `sta_power_autocal` | `CONFIG_WIFI_PROV_STA_POWER_AUTOCAL` | `true` |

### Portal / 配网门户

| Field / 字段 | Kconfig | Default / 默认值 |
|---|---|---|
| `portal_timeout` | `CONFIG_WIFI_PROV_PORTAL_TIMEOUT` | `300` (s) |
| `http_port` | `CONFIG_WIFI_PROV_HTTP_PORT` | `80` |

---

## Callbacks / 回调函数

```c
typedef void (*wifi_prov_on_connected_cb_t)(void);
typedef void (*wifi_prov_on_disconnected_cb_t)(uint8_t reason);
typedef void (*wifi_prov_on_portal_start_cb_t)(void);
```

**EN** — Set them in `wifi_prov_config_t`:

**中文** — 在 `wifi_prov_config_t` 中设置:

```c
config.on_connected    = my_on_connected;
config.on_disconnected = my_on_disconnected;
config.on_portal_start = my_on_portal_start;
```

---

## Examples / 示例

**EN** — A complete example is available in [`examples/basic/`](examples/basic/). It demonstrates:
- Non-blocking startup
- Persistent auto-reconnect configuration
- TX power fallback configuration
- Connect/disconnect/portal callbacks
- Main loop with connection state polling

**中文** — 完整示例见 [`examples/basic/`](examples/basic/),演示:
- 非阻塞启动
- 断连持续重连配置
- 发射功率降级配置
- 连接 / 断开 / 门户启动回调
- 主循环中轮询连接状态

---

## License / 许可证

**EN** — MIT — free for commercial and closed-source use. See [LICENSE](LICENSE).

**中文** — MIT 协议——可免费用于商用及闭源项目。详见 [LICENSE](LICENSE)。

Links

To add this component to your project, run:

idf.py add-dependency "junhui93/wifi_provisioner^1.3.3"

download archive

Stats

  • Archive size
    Archive size ~ 37.30 KB
  • Downloaded in total
    Downloaded in total 1 time
  • Weekly Downloads Weekly Downloads (All Versions)
  • Downloaded this version
    This version: 0 times

Badge

junhui93/wifi_provisioner version: 1.3.3
|