# Wi-Fi 配网

[English]

## 概述

该组件提供控制 Wi-Fi 配网服务的 API，可以通过 SoftAP 或低功耗蓝牙建立 协议通信 安全会话，接收和配置 Wi-Fi 凭证。通过一组 `wifi_prov_mgr_` API，可以快速实现配网服务，该服务具备必要功能、代码量少且足够灵活。

### 初始化

调用 `wifi_prov_mgr_init()` 可以配置和初始化配网管理器，因此在调用任何其他 `wifi_prov_mgr_` API 之前必须先调用此函数。请注意，该管理器依赖于 ESP-IDF 的其他组件，包括 NVS、TCP/IP、Event Loop 和 Wi-Fi，以及可选的 mDNS，因此在调用之前必须先初始化这些组件。调用 `wifi_prov_mgr_deinit()` 可以随时反初始化管理器。

>     wifi_prov_mgr_config_t config = {
>         .scheme = wifi_prov_scheme_ble,
>         .scheme_event_handler = WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
>     };

>     ESP_ERROR_CHECK( wifi_prov_mgr_init(config) );

以下配置结构体 `wifi_prov_mgr_config_t` 里包含的部分字段可用于指定特定管理器行为:

> - `wifi_prov_mgr_config_t::scheme` - 用于指定配网方案。每个方案对应一种 protocomm 支持的传输模式，因此支持三个选项：

>   > - `wifi_prov_scheme_ble` - 使用低功耗蓝牙传输和 GATT 服务器来处理配网命令。

>   > - `wifi_prov_scheme_softap` - 使用 Wi-Fi SoftAP 传输和 HTTP 服务器来处理配网命令。

>   > - `wifi_prov_scheme_console` - 使用串口传输和控制台来处理配网命令。

> - `wifi_prov_mgr_config_t::scheme_event_handler` - 为方案定义的专属事件处理程序。选择适当方案后，其专属事件处理程序支持管理器自动处理特定事项。目前，该选项不适用于 SoftAP 或基于控制台的配网方案，但对于低功耗蓝牙配网方案来说非常方便。因为蓝牙需要相当多内存才能正常工作，所以配网完成后，主应用程序需要使用低功耗蓝牙或经典蓝牙时，可能需要回收配网所占的全部或部分内存。此外，未来每当配网设备重启时，都需要再次回收内存。为了便于使用 `wifi_prov_scheme_ble` 选项，各方案定义了专属处理程序。设备会根据所选处理程序，在反初始化配网管理器时自动释放低功耗蓝牙、经典蓝牙或蓝牙双模的内存。可用选项包括：

>   > - `WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM` - 同时释放经典蓝牙和低功耗蓝牙或蓝牙双模的内存，可以在主应用程序不需要蓝牙时使用该选项。

>   > - `WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE` - 只释放低功耗蓝牙的内存，可以在主应用程序需要经典蓝牙时使用该选项。

>   > - `WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT` - 仅释放经典蓝牙的内存，可以在主应用程序需要低功耗蓝牙时使用该选项，内存会在初始化管理器时立即释放。

>   > - `WIFI_PROV_EVENT_HANDLER_NONE` - 不使用任何特定方案的专属处理程序。以下情况可使用该选项：不使用低功耗蓝牙配网方案，即使用 SoftAP 或控制台方案；主应用程序需要自行回收内存；主应用程序需要同时使用低功耗蓝牙和经典蓝牙。

> - `wifi_prov_mgr_config_t::app_event_handler` （不推荐）- 目前建议使用默认的事件循环处理程序捕获生成的 `WIFI_PROV_EVENT`。关于配网服务生成事件的列表，请参阅 `wifi_prov_cb_event_t` 的定义。以下是配网事件示例摘录：

>     static void event_handler(void* arg, esp_event_base_t event_base,
>                               int event_id, void* event_data)
>     {
>         if (event_base == WIFI_PROV_EVENT) {
>             switch (event_id) {
>                 case WIFI_PROV_START:
>                     ESP_LOGI(TAG, "Provisioning started");
>                     break;
>                 case WIFI_PROV_CRED_RECV: {
>                     wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)event_data;
>                     ESP_LOGI(TAG, "Received Wi-Fi credentials"
>                              "\n\tSSID     : %s\n\tPassword : %s",
>                              (const char *) wifi_sta_cfg->ssid,
>                              (const char *) wifi_sta_cfg->password);
>                     break;
>                 }
>                 case WIFI_PROV_CRED_FAIL: {
>                     wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)event_data;
>                     ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s"
>                              "\n\tPlease reset to factory and retry provisioning",
>                              (*reason == WIFI_PROV_STA_AUTH_ERROR) ?
>                              "Wi-Fi station authentication failed" : "Wi-Fi access-point not found");
>                     break;
>                 }
>                 case WIFI_PROV_CRED_SUCCESS:
>                     ESP_LOGI(TAG, "Provisioning successful");
>                     break;
>                 case WIFI_PROV_END:
>                     /*配网完成后，反初始化管理器。*/
>                     wifi_prov_mgr_deinit();
>                     break;
>                 default:
>                     break;
>             }
>         }
>     }

调用 `wifi_prov_mgr_deinit()` 可以随时反初始化管理器。

### 检查配网状态

在运行时，可以调用 `wifi_prov_mgr_is_provisioned()` 检查设备是否配网完成，该函数会在内部检查 Wi-Fi 凭据是否存储在 NVS 中。

请注意，目前管理器并没有自己的 NVS 命名空间来存储 Wi-Fi 凭据，而是依赖 `esp_wifi_` API 来设置和获取存储在默认位置的 NVS 中的凭据。

可以采用以下任一方法重置配网状态：

> - 手动擦除 NVS 分区的配网相关部分。

> - 主应用程序必须实现某种逻辑，以在运行时调用 `esp_wifi_` API 来擦除凭据。

> - 主应用程序必须实现某种逻辑，以在不考虑配网状态的情况下，强制启动配网。

>     bool provisioned = false;
>     ESP_ERROR_CHECK( wifi_prov_mgr_is_provisioned(&provisioned) );

### 启动配网服务

在启动配网服务时，需要指定服务名称和相应密钥，即：

