ESP8266で遊ぶための初期コードを作成する

　最近ESP8266でWebServerを使うことが出来るようになったので、
自分なりに欲しい機能をまとめたものをヘッダとして作成しました。

自分の要望事項はこんな感じでしょうか
・特定条件で起動するとAPモードで起動し、初期設定が行える
・SPIFFS内に設定ファイルが無くても、問題無く動作する
・SPIFFS用の簡易ファイラを実装している
・WiFi設定を行うWebページを用意し、設定はInifileに保存する
・Webページでのデータ更新作業が容易にできる
・Webページで更新した項目はInifileに保存される
・Webページの表示ではInifileに保存した項目を自動で表示する
・保存したInifileはプログラム内で用意に参照できる
・SPIFFSに書いたファイルをWebServerに公開する


#ESP8266環境で必要なリソース
　SPIFFS：　InifileとIndex.htmlとWifi.Htmlで3Kほど必要です。
　　　　　　WebServerのファイルを記録する場所でもあります。
　MDNS：　<端末名>.localアクセスを実現するのに必要です。
　inifile.h ： 私が以前登録したinifile.hです。
　WebServerToolHtml.h： 私が以前登録したWebServerToolHtml.hです。
　ESPWebServerTool.h： 今回紹介するコードです。


#ESP32は使えないの？
　ESP8266WebServer.hを移植して、SPIFFSの差異部分を修正すれば利用可能になります。


#初期設定モード（APモード）
　・A0端子に3.3Vを印加した状態で起動すると、WiFiのアクセスポイントとして起動します。
　　　　＝＞AP名：ESPsetupAP　で起動し、
　　　　　　ESPsetupAP.local/WiFi.htmlで初期設定ができます。


#WiFi設定画面
　・/wifi.htmlでは現状のWiFi設定を閲覧・変更できます。
　　各項目を変更し、Onボタンを押すとInifileの該当項目が更新されます。
　　更新内容は、次回の電源投入時に反映されます。
　　※esp8266initial.inoのinitialDataWiFiHTMLdetailにhtmlコードが書いてあります。
![wifihtml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255936/dc8f2273-b40c-886f-fa56-de6c05fc10b5.png)


#ファイルビューワ
　SPIFFS内のファイルメンテナンスの為に実装しました。
　・/files.htmlでは、SPIFFS内のファイル表示・削除・ダウンロード
　　の操作ができます。操作内容をコンボボックスより選び、対象の
　　ファイルを選択してOKボタンを押します。
　　またファイル名はリンクになっているので、ブラウザの「名前を付け
　　て保存する」でファイルダウンロードができます。
![fileshtml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255936/75527625-6910-5ce6-97cf-540c86f53000.png)
　　※画像は旧版で、リンク表示になっていません。


#ファイルアップローダ
　SPIFFS内へファイル追加する為に実装しました。
　・/update.htmlでは任意のファイルをアップロードできます。
　　選択したファイル名を利用するので注意してください。
　　※本ヘッダにはファイル名変更処理は未実装です。
　　※容量制限等エラー処理は未実装です。
![uploadhtml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255936/136b7ed7-b563-1c4d-cbd5-1503d8ab4866.png)


#WebServer機能　
　・htmlソースの入力項目 input type= 'text' 'password' 'radio' 'range' などの項目を
　　自動的にinifileに反映する機能を有しています。
　　/WiFi.htmlにて利用しているのでご確認ください。
　・サンプルのindex.htmlです。
![indexhtml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255936/8f5ed396-04fb-42f7-1ca6-989cbc513574.png)


#使い方
コンパイルして起動するだけです。
最初の起動ではSPIFFSにファイルが無いので初期値を書き出して
APモードで起動します。


APモードは、A0端子に3.3Vを印加した状態で起動すると移行します。
WiFiのアクセスポイントはESPsetupAPになります。
WiFiのアクセスポイントに接続して、ブラウザよりESPsetupAP.local/WiFi.html
にアクセスしてWiFi設定を変更してください。


通常モードは、A0端子を解放した状態で起動すると移行します。
WiFi Setting画面で設定したSSID/PASSを使いWiFiに接続します。
WiFi接続後はNAMEで設定した端末名でアクセスすることができます。


