ESP32でSPIFFSの読み書き(本体フラッシュメモリ)

Arduino/ESP32Arduino,ESP32,SPIFFS,ファイルシステム

ESP32をWebサーバーとして使うのに、プログラムからファイルを操作できると非常にラクなのでSPIFFSを使ってみる事に。

まずは公式のespressif / arduino-esp32をチェックしてみます。

GitHub:espressif / arduino-esp32 /libraries/SPIFFS/

examplesをざっくりと眺めた感じ、SPIIFSライブラリでインスタンスハンドルを取得してFSライブラリで操作するみたいですね。

FSクラスのラッパーみたいな感じで。

GitHub:espressif / arduino-esp32 /libraries/FS/

ファイル操作についてはこのFSライブラリをざっくり見ておけばいいでしょう。

SPIFFSでファイルの読み書き examples:SPIFFS_Test.ino

今回は簡単にexamplesを見てみます。

#include "FS.h"
#include "SPIFFS.h"

/* 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

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());
    }
    file.close();
}

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("- write failed");
    }
    file.close();
}

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");
    }
    file.close();
}

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");
    }
}

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");
    }
}

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" );
}

void loop(){

}

beginしてopenして書き込んだり読み込んだりしてcloseする。

ディレクトリも使える。

但しリッチではないのでパーミッションとかディレクトリシンボルとか無い、ファイルパスとしての概念だけなのかな?

何れにしてもごく普通のファイル操作の様な手順で簡単安心です。

GitHub:espressif / arduino-esp32 /libraries/SPIFFS/examples/SPIFFS_Test/SPIFFS_Test.ino

わざわざオープンして文字列変換してとか面倒な人用に、examplesの関数がそのまま実用的っぽいのでコピペしておいてもヨサゲ。

その他メソッドに、format、totalBytes、usedBytesなんてのもある様ですが、細かな機能は必要になったら調べれば良いでしょう。

注意点としては書き込み先はフラッシュメモリなので、秒毎にログを取る様な使い方をするとすぐ死んでしまいそうという事。

HTMLファイルを置いたり、設定ファイルを置いたりといった感じでしょうか。

SSLのCA証明書なんかもここに保存しておくと取扱がラクかもしれませんね。

一応ArduinoIDEのプラグインでファイルアップローダーみたいなのがある様で、とりあえずファイルを転送したい時に便利かも。

GitHub: me-no-dev / arduino-esp32fs-plugin

全体としてよくまとまっていて非常に使いやすいライブラリです。

もともとESP32のパーティションテーブルでもフラッシュメモリの後ろの方に配置されているので結構長持ちしそうな感じですね。

WebServerにファイル操作するインターフェースを作ってやってブラウザから操作とか便利かもです。

面倒なのでやらなそうですが・・・

とりあえずメモ帳代わりに残しておきます。