目录
ESP-ADF wifi_service子模块esp_wifi_setting数据交互函数详解
版本信息: v2.7-65-gcf908721
本章节分析的源码位于
/components/wifi_service/src/esp_wifi_setting.c
文件
WiFi配网数据交互函数分析
ESP-ADF的esp_wifi_setting模块提供了一组函数用于实现配网过程中的数据交互,包括设置通知句柄、通知WiFi信息以及管理用户数据。这些函数构成了配网模块与WiFi服务进行数据交换的桥梁,是实现不同配网方式与WiFi服务集成的关键。
esp_wifi_setting_register_notify_handle
esp_wifi_setting_register_notify_handle
函数用于注册配网结果通知的句柄。下面是其源码实现:
/**
* @brief 注册通知句柄
*
* 该函数完成以下主要操作:
* 1. 检查配网句柄是否有效
* 2. 设置通知句柄,通常是WiFi服务句柄
*
* 通知句柄是配网模块与WiFi服务通信的桥梁,当配网成功获取到WiFi信息后,
* 会通过该句柄将信息通知给WiFi服务。通常,这个句柄就是WiFi服务的
* periph_service_handle_t实例。
*
* @param handle 配网接口句柄,由esp_wifi_setting_create创建
* @param on_handle 通知句柄,通常是WiFi服务句柄
* @return esp_err_t 成功返回ESP_OK,失败返回错误码
*/
esp_err_t esp_wifi_setting_register_notify_handle(esp_wifi_setting_handle_t handle, void *on_handle)
{
AUDIO_NULL_CHECK(TAG, handle, return ESP_ERR_INVALID_ARG);
handle->notify_handle = on_handle;
return ESP_OK;
}
下面的时序图展示了esp_wifi_setting_register_notify_handle
函数的执行流程:
这个函数非常简单,但却是实现配网结果回传的关键。通过它,配网模块能够知道应该把配网结果通知给谁,从而建立起配网模块与WiFi服务之间的通信通道。
esp_wifi_setting_info_notify
esp_wifi_setting_info_notify
函数用于将获取到的WiFi信息通知给WiFi服务。下面是其源码实现:
/**
* @brief 通知WiFi信息
*
* 该函数完成以下主要操作:
* 1. 检查配网句柄是否有效
* 2. 调用wifi_service_update_sta_info函数将WiFi信息更新到WiFi服务
*
* 当配网过程成功获取到WiFi的SSID和密码后,配网实现应调用此函数
* 将信息传递给WiFi服务。WiFi服务收到信息后,会尝试连接获取到的WiFi网络。
*
* @param handle 配网接口句柄,由esp_wifi_setting_create创建
* @param info WiFi配置信息,包含SSID和密码
* @return esp_err_t 成功返回ESP_OK,失败返回错误码
*/
esp_err_t esp_wifi_setting_info_notify(esp_wifi_setting_handle_t handle, wifi_config_t *info)
{
AUDIO_NULL_CHECK(TAG, handle, return ESP_ERR_INVALID_ARG);
wifi_service_update_sta_info((periph_service_handle_t)handle->notify_handle, info);
return ESP_OK;
}
下面的时序图展示了esp_wifi_setting_info_notify
函数的执行流程:
这个函数是配网过程中最关键的一步,它将配网成功获取到的WiFi信息传递给WiFi服务,触发WiFi服务尝试连接新的WiFi网络。通常,配网实现在成功获取到WiFi信息后会立即调用此函数。
esp_wifi_setting_set_data
esp_wifi_setting_set_data
函数用于设置配网实现的用户自定义数据。下面是其源码实现:
/**
* @brief 设置用户数据
*
* 该函数完成以下主要操作:
* 1. 检查配网句柄是否有效
* 2. 设置用户自定义数据指针
*
* 用户数据是一种扩展机制,允许配网实现存储自己的配置和状态数据。
* 这个函数只存储数据指针,不复制数据内容,也不负责数据的生命周期管理。
* 调用者应确保数据在配网过程中保持有效,并在适当时机释放内存。
*
* @param handle 配网接口句柄,由esp_wifi_setting_create创建
* @param data 用户自定义数据指针
* @return esp_err_t 成功返回ESP_OK,失败返回错误码
*/
esp_err_t esp_wifi_setting_set_data(esp_wifi_setting_handle_t handle, void *data)
{
AUDIO_NULL_CHECK(TAG, handle, return ESP_ERR_INVALID_ARG);
handle->user_data = data;
return ESP_OK;
}
下面的时序图展示了esp_wifi_setting_set_data
函数的执行流程:
esp_wifi_setting_set_data
函数提供了一种简单的机制,允许配网实现存储自己的配置和状态数据。这个函数通常在创建配网接口实例后立即调用,用于设置配网所需的配置参数。
esp_wifi_setting_get_data
esp_wifi_setting_get_data
函数用于获取之前设置的用户自定义数据。下面是其源码实现:
/**
* @brief 获取用户数据
*
* 该函数完成以下主要操作:
* 1. 检查配网句柄是否有效
* 2. 返回之前通过esp_wifi_setting_set_data设置的用户数据指针
*
* 此函数通常在配网实现的start、stop和teardown回调函数中调用,
* 用于获取配置参数和状态数据。如果未设置用户数据,或者句柄无效,
* 函数将返回NULL。
*
* @param handle 配网接口句柄,由esp_wifi_setting_create创建
* @return void* 成功返回用户数据指针,失败返回NULL
*/
void *esp_wifi_setting_get_data(esp_wifi_setting_handle_t handle)
{
AUDIO_NULL_CHECK(TAG, handle, return NULL);
return handle->user_data;
}
下面的时序图展示了esp_wifi_setting_get_data
函数的执行流程:
esp_wifi_setting_get_data
函数与esp_wifi_setting_set_data
函数配对使用,允许配网实现在不同的回调函数中访问同一份配置数据。这种机制简化了配网实现的状态管理,避免了使用全局变量。
内部实现机制
从上面的分析可以看出,WiFi配网数据交互函数采用了以下设计模式:
- 简单直接:所有函数实现都非常简洁,直接操作配网句柄的内部字段
- 指针传递:使用指针传递数据,避免了大量数据复制
- 桥接模式:通过通知句柄建立配网模块与WiFi服务的桥接
- 状态共享:通过用户数据机制实现配网实现内部状态的共享
这种设计有以下几个优点:
- 高效轻量:没有复杂的数据结构和算法,保持了接口的轻量级
- 灵活扩展:用户数据机制支持不同配网实现的特定需求
- 低耦合:通过统一接口实现不同模块间的通信,降低了耦合度
- 简化实现:简洁的接口设计降低了实现的复杂性
数据流向
在ESP-ADF的配网架构中,数据交互函数实现了以下数据流向:
- 配置数据流:应用程序 → esp_wifi_setting_set_data → 配网实现
- WiFi信息流:配网实现 → esp_wifi_setting_info_notify → WiFi服务
- 通知机制:WiFi服务 → esp_wifi_setting_register_notify_handle → 配网实现
内部调用关系
数据交互函数与其他模块的调用关系如下:
应用示例
下面是一个使用配网数据交互函数的完整示例,展示了这些函数在实际应用中的使用方式:
#include "esp_log.h"
#include "esp_wifi_setting.h"
#include "audio_mem.h"
#include "wifi_service.h"
static const char *TAG = "CUSTOM_CONFIG";
// 自定义配置结构体
typedef struct {
int timeout_sec; // 配网超时时间(秒)
bool led_feedback; // 是否使用LED指示配网状态
int channel; // WiFi信道
char *device_name; // 设备名称,用于识别
} custom_config_t;
// 配网实现函数
static esp_err_t custom_config_start(esp_wifi_setting_handle_t handle)
{
// 获取配置数据
custom_config_t *cfg = (custom_config_t *)esp_wifi_setting_get_data(handle);
if (cfg == NULL) {
ESP_LOGE(TAG, "获取配置数据失败");
return ESP_FAIL;
}
ESP_LOGI(TAG, "启动自定义配网,超时时间: %d秒,设备名称: %s",
cfg->timeout_sec, cfg->device_name ? cfg->device_name : "未命名");
// 启动实际配网过程...
return ESP_OK;
}
static esp_err_t custom_config_stop(esp_wifi_setting_handle_t handle)
{
// 获取配置数据
custom_config_t *cfg = (custom_config_t *)esp_wifi_setting_get_data(handle);
if (cfg && cfg->led_feedback) {
// 关闭LED指示灯
ESP_LOGI(TAG, "关闭配网状态LED指示");
}
// 停止配网过程...
return ESP_OK;
}
// 模拟获取WiFi信息的函数
static void on_got_wifi_info(esp_wifi_setting_handle_t handle, const char *ssid, const char *password)
{
ESP_LOGI(TAG, "获取到WiFi信息 - SSID: %s", ssid);
// 构造WiFi配置
wifi_config_t wifi_config = {0};
strlcpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
if (password) {
strlcpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password));
}
// 通知WiFi服务
esp_err_t ret = esp_wifi_setting_info_notify(handle, &wifi_config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "通知WiFi信息失败: %d", ret);
}
}
// 创建自定义配网实例
esp_wifi_setting_handle_t custom_config_create(void)
{
// 创建配网接口
esp_wifi_setting_handle_t handle = esp_wifi_setting_create("custom_config");
if (handle == NULL) {
ESP_LOGE(TAG, "创建配网接口失败");
return NULL;
}
// 注册配网实现函数
esp_wifi_setting_register_function(handle, custom_config_start, custom_config_stop, NULL);
// 创建并设置配置数据
custom_config_t *cfg = audio_calloc(1, sizeof(custom_config_t));
if (cfg == NULL) {
ESP_LOGE(TAG, "分配配置内存失败");
esp_wifi_setting_destroy(handle);
return NULL;
}
// 设置默认配置
cfg->timeout_sec = 120;
cfg->led_feedback = true;
cfg->channel = 6;
cfg->device_name = audio_strdup("ESP-Audio");
// 设置用户数据
esp_wifi_setting_set_data(handle, cfg);
return handle;
}
// 销毁自定义配网实例
void custom_config_destroy(esp_wifi_setting_handle_t handle)
{
if (handle == NULL) {
return;
}
// 释放用户数据
custom_config_t *cfg = (custom_config_t *)esp_wifi_setting_get_data(handle);
if (cfg) {
// 释放字符串内存
if (cfg->device_name) {
audio_free(cfg->device_name);
}
// 释放配置结构体
audio_free(cfg);
}
// 销毁配网接口
esp_wifi_setting_destroy(handle);
}
// 应用程序示例
void app_main(void)
{
// 创建WiFi服务
wifi_service_config_t wifi_cfg = WIFI_SERVICE_DEFAULT_CONFIG();
periph_service_handle_t wifi_service = wifi_service_create(&wifi_cfg);
// 创建自定义配网
esp_wifi_setting_handle_t custom_handle = custom_config_create();
// 注册配网通知句柄
esp_wifi_setting_register_notify_handle(custom_handle, wifi_service);
// 注册配网到WiFi服务
int setting_index = 0;
wifi_service_register_setting_handle(wifi_service, custom_handle, &setting_index);
// 启动配网
wifi_service_setting_start(wifi_service, setting_index);
// 模拟配网获取到WiFi信息
vTaskDelay(5000 / portTICK_PERIOD_MS); // 延时5秒,模拟配网过程
on_got_wifi_info(custom_handle, "MyHomeWiFi", "password123");
// ... 其他应用逻辑 ...
// 清理资源
wifi_service_destroy(wifi_service); // 会自动清理关联的配网接口
}
在这个示例中,我们首先创建了一个自定义配网实现,定义了配置结构体并实现了配网函数。然后,通过数据交互函数完成了:
- 使用
esp_wifi_setting_set_data
存储配置数据 - 使用
esp_wifi_setting_get_data
在配网函数中获取配置 - 使用
esp_wifi_setting_register_notify_handle
注册WiFi服务句柄 - 使用
esp_wifi_setting_info_notify
通知获取到的WiFi信息
数据流转示例
下面的时序图展示了配网过程中数据的完整流转:
总结
ESP-ADF的WiFi配网数据交互函数提供了一套简洁有效的接口,用于在配网过程中处理数据流转和状态管理。这些函数实现了三个关键功能:
- 通知机制:通过
esp_wifi_setting_register_notify_handle
和esp_wifi_setting_info_notify
建立起配网模块与WiFi服务之间的通信通道,使配网结果能够传递给WiFi服务 - 数据管理:通过
esp_wifi_setting_set_data
和esp_wifi_setting_get_data
实现配网配置和状态的存储与获取,支持不同配网实现的特定需求 - 模块解耦:通过统一的接口隐藏了不同配网实现的内部细节,使WiFi服务可以统一管理多种配网方式
通过合理使用这些数据交互函数,开发者可以更方便地实现自定义配网方式,并与ESP-ADF的WiFi服务无缝集成,提高系统的灵活性和可维护性。