現状はAPモードでも通常モードでもFiles.html/Update.html/WiFi.htmlに
アクセスできる仕様となっております。



```ino:esp8266initial.ino:
// ESP8266初期ソフト
// Flush設定  SPIFFSを使える様にする
// A0端子に3.3Vを入れた状態で起動するとAPモード　AP名:ESPsetupAP

//#define SETUPFILEWRITE true       // 強制的に初期値を書き出す
#define SETUPFILEWRITE false      // ファイルが無いなら初期値を書き出す
//#define USE_APMODE                // ACCESS POINTモードでの動作
#define USE_NTP                   // NTPで時間合わせする処理を動作させる
//#define USE_FTP                   // FTPファイル転送のルーチンを動作させる
//#define UNUSE_MDNS                // MDNS機能を使わない
#define USE_handleNotFound        // WEBリクエストに対しSPIFFS内のファイルで応答する

#include <ESPWebServerTool.h>
//==INIFILEの初期値================================================================
const char *initialDataINI="[SETUP]\nSSID=guest\nPASSWORD=guest\nHOSTNAME=ESPsoftAP\n\
\n*";
//==index.htmlの初期値================================================================
const char *filenameIndexHTML = "/index.html";
const char *initialDataIndexHTMLdetail ="<form name='SETUP' action=SETUP method='post'>\n<p>\n\
Hello World!\
</br></form></html>\n";
//==WiFi.htmlの初期値================================================================
const char *filenameWiFiHTML = "/wifi.html";
const char *initialDataWiFiHTMLdetail ="<img src=\"demo.jpg\" alt=\"image\">\n\
<form name='SETUP' action=SETUP method='post'>\n<p>WiFi Settings</p>\n\
<p>SSID:<input type=\"text\" name=\"SSID\" size=\"30\" maxlength=\"30\" value=\"SAMPLESSID\"></p>\n\
<p>PASS:<input type=\"password\" name=\"PASSWORD\" size=\"30\" maxlength=\"30\" value=\"SAMPLEPASSWORD\"></p>\n\
<p>NAME:<input type=\"text\" name=\"HOSTNAME\" size=\"30\" maxlength=\"30\" value=\"HOSTNAME\">.local</p>\n\
</br><input type=submit value=on target=tif></br></form></html>\n";
//=====================================================================================
void setup() {
  ESPWebServerToolSetupProcess( );
  CreateInitialFile(     fileNameINI,       initialDataINI, SETUPFILEWRITE );
  CreateInitialHTMLfile( filenameIndexHTML, initialDataIndexHTMLdetail, SETUPFILEWRITE );
  CreateInitialHTMLfile( filenameWiFiHTML,  initialDataWiFiHTMLdetail, SETUPFILEWRITE );
  Serial.println( "program start" );
  MDNS.addService("http", "tcp", 80);
}
//=====================================================================================
// メインループ
void loop() {
  if( ESPWebServerToolSetupLoop()==false )    return;   //設定モードの時はON/OFFできない
}
//
```



今回作成したヘッダファイルです。

