ESP32を使っていて外部のサーバと通信するような場合、サーバのAPIを呼び出して、JSON形式のデータを取得するようなことがあります。
一時的に利用するデータであればメモリ上の変数に保持しておけばよいですが、永続的に保存したい場合に、ファイルに保存しておきたいという場合があると思います。
ファイルに保存する場合、ESP32ではLittleFSというファイルシステムを使ってファイルにするのが推奨されているので、タイトルの通りESP32でLittleFSを利用してファイルに保存する方法について書いておきたいと思います。
目次
ESP32での不揮発性ストレージへの保存について
上記のように不揮発性の領域にデータを永続的に保存したい場合、
ESP32では、小規模な設定値やセンサーなどの補正値といったデータであれば、NVS(Non-Volatile Storage)というキーバリュー型のストレージを利用するのが簡単で便利です。
例えば、 device_name: "ESP32", boot_count: 5 といったように、キーと値のペアで数値や文字列、任意のバイナリデータを保存することができます。
https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html
ただし、NVSでは大規模なデータを保存するのには適していません。
https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html
文字列型は最大 4000 バイト、可変長のBlob型(バイナリ)で508,000バイトまで保存可能です。
大規模なデータを保存する場合は、WindowsやMac、Linuxと同様にファイルに保存することができます。
ESP32ではFATファイルシステム、SPIFFS、LittleFSの3種類のファイルシステムが利用可能です。
それぞれの特徴をまとめると以下の表のようになります。
| ファイルシステム | 特徴 | 電断耐性 | ディレクトリのサポート* | 持ち運び可能性* |
|---|---|---|---|---|
| FAT | WindowsやMac、Linuxでサポートされている | 中 | あり | 高 |
| SPIFFS | SPIFlash向けのファイルシステム | 低 | なし | 低 |
| LittleFS | 電源障害やフラッシュ書き込みを考慮した組み込み向けファイルシステム | 高 | あり | 低 |
ESP32ではFATファイルシステムがサポートされているので、SDカードなどの記憶メディアを利用する場合は、FATファイルシステムを利用することでデータの持ち運びが可能になります。
ただし電源断耐性はLittleFSと比べると高くありません。電源障害が発生する可能性がある場合は、SPIFFSやLittleFSを利用することが推奨されます。
SPIFFSはSPIFlash向けのファイルシステムで、ESP32の内蔵フラッシュメモリに保存することができます。
ただし、電源障害に対する耐性はLittleFSと比べて弱く、またディレクトリのサポートもないため、ESP32の内蔵フラッシュメモリに保存する場合は、最近ではLittleFSを利用することが推奨されているようです。
LittleFSを利用する準備
LittleFSはESP-IDFの外部コンポーネントとして提供されているため、明示的にプロジェクトに追加する必要があります。
プロジェクトへの外部コンポーネントの追加は、idf.py add-dependencyコマンドを使うと簡単に行うことができます。
idf.py add-dependency joltwallet/littlefs==1.20.4
プロジェクトで使用するコンポーネントの情報はidf_component.yml に記載されます。
add-dependencyコマンドを使わずに直接このファイルを編集する形で外部コンポーネントを追加することも可能です。
ちなみに、一度 idf.py build を実行した後に add-dependencyコマンドを実行してコンポーネントを追加した場合は、
ビルドキャッシュが残っているため、追加したコンポーネントのヘッダーファイルが見つからずにビルドエラーになってしまいます。
この場合は、
idf.py clean
を実行して、ビルドキャッシュを削除する必要があります。
パーティションテーブルの設定
LittleFSを利用するためには、パーティションテーブルにLittleFS用のパーティションを追加する必要があります。
ESP-IDFのプロジェクトでは、CSV形式でパーティションテーブルを定義することができます。
プロジェクトのルートディレクトリに、partitions.csv というファイルを作成して、以下のようにLittleFS用のパーティションを追加します。
# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 24K, phy_init, data, phy, 0xf000, 4K, factory, app, factory, 0x10000, 1M, littlefs, data,littlefs, , 1M,
この例では、littlefs という名前のパーティションを追加しています。
partitions.csvを作成しただけでは、LittleFSが利用できるわけではありません。
パーティションテーブルを定義した後は、プロジェクトのコンフィギュレーションで、Partition Tableの項目をCustom partition table CSV に変更して、先ほど作成した partitions.csv を指定します。
これで、ビルド時にESP32のフラッシュメモリにLittleFS用のパーティションが作成されるようになります。
LittleFSを利用するコードの例
#include <stdio.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include "esp_err.h" #include "esp_log.h" #include "esp_system.h" #include "nvs_flash.h" #include "nvs.h" #include "esp_littlefs.h" static const char *TAG = "storage-sample"; void app_main(void) { esp_vfs_littlefs_conf_t conf = { .base_path = "/littlefs", .partition_label = NULL, .format_if_mount_failed = true, .dont_mount = false }; esp_err_t ret = esp_vfs_littlefs_register(&conf); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to mount LittleFS (%s)", esp_err_to_name(ret)); return; } ESP_LOGI(TAG, "LittleFS mounted"); // logsフォルダの存在チェック struct stat st; if (stat("/littlefs/logs", &st) != 0) { ESP_LOGI(TAG, "logs folder does not exist. Creating..."); ret = mkdir("/littlefs/logs", 0777); if (ret != 0) { ESP_LOGE(TAG, "Failed to create logs folder"); return; } } else { ESP_LOGI(TAG, "logs folder exists"); } // hoge.txtへの書き込み const char *write_buf = "Hello, LittleFS!"; FILE *fp = fopen("/littlefs/hoge.txt", "a"); if (fp == NULL) { ESP_LOGE(TAG, "Failed to open hoge.txt for appending"); return; } fwrite(write_buf, 1, strlen(write_buf), fp); fwrite("\n", 1, 1, fp); fclose(fp); esp_vfs_littlefs_unregister(conf.partition_label); ESP_LOGI(TAG, "LittleFS unmounted"); }
LittleFSを利用するためには、まずLittleFSをマウントする必要があります。
ファイルやディレクトリ操作に対応する関数
LittleFSをマウントした後は、ファイルやディレクトリの操作に対応する関数を利用して、ファイルの読み書きやディレクトリの作成などを行うことができます。
操作はC言語の標準ライブラリの関数で利用することができます。
主な操作で使用する関数は以下の通りです。
| 操作 | 関数 |
|---|---|
| ファイルの作成 | fopen |
| ファイルの読み書き | fread, fwrite |
| ファイルのクローズ | fclose |
| ディレクトリの作成 | mkdir |
| ディレクトリの削除 | rmdir |
| ディレクトリ情報へのアクセス | opendir,readdir,closedir |
| ファイルの削除 | unlink |
| ファイル・ディレクトリの存在確認 | stat |
ESP-IDFでの開発について
LittleFSのようなストレージ管理は、ESP-IDFのメモリ構造やパーティションテーブルの理解があると、より安全に設計できます。
- パーティションテーブルについて
- NVSとファイルシステムの使い分け
- PSRAMと内部RAMの違い
といった、ESP-IDF開発で必要になる基礎を体系的に解説しています。
FreeRTOSやWiFi・BLE通信、周辺機能(GPIO,UART,I2C)の使い方など幅広くまとめたコースになっておりますのでこれから本格的にESP32・ESP-IDFを使って開発される方はぜひご覧ください。