Hello Server (with .css etc.)

前回はSPIFFSを使ってHTMLファイルをフラッシュから読み込みました。今回は、Web設計に必要な CSSファイルや画像ファイル、スクリプトファイル等を読み込む機能を追加します。

先ずは CSS ファイルから

この例題スケッチをIDEから読み込んだ時に、server.onNotFound(handleNotFound); という関数が有ったのですが、当時は使わないので削除していました。今回はこれを使います。
server.on()はクリエを元に操作関数と関連付ける関数(例えば、server.on(“/led_on”, handle_led_on);)でした。
server.onNotFound()は、server.on()で定義した以外のクリエを処理する関数を定義する関数です。先ずはスケッチを見て下さい。


#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <SPIFFS.h>

const char* ssid = "XXXXXX";
const char* password = "YYYYYY";

WebServer server(80);

#define LED_pin 2
bool LED_status = LOW;

void setup() {
  Serial.begin(115200);
  pinMode(LED_pin, OUTPUT);
  digitalWrite(LED_pin, 0);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp32")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);
  server.on("/led_on", handle_led_on);
  server.on("/led_off", handle_led_off);
  server.onNotFound(handleWebRequests); 
  
  server.begin();
  Serial.println("HTTP server started");

  SPIFFS.begin();
}

void loop() {
  server.handleClient();
}

String load_HD() {
  File dataFile;
  String line;
  
    dataFile = SPIFFS.open("/index_hd.html", FILE_READ);
    while(dataFile.available()) 
      line += (dataFile.readStringUntil('\n') + "\n");
    dataFile.close();

    return line;
}

void handleWebRequests()
{
  String dataType = "text/plain";
  String path;
  File dataFile;
  int flg=0;
   
    path = server.uri();
    if(path.endsWith(".css")) { dataType = "text/css"; flg=1; }
    delay(5);
    
    if(flg)
    {
      dataFile = SPIFFS.open(path.c_str(), FILE_READ);
      server.streamFile(dataFile, dataType);
      dataFile.close();
      delay(5);
    }
    else
    {
      String message = "File Not Found\n\n";
      message += "URI: ";
      message += server.uri();
      message += "\nMethod: ";
      message += (server.method() == HTTP_GET) ? "GET" : "POST";
      message += "\nArguments: ";
      message += server.args();
      message += "\n";
      for (uint8_t i = 0; i < server.args(); i++) {
        message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
      }
      server.send(404, "text/plain", message);      
    }
}

void handleRoot() {
  LED_status = LOW;
  digitalWrite(LED_pin, LOW);
  server.send(200, "text/html", SendHTML(LED_status)); 
}

void handle_led_on() {
  LED_status = HIGH;
  digitalWrite(LED_pin, HIGH);
  server.send(200, "text/html", SendHTML(true)); 
}

void handle_led_off() {
  LED_status = LOW;
  digitalWrite(LED_pin, LOW);
  server.send(200, "text/html", SendHTML(false)); 
}

String SendHTML(uint8_t led_stat){
  String ptr;

  ptr = load_HD();
  if(led_stat)
    ptr +="<a class=\"button button-off\" href=\"/led_off\">OFF</a>\n";
  else
    ptr +="<a class=\"button button-on\" href=\"/led_on\">ON</a>\n";

  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}
  • 42行:server.onNotFound(handleWebRequests);
    • server.on()で登録したクリエ以外を処理する関数をここで登録
    • 処理関数名は、”handleWebRequests”
  • 58行:dataFile = SPIFFS.open(“/index_hd.html”, FILE_READ);
    • HTMLの固定部を読み込む部分
    • 前回からCSS関連を取り除いたので、ファイルネイムを、”/index_hd.txt”から”/index_hd.html”に変更
  • 66から99行:void handleWebRequests()
    • handleWebRequests()本体。
    • 73行:ここでクリエと取得
    • 74行:クリエの最後の4文字が、”.css”なら、dataTypeを”text/css”
    • 77行:クリエ指定のファイルが”.css”なら下記を実行
      • 79,80行:ファイルをオープンしてクライアントに送信
    • 84行:クリエ指定のファイルが”.css”で無ければ下記を実行
      • 86から97行:本来のHelloスケッチに有った処理そのまま使用

データフォルダーに保存するファイルは、”index_hd.html” と ”led.css”の2つです。先ずは、”index_hd.html”

index_hd.html

<!DOCTYPE html>
<html> 
<head><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> 
<link rel='stylesheet' type='text/css' href='/led.css' >
<title>LED Control</title> 
</head>
<body>
    <h2>Hello ESP32</h2>
    <h3>Using SPIFFS</h3>
    <h4>Handle CSS</h4>
  • 前回から、CSS関係を抜くとこんな感じになります。
  • 4行:ここで、CSSファイルとして、”led.css” を指定しています。
  • 10行:CSSファイル処理機能を付けたのこの1文を追加しました。

続いて、”led.css”

led.css

@charset "UTF-8";

html { 
    font-family: Helvetica; 
    display: inline-block; 
    margin: 0px auto; 
    text-align: center;
}

.button {
    display: block;
    width: 60px;
    background-color: #3498db;
    border: none;
    color: white;
    padding: 13px 30px;
    text-decoration: none;
    font-size: 25px;
    margin: 0px auto 35px;
    cursor: pointer;
    border-radius: 4px;
}

.button-on {
    background-color: #3498db;
}

.button-on:active {
    background-color: #2980b9;
}

.button-off {
    background-color: #34495e;
}
    
.button-off:active {
    background-color: #2c3e50;
}
  • 普通のCSSファイルです。特に説明無し