> - 使用 `wifi_prov_scheme_softap` 方案时，服务名称对应 Wi-Fi SoftAP 的 SSID，密钥对应密码。

> - 使用 `wifi_prov_scheme_ble` 方案时，服务名称对应低功耗蓝牙设备名称，无需指定密钥。

此外，由于管理器内部使用了 `protocomm`，可以选择其提供的任一安全功能：

> - Security 1 是安全通信，该安全通信需要先握手，其中涉及 X25519 密钥交换和使用所有权证明 `pop` 完成身份验证，随后使用 AES-CTR 加密或解密后续消息。

> - Security 0 是纯文本通信，会直接忽略 `pop`。

关于安全功能的更多详情，请参阅 统一配网。

>     const char *service_name = "my_device";
>     const char *service_key  = "password";

>     wifi_prov_security_t security = WIFI_PROV_SECURITY_1;
>     const char *pop = "abcd1234";

>     ESP_ERROR_CHECK( wifi_prov_mgr_start_provisioning(security, pop, service_name, service_key) );

如果收到有效的 Wi-Fi AP 凭据，且设备成功连接到该 AP 并获取了 IP，配网服务会自动结束。此外，调用 `wifi_prov_mgr_stop_provisioning()` 可以随时停止配网服务。

备注

如果设备使用提供的凭据无法连接，则它不再接受新的凭据，但在设备重新启动前，配网服务仍然会继续运行，并向客户端传递连接失败的信息。设备重新启动后配网状态将变为已配网，因为在 NVS 中找到了凭据，但除非出现与凭据匹配的可用 AP，否则设备仍然无法使用原凭据进行连接。可以通过重置 NVS 中的凭据或强制启动配网服务来解决这个问题，详情请参阅上文 检查配网状态。

### 等待配网完成

主应用程序通常会等待配网服务完成，然后反初始化管理器以释放资源，最后开始执行自己的逻辑。

有两种方法可以实现这一点，其中调用阻塞 `wifi_prov_mgr_wait()` 更为简单。

>     // 启动配网服务
>     ESP_ERROR_CHECK( wifi_prov_mgr_start_provisioning(security, pop, service_name, service_key) );

>     // 等待服务完成
>     wifi_prov_mgr_wait();

>     // 最后反初始化管理器
>     wifi_prov_mgr_deinit();

另一种方法是使用默认的事件循环处理程序捕获 `WIFI_PROV_EVENT` 并在事件 ID 为 `WIFI_PROV_END` 时调用 `wifi_prov_mgr_deinit()`：

>     static void event_handler(void* arg, esp_event_base_t event_base,
>                               int event_id, void* event_data)
>     {
>         if (event_base == WIFI_PROV_EVENT && event_id == WIFI_PROV_END) {
>             /* 配网完成后反初始化管理器 */
>             wifi_prov_mgr_deinit();
>         }
>     }

### 用户端实现

启动服务时，通过广播服务名称识别即将配网的设备。根据选择的传输方式，该服务名称为低功耗蓝牙设备的名称或 SoftAP SSID。

使用 SoftAP 传输方式时，为便于服务发现，必须在启动配网之前初始化 mDNS。在这种情况下，应使用主应用程序设置的主机名，并且在内部将服务类型设置为 `_esp_wifi_prov`。

使用低功耗蓝牙传输方式时，应使用 `wifi_prov_scheme_ble_set_service_uuid()` 设置一个自定义的 128 位 UUID。该 UUID 将包含在低功耗蓝牙广播中，并对应于提供配网端点作为 GATT 特征的主要服务。每个 GATT 特征都基于主要服务 UUID 形成，其中从第 0 个字节开始计数，第 12 和第 13 个字节为自动分配的不同字节。由于端点特征 UUID 自动分配，因此不应将其用于识别端点。客户端应用程序应通过读取每个特征的用户特征描述符 (`0x2901`) 来识别端点，该描述符包含特征的端点名称。例如，如果将服务 UUID 设置为 `55cc035e-fb27-4f80-be02-3c60828b7451`，每个端点特征将分配到一个类似于 `55cc____-fb27-4f80-be02-3c60828b7451` 的 UUID，其中第 12 和第 13 个字节具有唯一值。

连接设备后，可以通过以下方式识别与配网相关的 protocomm 端点：

配网服务提供的端点
端点名称 即低功耗蓝牙 + GATT 服务器 | URI 即 SoftAP + HTTP 服务器 + mDNS | 描述
--- | --- | ---
prov-session | http://<mdns-hostname>.local/prov-session | 用于建立会话的安全端点
prov-scan | http://wifi-prov.local/prov-scan | 用于启动 Wi-Fi 扫描和接收扫描结果的端点
prov-ctrl | http://wifi-prov.local/prov-ctrl | 用于控制 Wi-Fi 配网状态的端点
prov-config | http://<mdns-hostname>.local/prov-config | 用于在设备上配置 Wi-Fi 凭据的端点
proto-ver | http://<mdns-hostname>.local/proto-ver | 用于获取版本信息的端点

连接后，客户端应用程序可以立即从 `proto-ver` 端点获取版本或功能信息。所有与此端点的通信均未加密，因此在建立安全会话之前，可以检索相关必要信息，确保会话兼容。响应数据采用 JSON 格式，示例如下：`prov: { ver: v1.1, sec_ver: 1, sec_patch_ver: 0, cap: [no_pop] }, my_app: { ver: 1.345, cap: [cloud, local_ctrl] },....`。

其中，`prov` 标签提供以下信息：

> - 配网服务的版本 `ver`

> - 安全版本 `sec_ver`

> - 安全补丁版本 `sec_patch_ver` （默认为 0）

> - 功能 `cap`

目前仅支持 `no_pop` 功能，该功能表示服务无需用户提供所有权证明即可进行身份验证。任何与应用程序相关的版本或功能将由其他标签提供，如上述示例中的 `my_app`。使用 `wifi_prov_mgr_set_app_info()` 可以设置这些附加字段。

重要

建立会话时，客户端应依据 `sec_ver` 和 `sec_patch_ver` 字段来确定使用何种安全方案。

