Merge branch 'bugfix/nvs_idfgh_issue' into 'master'

feat(esp_wifi): Add example to write wifi config in nvs

Closes IDFGH-13678

See merge request espressif/esp-idf!36693
This commit is contained in:
Jiang Jiang Jian
2025-05-20 17:16:44 +08:00
21 changed files with 800 additions and 0 deletions

1
.gitignore vendored
View File

@@ -37,6 +37,7 @@ components/**/build/
components/**/build_*_*/
components/**/sdkconfig
components/**/sdkconfig.old
components/**/test_apps/wifi_nvs_config/nvs_data_suffix.csv
# Example project files
examples/**/build/

View File

@@ -3,3 +3,7 @@
components/esp_wifi/test_apps/:
disable:
- if: SOC_WIFI_SUPPORTED != 1
components/esp_wifi/test_apps/wifi_nvs_config:
disable:
- if: SOC_WIFI_SUPPORTED != 1

View File

@@ -0,0 +1,25 @@
#This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
if($ENV{CI_PIPELINE_ID})
idf_build_set_property(COMPILE_DEFINITIONS TEST_SUFFIX_STR="_$ENV{CI_PIPELINE_ID}" APPEND)
endif()
if(DEFINED ENV{CI_PIPELINE_ID})
set(TEST_SUFFIX_STR "_$ENV{CI_PIPELINE_ID}")
else()
string(TIMESTAMP TEST_SUFFIX_STR "%Y%m%d%H%M%S")
endif()
execute_process(
COMMAND python3 ${CMAKE_SOURCE_DIR}/update_csv_suffix.py ${TEST_SUFFIX_STR}
)
project(wifi_nvs_conn_test)

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- |

View File

@@ -0,0 +1,10 @@
idf_component_register(SRC_DIRS .
PRIV_INCLUDE_DIRS . ${CMAKE_CURRENT_BINARY_DIR}
PRIV_REQUIRES cmock test_utils nvs_flash esp_wifi esp_event
WHOLE_ARCHIVE)
# Create a NVS image from the contents of the `nvs_data` CSV file
# that fits the partition named 'nvs'. FLASH_IN_PROJECT indicates that
# the generated image should be flashed when the entire project is flashed to
# the target with 'idf.py -p PORT flash'.
nvs_create_partition_image(nvs ../nvs_data_suffix.csv FLASH_IN_PROJECT)

View File

@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_err.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in wifi and lwip
#define TEST_MEMORY_LEAK_THRESHOLD (-1536)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
}
void tearDown(void)
{
ESP_ERROR_CHECK(esp_wifi_deinit());
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
unity_run_menu();
ESP_ERROR_CHECK(esp_netif_deinit());
ESP_ERROR_CHECK(nvs_flash_deinit());
}

View File

