ESP32のSPIFFS領域内のHTMLファイルでWeb Serverを立てる
こんにちは!wak-techです。
以前作ったESP32を使ってスマホからLチカ(LED点滅)する【webserver】ではhtmlをarduinoのコードに変換してサーバーを立てていました。
今回はこれを改良して、SPIFFS領域と呼ばれるESP32内のファイル領域にHTMLとCSSをファイルとして保存・直接駆動できたので、方法を解説します。
参考にしたサイト・情報
準備
ESPAsyncWebServerのインストール
このプロジェクトではSPIFFS領域のHTMLを送信するためにESPAsyncWebServerというライブラリを使用します。このライブラリの詳細は記事の後半で解説します。
では、インストールしていきましょう!
- 公式Githubからzipファイルをダウンロードする
- zipを解凍する
- Arduinoのライブラリがある場所へ移動し、名前を「ESPAsyncWebServer」にする
これで基本のインストールは完了ですが、依存している「AsyncTCP」というライブラリも必要です。上記と同様に、以下の手順でインストールします。
- AsyncTCPのGithubページからzipファイルをダウンロードする
- zipを解凍する
- Arduinoのライブラリがある場所へ移動し、名前を「AsyncTCP」にする
これでサーバーに必要なライブラリは揃いました!
SPIFFS領域への音声ファイルの書き込み
ESP32にはフラッシュメモリが搭載されていて、4MBまでのファイルを入れることができます。この書き込むことの出来る領域をSPIFFS領域と言います。
Arduinoでの書き込みでは1MB程度が限度となっています。最大限使いたい場合はesp-idfを使いますが、ここではArduinoを使って説明します。
Sketch data uploadのインストール
SPIFFS領域にファイルを送るにはSKetch data uploadという機能が必要です。
mgo-techさんの解説ページにインストール方法がありますので、それに従います。
同様にESP8266_Spiramというライブラリも必要です。zipファイルをダウンロードし、Arduinoでライブラリとして読み込みましょう。
インストールできるとこのように機能が追加されます。
では、アップロードするファイルを準備しましょう。
ファイルの準備
この後作成するhtmlとcssファイルは、スケッチの入っているフォルダ内のdataフォルダに入れる必要があります。
下記画像の通りdataフォルダに入れるようにしてください。
dataフォルダの中身↓
htmlファイルの準備
以下の通りのhtmlファイルを作成します。作成には適当なエディタ(メモ帳やVisual Studio Code等)を使いましょう。ファイル名はindex.htmlにしてください。
<!DOCTYPE html>
<html>
<head>
  <title>ESP32 Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <h1>ESP32 Web Server</h1>
  <p>GPIO state: <strong> %STATE%</strong></p>
  <p><a href="/on"><button class="button">On</button></a></p>
  <p><a href="/off"><button class="button button2">Off</button></a></p>
</body>
</html>
CSSファイルの準備
CSSとはhtmlに書かれた要素を修飾し、色やアニメーションをつける設定ファイルです。以下の通りファイルを作成し、style.cssという名前で保存します。
html {
    font-family: Helvetica;
    display: inline-block;
    margin: 0px auto;
    text-align: center;
  }
  h1{
    color: #0F3376;
    padding: 2vh;
  }
  p{
    font-size: 1.5rem;
  }
  .button {
    display: inline-block;
    background-color: #008CBA;
    border: none;
    border-radius: 4px;
    color: white;
    padding: 16px 40px;
    text-decoration: none;
    font-size: 30px;
    margin: 2px;
    cursor: pointer;
  }
  .button2 {
    background-color: #f44336;
  }
SPIFFS領域へのアップロード
ここまでできたら、以下の画像のようにESP32 Sketch Data Uploadをクリックすることで、dataフォルダ内のファイルを送信・書き込みできます。
しばらくして下のコンソール画面がUploaded表示になれば完了です!
回路
ESP32のGPIO02とLEDを接続します。抵抗は適切な値(200~500Ω程度)にしましょう。
プログラム(ほぼコピペで動く)
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
// 自分の環境によって置き換え
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// GPIOの2番ピンをledPinとして設定
const int ledPin = 2;
// LEDの状態を保持する変数
String ledState;
// ポート80にサーバーを設定
AsyncWebServer server(80);
// 実際のピン出力によってhtmlファイル内のSTATEの文字を変える
String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if(digitalRead(ledPin)){
      ledState = "ON";
    }
    else{
      ledState = "OFF";
    }
    Serial.print(ledState);
    return ledState;
  }
  return String();
}
 