用户端应用程序需要根据所配置的安全方案实现签名握手，以建立和认证 protocomm 安全会话。当管理器配置为使用 protocomm security 0 时，则不需要实现签名握手。

关于安全握手和加密的详情，请参阅 统一配网。应用程序必须使用 protocomm/proto 中的 `.proto` 文件。`.proto` 文件定义了 `prov-session` 端点支持的 protobuf 消息结构。

建立会话后，以下 `wifi_config` 命令集可用于配置 Wi-Fi 凭据，这些命令会被序列化为 protobuf 消息，对应的 `.proto` 文件存放在 wifi_provisioning/proto 中。

> - `get_status` - 用于查询 Wi-Fi 连接状态。设备响应状态为连接中、已连接或已断开。如果状态为已断开，则还会包含断开原因。

> - `set_config` - 用于设置 Wi-Fi 连接凭据。

> - `apply_config` - 用于应用先前保存的凭据，即由 `set_config` 设置的凭据，并启动 Wi-Fi 站点。

建立会话后，客户端还可以从设备请求 Wi-Fi 扫描结果。返回结果为 AP SSID 的列表，按信号强度降序排序。由此，客户端应用程序可以在设备配网时显示附近的 AP，并且用户可以选择其中一个 SSID 并提供密码，然后使用上述 `wifi_config` 命令发送密码。`wifi_scan` 端点支持以下 protobuf 命令：

> - `scan_start` - 启动 Wi-Fi 扫描有多个选项，具体如下：

>   > - `blocking` （输入）- 如果参数为 true，则命令只会在扫描完成后返回。

>   > - `passive` （输入）- 如果参数为 true，则以被动模式启动扫描，扫描速度可能更慢。

>   > - `group_channels` （输入）- 该参数用于指定是否分组扫描。如果参数为 0，表示一次性扫描所有信道；如果参数为非零值，则表示分组扫描信道且参数值为每组中的信道数，每个连续组之间有 120 毫秒的延迟。分组扫描非常适用于使用 SoftAP 的传输模式，因为一次性扫描所有信道可能会导致 Wi-Fi 驱动没有足够时间发送信标，进而导致与部分站点断连。分组扫描时，管理器每扫描完一组信道，至少会等待 120 毫秒，确保驱动程序有足够时间发送信标。例如，假设共有 14 个 Wi-Fi 信道，将 `group_channels` 设置为 3 则将创建 5 个分组，每个分组包含 3 个信道，最后一个分组则为 14 除以 3 余下的 2 个信道。因此，扫描开始时，首先会扫描前 3 个信道，然后等待 120 毫秒，再继续扫描后 3 个信道，以此类推，直到扫描完 14 个信道。可以根据实际情况调整此参数，因为分组中信道数量过少可能会增加整体扫描时间，而信道数量过多则可能会导致连接再次断开。大多数情况下，将参数值设置为 4 即可。请注意，对于低功耗蓝牙等其他传输模式，可以放心将该参数设置为 0，从而在最短时间内完成扫描。

>   > - `period_ms` （输入）- 该扫描参数用于设置在每个信道上的等待时间。

> - `scan_status` - 可以返回扫描过程的状态：

>   > - `scan_finished` （输出）- 扫描完成时，该参数返回为 true。

>   > - `result_count` （输出）- 该参数返回到目前为止获取的结果总数。如果扫描仍在进行，该数字会不断更新。

> - `scan_result` - 用于获取扫描结果。即使扫描仍在进行，也可以调用此函数。

>   > - `start_index` （输入）- 从结果列表中获取条目的起始索引位置。

>   > - `count` （输入）- 从起始索引位置获取的条目数目。

>   > - `entries` （输出）- 返回条目的列表。每个条目包含 `ssid`、`channel` 和 `rssi` 信息。

客户端还可以使用 `wifi_ctrl` 端点来控制设备的配网状态。`wifi_ctrl` 端点支持的 protobuf 命令如下：

> - `ctrl_reset` - 仅在配网失败时，重置设备的内部状态机并清除已配置的凭据。

> - `ctrl_reprov` - 仅在设备已成功配网的前提下，设备需要重新配网获取新的凭据时，重置设备的内部状态机并清除已配置的凭据。

### 附加端点

如果用户想要根据自己的需求定制一些附加 protocomm 端点，可以通过两步完成。第一步是创建一个具有特定名称的端点，第二步是为该端点注册一个处理程序。关于端点处理程序的函数签名，请参阅 协议通信。自定义端点必须在初始化后、配网服务启动之前创建，但只能在配网服务启动后为该端点注册 protocomm 处理程序。注意在自定义端点处理程序的函数中，应使用堆来分配响应这些 protocomm 端点的内存，因为一旦传输层发送完毕，protocomm 层就会释放这部分内存。

>     wifi_prov_mgr_init(config);
>     wifi_prov_mgr_endpoint_create("custom-endpoint");
>     wifi_prov_mgr_start_provisioning(security, pop, service_name, service_key);
>     wifi_prov_mgr_endpoint_register("custom-endpoint", custom_ep_handler, custom_ep_data);

配网服务停止时，端点会自动取消注册。

在运行时，可以调用 `wifi_prov_mgr_endpoint_unregister()` 来手动停用某个端点。该函数也可以用于停用配网服务使用的内部端点。

### 何时以及如何停止配网服务？

当设备使用 `apply_config` 命令设置的 Wi-Fi 凭据成功连接，配网服务将默认停止，并在响应下一个 `get_status` 命令后自动关闭低功耗蓝牙或 softAP。如果设备没有收到 `get_status` 命令，配网服务将在超时 30 秒后停止。

如果设备因 SSID 或密码不正确等原因无法使用 Wi-Fi 凭据成功连接，配网服务将继续运行，并通过 `get_status` 命令持续响应为断连状态，并提供断连原因。此时设备不会再接受任何新的 Wi-Fi 凭据。除非强制启动配网服务或擦除 NVS 存储，这些凭据将保留。

可以调用 `wifi_prov_mgr_disable_auto_stop()` 来禁用默认设置。禁用后，只有在显式调用 `wifi_prov_mgr_stop_provisioning()` 之后，配网服务才会停止，且该函数会安排一个任务来停止配网服务，之后立即返回。配网服务将在一定延迟后停止，并触发 `WIFI_PROV_END` 事件。该延迟时间可以由 `wifi_prov_mgr_disable_auto_stop()` 的参数指定。

