ESP-ADF wifi_service子模块wifi_ssid_manager凭证管理函数详解

ESP-ADF wifi_service子模块wifi_ssid_manager凭证管理函数详解

版本信息: v2.7-65-gcf908721

本章节分析的源码位于 /components/wifi_service/src/wifi_ssid_manager.c 文件

WiFi凭证管理函数分析

ESP-ADF的wifi_ssid_manager模块提供了一组函数用于实现WiFi凭证的存储和管理,包括保存SSID和密码、擦除存储的凭证等功能。这些函数构成了WiFi服务的持久化存储基础,使设备能够保存和管理多个WiFi网络信息。

wifi_ssid_manager_save

wifi_ssid_manager_save函数用于保存WiFi的SSID和密码到NVS闪存。下面是其源码实现:

/**
 * @brief 保存WiFi SSID和密码
 * 
 * 该函数完成以下主要操作:
 * 1. 验证SSID和密码长度是否合法
 * 2. 检查SSID是否已存在
 * 3. 确定存储位置(新增或覆盖)
 * 4. 将SSID和密码保存到NVS存储
 * 5. 更新配置信息
 * 
 * 本函数在SSID数量达到上限时使用FIFO(先进先出)策略,
 * 即会删除最早保存的SSID以腾出空间保存新的SSID。
 * 
 * @param handle WiFi SSID管理器句柄
 * @param ssid WiFi网络名称
 * @param pwd WiFi密码
 * @return ESP_OK表示成功,ESP_FAIL表示失败
 */
esp_err_t wifi_ssid_manager_save(wifi_ssid_manager_handle_t handle, const char *ssid, const char *pwd)
{
    esp_err_t ret = ESP_OK;
    // 检查SSID和密码长度是否超出限制
    if (strlen(ssid) >= WIFI_SSID_MAX_LENGTH || strlen(pwd) >= WIFI_PWD_MAX_LENGTH) {
        ESP_LOGE(TAG, "The length of wifi ssid or password is too long");
        return ESP_FAIL;
    }
    AUDIO_NULL_CHECK(TAG, handle, return ESP_FAIL);
    
    // 获取当前SSID配置信息
    nvs_ssid_conf_t conf = {0};
    nvs_ssid_list_conf_get(handle, &conf);

    // 检查SSID是否已存在
    int8_t stored_id = get_stored_id_by_ssid(handle, conf.exsit_ssid_num, ssid);
    uint8_t key_id = 0;

    if (stored_id < 0) { // SSID不存在,需要新增
        if (conf.exsit_ssid_num < conf.max_ssid_num) {
            // 还有存储空间,直接添加
            key_id = conf.exsit_ssid_num;
            conf.latest_ssid = conf.exsit_ssid_num;
            conf.exsit_ssid_num++;
        } else {
            // 存储空间已满,使用FIFO策略
            key_id = nvs_get_write_id(handle, conf.exsit_ssid_num);
            conf.latest_ssid = key_id;
        }
    } else {
        // SSID已存在,直接更新
        key_id = stored_id;
        conf.latest_ssid = stored_id;
    }
    
    // 准备并保存WiFi信息
    nvs_stored_info_t info = {
        .cnt = 0,
        .choosen = false,
    };
    memcpy(info.ssid, ssid, strlen(ssid));
    memcpy(info.pwd, pwd, strlen(pwd));
    
    // 执行存储操作
    ret |= nvs_wifi_info_save(handle, key_id, &info);
    ret |= nvs_set_counter(handle, conf.exsit_ssid_num);
    ret |= nvs_ssid_list_conf_save(handle, &conf);
    ret |= nvs_reset_choosen_flag(handle, &conf);

    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Fail to save url to nvs, ret = 0x%x", ret);
        return ESP_FAIL;
    }
    return ESP_OK;
}

下面的流程图展示了wifi_ssid_manager_save函数的执行流程:

