esp-idf v4.0 smartconfig wifi配置信息保存在nvs_flash,实现掉电重新启动直接连入wifi(已完成)

往期文章:利用mesh ble_mesh_provisioner和 onoff_server建立网络传输温湿度以及pm2.5等数据

一、smartconfig一句话简介

esp-idf v4.0框架 smartconfig wifi配置信息保存在nvs_flash
smartconfig又称为快连,可以通过一键配置设备连入wifi,esp-idf已经有smartconfig demo 通过手机端ESPTOUCH就可以快速实现设备入网。

二、esp32 nvs_flash

我在编写存储wifi配置信息用到这几个API函数,在此列出,其他函数可以到乐鑫官网学习

乐鑫 nvs_flash API函数

(1) nvs_flash 初始化

**

esp_err_t nvs_flash_init(void);

**

初始化默认的NVS分区。
这个API初始化默认的NVS分区。默认的NVS分区是分区表中标记为“NVS”的分区。
调用时将会return一下三种返回值
Return

        ESP_OK    nvs成功初始化

        ESP_ERR_NVS_NO_FREE_PAGES    NVS存储不包含空页(如果NVS分区被截断,可能会发生这种情况)

        ESP_ERR_NOT_FOUND      在分区表中没有找到带有“nvs”标签的分区

(2)从已创建NVS分区打开具有给定名称空间的非易失性存储空间

**

esp_err_t nvs_open(const char *name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle);

**
多个内部ESP-IDF和第三方应用程序模块可以将它们的键值对存储在NVS模块中。为了减少键名上可能出现的冲突,每个模块都可以使用自己的名称空间。默认的NVS分区是分区表中标记为“NVS”的分区。
调用时将会return一下五种返回值
Return

    ESP_OK  成功打开存储句柄

    ESP_ERR_NVS_NOT_INITIALIZED   存储驱动程序没有初始化

    ESP_ERR_NVS_PART_NOT_FOUND  存储驱动程序没有初始化

    ESP_ERR_NVS_NOT_FOUND    id名称空间尚不存在,模式为NVS只读

    ESP_ERR_NVS_INVALID_NAME 名称空间名称不满足约束,则来自底层存储驱动程序的其他错误代码

(3)为给定的键设置可变长度的二进制值

**esp_err_t nvs_set_blob(nvs_handle_t handle, const char key, const void value, size_t length)

根据键的名称,这个函数族为键设置值。注意,在调用nvs提交函数之前,不会更新实际的存储。
调用时将会return一下七种返回值

Return

        ESP_OK 设置成功

        ESP_ERR_NVS_INVALID_HANDLE 句柄关闭或者为空

        ESP_ERR_NVS_READ_ONLY 存储空间句柄已经被打开,或者被设置为只读

        ESP_ERR_NVS_INVALID_NAME  键名不满足约束

        ESP_ERR_NVS_NOT_ENOUGH_SPACE  底层存储中没有足够的空间来保存该值

        ESP_ERR_NVS_REMOVE_FAILED  值没有更新是因为flash写操作失败。然而,该值是写入的,更新将在重新初始化nvs后完成,前提是flash操作不会再次失败

        ESP_ERR_NVS_VALUE_TOO_LONG 值设置太长

(4)将任何挂起的更改写入非易失性存储。

        **

esp_err_t nvs_commit(nvs_handle_t handle)

**
在设置任何值之后,必须调用nvs commit()来确保将更改写入非易失性存储。个别实现可能在其他时间写入存储器,但这并不能保证。
调用时将会return一下两种返回值
Return

        ESP_OK 写入成功

        ESP_ERR_NVS_INVALID_HANDLE 操作句柄被关闭或者为空

(5)关闭存储句柄并释放所有分配的资源

        **

void nvs_close(nvs_handle_t handle)

**

当句柄不再使用时,nvs打开的每个句柄都应该调用这个函数。关闭句柄可能不会自动将更改写入非易失性存储。这必须使用nvs提交函数显式地完成。一旦在句柄上调用此函数,就不应该再使用该句柄。

二、esp32 smartconfig 源码

