- 物聯網ESP32教程
- 首頁
- 物聯網概述
- ESP32簡介
- 在Arduino IDE中安裝ESP32板
- 設定RTOS以實現雙核和多執行緒操作
- ESP32與MPU6050介面
- ESP32與模擬感測器介面
- ESP32首選項
- ESP32 SPIFFS儲存(晶片本身中的迷你SD卡)
- ESP32與OLED顯示屏介面
- ESP32上的WiFi
- 使用HTTP透過WiFi傳輸資料
- 使用HTTPS透過WiFi傳輸資料
- 使用MQTT透過WiFi傳輸資料
- 透過藍牙傳輸資料
- 使用NTP客戶端獲取當前時間
- 執行ESP32韌體的(OTA)更新
- ESP32的應用
- 作為開發人員的後續步驟
- 物聯網ESP32有用資源
- 快速指南
- 有用資源
- 討論
ESP32中的SPIFFS
在上一章中,我們瞭解了首選項作為一種在非易失性儲存器中儲存資料的方式,並瞭解瞭如何使用它們來儲存鍵值對。在本節中,我們將介紹SPIFFS(SPI快閃記憶體檔案儲存),它用於以檔案形式儲存更大的資料。可以將SPIFFS視為ESP32晶片本身上的一個非常小的SD卡。預設情況下,大約1.5 MB的片上快閃記憶體分配給SPIFFS。您可以透過“工具”->“分割槽方案”來檢視。
您可以看到還有其他幾個分割槽選項可用。但是,現在我們先不要深入研究。對於大多數應用程式,更改分割槽方案都是不需要的。本教程中的所有章節都可以在預設分割槽方案下正常工作。
現在,讓我們透過一個示例瞭解建立、修改、讀取和從SPIFFS刪除檔案的過程。
程式碼演練
我們將再次使用提供的示例程式碼。轉到“檔案”->“示例”->“SPIFFS”->“SPIFFS_Test”。此程式碼非常適合瞭解SPIFFS可能進行的所有檔案操作。它也可以在GitHub上找到。
我們從包含兩個庫開始:FS.h和SPIFFS.h。FS代表檔案系統。
#include "FS.h" #include "SPIFFS.h"
接下來,您將看到一個宏定義FORMAT_SPIFFS_IF_FAILED。有一個關聯的註釋建議您僅在第一次執行測試時才需要格式化SPIFFS。這意味著您可以在第一次執行後將此宏的值設定為false。格式化SPIFFS需要時間,並且不需要每次執行程式碼時都執行。因此,人們採用的做法是為格式化SPIFFS編寫單獨的程式碼,並在重新整理主程式碼之前重新整理它。主程式碼不包含格式化命令。但是,在本示例中,為了完整起見,此宏已保留為true。
/* You only need to format SPIFFS the first time you run a test or else use the SPIFFS plugin to create a partition https://github.com/me−no−dev/arduino−esp32fs−plugin */ #define FORMAT_SPIFFS_IF_FAILED true
接下來,您可以看到已為不同的檔案系統操作定義了許多函式。它們是:
listDir - 列出所有目錄
readFile - 讀取特定檔案
writeFile - 寫入檔案(這會覆蓋檔案中原有的內容)
appendFile - 將內容追加到檔案(當您想要新增到現有內容而不是覆蓋它時使用)
renameFile - 更改檔名
deleteFile - 刪除檔案
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
Serial.printf("Listing directory: %s\r\n", dirname);
File root = fs.open(dirname);
if(!root){
Serial.println("− failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println(" − not a directory");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
Serial.print(" DIR : ");
Serial.println(file.name());
if(levels){
listDir(fs, file.name(), levels -1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print("\tSIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("− failed to open file for reading");
return;
}
Serial.println("− read from file:");
while(file.available()){
Serial.write(file.read());
}
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("− failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("− file written");
}else {
Serial.println("− frite failed");
}
}
void appendFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Appending to file: %s\r\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("− failed to open file for appending");
return;
}
if(file.print(message)){
Serial.println("− message appended");
} else {
Serial.println("− append failed");
}
}
void renameFile(fs::FS &fs, const char * path1, const char * path2){
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("− file renamed");
} else {
Serial.println("− rename failed");
}
}
void deleteFile(fs::FS &fs, const char * path){
Serial.printf("Deleting file: %s\r\n", path);
if(fs.remove(path)){
Serial.println("− file deleted");
} else {
Serial.println("− delete failed");
}
}
請注意,以上所有函式都沒有要求提供檔名。它們要求提供完整的檔案路徑。因為這是一個檔案系統。您可以在這些子目錄中擁有目錄、子目錄和檔案。因此,ESP32需要知道您要操作的檔案的完整路徑。
接下來是一個不完全是檔案操作函式的函式 - testFileIO。這更像是一個時間基準測試函式。它執行以下操作:
將大約1 MB(2048 * 512位元組)的資料寫入您提供的檔案路徑,並測量寫入時間
讀取相同的檔案並測量讀取時間
void testFileIO(fs::FS &fs, const char * path){
Serial.printf("Testing file I/O with %s\r\n", path);
static uint8_t buf[512];
size_t len = 0;
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("− failed to open file for writing");
return;
}
size_t i;
Serial.print("− writing" );
uint32_t start = millis();
for(i=0; i<2048; i++){
if ((i & 0x001F) == 0x001F){
Serial.print(".");
}
file.write(buf, 512);
}
Serial.println("");
uint32_t end = millis() − start;
Serial.printf(" − %u bytes written in %u ms\r\n", 2048 * 512, end);
file.close();
file = fs.open(path);
start = millis();
end = start;
i = 0;
if(file && !file.isDirectory()){
len = file.size();
size_t flen = len;
start = millis();
Serial.print("− reading" );
while(len){
size_t toRead = len;
if(toRead > 512){
toRead = 512;
}
file.read(buf, toRead);
if ((i++ & 0x001F) == 0x001F){
Serial.print(".");
}
len −= toRead;
}
Serial.println("");
end = millis() - start;
Serial.printf("- %u bytes read in %u ms\r\n", flen, end);
file.close();
} else {
Serial.println("- failed to open file for reading");
}
}
請注意,buf陣列從未初始化任何值。我們很可能會將垃圾位元組寫入檔案。這沒關係,因為該函式的目的是測量寫入時間和讀取時間。
在定義完函式後,我們將繼續進行設定,其中顯示了每個函式的呼叫。
void setup(){
Serial.begin(115200);
if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){
Serial.println("SPIFFS Mount Failed");
return;
}
listDir(SPIFFS, "/", 0);
writeFile(SPIFFS, "/hello.txt", "Hello ");
appendFile(SPIFFS, "/hello.txt", "World!\r\n");
readFile(SPIFFS, "/hello.txt");
renameFile(SPIFFS, "/hello.txt", "/foo.txt");
readFile(SPIFFS, "/foo.txt");
deleteFile(SPIFFS, "/foo.txt");
testFileIO(SPIFFS, "/test.txt");
deleteFile(SPIFFS, "/test.txt");
Serial.println( "Test complete" );
}
設定基本上執行以下操作:
它首先使用SPIFFS.begin()初始化SPIFFS。此處使用了開頭定義的宏。為true時,它會格式化SPIFFS(耗時);為false時,它會在不格式化的前提下初始化SPIFFS。
然後,它列出根級別上的所有目錄。請注意,我們已將級別指定為0。因此,我們沒有列出目錄內的子目錄。您可以透過增加levels引數來增加巢狀級別。
然後,它將“Hello”寫入根目錄中的hello.txt檔案。(如果檔案不存在,它將被建立)
然後,它讀取回hello.txt
然後,它將hello.txt重新命名為foo.txt
然後,它讀取foo.txt以檢視重新命名是否成功。您應該看到打印出“Hello”,因為這就是儲存在檔案中的內容。
然後,它刪除foo.txt
然後,它在新的檔案test.txt上執行testFileIO例程
執行完例程後,它刪除test.txt
就是這樣。此示例程式碼很好地列出了並測試了您可能想要與SPIFFS一起使用的所有函式。您可以繼續修改此程式碼,並嘗試不同的函式。
由於我們不想在這裡執行任何重複活動,因此迴圈為空。
void loop(){
}
序列監視器中顯示的輸出可能類似於下圖:
注意 - 如果在執行草圖時出現“SPIFFS Mount Failed”,請將FORMAT_SPIFFS_IF_FAILED的值設定為false,然後重試。