如果需要在成功建立 Wi-Fi 连接后的某个时间再停止配网服务，应用程序可以采取定制行为。例如，如果应用程序需要设备连接到某个云服务并获取另一组凭证，继而通过自定义 protocomm 端点交换凭证，那么成功完成此操作后，可以在 protocomm 处理程序中调用 `wifi_prov_mgr_stop_provisioning()` 来停止配网服务。设定适当的延迟时间可以确保 protocomm 处理程序的响应到达客户端应用程序后，才释放传输资源。

## 应用示例

- provisioning/wifi_prov_mgr 演示了如何使用 `wifi_provisioning` 组件将 ESP32-C61 配置为带有凭证的 Wi-Fi station，使用低功耗蓝牙作为默认传输方式。

## 配网工具

以下为各平台相应的配网应用程序，并附带源代码：

- Android:

    - Play Store 上的低功耗蓝牙配网应用程序。

    - Play Store 上的 SoftAP 配网应用程序。

    - GitHub 上的源代码： esp-idf-provisioning-android。

- iOS:

    - App Store 上的低功耗蓝牙配网应用程序。

    - App Store 上的 SoftAP 配网应用程序。

    - GitHub 上的源代码： esp-idf-provisioning-ios。

- Linux/MacOS/Windows: 基于 Python 的命令行工具 tools/esp_prov，可用于设备配网。

手机应用程序界面简洁，便于用户使用，而开发者可以使用命令行应用程序，便于调试。

## API 参考

### Header File

- components/wifi_provisioning/include/wifi_provisioning/manager.h

- This header file can be included with:

  >     #include "wifi_provisioning/manager.h"

- This header file is a part of the API provided by the `wifi_provisioning` component. To declare that your component depends on `wifi_provisioning`, add the following to your CMakeLists.txt:

  >     REQUIRES wifi_provisioning

  > or

  >     PRIV_REQUIRES wifi_provisioning

### Functions

esp_err_t wifi_prov_mgr_init(wifi_prov_mgr_config_t config)

  Initialize provisioning manager instance.

  Configures the manager and allocates internal resources

  Configuration specifies the provisioning scheme (transport) and event handlers

  Event WIFI_PROV_INIT is emitted right after initialization is complete

  参数:

    config -- [in] Configuration structure

  返回:

    - ESP_OK : Success

    - ESP_FAIL : Fail

void wifi_prov_mgr_deinit(void)

  Stop provisioning (if running) and release resource used by the manager.

  Event WIFI_PROV_DEINIT is emitted right after de-initialization is finished

  If provisioning service is still active when this API is called, it first stops the service, hence emitting WIFI_PROV_END, and then performs the de-initialization

esp_err_t wifi_prov_mgr_is_provisioned(bool *provisioned)

  Checks if device is provisioned.

  This checks if Wi-Fi credentials are present on the NVS

  The Wi-Fi credentials are assumed to be kept in the same NVS namespace as used by esp_wifi component

  If one were to call esp_wifi_set_config() directly instead of going through the provisioning process, this function will still yield true (i.e. device will be found to be provisioned)

  备注

  Calling wifi_prov_mgr_start_provisioning() automatically resets the provision state, irrespective of what the state was prior to making the call.

  参数:

    provisioned -- [out] True if provisioned, else false

  返回:

    - ESP_OK : Retrieved provision state successfully

    - ESP_FAIL : Wi-Fi not initialized

    - ESP_ERR_INVALID_ARG : Null argument supplied

bool wifi_prov_mgr_is_sm_idle(void)

  Checks whether the provisioning state machine is idle.

  返回:

    True if state machine is idle, else false

esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const void *wifi_prov_sec_params, const char *service_name, const char *service_key)

  Start provisioning service.

  This starts the provisioning service according to the scheme configured at the time of initialization. For scheme :

  - wifi_prov_scheme_ble : This starts protocomm_ble, which internally initializes BLE transport and starts GATT server for handling provisioning requests

  - wifi_prov_scheme_softap : This activates SoftAP mode of Wi-Fi and starts protocomm_httpd, which internally starts an HTTP server for handling provisioning requests (If mDNS is active it also starts advertising service with type _esp_wifi_prov._tcp)

  Event WIFI_PROV_START is emitted right after provisioning starts without failure

  备注

  This API will start provisioning service even if device is found to be already provisioned, i.e. wifi_prov_mgr_is_provisioned() yields true

  参数:

    - security -- [in] Specify which protocomm security scheme to use :

      - WIFI_PROV_SECURITY_0 : For no security

      - WIFI_PROV_SECURITY_1 : x25519 secure handshake for session establishment followed by AES-CTR encryption of provisioning messages

      - WIFI_PROV_SECURITY_2: SRP6a based authentication and key exchange followed by AES-GCM encryption/decryption of provisioning messages

    - wifi_prov_sec_params -- [in] Pointer to security params (NULL if not needed). This is not needed for protocomm security 0 This pointer should hold the struct of type wifi_prov_security1_params_t for protocomm security 1 and wifi_prov_security2_params_t for protocomm security 2 respectively. This pointer and its contents should be valid till the provisioning service is running and has not been stopped or de-inited.

    - service_name -- [in] Unique name of the service. This translates to:

      - Wi-Fi SSID when provisioning mode is softAP

      - Device name when provisioning mode is BLE

    - service_key -- [in] Key required by client to access the service (NULL if not needed). This translates to:

      - Wi-Fi password when provisioning mode is softAP

      - ignored when provisioning mode is BLE

  返回:

    - ESP_OK : Provisioning started successfully

    - ESP_FAIL : Failed to start provisioning service

    - ESP_ERR_INVALID_STATE : Provisioning manager not initialized or already started