void setup(){
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT); //GPIO02をアウトプットに
  // SPIFFSのセットアップ
  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
  // WiFiに接続
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
  // ESP32のローカルアドレスを表示
  Serial.println(WiFi.localIP());
  // サーバーのルートディレクトリにアクセスされた時のレスポンス
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  // style.cssにアクセスされた時のレスポンス
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");
  });
  // Onボタンが押された時のレスポンス
  server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(ledPin, HIGH);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  // Offボタンが押された時のレスポンス
  server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(ledPin, LOW);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  // サーバースタート
  server.begin();
}
 
void loop(){
  
}
書き込みが終了後、シリアルモニタを開きサーバーのipアドレスを確認しましょう。そのipアドレスにスマホなどの端末からアクセスすると、下記のような画面が出現します。
ON,OFFそれぞれをクリックしてLEDを制御できます。
解説
ESPAsyncWebServerについて
ESP32で非同期なサーバーを実現できるライブラリです。非同期とはデータ転送時に送信ー受信のタイミングを合わせなくてもいい方法です。
これで、プログラム上は常にclientを監視しなくてもよくなります。そのため、loop()には何もプログラムがありません。これで、loop内でLチカ等の他のプログラムを動かしながらサーバーも動かせます!
ESPAsyncWebServerの詳しい説明は公式githubリポジトリをご覧ください。
今回使用するrequest関数
非同期なサーバーへの対応はAsyncWebServerRequestを介して行います。使い方を超ざっくり説明すると
AsyncWebServerRequest *request; #requestというオブジェクトを作成
request->send(SPIFFS,"/hogehoge.hoge",String(),false,processor);
とすることで/hogehoge.hoge内にある%で囲まれた文字列がprocessor関数によって置換されます。※詳しくライブラリを見ていないので、String()とfalseの意味は分かりません。
例えばhtmlファイルで
<p>%brightness%</p>
のような段落があった時、以下のようなprocessor関数を定義して、周囲の明るさを数値として入力できます。
String processor(const String& var){
  if(var == "brightness"){
    int brightness = get_brightness();
    String brightness_str = String(brightness); //文字列に変換
    return brightness_str; //実際の明るさを返す
  }
  return String(); //brioghtness以外の文字の場合そのまま返す
}
今回のコードではGPIOの2番ピンの状態をチェックし、True(HIGH)であればONを、LowであればOFFという文字列を返してhtmlファイル中の%STATE%に代入しています。
各URLへの対応について
プログラムではOnボタンが押された時に、それに対応した動きがされるようになっています。
Onボタンのhtmlを見てみましょう。
<body>
  <h1>ESP32 Web Server</h1>
  <p>GPIO state: <strong> %STATE%</strong></p>
  <p><a href="/on"><button class="button">On</button></a></p>
  <p><a href="/off"><button class="button button2">Off</button></a></p>
</body>
/onにアクセスされるようになっていますね。ここにアクセスされた時の動作は、setup中のserver.onで登録されています。
// Onボタンが押された時のレスポンス
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(ledPin, HIGH);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
});
server.on("/hoge",HTTP_GET,function());で/hogeにアクセスがあった時function()を実行します。ここではラムダ関数という方法を使い、その場限りの関数を定義しています。ラムダ関数に関してはこちらをご覧ください。
この関数ではledPin,つまりGPIO02をオンにしてLEDを点灯しています。
まとめ
今回は非同期サーバーが実行できるESPAsyncWebServerというライブラリを使用して、SPIFFS内のhtmlファイルをポストできるようになりました。同様にrequest->send関数を使用してcsvファイルなどをダウンロードすることもできます。データロガーとして使えますし、非常にメンテンナンス性が上がりそうですね!
こんばんは、いつもお世話になっております。
ESP32 Sketch Data Uploadをツールに表示させるためのプラグインがリンク内に見当たらなかったので書かせていただきます。
https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/