ESP8266/ESP32向け、htmlの入力項目をinifileに従った内容に修正する

ESP8266の小物を作っていると、WebServer機能でのパラメータ登録機能があると
同じ小物を複数個運用するのに、設定や確認が楽だろうなぁと感じています。
今回はSPIFFSにあるhtmlファイルを読込んで、inifileに従った内容に更新するクラスを作成しました。
　＝＞xmlでやれば良かったと何気に後悔中です。
これだけだとイマイチなのですが、ESP8266WebServer.hを使うと良い感じになるのでは？　と期待して作っています。

#ESP8266環境で必要なリソース
　SPIFFS / FS等のファイルアクセスの環境
　inifile.h      私が作成したヤツです。

#機能
　1.　入力項目 input type="text"　の value をinifileの登録値に変更する
　2.　パスワード入力項目 input type="password" の value をinifileの登録値に変更する
　3.　ラジオボタンの初期値をinifileの登録値側にします。
　4.　レンジ入力の初期値をinifileのものに置き換えます。
　5.　コンボ入力の初期値をinifileのものに置き換えます。


#使い方
ソースはこんな風にします。

```Arduino:usage.ino
#include <inifile.h>
const char *iniFileName = "/setup.ini"; // Inifileのファイル名
const char *iniHeader = "SETUP";
iniFiles ini(iniFileName);
#include <WebServerToolHtml.h>

//==
void setup() {
  Serial.begin(115200);  Serial.flush(); delay(1000);
  Serial.println("program start");
  if( SPIFFS.begin() )
    Serial.println("SPIFFS Initialize....ok");
  else
  {
    Serial.println("SPIFFS Initialization...failed");
    return;
  }
  WebServerToolHtml.readHtmlFile( "/index.html" );
  Serial.println( WebServerToolHtml.buffer() );
  WebServerToolHtml.release();
}

void loop() {
}
```

ESP8266/ESP32のSPIFFSに、このsetup.iniファイルをコピーします。

```Arduino:SETUP.ini
[SETUP]
SSID=TEST_SSID
PASSWORD=TEST_PASSWORD
switch=on
range=22.2
sensor=HTU21D
*
```

ESP8266/ESP32のSPIFFSに、このhtmlファイルをコピーします。 注) '<' を '＜'に変更してあるので戻す必要があります。

```Arduino:index.html
＜html lang="ja">
＜head>
＜meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
＜meta name="viewport" content="width=device-width,initial-scale=1">
＜/head>
＜form name='SETUP' action=SETUP method='post'>
＜p>SWITCH＜/p>

＜p>SSID：＜input type="text" name="SSID" size="30" maxlength="30" value="SAMPLESSID">＜/p>

＜p>PASS：＜input type="password" name="PASSWORD" size="30" maxlength="30" value="SAMPLEPASSWORD">＜/p>

＜p>
＜input type="radio"  name="switch" value="on" >ON
＜input type="radio"  name="switch" value="off">OFF
＜/p>

＜p>＜input type="range" name="volume1" value="1" min="-10.0" max="29.9" step="0.1">＜/p>

＜p>
＜select name="sensor">
＜option value="DHT11">DHT11＜/option>
＜option value="LM75A">LM75A＜/option>
＜option value="HTU21D">HTU21D＜/option>
＜option value="SHT31">SHT31＜/option>
＜option value="BME280">BME280＜/option>
＜option value="BMP280">BMP280＜/option>
＜/select>
＜/p>

＜input type=submit value=on target=tif>
＜/form>
＜/html>
```



#ソースコード
cppには何も書いていません。