@@ -0,0 +1,219 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*
* This test code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "unity.h"
#include "esp_mac.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "test_utils.h"
#include "memory_checks.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#define CONNECT_TIMEOUT_MS (10000)
#define GOT_IP_EVENT (1)
#define WIFI_DISCONNECT_EVENT (1<<1)
#define WIFI_STA_CONNECTED (1<<2)
#define WIFI_AP_STA_CONNECTED (1<<3)
#define EVENT_HANDLER_FLAG_DO_NOT_AUTO_RECONNECT 0x00000001
static uint32_t wifi_event_handler_flag;
static const char* TAG = "test_wifi";
static esp_netif_t* s_ap_netif = NULL;
static esp_netif_t* s_sta_netif = NULL;
static EventGroupHandle_t wifi_events;
static void stop_wifi(void);
static void wifi_ap_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
ESP_LOGI(TAG, "wifi event handler: %"PRIi32, event_id);
switch (event_id) {
case WIFI_EVENT_AP_START:
ESP_LOGI(TAG, "WIFI_EVENT_AP_START");
break;
case WIFI_EVENT_AP_STACONNECTED:
ESP_LOGI(TAG, "WIFI_EVENT_AP_STACONNECTED");
if (wifi_events) {
xEventGroupSetBits(wifi_events, WIFI_AP_STA_CONNECTED);
}
break;
case WIFI_EVENT_AP_STADISCONNECTED:
ESP_LOGI(TAG, "WIFI_EVENT_AP_STADISCONNECTED");
ESP_LOGI(TAG, "sta disconnected");
break;
default:
break;
}
return;
}
static void wifi_sta_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
ESP_LOGI(TAG, "wifi event handler: %"PRIi32, event_id);
switch (event_id) {
case WIFI_EVENT_STA_START:
ESP_LOGI(TAG, "WIFI_EVENT_STA_START");
// make sure softap has started
vTaskDelay(1000 / portTICK_PERIOD_MS);
TEST_ESP_OK(esp_wifi_connect());
ESP_LOGI(TAG, "start esp_wifi_connect");
break;
case WIFI_EVENT_STA_CONNECTED:
ESP_LOGI(TAG, "WIFI_EVENT_STA_CONNECTED");
if (wifi_events) {
xEventGroupSetBits(wifi_events, WIFI_STA_CONNECTED);
}
break;
case WIFI_EVENT_STA_DISCONNECTED:
ESP_LOGI(TAG, "WIFI_EVENT_STA_DISCONNECTED");
wifi_event_sta_disconnected_t *event = (wifi_event_sta_disconnected_t *)event_data;
ESP_LOGI(TAG, "disconnect reason: %u", event->reason);
if (!(EVENT_HANDLER_FLAG_DO_NOT_AUTO_RECONNECT & wifi_event_handler_flag)) {
TEST_ESP_OK(esp_wifi_connect());
}
if (wifi_events) {
xEventGroupSetBits(wifi_events, WIFI_DISCONNECT_EVENT);
}
break;
default:
break;
}
return;
}
static void ip_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
ip_event_got_ip_t *event;
ESP_LOGI(TAG, "ip event handler");
switch (event_id) {
case IP_EVENT_STA_GOT_IP:
event = (ip_event_got_ip_t*)event_data;
ESP_LOGI(TAG, "IP_EVENT_STA_GOT_IP");
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
if (wifi_events) {
xEventGroupSetBits(wifi_events, GOT_IP_EVENT);
}
break;
default:
break;
}
return;
}
static esp_err_t event_init(void)
{
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &ip_event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
return ESP_OK;
}
static esp_err_t event_deinit(void)
{
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, &ip_event_handler));
ESP_ERROR_CHECK(esp_event_loop_delete_default());
return ESP_OK;
}
#define EMPH_STR(s) "****** "s" ******"
static void start_wifi(void)
{
event_init();
if (wifi_events == NULL) {
wifi_events = xEventGroupCreate();
}
xEventGroupClearBits(wifi_events, 0x00ffffff);
TEST_ESP_OK(esp_wifi_start());
}
static void stop_wifi(void)
{
ESP_LOGI(TAG, "Stopping wifi now");
TEST_ESP_OK(esp_wifi_stop());
event_deinit();
if (wifi_events) {
vEventGroupDelete(wifi_events);
wifi_events = NULL;
}
vTaskDelay(500 / portTICK_PERIOD_MS);
}
static void test_wifi_connection_sta(void)
{
EventBits_t bits;
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_sta_event_handler, NULL));
s_sta_netif = esp_netif_create_default_wifi_sta();
esp_wifi_set_mode(WIFI_MODE_STA);
start_wifi();
wifi_config_t cfg;
esp_wifi_get_config(WIFI_IF_STA, &cfg);
ESP_LOGI(TAG, "STA mode, %s %s", cfg.sta.ssid, cfg.sta.password);
bits = xEventGroupWaitBits(wifi_events, GOT_IP_EVENT, 1, 0, CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
TEST_ASSERT(bits & GOT_IP_EVENT);
xEventGroupClearBits(wifi_events, WIFI_DISCONNECT_EVENT);
wifi_event_handler_flag |= EVENT_HANDLER_FLAG_DO_NOT_AUTO_RECONNECT;
// Wait for 60s and the stop wifi
vTaskDelay(60000 / portTICK_PERIOD_MS);
esp_netif_destroy_default_wifi(s_sta_netif);
stop_wifi();
}
static void test_wifi_connection_softap(void)
{
EventBits_t bits;
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_ap_event_handler, NULL));
s_ap_netif = esp_netif_create_default_wifi_ap();
esp_wifi_set_mode(WIFI_MODE_AP);
start_wifi();
wifi_config_t cfg;
esp_wifi_get_config(WIFI_IF_AP, &cfg);
ESP_LOGI(TAG, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password);
// wait station connected
bits = xEventGroupWaitBits(wifi_events, WIFI_AP_STA_CONNECTED, 1, 0, CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
TEST_ASSERT(bits & WIFI_AP_STA_CONNECTED);
// wait 70s (longer than station side)
vTaskDelay((60000 + CONNECT_TIMEOUT_MS) / portTICK_PERIOD_MS);
esp_netif_destroy_default_wifi(s_ap_netif);
stop_wifi();
}
TEST_CASE_MULTIPLE_DEVICES("test wifi connection with station and AP", "[wifi][timeout=90]", test_wifi_connection_sta, test_wifi_connection_softap);