超出限制
长度合法
无效
有效
已存在
不存在
未达到
已达到
失败
成功
开始
检查SSID和密码长度
返回ESP_FAIL
检查handle是否有效
获取SSID配置信息
检查SSID是否已存在
使用已存在的ID
是否达到最大SSID数量
分配新ID并增加计数
获取最旧的ID进行覆盖
准备WiFi信息结构
保存WiFi信息到NVS
更新计数器
保存配置信息
重置所有SSID的选择标志
操作是否成功
返回ESP_OK

这个函数实现了以下几个关键特性:

  1. 自动去重:当保存已存在的SSID时,直接更新其密码
  2. 有限队列:当SSID数量达到上限时,使用FIFO策略覆盖最旧的SSID
  3. 标记最新:总是将最新保存的SSID标记为latest_ssid,便于优先连接
  4. 重置标志:保存新SSID后重置所有choosen标志,确保连接逻辑的正确性

wifi_ssid_manager_erase_all

wifi_ssid_manager_erase_all函数用于擦除所有保存的WiFi凭证。下面是其源码实现:

/**
 * @brief 擦除所有保存的WiFi凭证
 * 
 * 该函数完成以下主要操作:
 * 1. 获取当前最大SSID数量限制
 * 2. 清除所有WiFi信息命名空间数据
 * 3. 清除配置命名空间数据
 * 4. 重置配置信息(仅保留最大SSID数量设置)
 * 
 * 调用此函数后,所有保存的WiFi网络信息将被删除,
 * 但管理器的最大SSID数量限制会被保留。
 * 
 * @param handle WiFi SSID管理器句柄
 * @return ESP_OK表示成功,或者错误码
 */
esp_err_t wifi_ssid_manager_erase_all(wifi_ssid_manager_handle_t handle)
{
    AUDIO_NULL_CHECK(TAG, handle, return ESP_FAIL);
    esp_err_t ret = ESP_OK;
    
    // 保存最大SSID数量限制
    uint8_t max_ssid_num = 0;
    nvs_ssid_conf_t conf = {0};
    ret |= nvs_ssid_list_conf_get(handle, &conf);
    max_ssid_num = conf.max_ssid_num;
    
    // 重置配置,仅保留最大数量限制
    memset(&conf, 0, sizeof(nvs_ssid_conf_t));
    conf.max_ssid_num = max_ssid_num;
    
    // 清除所有NVS数据
    action_result_t result = { 0 };
    ret |= esp_dispatcher_execute_with_func(handle->dispatcher, nvs_action_erase_all, (void *)handle->info_nvs, NULL, &result);
    ret |= esp_dispatcher_execute_with_func(handle->dispatcher, nvs_action_erase_all, (void *)handle->conf_nvs, NULL, &result);
    
    // 保存重置后的配置
    ret |= nvs_ssid_list_conf_save(handle, &conf);
    
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Fail to erase nvs flash");
    }
    return ret;
}

下面的流程图展示了wifi_ssid_manager_erase_all函数的执行流程:

无效
有效
失败
成功
开始
检查handle是否有效
返回ESP_FAIL
获取当前配置信息
保存最大SSID数量限制
重置配置结构体
还原最大SSID数量限制
清除WiFi信息命名空间
清除配置命名空间
保存重置后的配置
操作是否成功
记录错误日志
返回错误码
返回ESP_OK

这个函数的主要特点包括:

  1. 保留配置限制:清除所有凭证但保留最大SSID数量设置,保持管理器功能的一致性
  2. 全面清除:同时清除WiFi信息和配置命名空间,确保所有凭证都被删除
  3. 重置状态:重新初始化配置信息,将存储状态恢复到初始状态
  4. 错误累积:使用|=运算符累积错误码,确保任何步骤失败都能被检测到

内部实现机制

存储策略分析