void wifi_prov_mgr_stop_provisioning(void)

  Stop provisioning service.

  If provisioning service is active, this API will initiate a process to stop the service and return. Once the service actually stops, the event WIFI_PROV_END will be emitted.

  If wifi_prov_mgr_deinit() is called without calling this API first, it will automatically stop the provisioning service and emit the WIFI_PROV_END, followed by WIFI_PROV_DEINIT, before returning.

  This API will generally be used along with wifi_prov_mgr_disable_auto_stop() in the scenario when the main application has registered its own endpoints, and wishes that the provisioning service is stopped only when some protocomm command from the client side application is received.

  Calling this API inside an endpoint handler, with sufficient cleanup_delay, will allow the response / acknowledgment to be sent successfully before the underlying protocomm service is stopped.

  Cleaup_delay is set when calling wifi_prov_mgr_disable_auto_stop(). If not specified, it defaults to 1000ms.

  For straightforward cases, using this API is usually not necessary as provisioning is stopped automatically once WIFI_PROV_CRED_SUCCESS is emitted. Stopping is delayed (maximum 30 seconds) thus allowing the client side application to query for Wi-Fi state, i.e. after receiving the first query and sending `Wi-Fi state connected` response the service is stopped immediately.

void wifi_prov_mgr_wait(void)

  Wait for provisioning service to finish.

  Calling this API will block until provisioning service is stopped i.e. till event WIFI_PROV_END is emitted.

  This will not block if provisioning is not started or not initialized.

esp_err_t wifi_prov_mgr_disable_auto_stop(uint32_t cleanup_delay)

  Disable auto stopping of provisioning service upon completion.

  By default, once provisioning is complete, the provisioning service is automatically stopped, and all endpoints (along with those registered by main application) are deactivated.

  This API is useful in the case when main application wishes to close provisioning service only after it receives some protocomm command from the client side app. For example, after connecting to Wi-Fi, the device may want to connect to the cloud, and only once that is successfully, the device is said to be fully configured. But, then it is upto the main application to explicitly call wifi_prov_mgr_stop_provisioning() later when the device is fully configured and the provisioning service is no longer required.

  备注

  This must be called before executing wifi_prov_mgr_start_provisioning()

  参数:

    cleanup_delay -- [in] Sets the delay after which the actual cleanup of transport related resources is done after a call to wifi_prov_mgr_stop_provisioning() returns. Minimum allowed value is 100ms. If not specified, this will default to 1000ms.

  返回:

    - ESP_OK : Success

    - ESP_ERR_INVALID_STATE : Manager not initialized or provisioning service already started

esp_err_t wifi_prov_mgr_set_app_info(const char *label, const char *version, const char **capabilities, size_t total_capabilities)

  Set application version and capabilities in the JSON data returned by proto-ver endpoint.

  This function can be called multiple times, to specify information about the various application specific services running on the device, identified by unique labels.

  The provisioning service itself registers an entry in the JSON data, by the label "prov", containing only provisioning service version and capabilities. Application services should use a label other than "prov" so as not to overwrite this.

  备注

  This must be called before executing wifi_prov_mgr_start_provisioning()

  参数:

    - label -- [in] String indicating the application name.

    - version -- [in] String indicating the application version. There is no constraint on format.

    - capabilities -- [in] Array of strings with capabilities. These could be used by the client side app to know the application registered endpoint capabilities

    - total_capabilities -- [in] Size of capabilities array

  返回:

    - ESP_OK : Success

    - ESP_ERR_INVALID_STATE : Manager not initialized or provisioning service already started

    - ESP_ERR_NO_MEM : Failed to allocate memory for version string

    - ESP_ERR_INVALID_ARG : Null argument

esp_err_t wifi_prov_mgr_endpoint_create(const char *ep_name)

  Create an additional endpoint and allocate internal resources for it.

  This API is to be called by the application if it wants to create an additional endpoint. All additional endpoints will be assigned UUIDs starting from 0xFF54 and so on in the order of execution.

  protocomm handler for the created endpoint is to be registered later using wifi_prov_mgr_endpoint_register() after provisioning has started.

  备注

  This API can only be called BEFORE provisioning is started

  备注

  Additional endpoints can be used for configuring client provided parameters other than Wi-Fi credentials, that are necessary for the main application and hence must be set prior to starting the application

  备注

  After session establishment, the additional endpoints must be targeted first by the client side application before sending Wi-Fi configuration, because once Wi-Fi configuration finishes the provisioning service is stopped and hence all endpoints are unregistered

  参数:

    ep_name -- [in] unique name of the endpoint

  返回:

    - ESP_OK : Success

    - ESP_FAIL : Failure

esp_err_t wifi_prov_mgr_endpoint_register(const char *ep_name, protocomm_req_handler_t handler, void *user_ctx)

  Register a handler for the previously created endpoint.

  This API can be called by the application to register a protocomm handler to any endpoint that was created using wifi_prov_mgr_endpoint_create().

  备注

  This API can only be called AFTER provisioning has started

  备注

  Additional endpoints can be used for configuring client provided parameters other than Wi-Fi credentials, that are necessary for the main application and hence must be set prior to starting the application

  备注

  After session establishment, the additional endpoints must be targeted first by the client side application before sending Wi-Fi configuration, because once Wi-Fi configuration finishes the provisioning service is stopped and hence all endpoints are unregistered

  参数:

    - ep_name -- [in] Name of the endpoint

    - handler -- [in] Endpoint handler function

    - user_ctx -- [in] User data

  返回:

    - ESP_OK : Success

    - ESP_FAIL : Failure

void wifi_prov_mgr_endpoint_unregister(const char *ep_name)

  Unregister the handler for an endpoint.

  This API can be called if the application wants to selectively unregister the handler of an endpoint while the provisioning is still in progress.

  All the endpoint handlers are unregistered automatically when the provisioning stops.

  参数:

    ep_name -- [in] Name of the endpoint

esp_err_t wifi_prov_mgr_get_wifi_state(wifi_prov_sta_state_t *state)

  Get state of Wi-Fi Station during provisioning.

  参数:

    state -- [out] Pointer to wifi_prov_sta_state_t variable to be filled

  返回:

    - ESP_OK : Successfully retrieved Wi-Fi state

    - ESP_FAIL : Provisioning app not running

