1.ESP32分區表
- 為什么ESP32要分區? ? 00:34--
- 簡述:其他單片機生成文件少,功能少,而ESP32功能多,文件多? ? ? ?
- 分區表各個文件簡介? ?--7:31
- vscode查看分區表? --9:33
- ota通過idf.py menuconfig配置命令調出配置菜單? --12.35
- 創建自己的分區? --17.41
- 寫.c文件
-
#include <stdio.h> #include "esp_partition.h" #include "esp_log.h" #include <string.h>static const char* TAG = "partition"; //TAG 區分日志#define USER_PARTITION_TYPE 0x40 #define USER_PARTITION_SUBTYPE 0x00 //定義分區類型static const esp_partition_t* partition_user = NULL; //定義分區指針操作分區的void app_main(void) {//找到自定義的分區,找得到之后會返回分區指針/*知識點1find與find_first的區別 --21.10find_first: 找到第一個符合條件的分區find: 找到符合條件的所有分區*/partition_user = esp_partition_find_first(USER_PARTITION_TYPE, USER_PARTITION_SUBTYPE, NULL); if (partition_user == NULL) {ESP_LOGE(TAG, "Failed to find user partition");return;}esp_partition_erase_range(partition_user, 0, 0x1000); //擦除分區const char *data = "Hello, ESP32!";esp_partition_write(partition_user, 0, data, strlen(data)); //寫入分區 char buffer[100];memset(buffer, 0, sizeof(buffer));esp_partition_read(partition_user, 0, buffer,strlen(data)); //讀取分區ESP_LOGI(TAG, "Read from user partition: %s", buffer);return;}
-
補充1:分區表
# Name, ? Type, SubType, Offset, ?Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, ? ? ?data, nvs, ? ? , ? ? ? ?0x6000,
phy_init, data, phy, ? ? , ? ? ? ?0x1000,
factory, ?app, ?factory, , ? ? ? ?1M,
user, ? ? 0x40, 0x01, ? ?, ? ? ? ?0x1000, ?列含義
Name
:分區的名稱,方便識別和引用。Type
:分區的類型,常見有?app
(應用程序)、data
(數據),也可以用十六進制表示自定義類型。SubType
:分區類型的子類型,進一步細分分區用途。Offset
:分區在存儲設備上的起始偏移地址。Size
:分區的大小,可以用十六進制(如?0x1000
)或單位(如?1M
)表示。Flags
:分區的額外標志,可選字段。每行解釋
nvs
:非易失性存儲分區,用于存儲系統和應用的配置信息。Type
?為?data
,SubType
?為?nvs
,大小是?0x6000
。phy_init
:物理層初始化數據分區,存儲與硬件物理層初始化相關的數據。Type
?為?data
,SubType
?為?phy
,大小是?0x1000
。factory
:工廠應用分區,用于存儲出廠默認的應用程序。Type
?為?app
,SubType
?為?factory
,大小是?1M
。user
:自定義分區,Type
?是十六進制?0x40
,SubType
?是?0x01
,大小是?0x1000
,可用于用戶自定義的數據或應用。補充2 OTA
OTA 分區作用
設備軟件更新時,新固件先下載到 OTA 分區,驗證通過后,系統從該分區加載新固件,避免直接覆蓋運行中的程序,提升更新安全性與可靠性。
OTA 分區常見配置示例及解釋
# Name, Type, SubType, Offset, Size, Flags ota_0, app, ota_0, , 1M, ota_1, app, ota_1, , 1M,
- Name:常見的 OTA 分區名稱有 ota_0 和 ota_1,系統會在這兩個分區間切換來實現固件更新。
- Type:分區類型為 app,用于存儲應用程序固件。
- SubType:ota_0 和 ota_1 是 OTA 分區的子類型,可區分不同 OTA 槽位,讓系統確定當前使用的分區。
- Offset:即分區在存儲設備上的起始偏移地址,留空由系統自動分配。
- Size:每個 OTA 分區大小設為 1M,最多能存儲 1MB 的應用程序固件。
設備實際使用時,通過 ota_0 和 ota_1 分區交替實現更新。若當前運行 ota_0 固件,新固件下載至 ota_1,驗證通過后,下次啟動便從 ota_1 加載新固件 。
本章脈絡?
2.ESP中的NVS?
如何更方便的管理NVS存儲操作
- 背景? ---3.20
- 操作??
- 新建nvs測試程序
- 創建項目文件夾
- esp32下idf.py create-project nvs_test
- nvs_test下idf.py build
- VScode下打開文件夾
- 代碼
- 定義兩個命名空間,兩個空間中有相同的鍵,向這兩個鍵寫入不同的值,觀察他們會不會受到影響
- 創建項目文件夾
- 新建nvs測試程序
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_err.h"#define NAME_SPACE_WIFI1 "wifi1"
#define NAME_SPACE_WIFI2 "wifi2"#define KEY_SSID_KEY "ssid"
#define KEY_PASSWORE_KEY "password"void nvs_blob_read(const char *name_space, const char *key, void *buffer, int maxlen)
{nvs_handle_t nvs_handle; //用于存儲NVS分區的句柄size_t length = 0;nvs_open(name_space, NVS_READONLY, &nvs_handle);nvs_get_blob(nvs_handle, key, (void*)&length, &length); //讀取鍵數據,并獲取鍵數據的長度(主要是這個)if (length && length <= maxlen) //如果鍵數據存在且長度不超過maxlen{nvs_get_blob(nvs_handle, key, buffer, &length); //讀取鍵數據}else{printf("nvs_blob_read: %s is not exist or too long\n", key); //打印錯誤信息}nvs_close(nvs_handle); //關閉NVS分區
}void app_main(void)
{nvs_handle_t nvs_handle1;esp_err_t ret = nvs_flash_init(); //初始化NVS分區if (ret != ESP_OK) { //如果初始化失敗,擦除NVS分區,再初始化NVS分區nvs_flash_erase(); //擦除NVS分區ESP_ERROR_CHECK(nvs_flash_init()); //ESP_ERROR_CHECK()是一個宏定義,用于檢查函數的返回值,如果返回值不為ESP_OK,則打印錯誤信息并終止程序}/*命名空間1 NVS寫入*/nvs_open(NAME_SPACE_WIFI1, NVS_READWRITE, &nvs_handle1); //打開NVS分區,讀寫模式nvs_set_blob(nvs_handle1, KEY_SSID_KEY, "wifi_esp32", strlen("wifi_esp32")); //寫入鍵數據nvs_set_blob(nvs_handle1, KEY_PASSWORE_KEY, "12345678", strlen("12345678")); //寫入密碼數據// strlen()函數是一個C語言庫函數,用于計算字符串的長度。nvs_commit(nvs_handle1); //提交數據/*如果不調用nvs_commit()函數,那么數據將不會被寫入到NVS分區中(flash)。寫入的時機要跟隨系統走如果調用nvs_commit()函數,那么數據將立即被寫入到NVS分區中(flash)。不許推遲寫入*/nvs_close(nvs_handle1); //關閉NVS分區/*命名空間2 NVS寫入*/ nvs_open(NAME_SPACE_WIFI2, NVS_READWRITE, &nvs_handle1); //打開NVS分區,讀寫模式nvs_set_blob(nvs_handle1, KEY_SSID_KEY, "hello,world", strlen("hello,world")); //寫入鍵數據nvs_set_blob(nvs_handle1, KEY_PASSWORE_KEY, "654321", strlen("654321")); //寫入密碼數據nvs_commit(nvs_handle1); //提交數據nvs_close(nvs_handle1); //關閉NVS分區vTaskDelay(1000 / portTICK_PERIOD_MS); //延時1秒char read_buffer[64];//用于存儲鍵數據//命名空間WIFI1 SSID的值memset(read_buffer, 0, sizeof(read_buffer)); //清空read_buffer數組nvs_blob_read(NAME_SPACE_WIFI1, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); //讀取鍵數據ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI1,KEY_SSID_KEY,read_buffer); //打印鍵數據
//命名空間WIFI1 PASSWORD的值memset(read_buffer, 0, sizeof(read_buffer)); nvs_blob_read(NAME_SPACE_WIFI1, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI1,KEY_PASSWORE_KEY,read_buffer);
//命名空間WIFI2 SSID的值memset(read_buffer, 0, sizeof(read_buffer)); nvs_blob_read(NAME_SPACE_WIFI2, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI2,KEY_SSID_KEY,read_buffer);
//命名空間WIFI2 PASSWORD的值memset(read_buffer, 0, sizeof(read_buffer)); nvs_blob_read(NAME_SPACE_WIFI2, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI2,KEY_PASSWORE_KEY,read_buffer);
}
3.ESP32中的SPIFFS文件系統
- 新建SPIFFS測試程序
- 創建項目文件夾----4.00
- esp32下idf.py create-project spiffs_test
- spiffs_test下 cp ../esp-idf/components/partition_table/partitions_singleapp.csv .
- 注意這個partitions,這里后面的s不要拉下
- 不要忘了后面有個點
- 通過idf.py menuconfig指定分區表
- spiffs_test下idf.py build
- VScode下打開文件夾
- 改分區表? 5.48--6.30
- 記得改成512k而不是1M,后面會改
- 代碼
- 配置spiffs文件系統
-
esp_vfs_spiffs_register具體實現邏輯? 9.10
-
-
文件操作
- 文件打開:
f = fopen("/spiffs/hello.txt","r");
?嘗試以只讀模式打開位于 SPIFFS 文件系統下的?hello.txt
?文件。如果打開失敗(f
?為?NULL
),則通過?ESP_LOGE
?輸出錯誤日志并返回,結束當前函數執行。
- 文件讀取:
fgets(line,sizeof(line),f);
?使用?fgets
?函數從打開的文件?f
?中讀取一行內容,存儲到字符數組?line
?中,最多讀取?sizeof(line) - 1
?個字符。fclose(f);
?讀取完成后關閉文件。
- 處理換行符:
char *pos = strchr(line,'\n');
?查找字符數組?line
?中第一次出現換行符?\n
?的位置。- 如果找到換行符(
pos
?不為?NULL
),則將該位置的字符設置為?'\0'
,即把換行符替換為字符串結束符,從而去掉換行符。
- 日志輸出:
ESP_LOGI("spiffs","read str:%s",line);
?使用?ESP_LOGI
?輸出讀取到的字符串內容。
- 文件系統注銷:
esp_vfs_spiffs_unregister(conf.partition_label);
?注銷之前注冊的 SPIFFS 文件系統,這里的?conf
?應該是之前定義好的配置結構體,partition_label
?是文件系統分區標簽。
- 文件打開:
- 配置spiffs文件系統
- 創建項目文件夾----4.00
????????VFS(虛擬文件系統)是一個抽象層,它讓操作系統能以統一的方式管理不同類型的文件系統,就像一個萬能的 “翻譯官”,讓你不用管具體的文件系統差異,就能方便地操作文件。
????????抽象層是一種編程和系統設計里常用的概念。
????????現實中存在很多不同類型的文件系統,比如 FAT、NTFS、ext4 等,它們存儲和管理文件的方式各不相同。
????????抽象層就像是一個 “翻譯官” 和 “協調者”。VFS 作為抽象層,把不同文件系統的具體操作細節都隱藏起來,給操作系統和用戶程序提供一套統一的接口。無論你用的是哪種底層文件系統,操作系統和程序都可以通過這套統一接口來進行文件的創建、讀取、修改、刪除等操作。
????????這樣一來,操作系統和程序開發者就不用關心具體是哪種文件系統在工作,降低了開發和使用的復雜度。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_spiffs.h"
#include "esp_err.h"
#include "esp_log.h"
#include <string.h>void app_main(void)
{// 定義 SPIFFS 配置結構體esp_vfs_spiffs_conf_t conf = {.base_path = "/spiffs", // 掛載基礎路徑.format_if_mount_failed = true, // 掛載失敗時格式化.max_files = 5, // 最大可打開文件數.partition_label = NULL // 分區標簽為 NULL};// 注冊 SPIFFS 文件系統esp_err_t ret = esp_vfs_spiffs_register(&conf);if (ret != ESP_OK) {ESP_LOGE("spiffs", "spiffs mount fail!");return;}// 檢查 SPIFFS 分區完整性ret = esp_spiffs_check(conf.partition_label);if (ret != ESP_OK) {ESP_LOGE("spiffs", "spiffs check fail!");return;}// 獲取 SPIFFS 分區信息size_t total = 0, used = 0;ret = esp_spiffs_info(conf.partition_label, &total, &used);if (ret != ESP_OK) {ESP_LOGE("spiffs", "spiffs info fail!");return;}ESP_LOGI("spiffs", "spiffs total size:%d,used:%d", total, used);// 若使用空間大于總空間,再次檢查分區完整性if (used > total) {ret = esp_spiffs_check(conf.partition_label);if (ret != ESP_OK) {ESP_LOGE("spiffs", "used > total & spiffs check fail!");return;}}// 以寫入模式打開文件FILE *f = fopen("/spiffs/helloworld.txt", "w");if (f == NULL) {ESP_LOGE("spiffs", "Failed to open file");return;}// 寫入內容fprintf(f, "Hello, World!\n");// 關閉寫入的文件fclose(f);// 延時 1 秒vTaskDelay(pdMS_TO_TICKS(1000));// 以讀取模式打開文件f = fopen("/spiffs/helloworld.txt", "r");if (f == NULL) {ESP_LOGE("spiffs", "Failed to open file for read");return;}// 讀取文件一行內容char line[64];fgets(line, sizeof(line), f);// 關閉讀取的文件fclose(f);// 去除換行符char *pos = strchr(line, '\n');if (pos) {*pos = 0;}// 打印讀取的內容ESP_LOGI("spiffs", "read str:%s", line);// 注銷 SPIFFS 文件系統esp_vfs_spiffs_unregister(conf.partition_label);
}