WiFi凭证管理函数采用了以下存储策略:

  1. 双空间存储:使用两个NVS命名空间分别存储配置元数据和实际WiFi凭证

    • WIFI_CONF_NVS_NAMESPACE:存储配置元数据,如最大SSID数量、存储状态等
    • WIFI_INFO_NVS_NAMESPACE:存储实际的WiFi凭证(SSID和密码)
  2. FIFO替换策略:当存储空间已满时,采用先进先出策略替换最早存储的凭证

    • 使用计数器(cnt)跟踪每个SSID的存储顺序
    • 通过nvs_get_write_id函数识别最旧的SSID
  3. 自动去重:保存时自动检查SSID是否已存在,避免重复存储

    • 使用get_stored_id_by_ssid函数查找SSID是否已存在
    • 如已存在,直接更新对应的密码信息
  4. 状态标记:使用多个标志位管理凭证状态

    • latest_ssid:标记最新保存的SSID,用于优先连接
    • choosen:标记当前选择的SSID,用于跟踪连接状态

内部数据流向

凭证管理函数内部的数据流向如下:

SSID+密码
检查重复
存储满时
保存信息
更新计数
保存配置
重置标志
清除请求
获取配置
清除数据
重置配置
应用程序
wifi_ssid_manager_save
get_stored_id_by_ssid
nvs_get_write_id
nvs_wifi_info_save
nvs_set_counter
nvs_ssid_list_conf_save
nvs_reset_choosen_flag
应用程序
wifi_ssid_manager_erase_all
nvs_ssid_list_conf_get
nvs_action_erase_all
nvs_ssid_list_conf_save

关键辅助函数分析

凭证管理函数依赖几个关键的内部辅助函数:

重要的内部辅助函数详解

以下三个函数是WiFi凭证管理的重要内部组件,虽然不直接暴露给用户,但它们实现了凭证管理的核心功能:

get_stored_id_by_ssid
/**
 * @brief 查找指定SSID在存储中的索引ID
 * 
 * 遍历所有已存储的WiFi凭证,查找与给定参数匹配的SSID。
 * 该函数是实现自动去重功能的核心,确保相同的SSID不会
 * 多次添加到存储中。
 * 
 * @param handle WiFi SSID管理器句柄
 * @param exsit_ssid_num 当前存在的SSID数量
 * @param ssid 要查找的SSID字符串
 * @return 成功返回找到的SSID索引,失败返回ESP_FAIL(-1)
 */
static int8_t get_stored_id_by_ssid(wifi_ssid_manager_handle_t handle, uint8_t exsit_ssid_num, const char *ssid)
{
    // 初始化信息结构体
    nvs_stored_info_t info = {0};
    
    // 遍历所有存储的WiFi凭证
    for (int i = 0; i < exsit_ssid_num; i++) {
        // 清空信息结构,准备读取下一个凭证
        memset(&info, 0, sizeof(nvs_stored_info_t));
        
        // 从存储中获取凭证信息
        nvs_wifi_info_get(handle, i, &info);
        
        // 快速过滤:比较SSID长度,如果不同则跳过
        if (strlen(info.ssid) != strlen(ssid)) {
            continue;
        }
        
        // 比较SSID内容是否相同
        if (strcmp(info.ssid, ssid) == 0) {
            ESP_LOGD(TAG, "Found the same ssid in flash, update it");
            return i; // 返回找到的凭证索引
        }
    }
    
    // 未找到匹配项,返回失败
    return ESP_FAIL;
}

该函数用于遍历已存储的所有WiFi凭证,判断指定的SSID是否已存在。它先比较SSID长度进行快速过滤,如果长度相同才进一步比较内容。这个函数在凭证保存时起到了关键作用,确保了同一网络的凭证不会重复存储,而是直接更新密码。

nvs_get_write_id
/**
 * @brief 获取应覆盖的WiFi凭证ID
 * 
 * 当存储空间已满时,该函数用于确定应该覆盖哪一个现有的
 * WiFi凭证。该函数实现了FIFO(先进先出)策略,即覆盖存储时间
 * 最长的凭证。
 * 
 * @param handle WiFi SSID管理器句柄
 * @param exsit_ssid_num 当前存在的SSID数量
 * @return 需覆盖的WiFi凭证ID
 */