esp_err_t wifi_prov_mgr_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t *reason)

  Get reason code in case of Wi-Fi station disconnection during provisioning.

  参数:

    reason -- [out] Pointer to wifi_prov_sta_fail_reason_t variable to be filled

  返回:

    - ESP_OK : Successfully retrieved Wi-Fi disconnect reason

    - ESP_FAIL : Provisioning app not running

esp_err_t wifi_prov_mgr_configure_sta(wifi_config_t *wifi_cfg)

  Runs Wi-Fi as Station with the supplied configuration.

  Configures the Wi-Fi station mode to connect to the AP with SSID and password specified in config structure and sets Wi-Fi to run as station.

  This is automatically called by provisioning service upon receiving new credentials.

  If credentials are to be supplied to the manager via a different mode other than through protocomm, then this API needs to be called.

  Event WIFI_PROV_CRED_RECV is emitted after credentials have been applied and Wi-Fi station started

  参数:

    wifi_cfg -- [in] Pointer to Wi-Fi configuration structure

  返回:

    - ESP_OK : Wi-Fi configured and started successfully

    - ESP_FAIL : Failed to set configuration

esp_err_t wifi_prov_mgr_reset_provisioning(void)

  Reset Wi-Fi provisioning config.

  Calling this API will restore WiFi stack persistent settings to default values.

  返回:

    - ESP_OK : Reset provisioning config successfully

    - ESP_FAIL : Failed to reset provisioning config

esp_err_t wifi_prov_mgr_reset_sm_state_on_failure(void)

  Reset internal state machine and clear provisioned credentials.

  This API should be used to restart provisioning ONLY in the case of provisioning failures without rebooting the device.

  返回:

    - ESP_OK : Reset provisioning state machine successfully

    - ESP_FAIL : Failed to reset provisioning state machine

    - ESP_ERR_INVALID_STATE : Manager not initialized

esp_err_t wifi_prov_mgr_reset_sm_state_for_reprovision(void)

  Reset internal state machine and clear provisioned credentials.

  This API can be used to restart provisioning ONLY in case the device is to be provisioned again for new credentials after a previous successful provisioning without rebooting the device.

  备注

  This API can be used only if provisioning auto-stop has been disabled using wifi_prov_mgr_disable_auto_stop()

  返回:

    - ESP_OK : Reset provisioning state machine successfully

    - ESP_FAIL : Failed to reset provisioning state machine

    - ESP_ERR_INVALID_STATE : Manager not initialized

### Structures

struct wifi_prov_event_handler_t

  Event handler that is used by the manager while provisioning service is active.

  Public Members

  wifi_prov_cb_func_t event_cb

    Callback function to be executed on provisioning events

  void *user_data

    User context data to pass as parameter to callback function

struct wifi_prov_conn_cfg_t

  Structure holding the configuration related to Wi-Fi provisioning.

  Public Members

  uint32_t wifi_conn_attempts

    Maximum number of allowed connection attempts for Wi-Fi. If value 0 same as legacy behavior of infinite connection attempts.

struct wifi_prov_scheme

  Structure for specifying the provisioning scheme to be followed by the manager.

  备注

  Ready to use schemes are available:

  - wifi_prov_scheme_ble : for provisioning over BLE transport + GATT server

  - wifi_prov_scheme_softap : for provisioning over SoftAP transport + HTTP server

  - wifi_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging)

  Public Members

  esp_err_t (*prov_start)(protocomm_t *pc, void *config)

    Function which is to be called by the manager when it is to start the provisioning service associated with a protocomm instance and a scheme specific configuration

  esp_err_t (*prov_stop)(protocomm_t *pc)

    Function which is to be called by the manager to stop the provisioning service previously associated with a protocomm instance

  void *(*new_config)(void)

    Function which is to be called by the manager to generate a new configuration for the provisioning service, that is to be passed to prov_start()

  void (*delete_config)(void *config)

    Function which is to be called by the manager to delete a configuration generated using new_config()

  esp_err_t (*set_config_service)(void *config, const char *service_name, const char *service_key)

    Function which is to be called by the manager to set the service name and key values in the configuration structure

  esp_err_t (*set_config_endpoint)(void *config, const char *endpoint_name, uint16_t uuid)

    Function which is to be called by the manager to set a protocomm endpoint with an identifying name and UUID in the configuration structure

  wifi_mode_t wifi_mode

    Sets mode of operation of Wi-Fi during provisioning This is set to :

    - WIFI_MODE_APSTA for SoftAP transport

    - WIFI_MODE_STA for BLE transport

struct wifi_prov_mgr_config_t

  Structure for specifying the manager configuration.

  Public Members

  wifi_prov_scheme_t scheme

    Provisioning scheme to use. Following schemes are already available:

    - wifi_prov_scheme_ble : for provisioning over BLE transport + GATT server

    - wifi_prov_scheme_softap : for provisioning over SoftAP transport + HTTP server + mDNS (optional)

    - wifi_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging)

  wifi_prov_event_handler_t scheme_event_handler

    Event handler required by the scheme for incorporating scheme specific behavior while provisioning manager is running. Various options may be provided by the scheme for setting this field. Use WIFI_PROV_EVENT_HANDLER_NONE when not used. When using scheme wifi_prov_scheme_ble, the following options are available:

    - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM

    - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE

    - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT

  wifi_prov_event_handler_t app_event_handler

    Event handler that can be set for the purpose of incorporating application specific behavior. Use WIFI_PROV_EVENT_HANDLER_NONE when not used.

  wifi_prov_conn_cfg_t wifi_prov_conn_cfg

    This config holds the Wi-Fi provisioning related configurations.

### Macros

WIFI_PROV_EVENT_HANDLER_NONE

  Event handler can be set to none if not used.

### Type Definitions

typedef void (*wifi_prov_cb_func_t)(void *user_data, wifi_prov_cb_event_t event, void *event_data)

typedef struct wifi_prov_scheme wifi_prov_scheme_t

  Structure for specifying the provisioning scheme to be followed by the manager.

  备注

  Ready to use schemes are available:

  - wifi_prov_scheme_ble : for provisioning over BLE transport + GATT server

  - wifi_prov_scheme_softap : for provisioning over SoftAP transport + HTTP server

  - wifi_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging)

typedef enum wifi_prov_security wifi_prov_security_t

  Security modes supported by the Provisioning Manager.

  These are same as the security modes provided by protocomm