(1)wifi 初始化(这段代码要配合event_handler 一起看,不然不太好理解)

/*初始化 wifi*/
static void initialise_wifi(void)
{   
    
    wifi_event_group = xEventGroupCreate();//创建一个事件组 smartconfig 事件组
     s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_netif_init());//创建一个事件组 从外部flash读取wifi配置信息 事件组
    
    ESP_ERROR_CHECK(esp_event_loop_create_default());//wifi事件
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );//wifi初始化

    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );  //获取链接信息  注意这句话回调到event_handler 往下看event_handler函数里面的对是否是第一次配网的判断
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );//获取链接信息
    if(wifi_flag!=1)ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );     //获取链接信息 外部flash无配置信息,进行smartconfig相关配置
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );//设置模式
    if(wifi_flag==1){//flash内有配置信息
     nvs_handle handle;
    static const char *NVS_CUSTOMER = "customer data";
    static const char *DATA1 = "param 1";
    wifi_config_t wifi_config_stored;
    memset(&wifi_config_stored, 0x0, sizeof(wifi_config_stored));
    uint32_t len = sizeof(wifi_config_stored);
    ESP_ERROR_CHECK( nvs_open(NVS_CUSTOMER, NVS_READWRITE, &handle) );
    ESP_ERROR_CHECK ( nvs_get_blob(handle, DATA1, &wifi_config_stored, &len) );
    nvs_close(handle);
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config_stored) );
    wifi_flag=1;}
    ESP_ERROR_CHECK( esp_wifi_start() );//启动wifi
    /*以下为相关配置,不过多讲解了*/
     
    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
  
            ESP_LOGI(TAG, "wifi_init_sta finished.");
            EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);
    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
      //  ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
      //           EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
     //   ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
      //           EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
        
    }

    ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
    ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
     xTaskCreate(&adc1_get_data_task, "adc1_get_data_task", 8192, NULL, 5, NULL);//创建adc1采集任务
    vEventGroupDelete(s_wifi_event_group);

}

