ESP32のSPIFFS領域内のHTMLファイルでLチカWeb Serverを立てる

ESP32のSPIFFS領域内のHTMLファイルでWeb Serverを立てる

こんにちは!wak-techです。
以前作ったESP32を使ってスマホからLチカ(LED点滅)する【webserver】ではhtmlをarduinoのコードに変換してサーバーを立てていました。
今回はこれを改良して、SPIFFS領域と呼ばれるESP32内のファイル領域にHTMLとCSSをファイルとして保存・直接駆動できたので、方法を解説します。

参考にしたサイト・情報

準備

ESPAsyncWebServerのインストール

このプロジェクトではSPIFFS領域のHTMLを送信するためにESPAsyncWebServerというライブラリを使用します。このライブラリの詳細は記事の後半で解説します。

では、インストールしていきましょう!

  1. 公式Githubからzipファイルをダウンロードする
  2. zipを解凍する
  3. Arduinoのライブラリがある場所へ移動し、名前を「ESPAsyncWebServer」にする

これで基本のインストールは完了ですが、依存している「AsyncTCP」というライブラリも必要です。上記と同様に、以下の手順でインストールします。

  1. AsyncTCPのGithubページからzipファイルをダウンロードする
  2. zipを解凍する
  3. 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ファイルなどをダウンロードすることもできます。データロガーとして使えますし、非常にメンテンナンス性が上がりそうですね!

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です