View File

@@ -0,0 +1,7 @@
key,type,encoding,value
nvs.net80211,namespace,,
sta.ssid,data,blob_sz_fill(32;0x00),TESTSSID
sta.pswd,data,blob_fill(64;0x00),TESTPASSWORD
ap.ssid,data,blob_sz_fill(32;0x00),TESTSSID
ap.passwd,data,blob_fill(64;0x00),TESTPASSWORD
ap.authmode,data,u8,3
1 key type encoding value
2 nvs.net80211 namespace
3 sta.ssid data blob_sz_fill(32;0x00) TESTSSID
4 sta.pswd data blob_fill(64;0x00) TESTPASSWORD
5 ap.ssid data blob_sz_fill(32;0x00) TESTSSID
6 ap.passwd data blob_fill(64;0x00) TESTPASSWORD
7 ap.authmode data u8 3

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 # Name, Type, SubType, Offset, Size, Flags
4 nvs, data, nvs, 0x9000, 0x6000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 factory, app, factory, 0x10000, 1M,

View File

@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded_idf.unity_tester import CaseTester
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.wifi_two_dut
@pytest.mark.parametrize('count', [2], indirect=True)
@idf_parametrize(
'target',
['esp32', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32s2', 'esp32s3'],
indirect=['target'],
)
def test_wifi_nvs_connect_cases(case_tester: CaseTester) -> None: # type: ignore
case_tester.run_all_cases()
@pytest.mark.wifi_two_dut
@pytest.mark.xtal_26mhz
@pytest.mark.parametrize(
'count, config, baud',
[
(2, 'esp32c2_xtal26m', '74880'),
],
indirect=True,
)
@idf_parametrize('target', ['esp32c2'], indirect=['target'])
def test_wifi_nvs_connect_cases_esp32c2_xtal26m(case_tester: CaseTester) -> None:
case_tester.run_all_cases()

View File

@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32c2"
CONFIG_XTAL_FREQ_26=y

View File

@@ -0,0 +1,5 @@
# ignore task watchdog triggered by unity_run_menu
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"

View File

@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import csv
import os
import sys
def update_csv_suffix(suffix: str) -> None:
# Hardcoded CSV file path
CSV_FILE = os.path.join(os.path.dirname(__file__), 'nvs_data.csv')
# Output CSV file path with suffix
NEW_CSV_FILE = os.path.join(os.path.dirname(__file__), 'nvs_data_suffix.csv')
with open(NEW_CSV_FILE, 'w', newline='') as outfile:
with open(CSV_FILE, 'r', newline='') as infile:
reader = csv.DictReader(infile)
fieldnames = reader.fieldnames if reader.fieldnames else []
writer = csv.DictWriter(outfile, fieldnames=fieldnames)
writer.writeheader()
for row in reader:
if row['value'] == 'TESTSSID':
row['value'] = f'TESTSSID_{suffix}'
elif row['value'] == 'TESTPASSWORD':
row['value'] = f'TESTPASSWORD_{suffix}'
writer.writerow(row)
print(f'[update_csv.py] Patched CSV with suffix: {suffix}, saved as {NEW_CSV_FILE}')
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: python update_csv_suffix.py <suffix>')
sys.exit(1)
suffix = sys.argv[1]
update_csv_suffix(suffix)

View File

@@ -0,0 +1,8 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(wifi_nvs_config)

View File

@@ -0,0 +1,217 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- |
# WiFi NVS Config Example
(See the `README.md` file in the upper-level 'examples' directory for more information about examples.)
This example demonstrates how to configure Wi-Fi settings in NVS directly using a CSV file and utilize the Wi-Fi functionality of the ESP Wi-Fi driver.
## How to Use the Example
### 1) Configuration
Open the CSV file `nvs_station_data.csv` or `nvs_ap_data.csv`.
- Set the Wi-Fi configuration keys as explained below. These values are written into the NVS partition and read by the Wi-Fi driver during `esp_wifi_init()`.
- If needed, adjust or set other options as per your requirements. The AP and station keys along with their encoding types are listed [here](#key-configuration).
**Note 1:** To set the device in SoftAP mode, enable the `CONFIG_ESP_WIFI_SOFTAP_SUPPORT` flag in `menuconfig`.
**Note 2:** Setting these values is optional. Defaults are applied when `esp_wifi_init()` is called.
### 2) Generate the Binary File Using the NVS Partition Generator Utility
- Check the size of the NVS partition in the `partition_example.csv` file provided in this example.
- Generate a `.bin` file of the specified size using the following command:
**Usage:**
```bash
python nvs_partition_gen.py generate [-h] [--version {1,2}] [--outdir OUTDIR] input output size
```
- Sample command:
```bash
python nvs_partition_gen.py generate nvs_station_data.csv sample_nvs.bin 0x6000
```
- Please update the NVS Partition Generator to the latest version to avoid encoding type errors.
- For more information, see the [NVS Partition Generator Utility Documentation](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_partition_gen.html).
### 3) Flash the NVS Binary File to the ESP Device
- Check the offset of the NVS partition in the `partition_example.csv` file.
- After flashing the build, flash the `sample_nvs.bin` file to the ESP device at the specified offset using `esptool.py`.
- Refer to the [Esptool Documentation](https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/index.html) for detailed usage.
### 4) Build and Flash
Build the project and flash it to the board, then run the monitor tool to view the serial output.
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
### 5) Monitor the Output
- Use `idf.py monitor` to view the console output of the example.
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html)
## Key Configuration
### Station Keys
| Key Name | Encoding Type | Mandatory/Optional | Description / Meaning |
|--------------------------|---------------|---------------------|----------------------------------------------------|
| sta.ssid | blob_sz_fill | Mandatory | SSID (Service Set Identifier) for station. (Refer parameter `uint8_t ssid[32]` from `struct wifi_sta_config_t`) |
| sta.pswd | blob_fill | Mandatory | Password for connecting to the Wi-Fi network. (Refer parameter `uint8_t password[64]` from `struct wifi_sta_config_t`) |
| bssid.set | u8 | Optional | Whether the BSSID is set or not. (Refer parameter `bool bssid_set` from `struct wifi_sta_config_t`) |
| sta.bssid | blob | Optional | BSSID (Basic Service Set Identifier) of the network. (Refer parameter `uint8_t bssid[6]` from `struct wifi_sta_config_t`) |
| sta.lis_intval | u16 | Optional | Listen interval (Refer parameter `uint16_t listen_interval` from `struct wifi_sta_config_t`) |
| sta.phym | u8 | Optional | Physical mode e.g., 802.11a/b/g/n/ac/ax. (Refer `esp_err_t esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap)`) |
| sta.phybw | u8 | Optional | Physical bandwidth (e.g., 20MHz, 40MHz) (Refer `esp_err_t esp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidths_t* bw)`) |
| sta.sort_method | u8 | Optional | Sorting method for APs e.g. enum wifi_sort_method_t (Refer parameter `wifi_sort_method_t sort_method` from `struct wifi_sta_config_t`)|
| sta.minrssi | i8 | Optional | Minimum RSSI for the station to connect. (Refer `esp_err_t esp_wifi_set_rssi_threshold()` OR `int8_t rssi` from `wifi_scan_threshold_t threshold`) |
| sta.minauth | u8 | Optional | Minimum authentication level required for APs. (Refer `wifi_auth_mode_t authmode` from `wifi_scan_threshold_t threshold`) |
| sta.pmf_r | u8 | Optional | Whether PMF is required. (Refer `wifi_pmf_config_t pmf_cfg` parameter from `struct wifi_sta_config_t`) |
| sta.btm_e | u8 | Optional | Whether BTM is enabled. (Refer parameter `uint32_t btm_enabled` from `struct wifi_sta_config_t`) |
| sta.rrm_e | u8 | Optional | Whether RRM is enabled. (Refer parameter `uint32_t rm_enabled` from `struct wifi_sta_config_t`) |
| sta.mbo_e | u8 | Optional | Whether MBO (Multi-band Operation) is enabled. (Refer parameter `uint32_t mbo_enabled` from `struct wifi_sta_config_t`) |
| sta.phym5g | u8 | Optional | 5 GHz physical mode. (Only for chips supporting 5GHz bandwidth. (Refer `esp_err_t esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap)`) |
| sta.phybw5g | u8 | Optional | 5 GHz bandwidth setting.(Only for chips supporting 5GHz bandwidth. (Refer `esp_err_t esp_wifi_set_bandwidths(wifi_interface_t ifx, wifi_bandwidths_t* bw)` |
| sta.ft | u8 | Optional | Whether fast transition is enabled. (Refer parameter `uint32_t ft_enabled` from `struct wifi_sta_config_t`) |
| sta.owe | u8 | Optional | Whether OWE (Opportunistic Wireless Encryption) is enabled. (Refer parameter `uint32_t owe_enabled` from `struct wifi_sta_config_t`) |
| sta.trans_d | u8 | Optional | Transition disabled setting. (Refer parameter `uint32_t transition_disable` from `struct wifi_sta_config_t`) |
| sta.sae_h2e | u8 | Optional | SAE PWE method (e.g. enum wifi_sae_pwe_method_t). (Refer parameter `wifi_sae_pwe_method_t sae_pwe_h2e` from `struct wifi_sta_config_t`) |
| sta.sae_pk_mode | u8 | Optional | SAE-pk mode setting (e.g. enum wifi_sae_pk_mode_t) (Refer parameter `wifi_sae_pk_mode_t sae_pk_mode` from `struct wifi_sta_config_t`) |
| sta.bss_retry | u8 | Optional | BSS retry counter. (Refer parameter `uint8_t failure_retry_cnt` from `struct wifi_sta_config_t`) |
| sta.sae_h2e_id | blob | Optional | Password identifier for H2E. This must be a null-terminated string (Refer parameter `uint8_t sae_h2e_identifier[SAE_H2E_IDENTIFIER_LEN]` from `struct wifi_sta_config_t`) |
| sta.rssi_5g_adj | u8 | Optional | RSSI adjustment for 5 GHz. (Refer `uint8_t rssi_5g_adjustment` from `wifi_scan_threshold_t threshold`) |
### AP Keys
| Key Name | Encoding Type | Mandatory/Optional | Description / Meaning |
|--------------------------|---------------|---------------------|----------------------------------------------------|
| ap.ssid | blob_sz_fill | Mandatory | SSID (Service Set Identifier) for the AP prefixed with the length of the SSID. (Refer parameter `uint8_t ssid[32]` from `struct wifi_ap_config_t`) |
| ap.passwd | blob_fill | Mandatory | Password for the AP's Wi-Fi network. (Refer parameter `uint8_t password[64]` from `struct wifi_ap_config_t`) |
| ap.chan | u8 | Mandatory | Channel number on which the AP is broadcasting. (Refer parameter `uint8_t channel` from `struct wifi_ap_config_t`) |
| ap.authmode | u8 | Mandatory | Authentication mode for the AP e.g. enum wifi_auth_mode_t. (Refer parameter `wifi_auth_mode_t authmode` from `struct wifi_ap_config_t`) |
| ap.hidden | u8 | Optional | Whether the AP is hidden (1 for hidden, 0 for visible). (Refer parameter `uint8_t ssid_hidden` from `struct wifi_ap_config_t`) |
| ap.max.conn | u8 | Optional | Maximum number of connections the AP supports. It is different for different chips.(Please refer parameter `uint8_t max_connection` from `struct wifi_ap_config_t` and [AP Basic Configurations](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#ap-basic-configuration) for more details). |
| bcn.interval | u8 | Optional | Beacon interval time for the AP. (Refer parameter `uint16_t beacon_interval` from `struct wifi_ap_config_t`)) |
| ap.pmf_r | u8 | Optional | Whether PMF is required on the AP. (Refer parameter `wifi_pmf_config_t pmf_cfg` from `struct wifi_ap_config_t`)) |
| ap.p_cipher | u8 | Optional | Pairwise Cipher suite used by the AP e.g. enum wifi_cipher_type_t (Refer parameter `wifi_cipher_type_t pairwise_cipher` from `struct wifi_ap_config_t`) |
| ap.ftm_r | u8 | Optional | Whether FTM (Fine Timing Measurement) responder is enabled. (Refer parameter `bool ftm_responder` from `struct wifi_ap_config_t`) |
| ap.sae_h2e | u8 | Optional | Config method for SAE e.g. enum `wifi_sae_pwe_method_t` (Refer parameter `wifi_sae_pwe_method_t sae_pwe_h2e` from `struct wifi_ap_config_t`) |
| ap.csa_count | u8 | Optional | Channel Switch Announcement count for the AP. (Refer parameter `uint8_t csa_count` from `struct wifi_ap_config_t`) |
| ap.dtim_period | u8 | Optional | DTIM period for the AP. (Refer parameter `uint8_t dtim_period` from `struct wifi_ap_config_t`) |
| ap.phym5g | u8 | Optional | 5 GHz physical mode. (Refer `esp_err_t esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap)`) |
| ap.phybw5g | u8 | Optional | 5 GHz bandwidth setting. (Refer `esp_err_t esp_wifi_set_bandwidths(wifi_interface_t ifx, wifi_bandwidth_t bw)`) |
### Generic Keys
| Key Name | Encoding Type | Mandatory/Optional | Description / Meaning |
|--------------------------|---------------|---------------------|----------------------------------------------------|
| opmode | u8 | Mandatory | Operating mode (e.g. enum `wifi_mode_t` OR refer `esp_err_t esp_wifi_set_mode(wifi_mode_t mode)`) |
| country | blob | Optional | Operating country (e.g. enum `wifi_country_t` OR refer `esp_err_t esp_wifi_set_country_code(const char *country, bool ieee80211d_enabled)`) |
| band_mode | u8 | Optional | Operating band (e.g. enum `wifi_band_t` or refer [esp_err_t esp_wifi_set_band_mode(wifi_band_mode_t band_mode)](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv422esp_wifi_set_band_mode16wifi_band_mode_t)) |
These keys can be configured by setting the desired values in `nvs_station_data.csv` or `nvs_ap_data.csv`.
For more details on station and AP configurations, refer to the following:
- [Station Basic Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#station-basic-configuration)
- [AP Basic Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#ap-basic-configuration)
Also review the limitations for values of each key in the [ESP-IDF API Reference Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html).
## Example Output
Note that the output, in particular the order of the output, may vary depending on the environment.
Console output if station connects to AP successfully:
```
I (599) wifi: wifi driver task: 3ffc08b4, prio:23, stack:3584, core=0
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (629) wifi: wifi firmware version: 2d94f02
I (629) wifi: config NVS flash: enabled
I (629) wifi: config nano formatting: disabled
I (629) wifi: Init dynamic tx buffer num: 32
I (629) wifi: Init data frame dynamic rx buffer num: 32
I (639) wifi: Init management frame dynamic rx buffer num: 32
I (639) wifi: Init management short buffer num: 32
I (649) wifi: Init static rx buffer size: 1600
I (649) wifi: Init static rx buffer num: 10
I (659) wifi: Init dynamic rx buffer num: 32
I (759) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
I (769) wifi: mode : sta (30:ae:a4:d9:bc:c4)
I (769) wifi station: wifi_init_sta finished.
I (889) wifi: new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1
I (889) wifi: state: init -> auth (b0)
I (899) wifi: state: auth -> assoc (0)
I (909) wifi: state: assoc -> run (10)
I (939) wifi: connected with #!/bin/test, aid = 1, channel 6, BW20, bssid = ac:9e:17:7e:31:40
I (939) wifi: security type: 3, phy: bgn, rssi: -68
I (949) wifi: pm start, type: 1
I (1029) wifi: AP's beacon interval = 102400 us, DTIM period = 3
I (2089) esp_netif_handlers: sta ip: 192.168.77.89, mask: 255.255.255.0, gw: 192.168.77.1
I (2089) wifi station: got ip:192.168.77.89
I (2089) wifi station: connected to ap SSID:myssid password:mypassword
```
Console output if the station failed to connect to AP:
```
I (599) wifi: wifi driver task: 3ffc08b4, prio:23, stack:3584, core=0
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (629) wifi: wifi firmware version: 2d94f02
I (629) wifi: config NVS flash: enabled
I (629) wifi: config nano formatting: disabled
I (629) wifi: Init dynamic tx buffer num: 32
I (629) wifi: Init data frame dynamic rx buffer num: 32
I (639) wifi: Init management frame dynamic rx buffer num: 32
I (639) wifi: Init management short buffer num: 32
I (649) wifi: Init static rx buffer size: 1600
I (649) wifi: Init static rx buffer num: 10
I (659) wifi: Init dynamic rx buffer num: 32
I (759) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
I (759) wifi: mode : sta (30:ae:a4:d9:bc:c4)
I (769) wifi station: wifi_init_sta finished.
I (889) wifi: new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1
I (889) wifi: state: init -> auth (b0)
I (1889) wifi: state: auth -> init (200)
I (1889) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (1889) wifi station: retry to connect to the AP
I (1899) wifi station: connect to the AP fail
I (3949) wifi station: retry to connect to the AP
I (3949) wifi station: connect to the AP fail
I (4069) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (4069) wifi: state: init -> auth (b0)
I (5069) wifi: state: auth -> init (200)
I (5069) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (5069) wifi station: retry to connect to the AP
I (5069) wifi station: connect to the AP fail
I (7129) wifi station: retry to connect to the AP
I (7129) wifi station: connect to the AP fail
I (7249) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (7249) wifi: state: init -> auth (b0)
I (8249) wifi: state: auth -> init (200)
I (8249) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
I (8249) wifi station: retry to connect to the AP
I (8249) wifi station: connect to the AP fail
I (10299) wifi station: connect to the AP fail
I (10299) wifi station: Failed to connect to SSID:myssid, password:mypassword
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "wifi_nvs_config_main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES esp_wifi nvs_flash)

View File

@@ -0,0 +1,136 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* WiFi example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
/* The examples use WiFi configuration that you can set via flashing nvs partition */
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define MAXIMUM_RETRY_COUNT 5
static const char *TAG = "wifi_nvs_config";
static int s_retry_num = 0;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_START) {
ESP_LOGI(TAG, "SoftAP started successfully!");
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "station join, AID=%d", event->aid);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI(TAG, "station leave, AID=%d, reason=%d", event->aid, event->reason);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < MAXIMUM_RETRY_COUNT) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init finished.");
/* 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) */
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, "Successfully connected to AP");
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to AP");
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
wifi_init();
}

View File

@@ -0,0 +1,8 @@
key,type,encoding,value
nvs.net80211,namespace,,
opmode,data,u8,2
ap.ssid,data,blob_sz_fill(32;0x00),myssid
ap.passwd,data,blob_fill(64;0x00),mypassword
ap.authmode,data,u8,3
ap.max.conn,data,u8,3
ap.pmf_r,data,u8,0
1 key type encoding value
2 nvs.net80211 namespace
3 opmode data u8 2
4 ap.ssid data blob_sz_fill(32;0x00) myssid
5 ap.passwd data blob_fill(64;0x00) mypassword
6 ap.authmode data u8 3
7 ap.max.conn data u8 3
8 ap.pmf_r data u8 0

View File

@@ -0,0 +1,17 @@
key,type,encoding,value
nvs.net80211,namespace,,
opmode,data,u8,1
sta.ssid,data,blob_sz_fill(32;0x00),myssid
sta.pswd,data,blob_fill(64;0x00),mypassword
bssid.set,data,u8,0
sta.lis_intval,data,u16,3
sta.scan_method,data,u8,1
sta.sort_method,data,u8,0
sta.pmf_r,data,u8,0
sta.btm_e,data,u8,0
sta.rrm_e,data,u8,0
sta.mbo_e,data,u8,0
sta.ft,data,u8,0
sta.trans_d,data,u8,0
sta.sae_h2e,data,u8,0
sta.sae_pk_mode,data,u8,0
1 key type encoding value
2 nvs.net80211 namespace
3 opmode data u8 1
4 sta.ssid data blob_sz_fill(32;0x00) myssid
5 sta.pswd data blob_fill(64;0x00) mypassword
6 bssid.set data u8 0
7 sta.lis_intval data u16 3
8 sta.scan_method data u8 1
9 sta.sort_method data u8 0
10 sta.pmf_r data u8 0
11 sta.btm_e data u8 0
12 sta.rrm_e data u8 0
13 sta.mbo_e data u8 0
14 sta.ft data u8 0
15 sta.trans_d data u8 0
16 sta.sae_h2e data u8 0
17 sta.sae_pk_mode data u8 0

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 # Name, Type, SubType, Offset, Size, Flags
4 nvs, data, nvs, 0x9000, 0x6000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 factory, app, factory, 0x10000, 1M,

View File

@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"