小嶋秀樹 | 研究室
日本語 | English
ファイルシステムの使い方(1): アップローダを使う

ESP は,プログラム(スケッチ)からアクセスするデータを「ファイル」としてフラッシュメモリに格納することができます.たとえばログデータや画像データ,HTML/CSS/JS などのウェブデータなどがその対象となります.ファイルは,フラッシュメモリ上に構築された「ファイルシステム」に格納され,このフラッシュメモリは SPI 接続によってコアプロセッサに接続されているため,このファイルシステムは SPIFFS (SPI Flash File System)と呼ばれます.

ファイルは,ディレクトリ(フォルダ)階層をもつ「パス名」によって識別されます.たとえば "/hoge.txt" や "/image/moge.png" のように,通常の Unix ファイルシステムのようにアクセスできます.

SPIFFS では,階層化されたパス名(たとえば "/image/moge.png" や "/image/foo/moge.png" など)を,これらパス名を「ファイル名」とする無階層のファイル群としてフラットに扱っています.このパス名は 31 文字が上限となっているので,あまり深い階層はつくれません.注意してください.

【SPIFFS の概要】

ESP-WROOM-02 には 4MB のフラッシュメモリが搭載されています.下図のように,そのうち 1MB は Arduino スケッチが書き込まれる「スケッチ領域」です.残りの 3MB は,ファイルをアップロードしたり,あるいは Arduino プログラムからファイル操作(読み・書き・新規作成・削除など)が可能な「ファイルシステム領域」です.(ここでは Tools > Flash Size: "4M (3M SPIFFS)" の設定を前提としています.)

これら2つの領域への書き込みは,独立した2つの方法で行えます.スケッチ領域(1MB)にプログラムを書き込むには,従来どおり,prog スイッチを押しながら ESP をリセットし,Arduino IDE の Upload ボタンを押すだけです.一方,ファイルシステム領域にファイルをアップロードするには「ファイルアップローダ (ESP8266 Sketch Data Upload)」というプラグイン機能をインストール・利用することが必要です.

ここで解説するのは,あらかじめ用意したファイル(HTML ファイルや PNG 画像など)を Arduino IDE によってファイルシステム領域にアップロードする方法です.アップロードされたファイルは,プログラムから自由にアクセス(読み・書き・削除など)することができます.(この方法のほかに,Arduino プログラムから新規ファイルを生成することも可能です.これについては,このページの後半で解説します.)

【ファイルアップローダ(プラグイン)のインストール】

まず,ファイルをアップロードするためのプラグインを Arduino IDE に組み込むことが必要になります.https://github.com/esp8266/arduino-esp8266fs-plugin からプラグインをダウンロードしてください.これを解凍し,ESP8266FS > tool の中にある esp8266fs.jar というファイルを,Arduino のルートフォルダ(自分のホーム > Documents > Arduino)> tools(もし無ければ新規作成)> ESP8266FS(新規作成)> tool(新規作成)の中にコピーまたは移動します.Arduino IDE を起動(または再起動)すると,下図のような新しい項目が Tools メニューに現れるはずです.(まだ実行はしないように!)

【データファイルの準備】

インストールしたプラグインは,HTML ファイルや画像ファイルなどのデータファイルを一括アップロードするときに使います.その元になるデータファイルは,そのデータファイルを扱うスケッチ(.ino)が含まれているフォルダの中に data というサブフォルダを作って,その中に入れておきます.この data フォルダの中身がごっそりアップロードされることになります.

たとえば esp_webFileTest というスケッチであれば,上図のようなファイル構成になります.data フォルダの中に,index.html と openlab.css,そして img フォルダの中に多くの画像ファイルが置かれています.これらファイルは,"/index.html", "/openlab.css", "/img/laser360sketch0.png" などとして,プログラムから「見える」ようになります.

【一括アップロード】

この data フォルダの中身をごっそり ESP のファイルシステム領域に転送するには,ESP を prog スイッチを押しながら reset スイッチをクリックし,前出の Tools > ESP8266 Sketch Data Upload を実行します.3MB のイメージ全体を一括転送するので多少の時間(数分)がかかります

注意1: data フォルダの中身は 3MB を超えてはいけません.data フォルダのプロパティを調べるなどして,ファイルの総バイト数をチェックしておいて ください.

注意2: この操作(ESP8266 Sketch Data Upload)によって,3MB のファイルシステム領域のすべてが新しいイメージで置き換わります.したがって,それ以前にファイルシステム領域に格納されていたファイルはすべて消去されます.ご注意ください.