typedef const char wifi_prov_security1_params_t

  Security 1 params structure This needs to be passed when using WIFI_PROV_SECURITY_1.

typedef protocomm_security2_params_t wifi_prov_security2_params_t

  Security 2 params structure This needs to be passed when using WIFI_PROV_SECURITY_2.

### Enumerations

enum wifi_prov_cb_event_t

  Events generated by manager.

  These events are generated in order of declaration and, for the stretch of time between initialization and de-initialization of the manager, each event is signaled only once

  Values:

  enumerator WIFI_PROV_INIT

    Emitted when the manager is initialized

  enumerator WIFI_PROV_START

    Indicates that provisioning has started

  enumerator WIFI_PROV_SET_STA_CONFIG

    Emitted before accepting the wifi credentials to set the wifi configurations according to requirement. NOTE - In this case event_data shall be populated with a pointer to `wifi_config_t`.

  enumerator WIFI_PROV_CRED_RECV

    Emitted when Wi-Fi AP credentials are received via `protocomm` endpoint `wifi_config`. The event data in this case is a pointer to the corresponding `wifi_sta_config_t` structure

  enumerator WIFI_PROV_CRED_FAIL

    Emitted when device fails to connect to the AP of which the credentials were received earlier on event `WIFI_PROV_CRED_RECV`. The event data in this case is a pointer to the disconnection reason code with type `wifi_prov_sta_fail_reason_t`

  enumerator WIFI_PROV_CRED_SUCCESS

    Emitted when device successfully connects to the AP of which the credentials were received earlier on event `WIFI_PROV_CRED_RECV`

  enumerator WIFI_PROV_END

    Signals that provisioning service has stopped

  enumerator WIFI_PROV_DEINIT

    Signals that manager has been de-initialized

enum wifi_prov_security

  Security modes supported by the Provisioning Manager.

  These are same as the security modes provided by protocomm

  Values:

  enumerator WIFI_PROV_SECURITY_0

    No security (plain-text communication)

  enumerator WIFI_PROV_SECURITY_1

    This secure communication mode consists of X25519 key exchange

    - proof of possession (pop) based authentication

    - AES-CTR encryption

  enumerator WIFI_PROV_SECURITY_2

    This secure communication mode consists of SRP6a based authentication and key exchange

    - AES-GCM encryption/decryption

### Header File

- components/wifi_provisioning/include/wifi_provisioning/scheme_ble.h

- This header file can be included with:

  >     #include "wifi_provisioning/scheme_ble.h"

- This header file is a part of the API provided by the `wifi_provisioning` component. To declare that your component depends on `wifi_provisioning`, add the following to your CMakeLists.txt:

  >     REQUIRES wifi_provisioning

  > or

  >     PRIV_REQUIRES wifi_provisioning

### Functions

void wifi_prov_scheme_ble_event_cb_free_btdm(void *user_data, wifi_prov_cb_event_t event, void *event_data)

void wifi_prov_scheme_ble_event_cb_free_ble(void *user_data, wifi_prov_cb_event_t event, void *event_data)

void wifi_prov_scheme_ble_event_cb_free_bt(void *user_data, wifi_prov_cb_event_t event, void *event_data)

esp_err_t wifi_prov_scheme_ble_set_service_uuid(uint8_t *uuid128)

  Set the 128 bit GATT service UUID used for provisioning.

  This API is used to override the default 128 bit provisioning service UUID, which is 0000ffff-0000-1000-8000-00805f9b34fb.

  This must be called before starting provisioning, i.e. before making a call to wifi_prov_mgr_start_provisioning(), otherwise the default UUID will be used.

  备注

  The data being pointed to by the argument must be valid at least till provisioning is started. Upon start, the manager will store an internal copy of this UUID, and this data can be freed or invalidated afterwards.

  参数:

    uuid128 -- [in] A custom 128 bit UUID

  返回:

    - ESP_OK : Success

    - ESP_ERR_INVALID_ARG : Null argument

esp_err_t wifi_prov_mgr_keep_ble_on(uint8_t is_on_after_ble_stop)

  Keep the BLE on after provisioning.

  This API is used to specify whether the BLE should remain on after the provisioning process has stopped.

  This must be called before starting provisioning, i.e. before making a call to wifi_prov_mgr_start_provisioning(), otherwise the default behavior will be used.

  备注

  The value being pointed to by the argument must be valid at least until provisioning is started. Upon start, the manager will store an internal copy of this value, and this data can be freed or invalidated afterwards.

  参数:

    is_on_after_ble_stop -- [in] A boolean indicating if BLE should remain on after provisioning

  返回:

    - ESP_OK : Success

    - ESP_ERR_INVALID_ARG : Null argument

esp_err_t wifi_prov_scheme_ble_set_mfg_data(uint8_t *mfg_data, ssize_t mfg_data_len)

  Set manufacturer specific data in scan response.

  This must be called before starting provisioning, i.e. before making a call to wifi_prov_mgr_start_provisioning().

  备注

  It is important to understand that length of custom manufacturer data should be within limits. The manufacturer data goes into scan response along with BLE device name. By default, BLE device name length is of 11 Bytes, however it can vary as per application use case. So, one has to honour the scan response data size limits i.e. (mfg_data_len + 2) < 31 - (device_name_length + 2 ). If the mfg_data length exceeds this limit, the length will be truncated.

  参数:

    - mfg_data -- [in] Custom manufacturer data

    - mfg_data_len -- [in] Manufacturer data length

  返回:

    - ESP_OK : Success

    - ESP_ERR_INVALID_ARG : Null argument

esp_err_t wifi_prov_scheme_ble_set_random_addr(const uint8_t *rand_addr)

  Set Bluetooth Random address.

  This must be called before starting provisioning, i.e. before making a call to wifi_prov_mgr_start_provisioning().

  This API can be used in cases where a new identity address is to be used during provisioning. This will result in this device being treated as a new device by remote devices.

  This API is only to be called to set random address. Re-invoking this API after provisioning is started will have no effect.

  备注

  This API will change the existing BD address for the device. The address once set will remain unchanged until BLE stack tear down happens when wifi_prov_mgr_deinit is invoked.

  参数:

    rand_addr -- [in] The static random address to be set of length 6 bytes.

  返回:

    - ESP_OK : Success

    - ESP_ERR_INVALID_ARG : Null argument