```h:ESPWebServerTool.h:
// UNUSE_MDNS	ＭＤＮＳ定義を行わない		(未宣言：MDNS応答する  ホスト名:*MYHOSTNAME
// USE_FTP		ＦＴＰによるファイル転送	(宣言：ＦＴＰを行う    ユーザ:esp パス:esp）
// USE_NTP		ＮＴＰによる時刻合わせ		(宣言：時刻合わせする)
// UNUSE_SERIAL
#include <ESP8266WiFi.h>
#ifndef UNUSE_MDNS
#include <ESP8266mDNS.h>
#endif
#include <ESP8266WebServer.h>
#ifdef USE_FTP
#include <ESP8266FtpServer.h>			// なるべく使わないこと
#endif
#include <FS.h>
ESP8266WebServer server(80);
#include <inifile.h>
const char *fileNameINI = "/setup.ini"; // Inifileのファイル名
const char *iniHeader = "SETUP";
iniFiles ini(fileNameINI);
#include <WebServerToolHtml.h>

#ifdef USE_NTP
#include <time.h>
#define JST 3600*9
#endif

#define ESP8266WebServerDebug

char *MYHOSTNAME;

bool APMODE = false;
#ifdef USE_FTP
/// FTPログオンの設定
const char *ftpUserServer = "esp";
FtpServer ftpSrv;   //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
#endif
#ifndef WEBSERVERTOOL_H
#define WEBSERVERTOOL_H
bool boolWebServerToolInifileUpdate;

// 標準的なHTMLヘッダ
const char *ESPHTMLHeader ="<html lang=\"ja\">\n<head>\n\
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n\
<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n</head>\n";

// 他の宣言位置に依存しているので注意してください。
// 　　WebServerにてserver定義
// 　　SPIFFSのヘッダ読込

//=====================================================================================
#ifndef UNUSE_SERIAL
void printMACADDRESS(char *header){
byte mac[6];
char buf[20];
  WiFi.macAddress(mac);
  Serial.print(header);
  sprintf(buf,"%02X:%02X:%02X:%02X:%02X:%02X",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
  Serial.println(buf);
}
#endif
//=====================================================================================
/// ＷＥＢサーバの応答
bool loadFromDataFile(String path) {
  String dataType = "text/plain";
  if (path.endsWith("/"))    path += "index.html";
  if (path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
  else if (path.endsWith(".htm" )) dataType = "text/html";
  else if (path.endsWith(".html")) dataType = "text/html";
  else if (path.endsWith(".xml" )) dataType = "text/xml";
  else if (path.endsWith(".css" )) dataType = "text/css";
  else if (path.endsWith(".png" )) dataType = "image/png";
  else if (path.endsWith(".gif" )) dataType = "image/gif";
  else if (path.endsWith(".jpg" )) dataType = "image/jpeg";
  else if (path.endsWith(".ico" )) dataType = "image/x-icon";
  else if (path.endsWith(".js"  )) dataType = "application/javascript";
  else if (path.endsWith(".pdf" )) dataType = "application/pdf";
  else if (path.endsWith(".zip" )) dataType = "application/zip";
  if( dataType != "text/html" ){
    File dataFile = SPIFFS.open(path.c_str(), "r");
/*  if(dataFile.isDirectory()){ path += "/index.htm"; dataType = "text/html"; dataFile = SPIFFS.open(path.c_str(), "r"); } /**/
    if (!dataFile )    return false;
    if (server.hasArg("download"))   dataType = "application/octet-stream";
    if (server.streamFile(dataFile, dataType) != dataFile.size()){
#ifndef UNUSE_SERIAL
      Serial.println("Sent less data than expected!");
#endif
    }
    dataFile.close();
  }else{
    WebServerToolHtml.readHtmlFile( path.c_str() );
    server.send( 200, dataType, WebServerToolHtml.buffer() );
    WebServerToolHtml.release();
    boolWebServerToolInifileUpdate = true;
  }
  return true;
}

char *ESPfilename = "filename";
//=====================================================================================
// 選択ファイルをテキストで表示する
void ESPlist(){
#ifndef UNUSE_SERIAL
  Serial.print( "display text : " );	Serial.println( server.arg(1) );
#endif
  String dataType = "text/plain";
  String path = "/" + server.arg(1);
  File dataFile = SPIFFS.open( path, "r");
  if (server.streamFile(dataFile, dataType) != dataFile.size()){
#ifndef UNUSE_SERIAL
    Serial.println("Sent less data than expected!");
#endif
  }
  dataFile.close();
}
//=====================================================================================
// 選択ファイルを起動する(htmlなら普通に表示
void ESPexec(){
#ifndef UNUSE_SERIAL
  Serial.print( "execute file : " );	Serial.println( server.arg(1) );
#endif
  String path = "/" + server.arg(1);
  loadFromDataFile( path );
}
//=====================================================================================
// 削除ファイルを実際に削除する
void ESPremove(){
  String path = "/" + server.arg(1);
  SPIFFS.remove(path);
#ifndef UNUSE_SERIAL
  Serial.print( "removed file : " );	Serial.println( server.arg(1) );
#endif
}
//=====================================================================================
// アップロード用htmlを表示する
void ESPuploadhtml( ){
#ifndef UNUSE_SERIAL
  Serial.println( "execute : upload.html" );
#endif
  String output = ESPHTMLHeader;
  output += "\
<form method=\"POST\" action=\"/upload\" enctype=\"multipart/form-data\">\n\
<p>Select upload file</p>\n<input type=\"file\" name=\"file\"/><br>\n\
<input type=\"submit\" value=\"SUBMIT\">\n</form>\n</html>\n";
  server.send(200, "text/html", output);
}
//=====================================================================================
// ファイル一覧及び削除ファイルの選択
void ESPfiles( ){
  if( 0<server.args() ){
    if( server.arg(0)=="remove" )			ESPremove();
    if( server.arg(0)=="list" ){			ESPlist();			return;	}
    if( server.arg(0)=="exec" ){			ESPexec();			return;	}
    if( server.arg(0)=="/upload.html" ){	ESPuploadhtml();	return;	}
  }
  String output = ESPHTMLHeader;
  output += "\
<form name='files' action=/files.html method='post'>\
<p><select name=\"mode\">\n\
<option value=\"exec\" selected>exec file</option>\
<option value=\"list\">source list</option>\
<option value=\"/upload.html\">upload file</option>\
<option value=\"remove\">removed file</option>\
</select><br>";
  Dir dir = SPIFFS.openDir( server.arg("dir") );
  while(dir.next()){
    File entry = dir.openFile("r");
    output += "<input type=\"radio\" name=\"remove\" value=\"";
    output += String(entry.name()).substring(1);
    output += "\">";
//
    output += "<a href=\"http://";
    output += MYHOSTNAME;
    output += ".local/";
    output += String(entry.name()).substring(1);
    output += "\">";
//
    output += String(entry.name()).substring(1);
    output += "\n<br>";
    entry.close();
  }
  output += "<input type=submit value=OK target=tif>\n</form>\n</html>\n";
  server.send(200, "text/html", output);
}
//=====================================================================================
// upload.htmlで選択したファイルをアップロードする
File fsUploadFile;
void ESPhandleFileUpload(){
  HTTPUpload& upload = server.upload();
  if(upload.status == UPLOAD_FILE_START){
    String filename = "/" + upload.filename;
#ifndef UNUSE_SERIAL
    Serial.print( "upload [" );
    Serial.print( filename );
    Serial.print( "] start:");
#endif
    fsUploadFile = SPIFFS.open( filename, "w");
  } else if(upload.status == UPLOAD_FILE_WRITE){
    if(fsUploadFile)
      fsUploadFile.write(upload.buf, upload.currentSize);
#ifndef UNUSE_SERIAL
    Serial.print( upload.currentSize );
    Serial.print( ":" );
#endif
  } else if(upload.status == UPLOAD_FILE_END){
    if(fsUploadFile)
      fsUploadFile.close();
#ifndef UNUSE_SERIAL
    Serial.println( "end");
#endif
  }
}
//=====================================================================================
// POSTで読み取ったデータはパーセントエンコーディングされているため、デコードを実施
// char* str : 変換する文字列を格納、 char* dstStr : 変換後の文字列を格納
void urlDecode(char* str, char* dstStr) {
    long dstIdx = 0;
    for(long idx = 0; idx < strlen(str); idx++) {
      if (str[idx] == '%') {
        char tmpHex[] = { str[idx+1], str[idx+2] };
        dstStr[dstIdx] = strtol(tmpHex, NULL, 16);
        idx += 2;
      }
      else if (str[idx] == '+') {
        dstStr[dstIdx] = ' ';
      }
      else {
        dstStr[dstIdx] = str[idx];
      }
      dstIdx++;
    }
    dstStr[dstIdx] = '\0';
}
//=====================================================================================
void ESPWebServerToolHandlePost(){
  String TITLE = server.uri();
  TITLE = TITLE.substring( 1, TITLE.length() );
#ifndef UNUSE_SERIAL
  Serial.print( "POST:" );  Serial.println( TITLE );
#endif
  for( int i=0; i<server.args(); i++ ){
      char outBuffer[512];				// 面倒なのでこのサイズにしてある
      urlDecode( (char*)server.arg(i).c_str(), outBuffer );
      ini.set( (char*)TITLE.c_str(), (char*)server.argName(i).c_str(), outBuffer );/**/
#ifndef UNUSE_SERIAL
      Serial.print( TITLE );
      Serial.print( " : " );
      Serial.print( server.argName(i) );
      Serial.print( " : " );
      Serial.println( outBuffer );		// UTF-8文字は化けます(Shift-JISに変換すると良い)
#endif
  }
  loadFromDataFile( "/" );
  boolWebServerToolInifileUpdate = true;
}
//=====================================================================================
bool webServerToolInifileUpdate( bool setTrue=false )
{
  if( setTrue==true ){					boolWebServerToolInifileUpdate = true;		return true;	}
  if( boolWebServerToolInifileUpdate!=true ){										return false;	}
  boolWebServerToolInifileUpdate = false;											return true;
}
//=====================================================================================
#ifdef USE_handleNotFound
void handleNotFound(){
#ifndef UNUSE_SERIAL
  Serial.println( server.uri() );
#endif
  if ( server.method()!=HTTP_GET ) return ESPWebServerToolHandlePost();
  if ( loadFromDataFile(server.uri()) ) return;
  String url = server.uri();
  url.toLowerCase();
  String message = "File Not Found\n\nURI: ";
  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);
}
#endif
//=====================================================================================
void ESPCreateInitialFile( int num, const char *fileName, const char *data, bool mustWrite=false ){
  if( SPIFFS.exists(fileName)==false || mustWrite==true ){
#ifndef UNUSE_SERIAL
      Serial.print("File Create :");
      Serial.println( fileName );
#endif
      File fp = SPIFFS.open( fileName, "w" );
      if( !fp ){
#ifndef UNUSE_SERIAL
        Serial.println("file open failed");
#endif
      }else{
        int filesize = strlen( ESPHTMLHeader );
        if( num==0 )	fp.write( (uint8_t*)ESPHTMLHeader, filesize );
        filesize = strlen( data );
        fp.write( (uint8_t*)data, filesize );
        fp.close( );
      }
  }
}
//=====================================================================================
void CreateInitialFile( const char *fileName, const char *data, bool mustWrite=false )
{	ESPCreateInitialFile( 1, fileName, data, mustWrite );	}
//=====================================================================================
// HTML用初期データ作成 ヘッダ部は別途書きだす
void CreateInitialHTMLfile( const char *fileName, const char *data, bool mustWrite=false )
{	ESPCreateInitialFile( 0, fileName, data, mustWrite );	}
//=====================================================================================
// システムの初期設定を行う
// A0端子に3.3Vを加えた状態なら、強制APMODEで起動します。
bool ESPWebServerToolSetupProcess( )
{
  int Count = analogRead(A0);
#ifndef UNUSE_SERIAL
  Serial.begin(115200);  Serial.flush(); delay(1000);
  Serial.println("program start");
  if ( SPIFFS.begin() ) Serial.println("SPIFFS opened!");
  else                  Serial.println("SPIFFS open error!?");
  Serial.print("A0 analog input value :");  Serial.println( Count );
#else
  SPIFFS.begin();
#endif
  bool APMODE = ((Count < 512) ? false : true);
  if( ini.exists()==false ) APMODE = true;
#ifdef USE_APMODE
  APMODE = true;
#endif
  if( APMODE!=false ){
    WiFi.mode( WIFI_AP );
    MYHOSTNAME       = "ESPsetupAP";
    WiFi.softAP( MYHOSTNAME, "" );
    WiFi.config( WiFi.softAPIP(), WiFi.gatewayIP(), WiFi.subnetMask());
#ifdef USE_FTP
    ftpSrv.begin( ftpUserServer, ftpUserServer );
#endif
#ifndef UNUSE_SERIAL
    Serial.println("Access Point mode");
    Serial.print("Soft  AP name   : ");  Serial.println( MYHOSTNAME );
    Serial.print("Soft  IP address: ");  Serial.println( WiFi.softAPIP() );
#endif
#ifndef UNUSE_MDNS
    MDNS.begin( MYHOSTNAME );
#endif
    server.onNotFound(handleNotFound);
    server.begin();
#ifdef PIN2LED
    pinMode( 2, OUTPUT );
#endif
  }else{
    String APSSID, APPASSWORD, FTP;
    APSSID           = ini.get( (char *)iniHeader, "SSID",     "guest" );
    APPASSWORD       = ini.get( (char *)iniHeader, "PASSWORD", "guest" );
    MYHOSTNAME       = ini.getChar( (char *)iniHeader, "HOSTNAME", "ESPSETUP" );
    WiFi.begin(APSSID.c_str(), APPASSWORD.c_str());
    WiFi.mode( WIFI_STA );
#ifdef USE_FTP
    ftpSrv.begin( ftpUserServer, ftpUserServer );
#endif
#ifndef UNUSE_SERIAL
    Serial.println( APSSID );
    Serial.println( APPASSWORD );
#ifndef UNUSE_MDNS
    Serial.println( MYHOSTNAME );
#endif
#endif
    Count = 0;
#ifdef PIN2LED
    pinMode( 2, OUTPUT );
#endif
    while (WiFi.status() != WL_CONNECTED) {
#ifndef UNUSE_SERIAL
      Serial.print(".");
#endif
#ifdef PIN2LED
      delay(100);      digitalWrite( 2, 1);
      delay(500);      digitalWrite( 2, 0);
      delay(100);      digitalWrite( 2, 1);
      delay(100);      digitalWrite( 2, 0);
      delay(100);      digitalWrite( 2, 1);
      delay(100);      digitalWrite( 2, 0);
#else
      delay(1000);
#endif
      Count++;
      if( Count>30 ){    // ３０秒以上繋がらないならリセットして、再接続を試みる
        WiFi.disconnect();
        delay(1000);
        ESP.restart();
        delay(1000);
      }
    }
#ifdef PIN2LED
    digitalWrite( 2, 1);
#endif
#ifndef UNUSE_MDNS
    MDNS.begin( MYHOSTNAME );
//    MDNS.addService("http", "tcp", 80);    // MDNSの応答はメインソースに記述すべき
#endif
#ifndef UNUSE_SERIAL
    Serial.println("");
    Serial.print("IP address: ");  Serial.println(WiFi.localIP());
#endif
    server.on("/upload", HTTP_POST, [](){ ESPfiles(); }, ESPhandleFileUpload);
    server.on("/files.html", ESPfiles);
    server.on("/upload.html", ESPuploadhtml);
    server.onNotFound(handleNotFound);
    server.begin();
#ifdef USE_NTP
    configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
#endif
  }
#ifndef UNUSE_SERIAL
#ifndef UNUSE_MDNS
  Serial.print("Web Access      : ");  Serial.print( MYHOSTNAME );Serial.println( ".local/" );
#endif
#endif
  return true;
}
//=====================================================================================
bool ESPWebServerToolSetupLoop()
{
  server.handleClient( );
#ifdef USE_FTP							// ＦＴＰモード専用のループ(Inifile設定用)
#ifdef PIN2LED
  digitalWrite( 2, (((millis()/500)%2)?0:1));
#endif
  ftpSrv.handleFTP( );
#endif
#ifndef UNUSE_MDNS
  MDNS.update();
#endif
  if( APMODE==true )    return false;	// APモードなら、通常の処理を行わない
#ifdef USE_NTP							// 日付が1970年なら、ＮＴＰの日付取得待ちでfalse
  time_t t = time(NULL);
  struct tm *tm = localtime(&t);
  if ( (tm->tm_year + 1900) == 1970 ) {
    delay(1000);  // 日付が更新されていないので、1秒待機
    return false;
  }
#endif
  return true;
}
#endif
//=====================================================================================

```

#update
2019/09/19
　　　　・たま吉さんのフォントライブラリ(ESP8266-KanjiFont-Library-SPIFFS)がutf-8だったので、
　　　　　htmlヘッダの charset=Shift_JIS を charset=utf-8 にしました。
　　　　　この改修により、ESP8266のWebServerで入力した文字列を
　　　　　そのままLCDに表示することが可能になりました。
　　　　・ファイルビューワで表示されるファイル名を直接リンクに変更しました。
　　　　　この改修により、ブラウザにて「名前をつけてリンク先を保存」が可能になりました。
　　　　　これでファイルのダウンロードを行います。