注意2: データファイルのアップロード時は,シリアルモニタを閉じておいてください.開いたままだとエラーとなります.

【ウェブサーバでテストしてみる】

SPIFFS の動作を実際にテストしてみましょう.ここではウェブサーバ(独自 AP)を動作させ,ファイルシステムに書き込んだ HTML ファイルや画像ファイルなどを外部クライアントから閲覧することを試します.プログラムはつぎのようになります.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <FS.h>

const char *ssid = "xkozima";        //  *** 書き換え必要 ***
const char *pass = "dongbeino";      //  *** 書き換え必要(8文字以上)***
ESP8266WebServer Server(80);         //  ポート番号(HTTP)

//  Server.on(...) を指定せず,すべてを handleNotFound で処理する.
//  URI で指定されるファイルがあればクライアントに転送する.
//  なければ 404 エラー!
void handleNotFound() {
  if (! handleFileRead(Server.uri())) {
    //  ファイルが見つかりません
    Serial.println("404 not found");
    Server.send(404, "text/plain", "File not found in Dongbeino...");
  }
}
//  MIMEタイプを推定
String getContentType(String filename){
  if(filename.endsWith(".html") || filename.endsWith(".htm")) return "text/html";
  else if(filename.endsWith(".css")) return "text/css";
  else if(filename.endsWith(".js")) return "application/javascript";
  else if(filename.endsWith(".png")) return "image/png";
  else if(filename.endsWith(".gif")) return "image/gif";
  else if(filename.endsWith(".jpg")) return "image/jpeg";
  else return "text/plain";
}
//  SPIFSS のファイルをクライアントに転送する
bool handleFileRead(String path) {
  Serial.println("handleFileRead: trying to read " + path);
  // パス指定されたファイルがあればクライアントに送信する
  if (path.endsWith("/")) path += "index.html";
  String contentType = getContentType(path);
  if (SPIFFS.exists(path)) {
    Serial.println("handleFileRead: sending " + path);
    File file = SPIFFS.open(path, "r");
    Server.streamFile(file, contentType);
    file.close();
    Serial.println("handleFileRead: sent " + path);
    return true;
  }
  else {
    Serial.println("handleFileRead: 404 not found");
    Server.send (404, "text/plain", "ESP: 404 not found");
    return false;
  }
}
//  メインプログラム
void setup() {
  //  ファイルシステム
  SPIFFS.begin();
  //  シリアルモニタ(動作ログ)
  Serial.begin(115200);               //  ESP 標準の通信速度 115200
  delay(100);                         //  100ms ほど待ってからログ出力可
  Serial.println("\n*** Dongbeino ***");
  //  アクセスポイントの構成
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, pass);
  Serial.print("network: "); Serial.println(ssid);
  Serial.print("address: "); Serial.println(WiFi.softAPIP());
  //  ウェブサーバの設定
  Server.onNotFound(handleNotFound);  //  ファイルアクセス時の応答関数を設定
  Server.begin();                     //  ウェブサーバ開始
}

void loop() {
  //  クライアントからの要求を処理する
  Server.handleClient();
}

ファイルシステムの内容は,index.html を含むウェブ文書一式です.CSS ファイルや JavaScript ファイルなどが含まれていても構いません.またサブフォルダを作ってツリー状にファイルを整理してあっても構いません.ただし,全体として 3MB を超えてはいけません.上述のように,Tools > ESP8266FS Sketch Data Upload によって書き込んでください.

スケッチ(たとえば esp_webFileTest/esp_webFileTest.ino)のアップロードと,データ(esp_webFileTest/data)のアップロードは別々に独立して行います.新しい別のスケッチを書き込んでも,ファイルシステム上のデータファイルは不変です.また,データファイルを書き換えても,スケッチには影響しません.

スケッチとデータの書き込みが終了すれば,ESP はウェブサーバとして動作しはじめます.ここでは xkozima という SSID をもつ無線 LAN が構成されるので,クライアント(PC やスマホ)からこの無線 LAN に接続し,ESP のデフォルトの IP アドレスである 192.168.4.1 にウェブブラウザでアクセスしてください.ファイルの読み出しには若干時間がかかりますが,しっかりしたウェブサーバとして ESP が機能しているのがわかります.

画像ファイルの表示には多少時間がかかるようです.これは SPI 接続されたフラッシュメモリ上にファイルシステムが構築されているため,読み出し(と書き込み)に時間がかかるためです.ウェブブラウザ側にキャッシュの機能があれば,アクセス2回目以降はストレスなくブラウジングできるでしょう.

ファイルシステムの使い方(2): プログラムからアクセス

仕込み中です.