static uint8_t nvs_get_write_id(wifi_ssid_manager_handle_t handle, uint8_t exsit_ssid_num)
{
    // 初始化计数器最大值和对应的ID
    uint8_t max_cnt = 0, max_cnt_id = 0;
    nvs_stored_info_t info = {0};
    
    // 遍历所有存储的WiFi凭证
    for (int i = 0; i < exsit_ssid_num; i++) {
        // 清空信息结构体
        memset(&info, 0, sizeof(nvs_stored_info_t));
        
        // 获取凭证信息
        nvs_wifi_info_get(handle, i, &info);
        
        // 如果当前凭证的计数器值大于或等于已记录的最大值
        // 则更新最大计数器值和对应ID
        if (info.cnt >= max_cnt) {
            max_cnt = info.cnt;
            max_cnt_id = i;
        }
    }
    
    // 返回计数器值最大的凭证ID
    return max_cnt_id;
}

该函数在存储空间已满需要替换现有凭证时使用。它通过遍历所有凭证并查找计数器值最大的凭证,实现了FIFO(先进先出)替换策略。这确保了在空间不足时,始终会覆盖最早添加的WiFi凭证。

nvs_set_counter 函数
/**
 * @brief 递增所有WiFi凭证的计数器值
 * 
 * 递增所有已存储凭证的计数器值,用于跟踪凭证的“年龄”。
 * 该函数是实现FIFO(先进先出)替换策略的关键部分,确保
 * 新添加的凭证计数器值保持最小。
 * 
 * @param handle WiFi SSID管理器句柄
 * @param exsit_ssid_num 当前存在的SSID数量
 * @return 成功返回ESP_OK,失败返回ESP_FAIL
 */
static esp_err_t nvs_set_counter(wifi_ssid_manager_handle_t handle, uint8_t exsit_ssid_num)
{
    // 初始化返回值和信息结构体
    esp_err_t ret = ESP_OK;
    nvs_stored_info_t info = {0};
    
    // 遍历所有存在的WiFi凭证
    for (int i = 0; i < exsit_ssid_num; i++) {
        // 清空信息结构体,准备读取下一个凭证
        memset(&info, 0, sizeof(nvs_stored_info_t));
        
        // 从存储中获取凭证信息,并累积错误码
        ret |= nvs_wifi_info_get(handle, i, &info);
        
        // 将计数器值递增1,这是实现FIFO策略的关键
        info.cnt ++;
        
        // 保存更新后的凭证信息,并累积错误码
        ret |= nvs_wifi_info_save(handle, i, &info);
    }
    
    // 检查操作过程中是否有错误发生
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Fail to set counter");
        return ESP_FAIL;
    }
    
    // 所有操作成功,返回ESP_OK
    return ESP_OK;
}

该函数的作用是递增所有已存储WiFi凭证的计数器值,是实现FIFO替换策略的关键辅助函数。它确保了计数器值能准确反映凭证的“年龄”。

nvs_reset_choosen_flag

重置所有凭证的选择标志

  • 确保连接逻辑的正确性
  • 防止多个凭证同时被标记为已选择

应用示例

下面是一个使用WiFi凭证管理函数的完整示例,展示了这些函数在实际应用中的使用方式:

#include <string.h>
#include "esp_log.h"
#include "wifi_ssid_manager.h"

static const char *TAG = "WIFI_CRED_EXAMPLE";