```Arduino:webservertoolhtml.h
#include <inifile.h>

#ifndef HTMLANDINIFILE_H
#define HTMLANDINIFILE_H

class WebServerToolHtmlClass {
    // 空白文字の判定(タブ・スペース・改行)
    bool charSpace( char chData ) {  return ((chData == ' ' || chData == 0x9 || chData == 0xd || chData == 0xa) ? true : false);  }
    // '>' と 空白文字の判定 
    bool charDataTerminate( char chData ){  return ((charSpace(chData)==true || chData=='>')?true:false);  }
    File fp;
    char *writeBuffer;
    int writePos;
    char readCharSkip( ){  char ch = fp.read();                                            return ch;  }
    char readChar( ){      char ch = writeBuffer[writePos] = readCharSkip();  writePos++;  return ch;  }
    String upperCaseChar( char *str ){  String s = str;  s.toUpperCase();  return s;  }
    //== タグを見つけて、区切り文字列を返す ========
    String OLDTAG, TAG;
    char readTAG() {
      char chData[15], POS = 0;  TAG = "";
      while ( fp.available() ) {
        char ch = chData[POS] = readChar();  
        if ( charDataTerminate(ch) ){  chData[POS] = 0x00;  TAG = upperCaseChar(chData);         return ch;  }  // タグが読めた
        POS++;
      }
      return -1;
    }
    //== 設定内容の読込　（設定の右辺）=============
    String detailData;
    char readDetailData() {
      char chData[48], POS = 0;  detailData = "";
      while ( fp.available() ) {
        char ch = chData[POS] = readChar();
        if ( charDataTerminate(ch) ){  chData[POS] = 0x00;  detailData = upperCaseChar(chData);
//          Serial.println( detailData );
          return ch;
        }  // データが読めた
        POS++;
      }
      return -1;
    }
    //==設定内容の読込　（設定の右辺　　　　バッファへは移さない
    char replaceDetailData( char *data ) {
      int oldWritePos = writePos;
      for( int POS=0 ; data[POS]!=0 ; POS++ ){  writeBuffer[ writePos ] = data[POS];  writePos++;  }  // データをバッファに書き出す
      while ( fp.available() ) {
        char ch = readCharSkip();
        if ( charDataTerminate(ch) ){     // データが読めた
          writeBuffer[ writePos++ ] = ch;
  //        writeBuffer[ writePos ] = 0;          Serial.print( "[rep]=" );      Serial.println( &writeBuffer[oldWritePos] );
          return ch;
        }
      }
      return -1;
    }
    //==
    String TYPE, NAME, VALUE, lineType, FORMNAME, OLDNAME;
    //============================================
    char readDetailTitle() {
    char chData[48], POS = 0;
      while ( fp.available() ) {
        char ch = readChar();
        if ( charSpace(ch) )        continue;                                                                   // 読み飛ばし
        if ( ch == '>' )                                                                         return ch;     // 検索中に終端
        chData[ POS++ ] = ch;
        while ( fp.available() ) {
          ch = readChar();
          if ( ch == '=' ) {  // タイトルが読めた
            chData[ POS ] = 0x00;
            String titleData = upperCaseChar( chData );
//            Serial.print( "LINE:" );      Serial.print( titleData );
            if( titleData=="TYPE" ){    ch = readDetailData();  TYPE = detailData.substring( 1, detailData.length()-1 );  return ch;  }
            if( titleData=="NAME" ){    ch = readDetailData();  NAME = detailData.substring( 1, detailData.length()-1 );  return ch;  }
            if( titleData=="VALUE" ){
              if ( TYPE != "TEXT" && TYPE != "PASSWORD" && TYPE != "RANGE" ){
//                Serial.print( "[noquate]" );  Serial.print( TYPE );   
                                        ch = readDetailData();  VALUE = detailData.substring( 1, detailData.length()-1 ); return ch;  }
              String tempString = "\"";
              tempString += ini.get( (char*)iniHeader, (char*)NAME.c_str(), "" );
              tempString += "\"";
              if( tempString=="\"\"" )  ch = readDetailData();  else  ch = replaceDetailData( (char*)tempString.c_str() );
                                                                                    return ch;  }
                                        ch = readDetailData();                      return ch;
          }
          if( charSpace(ch) ){
            chData[ POS ] = 0x00;
            String titleData = upperCaseChar( chData );
//            Serial.print( "SINGLE" );
//            Serial.println( titleData );
            if( titleData=="FORM" )   lineType = "FORM";
            if( titleData=="/FORM" )  FORMNAME = "";                                return ch;
          }
          if ( charDataTerminate( ch )==true  ){                                    return ch;  }  // タイトルが読めた / 読込中に終端
          chData[ POS++ ] = ch;
        }
      }
      return -1;
    }
  public:
    WebServerToolHtmlClass(){ writeBuffer=NULL; }
    ~WebServerToolHtmlClass(){ release(); }
    void release(){  if( writeBuffer!=NULL )  free( writeBuffer );  writeBuffer=NULL;  }
    char *buffer(){  return writeBuffer; }
    //============================================
    char *readHtmlFile( const char *htmlFileName ) {
      if( writeBuffer!=NULL )  release();
      fp = SPIFFS.open( htmlFileName, "r" );
      int filesize = fp.size();
      writeBuffer = (char*)malloc( filesize+1000 );
      writePos = 0;
      FORMNAME = "";
//      Serial.print( "filesize:" );      Serial.println( filesize );
      while ( fp.available() ) {
        if ( readChar() != '<' )      continue;  // 先頭文字を探す
        char ch = readTAG( );                    // タグを見つけて、区切り文字列を返す
        if( ch==-1 || ch=='>' ){
               continue;
        }else{
          TYPE = "";  NAME = "";  VALUE = ""; 
          for( ; ; )  if( !charSpace(ch=readDetailTitle()) )  break;  // データを解釈する
          if( ch=='>' ){
//            Serial.print( "LINE NAME:" );
//            Serial.print( NAME );
//            Serial.print( " VALUE:" );
//            Serial.println( VALUE );
            if( TYPE=="RADIO" ){
              String tempString = ini.get( (char*)iniHeader, (char*)NAME.c_str(), "" );
              tempString.toUpperCase();
//              Serial.print( " STRING:" );
//              Serial.println( tempString );
              if( VALUE == tempString ){
//                Serial.print( "[+checked]" );
                writeBuffer[ writePos-1 ] = ' ';
                strcpy( &writeBuffer[ writePos ], "checked>" );
                writePos += 8;
              }
            }
//            Serial.print(TAG);
            if( TAG!="OPTION" ){
              OLDTAG=TAG;  OLDNAME=NAME;
//              Serial.println("");
            }else{
//              Serial.print(OLDTAG);
//              Serial.print(OLDNAME);
              if( OLDTAG=="SELECT" ){
                String tempString = ini.get( (char*)iniHeader, (char*)OLDNAME.c_str(), "" );
                tempString.toUpperCase();
//                Serial.print(":");
//                Serial.print(VALUE);
//                Serial.print(tempString);
                if( VALUE == tempString ){
                  writeBuffer[ writePos-1 ] = ' ';
                  strcpy( &writeBuffer[ writePos ], "selected>" );
                  writePos += 9;
                }
              }
              Serial.println("");
            }
          }
        }
      }
//      Serial.println( "[EOF]" );
      writeBuffer[ writePos++ ] = 0;
      fp.close();
      return writeBuffer;
    }
    //============================================
};

WebServerToolHtmlClass WebServerToolHtml;
#endif
```