準備が出来たところで

  • 現在のプロダクトフォルダーに、”data”フォルダーを作り、そこに”index_hd.html” と”led.css”を保存
  • 先ずはスケッチをコンパイル
  • 続いて”index_hd.html” と”led.css”をアップロード
  • ”EN”ボタンを押してスケッチを実行
  • Webブラウザに、”esp32.local”と入力する。

この画面が出ます

このページのソースコードは、

予定通りです。

その他のファイル

その他のファイルも同様に処理出来ます。今回は、画像ファイルとして、”png”、”gif”、”jpg”の3種類と、java scriptファイルを処理出来る様に変更しています。下記は、”png”とjava scriptファイルを読み込むスケッチの例です


#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <SPIFFS.h>

const char* ssid = "XXXXXX";
const char* password = "YYYYYY";

WebServer server(80);

#define LED_pin 2
bool LED_status = LOW;

void setup() {
  Serial.begin(115200);
  pinMode(LED_pin, OUTPUT);
  digitalWrite(LED_pin, 0);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp32")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);
  server.on("/led_on", handle_led_on);
  server.on("/led_off", handle_led_off);
  server.onNotFound(handleWebRequests); 
  
  server.begin();
  Serial.println("HTTP server started");

  SPIFFS.begin();
}

void loop() {
  server.handleClient();
}

String load_HD() {
  File dataFile;
  String line;
  
    dataFile = SPIFFS.open("/index_hd.html", FILE_READ);
    while(dataFile.available()) 
      line += (dataFile.readStringUntil('\n') + "\n");
    dataFile.close();

    return line;
}

void handleWebRequests()
{
  String dataType = "text/plain";
  String path;
  File dataFile;
  int flg=0;
   
    path = server.uri();
    if(path.endsWith(".css")) { dataType = "text/css"; flg=1; }
    else if(path.endsWith(".js")) { dataType = "application/javascript"; flg=1; }
    else if(path.endsWith(".png")) { dataType = "image/png"; flg=1; }
    else if(path.endsWith(".gif")) { dataType = "image/gif"; flg=1; }
    else if(path.endsWith(".jpg")) { dataType = "image/jpeg"; flg=1; }
    delay(5);
    
    if(flg)
    {
      dataFile = SPIFFS.open(path.c_str(), FILE_READ);
      server.streamFile(dataFile, dataType);
      dataFile.close();
      delay(5);
    }
    else
    {
      String message = "File Not Found\n\n";
      message += "URI: ";
      message += server.uri();
      message += "\nMethod: ";
      message += (server.method() == HTTP_GET) ? "GET" : "POST";
      message += "\nArguments: ";
      message += server.args();
      message += "\n";
      for (uint8_t i = 0; i < server.args(); i++) {
        message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
      }
      server.send(404, "text/plain", message);      
    }
}

void handleRoot() {
  LED_status = LOW;
  digitalWrite(LED_pin, LOW);
  server.send(200, "text/html", SendHTML(LED_status)); 
}

void handle_led_on() {
  LED_status = HIGH;
  digitalWrite(LED_pin, HIGH);
  server.send(200, "text/html", SendHTML(true)); 
}

void handle_led_off() {
  LED_status = LOW;
  digitalWrite(LED_pin, LOW);
  server.send(200, "text/html", SendHTML(false)); 
}

String SendHTML(uint8_t led_stat){
  String ptr;

  ptr = load_HD();
  if(led_stat)
    {
      ptr +="<a class='button button-off' href='/led_off'>OFF</a>\n";
      ptr +="<div id='a' title='1'></div>\n";
    }
  else
    {
      ptr +="<a class='button button-on' href='/led_on'>ON</a>\n";
      ptr +="<div id='a' title='0'></div>\n";
    }
  ptr +="<script type='text/javascript' src='/test.js'></script>\n";

  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}
  • 75行:ジャバスクリプトファイルの追加
  • 76行:png (画像)ファイルの追加
  • 78行: gif ファイルの追加
  • 78行: jpg ファイルの追加。
  • 130行:prt += “<div id=’a’ title=’1′></div>”
    • title属性にID を”a”とし、”1”を代入
  • 135行:prt += “<div id=’a’ title=’0′></div>”
    • title属性にID を”a”とし、”0”を代入
  • 137行:ここで外部よりスクリプトファイル、”test.js” を読み込む

”test.js”は、java script ファイルです。

test.js

var a = document.getElementById( 'a' ).title;
if(a == '1')
    document.write('<img src="/led_on.png" >');
else
    document.write('<img src="/led_off.png" >');
  • 簡単なスクリプトファイルです。
  • ”a”のtitleを読み込んで
    • “1”だったら”led_on.png”画像の表示
    • それ以外は”led_on.png”画像の表示

“index_hd.html”も書き換えました。

index_hd.html
<!DOCTYPE html>
<html> 
<head><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> 
<link rel='stylesheet' type='text/css' href='/led.css' >
<title>LED Control</title> 
</head>
<body>
    <h2>Hello ESP32</h2>
    <h3>Using SPIFFS</h3>
    <h4>Handle CSS,JPG,PNG,GIF,JS</h4>

最後の行に対応したファイルの拡張子を追加しました。

今回は、画像ファイル、”led_on.png”、”led_on.png”が必要です。これらも”data”フォルダーに予め保存して置きます。

これで準備完了です。コンパイル、DATAのアップロードを行ってから実行して下さい。esp32.localにアクセスすると

この画面が表示されます。ここで、”ON”ボタンを押すと

下の電球がついた状態に変わります。この状態のページのソースは

“png”ファイルはこのコードに有りませんが、これで”CSS”,”JS” ,”PNG”ファイルの処理が行えました。

今回のプロジェクトファアイルを、”ここに” 保存しました。