// 凭证管理示例函数
void wifi_credential_management_example(void)
{
    // 创建WiFi SSID管理器,最多保存5个SSID
    wifi_ssid_manager_handle_t mgr = wifi_ssid_manager_create(5);
    if (mgr == NULL) {
        ESP_LOGE(TAG, "创建WiFi SSID管理器失败");
        return;
    }
    
    // 保存多个WiFi凭证
    ESP_LOGI(TAG, "保存WiFi凭证1");
    esp_err_t ret = wifi_ssid_manager_save(mgr, "HomeWiFi", "home123456");
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "保存WiFi凭证1失败");
    }
    
    ESP_LOGI(TAG, "保存WiFi凭证2");
    ret = wifi_ssid_manager_save(mgr, "OfficeWiFi", "office789");
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "保存WiFi凭证2失败");
    }
    
    ESP_LOGI(TAG, "保存WiFi凭证3");
    ret = wifi_ssid_manager_save(mgr, "CafeWiFi", "cafe2023");
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "保存WiFi凭证3失败");
    }
    
    // 显示当前存储的所有WiFi凭证
    ESP_LOGI(TAG, "显示所有存储的WiFi凭证:");
    wifi_ssid_manager_list_show(mgr);
    
    // 更新已存在的WiFi凭证密码
    ESP_LOGI(TAG, "更新HomeWiFi的密码");
    ret = wifi_ssid_manager_save(mgr, "HomeWiFi", "newhome888");
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "更新WiFi密码失败");
    }
    
    // 尝试获取最佳WiFi配置(通常会扫描并匹配信号最强的网络)
    wifi_config_t wifi_config = {0};
    ret = wifi_ssid_manager_get_best_config(mgr, &wifi_config);
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "获取到最佳WiFi配置: SSID=%s", (char *)wifi_config.sta.ssid);
    } else {
        ESP_LOGE(TAG, "获取最佳WiFi配置失败");
    }
    
    // 获取最新保存的WiFi配置
    memset(&wifi_config, 0, sizeof(wifi_config_t));
    ret = wifi_ssid_manager_get_latest_config(mgr, &wifi_config);
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "获取到最新WiFi配置: SSID=%s", (char *)wifi_config.sta.ssid);
    } else {
        ESP_LOGE(TAG, "获取最新WiFi配置失败");
    }
    
    // 擦除所有WiFi凭证
    ESP_LOGI(TAG, "擦除所有WiFi凭证");
    ret = wifi_ssid_manager_erase_all(mgr);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "擦除WiFi凭证失败");
    }
    
    // 确认所有凭证已被擦除
    ESP_LOGI(TAG, "确认所有凭证已被擦除:");
    wifi_ssid_manager_list_show(mgr);
    
    // 销毁WiFi SSID管理器
    ESP_LOGI(TAG, "销毁WiFi SSID管理器");
    wifi_ssid_manager_destroy(mgr);
}

void app_main(void)
{
    // 初始化NVS闪存(通常在应用程序开始时执行)
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        // NVS分区被充满或发现新版本,需要擦除处理
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    
    // 执行凭证管理示例
    wifi_credential_management_example();
}

在这个示例中,我们演示了WiFi凭证管理函数的典型用法:

  1. 创建管理器并设置最大存储数量
  2. 保存多个WiFi凭证(包括新增和更新)
  3. 列出当前存储的所有凭证
  4. 获取最佳配置和最新配置
  5. 擦除所有凭证
  6. 销毁管理器释放资源

常见使用场景

WiFi凭证管理函数在以下场景中特别有用:

  1. 多网络管理:设备需要在多个WiFi网络间切换,如家庭/办公室/公共场所
  2. 配网后存储:通过配网(如SmartConfig、蓝牙配网等)获取的WiFi凭证需要持久化存储
  3. 备用网络:存储多个备用网络,当主网络不可用时自动切换
  4. 网络优先级:基于信号强度或保存顺序确定连接优先级
  5. 恢复出厂设置:需要清除所有存储的网络凭证

总结

ESP-ADF的WiFi凭证管理函数提供了一套完整的接口,用于管理WiFi网络凭证的存储、检索和清除。这些函数具有以下几个关键特性:

  1. 有限存储管理:通过最大SSID数量限制和FIFO替换策略,有效管理有限的存储空间
  2. 自动去重:保存时自动检查SSID是否已存在,避免重复存储
  3. 优先级管理:通过latest_ssid和choosen标志管理网络连接优先级
  4. 持久化存储:利用NVS闪存实现WiFi凭证的持久化存储,断电不丢失
  5. 完整的生命周期:提供从保存到擦除的完整生命周期管理

通过合理使用这些凭证管理函数,开发者可以轻松实现设备的多网络管理,提升用户体验和设备连接可靠性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

omnibots

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值