### Macros

WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM

WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE

WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT

### Header File

- components/wifi_provisioning/include/wifi_provisioning/scheme_softap.h

- This header file can be included with:

  >     #include "wifi_provisioning/scheme_softap.h"

- This header file is a part of the API provided by the `wifi_provisioning` component. To declare that your component depends on `wifi_provisioning`, add the following to your CMakeLists.txt:

  >     REQUIRES wifi_provisioning

  > or

  >     PRIV_REQUIRES wifi_provisioning

### Functions

void wifi_prov_scheme_softap_set_httpd_handle(void *handle)

  Provide HTTPD Server handle externally.

  Useful in cases wherein applications need the webserver for some different operations, and do not want the wifi provisioning component to start/stop a new instance.

  备注

  This API should be called before wifi_prov_mgr_start_provisioning()

  参数:

    handle -- [in] Handle to HTTPD server instance

### Header File

- components/wifi_provisioning/include/wifi_provisioning/scheme_console.h

- This header file can be included with:

  >     #include "wifi_provisioning/scheme_console.h"

- This header file is a part of the API provided by the `wifi_provisioning` component. To declare that your component depends on `wifi_provisioning`, add the following to your CMakeLists.txt:

  >     REQUIRES wifi_provisioning

  > or

  >     PRIV_REQUIRES wifi_provisioning

### Header File

- components/wifi_provisioning/include/wifi_provisioning/wifi_config.h

- This header file can be included with:

  >     #include "wifi_provisioning/wifi_config.h"

- This header file is a part of the API provided by the `wifi_provisioning` component. To declare that your component depends on `wifi_provisioning`, add the following to your CMakeLists.txt:

  >     REQUIRES wifi_provisioning

  > or

  >     PRIV_REQUIRES wifi_provisioning

### Functions

esp_err_t wifi_prov_config_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, void *priv_data)

  Handler for receiving and responding to requests from master.

  This is to be registered as the `wifi_config` endpoint handler (protocomm `protocomm_req_handler_t`) using `protocomm_add_endpoint()`

### Structures

struct wifi_prov_sta_conn_info_t

  WiFi STA connected status information.

  Public Members

  char ip_addr[IP4ADDR_STRLEN_MAX]

    IP Address received by station

  char bssid[6]

    BSSID of the AP to which connection was estalished

  char ssid[33]

    SSID of the to which connection was estalished

  uint8_t channel

    Channel of the AP

  uint8_t auth_mode

    Authorization mode of the AP

struct wifi_prov_sta_connecting_info_t

  WiFi STA connecting status information.

  Public Members

  uint32_t attempts_remaining

    Number of Wi-Fi connection attempts remaining

struct wifi_prov_config_get_data_t

  WiFi status data to be sent in response to `get_status` request from master.

  Public Members

  wifi_prov_sta_state_t wifi_state

    WiFi state of the station

  wifi_prov_sta_fail_reason_t fail_reason

    Reason for disconnection (valid only when `wifi_state` is `WIFI_STATION_DISCONNECTED`)

  wifi_prov_sta_conn_info_t conn_info

    Connection information (valid only when `wifi_state` is `WIFI_STATION_CONNECTED`)

  wifi_prov_sta_connecting_info_t connecting_info

    Connecting information (valid only when `wifi_state` is `WIFI_STATION_CONNECTING`)

struct wifi_prov_config_set_data_t

  WiFi config data received by slave during `set_config` request from master.

  Public Members

  char ssid[33]

    SSID of the AP to which the slave is to be connected

  char password[64]

    Password of the AP

  char bssid[6]

    BSSID of the AP

  uint8_t channel

    Channel of the AP

struct wifi_prov_config_handlers

  Internal handlers for receiving and responding to protocomm requests from master.

  This is to be passed as priv_data for protocomm request handler (refer to `wifi_prov_config_data_handler()`) when calling `protocomm_add_endpoint()`.

  Public Members

  esp_err_t (*get_status_handler)(wifi_prov_config_get_data_t *resp_data, wifi_prov_ctx_t **ctx)

    Handler function called when connection status of the slave (in WiFi station mode) is requested

  esp_err_t (*set_config_handler)(const wifi_prov_config_set_data_t *req_data, wifi_prov_ctx_t **ctx)

    Handler function called when WiFi connection configuration (eg. AP SSID, password, etc.) of the slave (in WiFi station mode) is to be set to user provided values

  esp_err_t (*apply_config_handler)(wifi_prov_ctx_t **ctx)

    Handler function for applying the configuration that was set in `set_config_handler`. After applying the station may get connected to the AP or may fail to connect. The slave must be ready to convey the updated connection status information when `get_status_handler` is invoked again by the master.

  wifi_prov_ctx_t *ctx

    Context pointer to be passed to above handler functions upon invocation

### Type Definitions

typedef struct wifi_prov_ctx wifi_prov_ctx_t

  Type of context data passed to each get/set/apply handler function set in `wifi_prov_config_handlers` structure.

  This is passed as an opaque pointer, thereby allowing it be defined later in application code as per requirements.

typedef struct wifi_prov_config_handlers wifi_prov_config_handlers_t

  Internal handlers for receiving and responding to protocomm requests from master.

  This is to be passed as priv_data for protocomm request handler (refer to `wifi_prov_config_data_handler()`) when calling `protocomm_add_endpoint()`.

### Enumerations

enum wifi_prov_sta_state_t

  WiFi STA status for conveying back to the provisioning master.

  Values:

  enumerator WIFI_PROV_STA_CONNECTING
  enumerator WIFI_PROV_STA_CONNECTED
  enumerator WIFI_PROV_STA_DISCONNECTED
  enumerator WIFI_PROV_STA_CONN_ATTEMPT_FAILED

enum wifi_prov_sta_fail_reason_t

  WiFi STA connection fail reason.

  Values:

  enumerator WIFI_PROV_STA_AUTH_ERROR
  enumerator WIFI_PROV_STA_AP_NOT_FOUND

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。