(2)event_handler wifi回调函数 (smartconfig 过程中将会执行三次

static void event_handler(void* arg, esp_event_base_t event_base, 
                                int32_t event_id, void* event_data)
{  nvs_handle handle;//定义nv_flash 操作句柄
    esp_err_t err;//一个esp_err_t变量
    static const char *NVS_CUSTOMER = "customer data";
    static const char *DATA1 = "param 1";
    wifi_config_t wifi_config_stored;
    memset(&wifi_config_stored, 0x0, sizeof(wifi_config_stored));//结构体变量wifi_config_stored,所有变量初始化为 0
    uint32_t len = sizeof(wifi_config_stored);
    // Open
    err =nvs_open(NVS_CUSTOMER, NVS_READWRITE, &handle);//尝试打开外部flash 读取wifi配置信息
    if (err != ESP_OK) return err;
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {//第一步执行
         err = nvs_get_blob(handle, DATA1, &wifi_config_stored, &len);//读取wifi配置信息
	    if (err == ESP_ERR_NVS_NOT_FOUND) //读取失败
		{wifi_flag=0;// wifi_flag清零,注意这句,很重要,决定是重新通过smartconfig 配网,还是通过从flash读取的信息进行配网
		 xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);//如果读取失败,创建smartconfig任务,通过esptouch配网
		}
		else if(err == ESP_OK)//读取成功
		{wifi_flag=1;//读取成功wifi_flag=1;
        
           esp_wifi_connect();
           nvs_close(handle);//关闭nvs操作句柄
		}
        //创建smartconfig任务
       
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if(wifi_flag==1)
        {
               if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {//如果未连上,尝试重新链接
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);  }//尝试EXAMPLE_ESP_MAXIMUM_RETRY次后,仍未链接成功,将清除nvs_flash所有信息
         /****************清除nvs_flash*************/
        ESP_ERROR_CHECK( nvs_open( NVS_CUSTOMER, NVS_READWRITE, &handle) );
         ESP_ERROR_CHECK(  nvs_erase_key(handle,DATA1));
         usleep(5000*1000);
         ESP_ERROR_CHECK( nvs_erase_all(handle));
         usleep(5000*1000);
         ESP_ERROR_CHECK( nvs_commit(handle) );
         nvs_close(handle);
         esp_restart();//重新启动esp32,等待重新配网
        ESP_LOGI(TAG,"connect to the AP fail");
        }else{//断线重连  
        esp_wifi_connect();
        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);//清除标志位
        }
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {//获取到wifi信息后,执行, 第七步
             if(wifi_flag==1)
                      {
                           ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
                           ESP_LOGI(TAG2, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
                           s_retry_num = 0;
                           xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
                      }else{
                               //sta链接成功,set事件组
                               xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);}
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {//等待配网 第四步,等待手机配网
        ESP_LOGI(TAG1, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {//扫描信道 第五步,找到udp广播
        ESP_LOGI(TAG1, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) { //获取到ssid和密码   第六步,获取 wifi信息
        ESP_LOGI(TAG1, "Got SSID and password");
    

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }
        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        
        //打印账号密码
        ESP_LOGI(TAG1, "SSID:%s", ssid);
        ESP_LOGI(TAG1, "PASSWORD:%s", password);
        //断开默认的
        ESP_ERROR_CHECK( esp_wifi_disconnect() );
          //设置获取的ap和密码到寄存器
        ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
        //将获取的密码保存起来 nvs_flash
         ESP_ERROR_CHECK( nvs_open( NVS_CUSTOMER, NVS_READWRITE, &handle) );//打开新的nvs_flash
         ESP_ERROR_CHECK( nvs_set_blob( handle, DATA1, &wifi_config, sizeof(wifi_config)) );//将smartconfig获取到的wifi配置信息保存到外部flash
         ESP_ERROR_CHECK( nvs_commit(handle) );//提交保存信息
         nvs_close(handle);
         /****************清除nvs_flash*************/
        /* ESP_ERROR_CHECK( nvs_open( NVS_CUSTOMER, NVS_READWRITE, &handle) );
         ESP_ERROR_CHECK(  nvs_erase_key(handle,DATA1));
         ESP_ERROR_CHECK( nvs_erase_all(handle));
         ESP_ERROR_CHECK( nvs_commit(handle) );
         nvs_close(handle);*/
       
        //连接获取的ssid和密码
        ESP_ERROR_CHECK( esp_wifi_connect() );
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

以上就是全部内容,如有错误希望各位留言,谢谢各位

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
nvs_flash_init函数是ESP-IDFESP32 IoT Development Framework)中的一个重要函数,用于初始化Flash驱动器的非易失性存储(NVS)分区。NVS分区是ESP32中用于存储应用程序参数和配置的重要分区之一。该函数的目的是在ESP32启动时初始化NVS分区,以便应用程序可以访问和写入NVS存储器。以下是nvs_flash_init()函数的详细讲解: 函数原型:esp_err_t nvs_flash_init(void) 函数返回值:esp_err_t类型,如果函数执行成功,则返回ESP_OK,否则返回错误代码 函数功能:该函数用于初始化Flash驱动器的NVS分区。在ESP32启动时,该函数应该被调用一次,以便应用程序可以访问和写入NVS存储器。 函数说明:该函数是ESP-IDF中的一个重要函数,需要在应用程序主函数之前调用。如果未初始化NVS分区,则应用程序将无法访问和写入NVS存储器。在调用该函数之前,需要确保已经初始化了ESP32的Flash驱动器。 函数示例: ``` #include <nvs_flash.h> void app_main(void) { // 初始化Flash驱动器的NVS分区 esp_err_t ret = nvs_flash_init(); if (ret == ESP_OK) { printf("NVS分区初始化成功"); } else { printf("NVS分区初始化失败"); } } ``` 需要注意的是,nvs_flash_init()函数仅需要在ESP32首次启动时调用一次,并且不应该在应用程序的主循环中重复调用该函数。如果应用程序需要重新初始化NVS分区,则可以使用nvs_flash_erase()函数清除NVS分区中的数据,并再次调用nvs_flash_init()函数以重新初始化NVS分区。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值