2019年7月15日月曜日

ESP32/arduino : WEBから制御可能なNゲージ用 PWM制御パワーパック_改3

目的:

Nゲージ用 PWM制御パワーパックの改良/機能追加として、以下を行う。
(1) 各設定のダウンロード/アップロード機能追加
(2) WiFi 設定時に WiFIを再起動するかどうかの設定 を追加
(3) WiFi ハングアップ対応

追加機能:

パワーパック改2 からの追加機能は、以下の通り。
  1. 各設定のダウンロード/アップロード機能追加
    WiFi 設定のダウンロード、アップロード 機能を追加。
     ( SPIFFSのテキストファイルをダウンロードする
       テキストファイルをアップロードする
         と同じ )
    自動運行設定 のダウンロード、アップロード機能を追加。
     ( WiFi 設定と同様 )
  2. WiFi 設定ボタンを押下した時に WiFi を再起動するかどうかの設定を追加。
  3. Arduino core for the ESP32 の Ver 1.0.2 で、XHR多重発行時に WiFi が ハングアップするケースが有る為、対応を行う。
     (1) 多重発行の抑止
             ・XHR応答受信後、100mS 経過以上で 次の XHR を発行可能とする。
               ・
     (2) WiFi ハングアップ時、操作盤からの操作で WiFi を再起動。

パワーパック ハードウェア:

ハードウェア(回路) はここちら
  ( パワーパック改 から、RESERV 用スイッチ (SW2) をモード設定用に変更。)

WEB画面:

WiFi設定画面 ( WiFi接続先, ESP32 の IPアドレス等を設定する )
 PWPK設定画面 ( 自動運行の設定を行う )
 
 PWPKメイン画面 ( PWPK の操作画面 )
 

スケッチ、HTML等 ファイル構成:

各ファイルは以下の様に分割。
( ファイルの分割は、まだうまく整理できていない。今後見直し要。
まだ、バグ等があると思われるため、参考にする場合は十分確認願います。)
スケッチの拡張子は全て .ino
ファイル一式はこちら。(zipファイル)

HTML     :   PWPK MAIN HTML (pwpk.html)
PWPK設定 HTML   (pwpk_conf.html)
PWPK XHR 応答 HTML (pwpk_resp.html)

WiFi設定 HTML (wifi_conf.html)
WiFi XHR 応答 HTML (wifi_resp.html)
スケッチ :    MAIN (pwpk_4.ino : 変数定義, setup, loop 関数)
WIFI設定 (wifi_config.ino : 主に WiFI 設定用 の関数)
PWPK処理 (pwpk.ino : 主に PWPK処理、PWPK 設定用 の関数)


 HTML

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
       #base          {font-size:16pt; text-align:center; width:600px; border:solid 4px #000000; background-color:#c0c0c0; }
       #radio_box     {font-size:12pt; float:left ;text-align:left; width:30%; margin: 0px 5%  20px 15% ; }
       #dir_box       {font-size:12pt; float:right;text-align:left; width:30%; margin: 0px 15% 20px  5% ; }
       #disp_box      {font-size:12pt; clear:both; text-align:left; margin: 0px 0px 20px  100px ; }
       #pwr_box       {font-size:12pt; text-align:left; margin: 0px 0px 0px 100px ; }
       #slid_box      {font-size:12pt; text-align:left; margin: 10px 0px 10px 50px ; }
       #sw_box        {font-size:12pt; text-align:center; margin: 10px 0% 10px 0% ; }
       #cnfgbtn1      {font-size:12pt; text-align:right ; margin: 0px 5%;width 100%}
       #cnfgbtn2      {font-size:12pt; text-align:right ; margin: 0px 5%;width 100%}
       #footer        {font-size:12pt; clear:both;}
       output.disp    {margin-left:200px; font-size:14pt; } 
       input.radio    {margin-left:8px; width:30px;}
       input.dir      {margin-left:8px; width:30px;}
       input.value    {margin-left:100px; width:40px; font-size:12pt; }
       input.setbutton{margin-left:8px; width:40px;}
       input.slider   {margin-left:50px; width:400px;}
       input.button   {margin:0px 13%; width:20%;}
       </style>

<title>Power PWN Controller</title></head>

<body>
<div id="base">
  <p>運 行 管 理</p>
  <div id="radio_box">
    <form method="get">
      <input class="radio" type="radio" name="remote" id="rad_lo" value="local" $checked_lo onclick="disp_ctrl(this.value); submit(this.value)">制御盤<br>
      <input class="radio" type="radio" name="remote" id="rad_rm" value="remote" $checked_rm onclick="disp_ctrl(this.value); submit(this.value)">遠隔<br>
    </form>
  </div>
  <div id="dir_box">
    <form method="get">
      <input class="dir" type="radio" name="dir" id="dir_fw" value="foward" $checked_fw onclick="submit(this.value)" disabled >前進<br>
      <input class="dir" type="radio" name="dir" id="dir_bw" value="backward" $checked_bw onclick="submit(this.value)" disabled >後退<br>
    </form>
  </div>
  <div id="disp_box">
    <span> 現在出力 </span>
    <output class='disp' id='o1'> 
    $pwr 
    </output>
  </div>
  <div id="pwr_box">
    <form method="get">
      <span> 出力設定 (0-255)</span> 
      <input class='value'  type='text' name='pwr_v' value= $pwr id='pwr_v' disabled>
      <input class='setbutton' type='submit' name='set' id='set' value='SET' disabled>
    </form> 
  </div>
  <div id="slid_box">
    <form method='get'> 
      <input class='slider' type='range' name='pwr_s' id='pwr_s' value= $pwr min=$pwmin max='255' step='1' disabled
        onchange='setval(this.value);' oninput='setval(this.value)'; onmouseup='submit(this.form)': ontouchend='submit(this.form)'>
    </form> 
  </div>
  <div id="sw_box">
    <form method='get'> 
        <input class='button' type='submit' name='ready' id='ready' value= '準備/解除'  disabled >
        <input class='button' type='button' name='start' id='start' value= '発車/停車'  onclick='sw_start()' disabled >
    </form> 
  </div>
  <div id="cnfgbtn1">
    <form method="get">
      <input type='submit' name='mconf' id='mconf' style="width:100px" value='運行設定画面'>
    </form>  
  </div>
  <div id="cnfgbtn2">
    <form method="get">
      <input type='submit' name='config' id='config' style="width:100px" value='WiFi設定画面'>
    </form>  
  </div>
  <div id="footer">
    <br/>
  </div>
</div>


<script>
var polling = null ;
var sendbusy = 0 ;

// スライダー操作時に実行する -------------------------------------------------------------
function setval(pwr){
        if (sendbusy == 0) {
            sendbusy = 1 ;
   var xhr = new XMLHttpRequest();
          xhr.open("get", "?slid="+pwr );
          xhr.setRequestHeader('Cache-Control', 'no-cache');
          xhr.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
          xhr.responseType = 'document' ;
          xhr.timeout = 5000; // time in milliseconds

          xhr.onreadystatechange = function() {
            if( (xhr.readyState == 4) && (xhr.status == 200) ) {
              document.getElementById('pwr_v').value  = parseInt(xhr.response.getElementById("output1").innerHTML) ;
              document.getElementById('o1').innerHTML = xhr.response.getElementById("output1").innerHTML;
              setTimeout( function() { sendbusy = 0 ; },100) ;
            }
          }

          xhr.ontimeout = function(e) {
            xhr.abort() ;
            setTimeout( function() { sendbusy = 0 ; },100) ;
          }
          xhr.send();
        }
}

// ポーリング時に実行する ----------------------------------------------------------------
function getval(){
  if (sendbusy == 0) {
  sendbusy = 1 ;
    var xhrget = new XMLHttpRequest();
    xhrget.open("get", "?pol" );
    xhrget.setRequestHeader('Cache-Control', 'no-cache');
    xhrget.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
    xhrget.responseType = 'document' ;
    xhrget.timeout = 5000; // time in milliseconds

    xhrget.onreadystatechange = function() {
      if( (xhrget.readyState == 4) && (xhrget.status == 200) ) {
        document.getElementById('o1').innerHTML = xhrget.response.getElementById("output1").innerHTML;
        document.getElementById('pwr_s').value  = parseInt(xhrget.response.getElementById("output1").innerHTML);
        document.getElementById('pwr_v').value  = parseInt(xhrget.response.getElementById("output1").innerHTML);
        if (xhrget.response.getElementById("output2").innerHTML == 1) {
          document.getElementById('dir_fw').checked = false;
          document.getElementById('dir_bw').checked = true ;
        } else {
          document.getElementById('dir_fw').checked = true ;
          document.getElementById('dir_bw').checked = false;
        }
        if (xhrget.response.getElementById("output3").innerHTML == 1) {
          document.getElementById('rad_lo').checked = true ;
          document.getElementById('rad_rm').checked = false;
          disp_ctrl("local") ;
        } else {
          document.getElementById('rad_lo').checked = false;
          document.getElementById('rad_rm').checked = true ;
          disp_ctrl("remote") ;
        }
        setTimeout( function() { sendbusy = 0 ; },100) ;
      }
    }

    xhrget.ontimeout = function(e) {
      xhrget.abort() ;
      setTimeout( function() { sendbusy = 0 ; },100) ;
    }
    xhrget.send();
  }
}
// --------------------------------------------------------------------------------------- 

// 発車/停車 を押下された時に実行する -----------------------------------------------------
function sw_start() {
  while (1) {
    if (sendbusy == 0) {
      sendbusy = 1 ;
      var xhr_start = new XMLHttpRequest();
      xhr_start.open("get", "?start" );
      xhr_start.setRequestHeader('Cache-Control', 'no-cache');
      xhr_start.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
      xhr_start.responseType = 'document' ;
      xhr_start.timeout = 5000; // time in milliseconds
      
      xhr_start.onreadystatechange = function() {
        if( (xhr_start.readyState == 4) && (xhr_start.status == 200) ) {
          if (polling == null) polling = setInterval(getval,200) ;
        clearInterval(polling);
        polling = setInterval(getval,200) ;
          setTimeout( function() { sendbusy = 0 ; },100) ;
        }
      }
      
      xhr_start.ontimeout = function(e) {
        xhr_start.abort() ;
          setTimeout( function() { sendbusy = 0 ; },100) ;
      }
      xhr_start.send();
      break ;
    } else {
    }
  }
}
// --------------------------------------------------------------------------------------- 

// Remote/Local 切り替え時に実行する -----------------------------------------------------
function disp_ctrl( radioid ) {
  if(radioid == 'remote') {
    document.getElementById('dir_fw').disabled = false;
    document.getElementById('dir_bw').disabled = false;
    document.getElementById('pwr_v').disabled = false;
    document.getElementById('set').disabled = false;
    document.getElementById('pwr_s').disabled = false;
    document.getElementById('ready').disabled = false;
    document.getElementById('start').disabled = false;
    clearInterval(polling);
    polling = setInterval(getval,1000) ;
  } else {
    document.getElementById('dir_fw').disabled = true;
    document.getElementById('dir_bw').disabled = true;
    document.getElementById('pwr_v').disabled = true;
    document.getElementById('set').disabled = true;
    document.getElementById('pwr_s').disabled = true;
    document.getElementById('ready').disabled = true;
    document.getElementById('start').disabled = true;
    clearInterval(polling);
    polling = setInterval(getval,200) ;
  }

}
// --------------------------------------------------------------------------------------- 

// 最初に実行する ------------------------------------------------------------------------
window.onload = function() {
  if(document.getElementById("rad_rm").checked) {
    document.getElementById('dir_fw').disabled = false;
    document.getElementById('dir_bw').disabled = false;
    document.getElementById('pwr_v').disabled = false;
    document.getElementById('set').disabled = false;
    document.getElementById('pwr_s').disabled = false;
    document.getElementById('ready').disabled = false;
    document.getElementById('start').disabled = false;
    polling = setInterval(getval,1000) ;
  } else if(document.getElementById("rad_lo").checked) {
    document.getElementById('dir_fw').disabled = true;
    document.getElementById('dir_bw').disabled = true;
    document.getElementById('pwr_v').disabled = true;
    document.getElementById('set').disabled = true;
    document.getElementById('pwr_s').disabled = true;
    document.getElementById('ready').disabled = true;
    document.getElementById('start').disabled = true;
    polling = setInterval(getval,200) ;
  }
}
// --------------------------------------------------------------------------------------- 

</script>

</body>
</html>

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
    #base         {font-size:16pt; text-align:center; width:600px; border:solid 4px #00000000; background-color:#c0c0c0; }
    #item_box     {height:30px; font-size:12pt; text-align:left; }
    #item_0       {font-size:10pt; float:left ; text-align:left; width:20%;  }
    #item_1       {font-size:10pt; float:left ; text-align:center; width:20%;  }
    #item_def     {font-size:10pt; float:left ; text-align:center; width:15.5%; }
    #chec_box     {height:30px; font-size:12pt; cleart:both ;text-align:left;  }
    #conf_box     {height:30px; font-size:12pt; text-align:left;  }
    #button       {font-size:12pt; clear:both; width 50%}
    #dlul         {font-size: 8pt; text-align:left  ; height:12pt; float:left;  margin:10px 0px 0px 25% ; border:dotted 1px #800080  }
    input.button  {margin:10px 10% ; width: 25%;}
</style>

<title>運行設定画面</title></head>

<body>
<div id="base">
  <p>設定画面</p>
  <form method="get">
    <div id="chec_box">
      <div id="item_0">
        <span> Loop </span>
        <input type="checkbox" name="loop" id="loop" value="1" $checked_loop autocomplete="off" onclick="disp_ctrl_0(this.checked)" >
      </div>
      <div>
        <span> 回数 </span>
        <input type="number" name="loopnum" id="loopnum" value=$loopnum max="36000"  style="width:80px;" >
      </div>
    </div>

    <!-- 項目名の表示 =========================================    -->
    <div id="item_box">
     <div id="item_0"> <span>  </span> </div>
     <div id="item_1"> <span>起点</span> </div> 
     <div id="item_def"> <span>加速度</span> </div> 
     <div id="item_def"> <span>目標速度</span> </div> 
     <div id="item_def"> <span>時間(x0.1秒)</span> </div> 
    </div>

    <!-- 設定 1 の 入力欄 =====================================    -->
    <div id="conf_box">
      <div id="item_0" >
        <span style="margin-left:25px">  設定1 : </span>
      </div>
      <div id="item_1">
        <span>  </span>
      </div>
      <div id="item_def">
        <input type="number" name="acc1" id="acc1" value=$acc1 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="spd1" id="spd1" value=$spd1 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="tim1" id="tim1" value=$tim1 max="36000" style="width:50px;" >
      </div>
    </div>

    <!-- 設定 2 の 入力欄 =====================================    -->
    <div id="conf_box">
      <div id="item_0">
        <input type="checkbox" name="chk2" id="chk2" value="1" $en2 autocomplete="off"  onclick="disp_ctrl_2(this.checked)" >
        <span> 設定2 : </span>
      </div>
      <div id="item_1">
        <select name="trg2" id="trg2" autocomplete="off">
          <option value="0" $trg2_sel0 >前項の続き</option>
     <option value="1" $trg2_sel1 >スイッチ押下</option>
        </select>
      </div>
      <div id="item_def">
        <input type="number" name="acc2" id="acc2" value=$acc2 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="spd2" id="spd2" value=$spd2 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="tim2" id="tim2" value=$tim2 max="36000" style="width:50px;" >
      </div>
    </div>

    <!-- 設定 3 の 入力欄 =====================================    -->
    <div id="conf_box">
      <div id="item_0">
        <input type="checkbox" name="chk3" id="chk3" value="1" $en3 autocomplete="off"  onclick="disp_ctrl_3(this.checked)" >
        <span> 設定3 : </span>
      </div>
      <div id="item_1">
        <select name="trg3" id="trg3" autocomplete="off">
          <option value="0" $trg3_sel0>前項の続き</option>
     <option value="1" $trg3_sel1>スイッチ押下</option>
        </select>
      </div>
      <div id="item_def">
        <input type="number" name="acc3" id="acc3" value=$acc3 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="spd3" id="spd3" value=$spd3 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="tim3" id="tim3" value=$tim3 max="36000" style="width:50px;" >
      </div>
    </div>

    <!-- 設定 4 の 入力欄 =====================================    -->
    <div id="conf_box">
      <div id="item_0">
        <input type="checkbox" name="chk4" id="chk4" value="1" $en4 autocomplete="off"  onclick="disp_ctrl_4(this.checked)" >
        <span> 設定4 : </span>
      </div>
      <div id="item_1">
        <select name="trg4" id="trg4" autocomplete="off">
          <option value="0" $trg4_sel0>前項の続き</option>
     <option value="1" $trg4_sel1>スイッチ押下</option>
        </select>
      </div>
      <div id="item_def">
        <input type="number" name="acc4" id="acc4" value=$acc4 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="spd4" id="spd4" value=$spd4 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="tim4" id="tim4" value=$tim4 max="36000" style="width:50px;" >
      </div>
    </div>

    <!-- 設定 5 の 入力欄 =====================================    -->
    <div id="conf_box">
      <div id="item_0">
        <input type="checkbox" name="chk5" id="chk5" value="1" $en5 autocomplete="off"  onclick="disp_ctrl_5(this.checked)" >
        <span> 設定5 : </span>
      </div>
      <div id="item_1">
        <select name="trg5" id="trg5" autocomplete="off">
          <option value="0" $trg5_sel0>前項の続き</option>
     <option value="1" $trg5_sel1>スイッチ押下</option>
        </select>
      </div>
      <div id="item_def">
        <input type="number" name="acc5" id="acc5" value=$acc5 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="spd5" id="spd5" value=$spd5 max="255" style="width:50px;" >
      </div>
      <div id="item_def">
        <input type="number" name="tim5" id="tim5" value=$tim5 max="36000" style="width:50px;" >
      </div>
    </div>
    <br/>
    <div id="button">
      <input type="submit" name="set" id="set" value="設定" style="width:100px" >
      <input type="checkbox" name="save" id="save" autocomplete="off" >
      <span>ファイルに保存する</span>
      <input type="submit" name="rtn" id="rtn" value="戻る" style="width:100px; margin-left:10%" >
    </div>
    <div id="dlul">
      <span>設定を </span>
      <a href='javascript:confdl();'>ダウンロード</a>
      <span>/ </span>
      <a href='javascript:selfile();'>アップロード</a>
      <span>する</span>
      <form id='form_ul'>
      <input name='fsel' id='fsel' type='file' onchange='conful()' hidden />
      </form>
    </div>
   <br>
 </div>

  </form>
</div>

<!-- JAVA SCRIPT ========================================================= -->
<script>

function disp_ctrl_0(enable) {
  if (enable == true) {
    document.getElementById('loopnum').disabled = false ;
  }else {
    document.getElementById('loopnum').disabled = true  ;
  }
}

function disp_ctrl_2(enable) {
  if (enable == true) {
    document.getElementById('trg2').disabled = false ;
    document.getElementById('acc2').disabled = false ;
    document.getElementById('spd2').disabled = false ;
    document.getElementById('tim2').disabled = false ;
  }else {
    document.getElementById('trg2').disabled = true  ;
    document.getElementById('acc2').disabled = true  ;
    document.getElementById('spd2').disabled = true  ;
    document.getElementById('tim2').disabled = true  ;
  }
}

function disp_ctrl_3(enable) {
  if (enable == true) {
    document.getElementById('trg3').disabled = false ;
    document.getElementById('acc3').disabled = false ;
    document.getElementById('spd3').disabled = false ;
    document.getElementById('tim3').disabled = false ;
  }else {
    document.getElementById('trg3').disabled = true  ;
    document.getElementById('acc3').disabled = true  ;
    document.getElementById('spd3').disabled = true  ;
    document.getElementById('tim3').disabled = true  ;
  }
}

function disp_ctrl_4(enable) {
  if (enable == true) {
    document.getElementById('trg4').disabled = false ;
    document.getElementById('acc4').disabled = false ;
    document.getElementById('spd4').disabled = false ;
    document.getElementById('tim4').disabled = false ;
  }else {
    document.getElementById('trg4').disabled = true  ;
    document.getElementById('acc4').disabled = true  ;
    document.getElementById('spd4').disabled = true  ;
    document.getElementById('tim4').disabled = true  ;
  }
}

function disp_ctrl_5(enable) {
  if (enable == true) {
    document.getElementById('trg5').disabled = false ;
    document.getElementById('acc5').disabled = false ;
    document.getElementById('spd5').disabled = false ;
    document.getElementById('tim5').disabled = false ;
  }else {
    document.getElementById('trg5').disabled = true  ;
    document.getElementById('acc5').disabled = true  ;
    document.getElementById('spd5').disabled = true  ;
    document.getElementById('tim5').disabled = true  ;
  }
}

// 設定ファイルのダウンロード ---------------------------------------------------------
function confdl() {
  "use strict";

  var xhr = new XMLHttpRequest();
  xhr.open("get", "?mcnfld=DLD");
  xhr.setRequestHeader('Cache-Control', 'no-cache');
  xhr.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
  xhr.responseType = 'document' ;
 
  xhr.onreadystatechange = function() {
    if( (xhr.readyState == 4) && (xhr.status == 200) ) {
      var filename = "pwpkcnf.txt" ;
      var str = xhr.response.getElementById("output").innerHTML ;
      var blob = new Blob([str],{type:'text/plain'}) ;
      if (window.navigator.msSaveBlob) {
      //window.navigator.msSaveBlob(blob, filename);
        window.navigator.msSaveOrOpenBlob(blob,filename) ;
      }
      else {
        var objectURL = window.URL.createObjectURL(blob);
        var link = document.createElement("a");
        document.body.appendChild(link);
        link.href = objectURL;
        link.download = filename;
        link.click();
        document.body.removeChild(link);
      }
    }
  }
  xhr.send("");
}


// 設定ファイルのアップロード ---------------------------------------------------------
//   --- "アップロード" クリック時の処理 ---
function selfile() {
  document.getElementById('fsel').click() ;
}

//   --- "アップロード" の処理 ---
function conful() {
  "use strict";

  var ulfile  = document.getElementById("fsel") ;  // ファイル選択 id=fsel のエレメントを取得
  var file = ulfile.files[0] ;                     // 選択された ファイルオブジェクトを取得
  var reader = new FileReader() ;                  // ファイルリーダーオブジェクトの生成
  var pos = 0 ;                                    // テキスト解析用のポインタ
  var work = "";                                   // 一時格納用の変数
  var res = "";                                    // 一時格納用の変数

  var xhr_ul = new XMLHttpRequest();               // ファイル送信用の XHR オブジェクト生成
  xhr_ul.open("post", "?mcnful");                  // XHR の HTTPリクエスト を OPEN で作成
  xhr_ul.setRequestHeader('Cache-Control', 'no-cache');  // キャッシュ無効化
  xhr_ul.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
  xhr_ul.responseType = 'document' ;               // response type を document に設定

  xhr_ul.onreadystatechange = function() {
    if( (xhr_ul.readyState == 4) && (xhr_ul.status == 200) ) {
    // XHR の応答があった場合の処理 : 特になし  
    }
  }

  reader.readAsText(file) ;          // ファイルオブジェクトをテキストとして読み込む
  reader.onload = function(e){       // 読み込みが終了した場合に実行
    console.log(reader.result) ;
    // テキストを解析して、各要素に値を代入する ----------------------------
    // -- 読み込んだ設定ファイルから 繰り返し設定を取得して 要素に代入
      pos = reader.result.indexOf("prg_loop") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      if (res[0] == 1) {
        document.getElementById('loop').checked = true ;
        disp_ctrl_0(true) ;
      } else
        document.getElementById('loop').checked = false ;
    // 読み込んだ設定ファイルから 繰り返し回数を取得して 要素に代入
    pos = reader.result.indexOf("prg_loopnum") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('loopnum').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定1の加速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_1_acc") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('acc1').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定1の目標速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_1_spd") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('spd1').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定1の時間 を取得して 要素に代入
    pos = reader.result.indexOf("prg_1_tim") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('tim1').value = res[0].trim() ;
    // -- 読み込んだ設定ファイルから 設定2のイネーブル を取得して 要素に代入
      pos = reader.result.indexOf("prg_2_en") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      if (res[0] == 1) {
        document.getElementById('chk2').checked = true ;
        disp_ctrl_2(true) ;
      } else
        document.getElementById('chk2').checked = false ;
    // 読み込んだ設定ファイルから 設定2の起点 を取得して 要素に代入
    pos = reader.result.indexOf("prg_2_trg") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('trg2').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定2の加速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_2_acc") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('acc2').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定2の目標速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_2_spd") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('spd2').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定2の時間 を取得して 要素に代入
    pos = reader.result.indexOf("prg_2_tim") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('tim2').value = res[0].trim() ;
    // -- 読み込んだ設定ファイルから 設定3のイネーブル を取得して 要素に代入
      pos = reader.result.indexOf("prg_3_en") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      if (res[0] == 1) {
        document.getElementById('chk3').checked = true ;
        disp_ctrl_3(true) ;
      } else
        document.getElementById('chk3').checked = false ;
    // 読み込んだ設定ファイルから 設定3の起点 を取得して 要素に代入
    pos = reader.result.indexOf("prg_3_trg") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('trg3').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定3の加速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_3_acc") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('acc3').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定3の目標速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_3_spd") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('spd3').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定3の時間 を取得して 要素に代入
    pos = reader.result.indexOf("prg_3_tim") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('tim3').value = res[0].trim() ;
    // -- 読み込んだ設定ファイルから 設定4のイネーブル を取得して 要素に代入
      pos = reader.result.indexOf("prg_4_en") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      if (res[0] == 1) {
        document.getElementById('chk4').checked = true ;
        disp_ctrl_4(true) ;
      } else
        document.getElementById('chk4').checked = false ;
    // 読み込んだ設定ファイルから 設定4の起点 を取得して 要素に代入
    pos = reader.result.indexOf("prg_4_trg") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('trg4').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定4の加速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_4_acc") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('acc4').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定4の目標速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_4_spd") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('spd4').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定4の時間 を取得して 要素に代入
    pos = reader.result.indexOf("prg_4_tim") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('tim4').value = res[0].trim() ;
    // -- 読み込んだ設定ファイルから 設定5のイネーブル を取得して 要素に代入
      pos = reader.result.indexOf("prg_5_en") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      if (res[0] == 1) {
        document.getElementById('chk5').checked = true ;
        disp_ctrl_5(true) ;
      } else
        document.getElementById('chk5').checked = false ;
    // 読み込んだ設定ファイルから 設定5の起点 を取得して 要素に代入
    pos = reader.result.indexOf("prg_5_trg") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('trg5').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定5の加速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_5_acc") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('acc5').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定5の目標速度 を取得して 要素に代入
    pos = reader.result.indexOf("prg_5_spd") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('spd5').value = res[0].trim() ;
    // 読み込んだ設定ファイルから 設定5の時間 を取得して 要素に代入
    pos = reader.result.indexOf("prg_5_tim") ;
    pos = reader.result.indexOf(":",pos) ;
    res = reader.result.substr(pos+1).split("\n") ;
    document.getElementById('tim5').value = res[0].trim() ;

    // XHR を送信。 (ファイルのテキストを ESP32 に送信 )
    xhr_ul.send(reader.result);
  } 
}

window.onload = function() {
  if(document.getElementById('loop').checked) {
    document.getElementById('loopnum').disabled = false ;
  }else{
    document.getElementById('loopnum').disabled = true  ;
  }
  if(document.getElementById('chk2').checked) {
    document.getElementById('trg2').disabled = false ;
    document.getElementById('acc2').disabled = false ;
    document.getElementById('spd2').disabled = false ;
    document.getElementById('tim2').disabled = false ;
  }else{
    document.getElementById('trg2').disabled = true  ;
    document.getElementById('acc2').disabled = true  ;
    document.getElementById('spd2').disabled = true  ;
    document.getElementById('tim2').disabled = true  ;
  }
  if(document.getElementById('chk3').checked) {
    document.getElementById('trg3').disabled = false ;
    document.getElementById('acc3').disabled = false ;
    document.getElementById('spd3').disabled = false ;
    document.getElementById('tim3').disabled = false ;
  }else{
    document.getElementById('trg3').disabled = true  ;
    document.getElementById('acc3').disabled = true  ;
    document.getElementById('spd3').disabled = true  ;
    document.getElementById('tim3').disabled = true  ;
  }
  if(document.getElementById('chk4').checked) {
    document.getElementById('trg4').disabled = false ;
    document.getElementById('acc4').disabled = false ;
    document.getElementById('spd4').disabled = false ;
    document.getElementById('tim4').disabled = false ;
  }else{
    document.getElementById('trg4').disabled = true  ;
    document.getElementById('acc4').disabled = true  ;
    document.getElementById('spd4').disabled = true  ;
    document.getElementById('tim4').disabled = true  ;
  }
  if(document.getElementById('chk5').checked) {
    document.getElementById('trg5').disabled = false ;
    document.getElementById('acc5').disabled = false ;
    document.getElementById('spd5').disabled = false ;
    document.getElementById('tim5').disabled = false ;
  }else{
    document.getElementById('trg5').disabled = true  ;
    document.getElementById('acc5').disabled = true  ;
    document.getElementById('spd5').disabled = true  ;
    document.getElementById('tim5').disabled = true  ;
  }
}

</script>

</body>
</html>

<!DOCTYPE html><html lang='ja'>
  <head> <title>Color LED Controller</title></head>
  <body>

    <output id='output1'> $pwr</output>
    <output id='output2'> $dir</output>
    <output id='output3'> $local</output>

  </body> </html>

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
    #base       {font-size:16pt; text-align:center; width:600px; border:solid 4px #00000000; background-color:#c0c0c0; }
    #area_1     {font-size:12pt; text-align:center; width:100%;  border:dotted 0px #0000ff; }
    #item_text  {font-size:12pt; text-align:left;   width:45%;   height: 25px; float:left ; }
    #input_box  {font-size:12pt; text-align:left;   width:55%;   height: 25px; float:right; }
    #ip_box     {font-size:12pt; text-align:left;   width:15%;   height: 25px; float:right; }
    #radio_box  {font-size:12pt; text-align:center; width:80% ;  margin: 0% 0%; clear:both; }
    #area_2     {font-size:12pt; text-align:center; width:100%;  border:dotted 0px #00ff00; }
    #rbt_chk    {font-size:10pt; text-align:left; margin : 0% 0% 0% 20% ; }
    #area_3     {font-size:12pt; text-align:center; width:100%;  border:dotted 0px #00ff00; }
    #area_3-1   {font-size:12pt; text-align:left  ; width:25%;   float:left;  margin:0px 0px 0px 15%  ; border:dotted 1px #008080 }
    #area_3-2   {font-size: 8pt; text-align:left  ; height:12pt; float:left;  margin:10px 0px 0px 25% ; border:dotted 1px #800080  }
    #foot        {font-size:16pt; clear:both;}
    input.val    {width: 90%;}
    input.ip     {width: 20%;}
    input.button {margin:10px 10% 0% 10%; width: 25%;}
    input.radio  {margin:10px 0px 0px 15% ; }
</style>

<title>設定画面</title></head>

<body>
<div id="base">
  <p>設定画面</p>
  <div id="item_text">
    <span> WiFi 接続先 SSID </span>
  </div>
 <form method="get">
  <div id="input_box">
      <input class='val' type='text' name='ssid' id='ssid' value=$ssid>
  </div>
  <div id="item_text">
    <span> WiFi 接続先 PASSWORD </span>
  </div>
  <div id="input_box">
      <input class='val' type='text' name='pass' id='pass' value=$pass>
  </div>
  <div id="item_text">
    <span> WiFi 接続  IP アドレス </span>
  </div>
  <div id="input_box">
      <input class='ip' type='number' name='ip1' id='ip1' min=0 max=255 value=$ip1 >
      <input class='ip' type='number' name='ip2' id='ip2' min=0 max=255 value=$ip2 >
      <input class='ip' type='number' name='ip3' id='ip3' min=0 max=255 value=$ip3 >
      <input class='ip' type='number' name='ip4' id='ip4' min=0 max=255 value=$ip4 >
  </div>
  <div id="item_text">
    <span> WiFi 接続  サブネットマスク</span>
  </div>
  <div id="input_box">
      <input class='ip' type='number' name='sm1' id='sm1' min=0 max=255 value=$sm1 >
      <input class='ip' type='number' name='sm2' id='sm2' min=0 max=255 value=$sm2 >
      <input class='ip' type='number' name='sm3' id='sm3' min=0 max=255 value=$sm3 >
      <input class='ip' type='number' name='sm4' id='sm4' min=0 max=255 value=$sm4 >
  </div>
  <div id="item_text">
    <span> WiFi 接続  デフォルトゲートウェイ</span>
  </div>
  <div id="input_box">
      <input class='ip' type='number' name='gw1' id='gw1' min=0 max=255 value=$gw1 >
      <input class='ip' type='number' name='gw2' id='gw2' min=0 max=255 value=$gw2 >
      <input class='ip' type='number' name='gw3' id='gw3' min=0 max=255 value=$gw3 >
      <input class='ip' type='number' name='gw4' id='gw4' min=0 max=255 value=$gw4 >
  </div>
  <div id="item_text">
    <span> WiFi 接続  DNSアドレス</span>
  </div>
  <div id="input_box">
      <input class='ip' type='number' name='dns1' id='dns1' min=0 max=255 value=$dns1 >
      <input class='ip' type='number' name='dns2' id='dns2' min=0 max=255 value=$dns2 >
      <input class='ip' type='number' name='dns3' id='dns3' min=0 max=255 value=$dns3 >
      <input class='ip' type='number' name='dns4' id='dns4' min=0 max=255 value=$dns4 >
  </div>
  <div id="radio_box">
      <input class='radio' type='radio' name='wifi_stamode' id='rad_sta' value='sta' $checked_sta > STA MODE
      <input class='radio' type='radio' name='wifi_stamode' id='rad_ap' value='ap' $checked_ap > AP MODE
  </div>
  <div id="area_2">
      <input class='button' type='submit' name='set' id='set' value='設定'>
      <input class='button' type='submit' name='rtn' id='rtn' value='戻る'>
    <div id="rbt_chk">
      <input type="checkbox" name="rebt" id="rebt" value= "on" $checked_rebt autocomplete="off" >
      <span>設定時、リブートする</span>
    </div>
  </div>
 </form>
 <div id="area_3">
    
    <div id="area_3-2">
      <span>設定を </span>
      <a href='javascript:confdl();'>ダウンロード</a>
      <span>/ </span>
      <a href='javascript:selfile();'>アップロード</a>
      <span>する</span>
      <form id='form_ul'>
      <input name='fsel' id='fsel' type='file' onchange='conful()' hidden />
      </form>
     </div>
   <br>
 </div>
 <div id="foot">
   <span>$footer</span>
 </div>
</div>

  <script>
function confdl() {
  "use strict";

  var xhr = new XMLHttpRequest();
  xhr.open("get", "?confld=CLD");
  xhr.setRequestHeader('Cache-Control', 'no-cache');
  xhr.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
  xhr.responseType = 'document' ;
 
  xhr.onreadystatechange = function() {
    if( (xhr.readyState == 4) && (xhr.status == 200) ) {
      var filename = "config.txt" ;
      var str = xhr.response.getElementById("output").innerHTML ;
      var blob = new Blob([str],{type:'text/plain'}) ;
      if (window.navigator.msSaveBlob) {
      //window.navigator.msSaveBlob(blob, filename);
        window.navigator.msSaveOrOpenBlob(blob,filename) ;
      }
      else {
        var objectURL = window.URL.createObjectURL(blob);
        var link = document.createElement("a");
        document.body.appendChild(link);
        link.href = objectURL;
        link.download = filename;
        link.click();
        document.body.removeChild(link);
      }
    }
  }
  xhr.send("");
}

function selfile() {
  document.getElementById('fsel').click() ;
}

function conful() {
  "use strict";

  var ulfile  = document.getElementById("fsel") ;  // ファイル選択 id=fsel のエレメントを取得
  var file = ulfile.files[0] ;                     // 選択された ファイルオブジェクトを取得
  var reader = new FileReader() ;                  // ファイルリーダーオブジェクトの生成
  var pos = 0 ;                                    // テキスト解析用のポインタ
  var work = "";                                   // 一時格納用の変数
  var res = "";                                    // 一時格納用の変数

  var xhr_ul = new XMLHttpRequest();               // ファイル送信用の XHR オブジェクト生成
  xhr_ul.open("post", "?conful");                  // XHR の HTTPリクエスト を OPEN で作成
  xhr_ul.setRequestHeader('Cache-Control', 'no-cache');  // キャッシュ無効化
  xhr_ul.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
  xhr_ul.responseType = 'document' ;               // response type を document に設定

  xhr_ul.onreadystatechange = function() {
    if( (xhr_ul.readyState == 4) && (xhr_ul.status == 200) ) {
    // XHR の応答があった場合の処理 : 特になし  
    }
  }

  reader.readAsText(file) ;          // ファイルオブジェクトをテキストとして読み込む
  reader.onload = function(e){       // 読み込みが終了した場合に実行
    console.log(reader.result) ;
      // テキストを解析して、各要素に値を代入する ----------------------------
      // 読み込んだ設定ファイルから ssid を取得して 要素に代入
      pos = reader.result.indexOf("wifi_ssid") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      document.getElementById('ssid').value = res[0].trim() ;
      // 読み込んだ設定ファイルから パスワード を取得して 要素に代入
      pos = reader.result.indexOf("wifi_pass") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      document.getElementById('pass').value = res[0].trim() ;
      // 読み込んだ設定ファイルから IPアドレス を取得して 要素に代入
      pos = reader.result.indexOf("wifi_ip") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      res = res[0].split(".") ;
      document.getElementById('ip1').value = res[0].trim() ;
      document.getElementById('ip2').value = res[1].trim() ;
      document.getElementById('ip3').value = res[2].trim() ;
      document.getElementById('ip4').value = res[3].trim() ;
      // 読み込んだ設定ファイルから サブネットマスク を取得して 要素に代入
      pos = reader.result.indexOf("wifi_sm") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      res = res[0].split(".") ;
      document.getElementById('sm1').value = res[0].trim() ;
      document.getElementById('sm2').value = res[1].trim() ;
      document.getElementById('sm3').value = res[2].trim() ;
      document.getElementById('sm4').value = res[3].trim() ;
      // 読み込んだ設定ファイルから ゲートウェイアドレス を取得して 要素に代入
      pos = reader.result.indexOf("wifi_gw") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      res = res[0].split(".") ;
      document.getElementById('gw1').value = res[0].trim() ;
      document.getElementById('gw2').value = res[1].trim() ;
      document.getElementById('gw3').value = res[2].trim() ;
      document.getElementById('gw4').value = res[3].trim() ;
      // 読み込んだ設定ファイルから DNSアドレス を取得して 要素に代入
      pos = reader.result.indexOf("wifi_dns") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      res = res[0].split(".") ;
      document.getElementById('dns1').value = res[0].trim() ;
      document.getElementById('dns2').value = res[1].trim() ;
      document.getElementById('dns3').value = res[2].trim() ;
      document.getElementById('dns4').value = res[3].trim() ;
      // 読み込んだ設定ファイルから wifiモード を取得して 要素に代入
      pos = reader.result.indexOf("wifi_mode") ;
      pos = reader.result.indexOf(":",pos) ;
      res = reader.result.substr(pos+1).split("\n") ;
      if (res[0] == "ap")
        document.getElementById('rad_ap').checked = true ;
      else
        document.getElementById('rad_sta').checked = true ;

      // XHR を送信。 (ファイルのテキストを ESP32 に送信 )
      xhr_ul.send(reader.result);
  } ;
}

  </script>

</body>
</html>

<!DOCTYPE html><html lang='ja'>
  <head> <title>Color LED Controller</title></head>
  <html> <body>
    <output id='output'>$conf_text</output>
  </body> </html>

スケッチ

MAIN (pwpk_4.ino : 変数定義, setup, loop 関数)
//+===========================================================================+
// | PWPK Control                                                             |
// |                                                                          |
// |  Rev.  0.4                                                               |
//+===========================================================================+
                                                  //
#include <WiFi.h>                                 // WiFi 使用の為
#include "FS.h"                                   // SPIFFS 使用の為
#include "SPIFFS.h"                               // SPIFFS 使用の為
#include <Ticker.h>                               //
                                                  //
  #define DEBUG_HTML                              // Debug用 Serial 表示制御用
//#define DEBUG_HTML2                             // Debug用 Serial 表示制御用
//#define DEBUG_WiFi                              // Debug用 Serial 表示制御用
  #define DEBUG                                   // Debug用 Serial 表示制御用
                                                  //
// ---------------------------------------------------------------------------
// | WiFi 設定用 定義                                                        |
// ---------------------------------------------------------------------------
// AP モード用 WIFI 設定 -----------------------------------------------------
const char ssid[] = "ESP32_AP";                   // SSID for softAP
const char pass[] = "password";                   // password for softAP
const IPAddress ip(192, 168, 4, 1);               // IPアドレス for softAP
const IPAddress subnet(255, 255, 255, 0);         // サブネットマスク for softAP
                                                  //
// STA モード用 WiFi設定 ------------------------------------------------------
char wifi_ssid[128] = "" ;                        // SSID        for WiFi STA
char wifi_pass[128] = "" ;                        // password    for WiFi STA
IPAddress wifi_ip(192,168,1,66) ;                 // IP Address  for WiFi STA
IPAddress wifi_gw(192,168,1,1)  ;                 // gate way    for WiFi STA
IPAddress wifi_sm(255,255,255,0) ;                // subnet mask for WiFi STA
IPAddress wifi_dns(192,168,1,1) ;                 // dns address for WiFi STA
char wifi_mode[16] = ""   ;                       // wifi mode
int  set_reboot = 0 ;                             // 設定時リブート
                                                  //
// WiFi 動作用フラグ ----------------------------------------------------------
String foot_msg ="" ;                             // リブートメッセージ用
bool  wifi_restartreq = false ;                   // WiFiリスタート要求フラグ
bool  rebootreq = false ;                         // リブート要求フラグ
bool  wifi_confdl = false ;                       // wifi 設定ダウンロード
                                                  //
WiFiServer server(80);                            //
                                                  //
// 動作モードフラグ -----------------------------------------------------------
#define WIFI_CONF   0                             //  html モード用定義
#define MAIN        1                             //
#define MAIN_CONF   2                             //
                                                  //
bool stamode = true ;                             // WiFi STA 接続モード : true
bool sta_exec = false ;                           // WiFi STA接続モード実行中フラグ
int  html_mode = MAIN ;                           // HTML 選択状態
int  html_exec = MAIN ;                           // 実行中の HTML 選択状態 
                                                  //
// -----------------------------------------------------------------------------
// | HTML ファイル名等                                                         |
// -----------------------------------------------------------------------------
const char fname_wfCONF[] = "/wifi_conf.html" ;   // WiFi 設定用 HTML ファイル名
const char fname_wfRESP[] = "/wifi_resp.html" ;   // WiFi XHR レスポンスHTML
const char fname_pwpk[]   = "/pwpk.html"   ;      // PWPK処理 HTML ファイル名
const char fname_mRESP[]  = "/pwpk_resp.html" ;   // PWPK XHR レスポンスHTML
const char fname_mCONF[]  = "/pwpk_conf.html";    // PWPK 設定用 HTML ファイル名
                                                  //
String html_wfCONF ;                              // WiFi設定用 HTML 格納用
String html_wfRESP ;                              // WiFi XHR 応答用 HTML 格納用
String html_pwpk ;                                // PWPK処理HTML 格納用
String html_mCONF ;                               // PWPK設定用 HTML 格納用
String html_mRESP ;                               // PWPK XHR 応答用 HTML 格納用
                                                  //
// -----------------------------------------------------------------------------
// - PWPK処理用                                                                -
// -----------------------------------------------------------------------------
// 運行設定用 変数定義 --------------------------------------------------------
int      prg_tbl[6][5] =     // 運行設定
          { {0,1,0,0,0},     // prg_loop,prg_loop_intval, resrv,reserv,reserv,
            {1,0,5,145,0},   // prt_1_en,reserv,   prg_1_acc,prt_1_spd,prg_1_tim,
            {0,0,0,0,0},     // prt_2_en,prg_2_trg,prg_2_acc,prg_2_spd,prg_2_tim,
            {0,0,0,0,0},     // prt_3_en,prg_3_trg,prg_3_acc,prg_3_spd,prg_3_tim,
            {0,0,0,0,0},     // prt_4_en,prg_4_trg,prg_4_acc,prg_4_spd,prg_4_tim,
            {0,0,0,0,0} } ;  // prt_5_en,prg_5_trg,prg_5_acc,prg_5_spd,prg_5_tim,
                                                  //
// グローバル変数定義 ---------------------------------------------------------
bool     local      = true   ;        // local フラグ
bool     localen    = false  ;        // 制御盤操作許可
byte     duty       = 0      ;        // PWM デューティ設定 (0~255)
int      loopcnt    = 0      ;        // 定期割り込み回数カウント用
bool     ticker_val = false  ;        // 定期割り込みフラグ 
bool     pre_strt   = false  ;        // 発車要求スイッチ状態保持用
bool     start_sw   = false  ;        // 発車要求スイッチ押下検出
bool     running    = false  ;        // 走行中フラグ
int      base       = 25     ;        // 準備時 (LED点灯時) の duty 値
int      target     = 145    ;        // 目標デューティ
int      rate       = 5      ;        // 増減速の割合 (1/10 S 当たりの 増加 duty値)
unsigned long start_time = 0 ;        // 開始時刻
unsigned long now_time = 0 ;          // 現在時刻
                                      //
bool  pwpk_confdl = false ;           // pwpk 設定ダウンロード
                                      //
//  ( 制御盤/WEB 操作情報用 ) --------
int      vol_value  = 0      ;        // 制御盤ボリューム値 (0~4095)
int      base_val   = 0      ;        // 最低速度時のDuty 値
bool     dir_bkwd   = false  ;        // 進行方向フラグ
bool     dir_change = false  ;        // 進行方向変化フラグ
bool     pre_rdy    = false  ;        // 準備要求スイッチ状態保持用
bool     ready      = false  ;        // 準備要求フラグ ( LED 常時点灯要求)
bool     rdy_change = false  ;        // 準備要求状態変化
                                      //
//  ( 自動運転用 ) -------------------
bool     prg_run    = false  ;        // 自動運行実行中
bool     prg_start  = false  ;        // 自動運行開始指示
bool     prg_next   = false  ;        // 自動運行設定更新指示
int      prg_no     = 0      ;        // 実行中の運行設定 No.
int      nextno     = 0      ;        // 次の運行設定 No.
int      prg_tim    = 0      ;        // 運行時間
int      prg_loopnum= 1      ;        // 自動運行繰り返し回数
bool     start      = false  ;        // 発車要求フラグ
bool     stop       = false  ;        // 停車要求フラグ
bool     autorun    = false  ;        // 自動走行中フラグ
bool     wait_sw    = false  ;        // STARTスイッチ待ち
bool     time_ovr   = false  ;        // 運行時間オーバー
                                      //
//  ( LED 制御用 ) -------------------
bool     st_blink   = false  ;        // ステータス 点滅
int      st_bl_intvl= 0      ;        // ステータス 点滅間隔
int      st_bl_cnt  = 0      ;        // ステータス 点滅時 カウント値
bool     st_bl_off  = false  ;        // ステータス 点滅時 OFF 状態
bool     st_on      = false  ;        // ステータス 点灯
int      st_val     = 0      ;        // ステータス値
                                      //
Ticker ticker;                        // 定期割込み用
                                      //
// ------ Define for IO Port --------------------------------------------------
#define PWR_VOL   A7                  // ADC1 A7 (IO35)
#define PWR_OUT    2                  // Degital Output IO2
#define DIR_OUT    4                  // Degital Output IO4 (0:FWD,1:BWD)
#define SW_FWD    27                  // Degital Input  IO27
#define SW_BWD    14                  // Degital Input  IO14    
#define SW_READY  13                  // Degital Input  IO13
#define SW_START  16                  // Degital Input  IO16
#define LED_FWD   17                  // Degital Output IO17
#define LED_BWD   12                  // Degital Output IO12
#define LED_ST1   23                  // Degital Output IO23
#define LED_ST2   19                  // Degital Output IO19
#define LED_ST3   18                  // Degital Output IO18
#define MNT_MODE  32                  // Degital Output IO32
#define PRG_MODE  33                  // Degital Output IO33
                                      //
// ----- Define for LEDC ------------------------------------------------------
#define PWM_FEQ   20000               //
#define DUTY_bw   8                   //
                                      //
// -----------------------------------------------------------------------------
//  arduino 初期化処理                                                        --
// -----------------------------------------------------------------------------
void setup() {                                    //
  int res = 0 ;                                   // 結果格納用 (ワーク)
                                                  //
  pinMode(PWR_OUT,OUTPUT) ;                       // Set pin mode for SPEED control
  pinMode(DIR_OUT,OUTPUT) ;                       // Set pin mode for Dirction control
  pinMode(LED_FWD,OUTPUT) ;                       // Set pin mode for Forward LED
  pinMode(LED_BWD,OUTPUT) ;                       // Set pin mode for Backward LED
  pinMode(LED_ST1,OUTPUT) ;                       // Set pin mode for status 1 LED
  pinMode(LED_ST2,OUTPUT) ;                       // Set pin mode for status 2 LED
  pinMode(LED_ST3,OUTPUT) ;                       // Set pin mode for status 3 LED
  pinMode(MNT_MODE,INPUT) ;                       // Set pin mode
  pinMode(PRG_MODE,INPUT) ;                       // Set pin mode 
                                                  //
  // セットアップ中、LED を点灯させる------------------------------------------
  digitalWrite(LED_FWD,1) ;                       //
  digitalWrite(LED_BWD,1) ;                       //
  digitalWrite(LED_ST1,1) ;                       //
  digitalWrite(LED_ST2,1) ;                       //
  digitalWrite(LED_ST3,1) ;                       //
                                                  //
  Serial.begin(115200);                           // シリアル 開始
  SPIFFS.begin(true) ;                            // SPIFFS 開始
                                                  //
  // WiFi設定用 HTML ファイルの読み込み ---------------------------------------
  res = rd_SPIFFS(fname_wfCONF,html_wfCONF) ;     // html_wfCONF に html を格納
                                                  //
  // WiFi XHR 応答ファイルの読み込み ------------------------------------------
  res = rd_SPIFFS(fname_wfRESP,html_wfRESP) ;     // html_wfRESP に html を格納
                                                  //
  // PWPK HTML ファイルの読み込み ---------------------------------------------
  res = rd_SPIFFS(fname_pwpk,html_pwpk) ;         // html_pwpk に html を格納
                                                  //
  // PWPK用設定 HTML ファイルの読み込み--- ------------------------------------
  res = rd_SPIFFS(fname_mCONF,html_mCONF) ;       // html_mCONF に html を格納
                                                  //
  // PWPK XHR 応答ファイルの読み込み ------------------------------------------
  res = rd_SPIFFS(fname_mRESP,html_mRESP) ;       // html_mRESP に html を格納
                                                  //
  // wifi 初期設定ファイルを読み込み、変数に格納し、wifi,画面モードを決定 -----
  res = rd_wificonf() ;                           //
                                                  //
  // wifiモード(stamode) に応じて AP か STA かを選択してサーバー起動 ----------
  if (stamode == false) {                         // APモード時 softAP WiFi 開始
    start_AP_server() ;                           // APモードでサーバー起動
    sta_exec = false ;                            //
  } else {                                        // STAモードの場合 Wifi 開始
    start_STA_server() ;                          // APモードでサーバー起動
    sta_exec = true ;                             //
  }                                               //
                                                  //
  // 実行中のモード (設定かメインか) を設定 ------------------------------------
  html_exec = html_mode ;                         // 実行中の html_mode を 設定
                                                  //
  Serial.println("Server start!");                //
                                                  //
  // for PWM Control ===========================================================
                                                  //
  // メイン設定ファイルを読み込み、変数に格納する ------------------------------
  res = rd_mainconf() ;                           //
                                                  //
  // ledc 設定 -----------------------------------------------------------------
  ledcSetup(0, PWM_FEQ, DUTY_bw);    // setup channel 0 with frequency 5000 Hz, 
                                     // 8 bit precission for LEDC timer
  ledcAttachPin(PWR_OUT,0);          // attach pin 2 to channel 0
  ledcWrite(0, 0);                   // initialize channel 0 to off     
                                                  //
  // LED を初期状態に設定。 ----------------------------------------------------
  digitalWrite(LED_FWD,1) ;                       //
  digitalWrite(LED_BWD,0) ;                       //
  digitalWrite(LED_ST1,0) ;                       //
  digitalWrite(LED_ST2,0) ;                       //
  digitalWrite(LED_ST3,0) ;                       //
                                                  //
  // 周期割り込みを開始 --------------------------------------------------------
  ticker.attach_ms(100, execTicker) ;       // 割り込み間隔と割り込み処理を設定。
                                                  //
}                                                 //
// -----------------------------------------------------------------------------
                                                  //
// -----------------------------------------------------------------------------
//  arduino メインループ処理                                                  --
// -----------------------------------------------------------------------------
void loop() {                                     //
  WiFiClient client = server.available();         //
  String htmlwk = "" ;                            // html 用 ワーク変数
  String line   = "" ;                            // クライアントからの入力用
  int xhr = 0 ;                                   //
  bool post_req = false ;                         // POST 要求フラグ
  bool wifi_conful = false ;                      //   WiFi 設定uploadフラグ
  bool pwpk_conful = false ;                      //   PWPK 設定uploadフラグ
  char buf[257] ;                                 // 受信バッファ
  int  n=0 ;                                      // ワーク用変数
  unsigned long tim = 0 ;                         // 開始時刻
                                                  //
  if (ticker_val) {      // 定期割込みフラグが true だったら、定期処理を実行。
     do_JOB() ;                                   //
  }                                               //
                                                  //
  // HTML クライアント処理 ------------------------------------------------------
  if (client) {                                   //
    # ifdef DEBUG_HTML                            //
      tim = millis() ;                            //
      Serial.print(" -- new client! ( ");         //
      Serial.print(tim) ;                         //
      Serial.print(") | ") ;                      //
    # endif                                       //
    while (client.connected()) {                  // client から接続されたとき
      if (client.available()) {                   //
        line = client.readStringUntil('\n');      // 1行分取得
 # ifdef DEBUG_HTML2                       //
          Serial.println("") ;                    //
          Serial.println(line) ;                  //
 # endif                                   //
        // GET 処理を受信した場合 ---------------------------------------------
 if (line.indexOf("GET /?") != -1) {       // GET 処理
   if (html_mode == WIFI_CONF){            //  WiFi 初期設定時
     xhr = set_form2wifiparam(line) ;      //   フォームデータを変数に格納
   }else if (html_mode == MAIN_CONF) {     //  PWPK 初期設定時
     xhr = set_mconf2param(line) ;         //   フォームデータを変数に格納
          } else {                                //  PWPK 操作時
     xhr = proc_main(line) ;               //   フォームデータを変数に格納
   }                                       //
 }                                         //
        if (line.indexOf("POST /") != -1) {       // POST 要求の場合
          post_req = true ;                       //   POST要求フラグをセット
          if (line.indexOf("?conful") != -1) {    //     wifi upload の場合  
            wifi_conful = true ;                  //       wifi upload フラグセット
          }                                       //
          if (line.indexOf("?mcnful") != -1) {    //     upload の場合  
            pwpk_conful = true ;                  //       pwpk upload フラグセット
          }                                       //
        }                                         //
        // 最終行(空行)を受信した時 -------------------------------------------
        if (line.length() == 1 && line[0] == '\r'){ // 最終行判定
          // POST 要求があった場合、データを取得/処理する ---------------------
          if (post_req) {                         //  POST 要求の場合
            // 残りデータ受信 --------------------//
            line = ""  ;                          //    データ格納用変数初期化
            while (n=client.available()) {        //    残りデータを全て受信
              if (n<256){                         //    
                client.readBytes(buf,n) ;         //
                buf[n] = 0 ;                      //
              } else {                            //
                client.readBytes(buf,256) ;       //
                buf[256] = 0 ;                    //
              }                                   //
              line += buf ;                       //
            }                                     //
            // POST データ処理 -------------------//
            if (wifi_conful == true) {            //   wifi 設定upload の場合
              #ifdef DEBUG_HTML2                  //
                Serial.println(" UP LOAD :") ;    //    内容をシリアルに表示
                Serial.print(line) ;              //    ( 他の処理は無し)
                Serial.println("") ;              //
              #endif                              //
              wifi_conful = false ;               //   アップロード処理終了
            } else if (pwpk_conful == true) {     //   pwpk 設定upload の場合
              #ifdef DEBUG_HTML2                  //
                Serial.println(" UP LOAD :") ;    //    内容をシリアルに表示
                Serial.print(line) ;              //    ( 他の処理は無し )
                Serial.println("") ;              //
              #endif                              //
              pwpk_conful = false ;               //   アップロード処理終了
            } else if (html_mode == WIFI_CONF){   // WiFi 初期設定時
       xhr = set_form2wifiparam(line) ;    //   フォームデータを変数に格納
     }else if (html_mode == MAIN_CONF) {   // PWPK 初期設定時
       xhr = set_mconf2param(line) ;       //   フォームデータを変数に格納
            } else {                              // PWPK 操作時
       xhr = proc_main(line) ;             //   フォームデータのメイン処理
     }                                     //
          }                                       //
          // 最終処理 ---------------------------------------------------------
   if (stamode != sta_exec) {              // sta_mode が変わった場合
     if (strcmp(wifi_mode,"ap") == 0){     //  リブートメッセージを設定
       foot_msg = "アクセスポイントモードに移行します。<br>" ;
       foot_msg += "この画面を閉じてアクセスポイントに接続して下さい。" ;
     }else{                                //
       foot_msg = "ステーションモードに移行します。<br>"; 
       foot_msg += "この画面を閉じて 設定したアドレスに接続して下さい。" ;
     }
            if (html_exec == WIFI_CONF) {         //   WiFi 設定中なら
              send_wfCONF_html(client) ;          //     WiFi 設定 HTML 送信
     } else {                              //   WiFi 設定中でなければ
              send_PWPK_html(client) ;            //     PWPK処理 HTML 送信
       html_exec = MAIN ;                  //
     }                                     //    ( その後 リブート)
            rebootreq = true ;                    //
          } else {                                // sta_mode が変わらない場合 
            if (html_mode == WIFI_CONF) {         //  WiFi 設定中の時
              if (xhr) {                          //   XHR 応答の場合
                send_wfRESP_html(client) ;        //     WiFi XHR応答
              } else {                            //   XHR 応答でなければ
                send_wfCONF_html(client) ;        //     WiFi 設定 HTML 送信
              }                                   //
     } else if (html_mode == MAIN_CONF) {  //  PWPK 設定中なら
              if (xhr) {                          //
                send_pwpkCONF_RESP(client) ;      //     PWPK 設定 応答 送信
              } else {                            //
                send_pwpkCONF_html(client) ;      //     PWPK 設定 HTML 送信
              }                                   //
            } else {                              //  PWPK 処理中なら
              if (xhr) {                          //   XHR 応答の場合
                send_pwpkRESP_html(client) ;      //    PWPK XHR 応答HTML 送信
              } else {                            //   XHR 応答でなければ
                send_PWPK_html(client) ;          //    PWPK処理 HTML 送信
              }                                   //
            }                                     //
            html_exec = html_mode ;               //
          }                                       //
          break ;                                 // ループ終了 
        }                                         //
 //-------------------------------------------------------------------
      }                                           //
    }                                             //
    // 接続が切れた場合 ------------------------------------------------------
    delay(5);                                     // 
    client.stop();                                //
    # ifdef DEBUG_HTML                            //
      tim = millis() ;                            //
      Serial.print("client disonnected (");       //
      Serial.print(tim) ;                         //
      Serial.println(") ----------------------") ;//
    # endif                                       //
    delay(50);                                    // 
  }                                               //
                                                  //
  if (rebootreq) {                                // リブート要求時
    Serial.println("------------------- リブートします------------------- ");
    delay(500);                                   // 
    ESP.restart() ;                               // リブート
  } else {                                        //
  }                                               //
                                                  //
  // --------------------------------------------------------------------------
  if (wifi_restartreq) {                          // WiFI リスタート要求時
    Serial.println("------------------- WiFi リスタートします------------- ");
    delay(500);                                   // 
    if (stamode == false) {                       // APモード時 softAP WiFi 開始
      start_AP_server() ;                         // APモードでサーバー起動
      sta_exec = false ;                          //
    } else {                                      // STAモードの場合 Wifi 開始
      start_STA_server() ;                        // APモードでサーバー起動
      sta_exec = true ;                           //
    }                                             //
  } else {                                        //
  }                                               //
  wifi_restartreq = false ;                       //
}                                                 //
                                                  //

// -----------------------------------------------------------------------------
//  WiFi 設定関連 定義                                                       --
// -----------------------------------------------------------------------------
// *****************************************************************************
// *  初期設定ファイル をリードし、グローバル変数に値をセットする              *
// *****************************************************************************
int rd_wificonf() {                               //
  String conf ;                                   //
  File fp       ;                                 // ファイルポインタ
  int result = 0 ;                                // 戻り値
                                                  //
  if (SPIFFS.exists("/config.txt")) {             // 設定ファイル存在確認
    // ファイルがあった場合 ----------------------//
    result = rd_SPIFFS("/config.txt",conf) ;      // confにconfig.txtを格納
  } else {                                        //
    // ファイルが無かった場合 --------------------//
    Serial.println("WiFi設定ファイルなし") ;      // 無かったら、APモード
    result = -1 ;                                 //
  }                                               //
                                                  //
  // ファイルが読み込めたら、グローバル変数にセットする
  if (result == 0)                                //
    # ifdef DEBUG                                 // デバッグ用表示
      Serial.println("--- config.txt --- ") ;     //
      Serial.println(conf ) ;                     //
    # endif                                       //
    set_conf2wifiparam(conf) ;                    // conf の内容を変数に設定
  // 初期設定ファイルの状態で wifiモード, 画面モードを設定 ----------------------
  if (result != -1) {                             // 変数に設定できた場合
    if (( strcmp(wifi_ssid,"") ==0) || (strcmp(wifi_pass,"") ==0)) {
                                                  // ssid,pass 設定なければ
      stamode = false ;                           //   APモード
      if ( strcmp(wifi_mode,"ap") == 0){          // wifi_mode が"ap"なら
        html_mode = MAIN ;                        //   メイン画面
      }else{                                      // wifi_mode が"ap"でなければ
        html_mode = WIFI_CONF ;                   //   初期設定画面
      }                                           //
    } else {                                      // ssid,pass の設定があれば、
      html_mode = MAIN ;                          //   メイン画面
      if ( strcmp(wifi_mode,"ap") == 0)           // wifi_mode が "ap" なら
        stamode = false ;                         //   APモード
      else                                        // "ap" でなければ
        stamode = true  ;                         //   STAモード
    }                                             //
  } else {                                        // 変数に設定できなかった場合
    stamode = false ;                             //   APモード
    html_mode = WIFI_CONF ;                       //   初期設定画面
  }                                               //
                                                  //
  return result ;                                 //
}                                                 //
                                                  //
// ----------------------------------------------------------------------------
// - 設定ファイルの内容を グローバル変数に 設定する                           -
// ----------------------------------------------------------------------------
void set_conf2wifiparam(String &conf) {           //
  int    pos = 0 ;                                //
  int    npos = 0 ;                               //
  String s_work ;                                 //
  String s_name ;                                 //
  String s_data ;                                 //
  // 記載内容を順次処理 ( 設定情報を取り出し、グローバル変数に設定する ) ------
  while(1) {                                      //
    if (conf.charAt(pos) == '/') {                // 先頭が '/' なら 次の行へ 
      npos = conf.indexOf('\n',pos) ;             //
      pos = npos + 1 ;                            //
    } else {                                      //
      npos = conf.indexOf(':',pos) ;              // ':'までの文字をs_nameに取得
      if (npos == -1) break ;                     //   見つからなければ終了
      s_name = conf.substring(pos,npos) ;         //
      s_name.trim() ;                             //
      pos = npos+1 ;                              // ポインタを':'の次へ
      npos = conf.indexOf('\n',pos) ;             // '\n'までの文字をs_dataに取得
      if (npos == -1) npos = conf.length() ;      //
      s_data = conf.substring(pos,npos) ;         //
      s_data.trim() ;                             //
      pos = npos+1 ;                              //
      set_wifiparam(s_name,s_data) ;              // 取得内容を変数に設定
    }                                             //
  //disp_param() ;                                //
  }                                               //
}                                                 //
                                                  //
// - 設定内容を判定し、グローバル変数に設定 ------------------------------------
void set_wifiparam(String &name, String &data) {  //
  if (name.compareTo("wifi_ssid")==0) {           // 'wifi_ssid' の場合
    data.toCharArray(wifi_ssid,128) ;             //
  }                                               //
  if (name.compareTo("wifi_pass")==0) {           // 'wifi_pass' の場合
    data.toCharArray(wifi_pass,128) ;             //
  }                                               //
  if (name.compareTo("wifi_ip")==0) {             // 'wifi_ip' の場合
    wifi_ip = stoip(data) ;                       //  取得文字列をIPAddressに変換
  }                                               //
  if (name.compareTo("wifi_gw")==0) {             // 'wifi_gw' の場合
    wifi_gw = stoip(data) ;                       //
  }                                               //
  if (name.compareTo("wifi_sm")==0) {             // 'wifi_sm' の場合
    wifi_sm = stoip(data) ;                       //
  }                                               //
  if (name.compareTo("wifi_dns")==0) {            // 'wifi_dns' の場合
    wifi_dns = stoip(data) ;                      //
  }                                               //
  if (name.compareTo("wifi_mode")==0) {           // 'wifi_mode' の場合
    data.toCharArray(wifi_mode,16) ;              //
  }                                               //
  if (name.compareTo("set_reboot")==0) {          // 'set_reboot' の場合
    set_reboot = data.toInt() ;                   //
  }                                               //
//disp_param() ;                                  //
}                                                 //
                                                  //
// - 文字列 から IPAddress へ変換 ----------------------------------------------
IPAddress stoip(String &data) {                   //
  IPAddress ip ;                                  //
  String num = "" ;                               //
  int pos = 0 ;                                   //
  while(data.charAt(pos) != '.') {                // 先頭から'.'までの文字を取得
    num += data.charAt(pos++) ;                   //
  }                                               //
  ip[0] = num.toInt() ;                           // 数値に変換して代入
  pos++ ;                                         // '.' の次から
  num="";                                         //
  while(data.charAt(pos) != '.') {                // '.' までの文字を取得
    num += data.charAt(pos++) ;                   //
  }                                               //
  ip[1] = num.toInt() ;                           // 数値に変換して代入
  pos++ ;                                         // '.' の次から
  num="";                                         //
  while(data.charAt(pos) != '.') {                // '.' までの文字を取得
    num += data.charAt(pos++) ;                   //
  }                                               //
  ip[2] = num.toInt() ;                           //
  pos++ ;                                         // '.' の次から
  num="";                                         // 
  while(data.charAt(pos) != '.') {                // '.'又は最後までの文字を取得
    num += data.charAt(pos++) ;                   //
    if (pos > data.length()) break ;              //
  }                                               //
  ip[3] = num.toInt() ;                           // 数値に変換して代入
                                                  //
  return ip ;                                     // IPAddress を戻り値とする
}                                                 //
                                                  //
// ****************************************************************************
// * アクセスポイント モードで サーバーを起動                                 * 
// ****************************************************************************
void start_AP_server() {               //
  Serial.println(" AP Server exec") ;  //
  WiFi.softAP(ssid, pass);             // SSIDとパスの設定
  delay(100);                          // delayが必要
  WiFi.softAPConfig(ip, ip, subnet);   // IP address, gateway, subnetmask の設定
  IPAddress myIP = WiFi.softAPIP();    // WiFi.softAPIP()でWiFi起動
  server.begin();                      // サーバーを起動(htmlを表示させるため)
}                                      //
                                       //
// ****************************************************************************
// * デバッグ表示用                                                           *
// ****************************************************************************
void disp_mode() {                             //
  # ifdef DEBUG_HTML                           // デバッグ用表示
    Serial.println("") ;                       //
    Serial.print("config_mode - exec : ") ;    // 設定画面か メイン画面か
    if (html_mode == WIFI_CONF)                // 変数の状態と実行状況を表示
      Serial.print("WiFi Config MODE - ") ;    //
    else                                       //
      Serial.print("Main MODE - ") ;           //
    if (html_exec == WIFI_CONF)                //
      Serial.println("WiFi Config MODE") ;     //
    else                                       //
      Serial.println("Main MODE") ;            //
                                               //
    Serial.print("wifi_mode - exec   : ") ;    // Wifi モードを
    if (stamode)                               // 変数の状態と実行状況を表示
      Serial.print("STA - ") ;                 //
    else                                       //
      Serial.print("AP - ") ;                  //
    if (sta_exec)                              //
      Serial.println("STA") ;                  //
    else                                       //
      Serial.println("AP") ;                   //
  # endif                                      //
}                                              //
                                               //
void disp_param() {                            //
  # ifdef DEBUG_HTML                           // デバッグ用表示
    Serial.println("") ;                       //
    Serial.print("wifi_ssid : ") ;             // 設定画面か メイン画面か
    Serial.println(wifi_ssid) ;                // SSID        for WiFi STA
    Serial.print("wifi_pass : ") ;             // 設定画面か メイン画面か
    Serial.println(wifi_pass) ;                // SSID        for WiFi STA
    Serial.print("wifi_ip   : ") ;             // 設定画面か メイン画面か
    Serial.println(wifi_ip) ;                  // SSID        for WiFi STA
    Serial.print("wifi_gw   : ") ;             // 設定画面か メイン画面か
    Serial.println(wifi_gw) ;                  // SSID        for WiFi STA
    Serial.print("wifi_sm   : ") ;             // 設定画面か メイン画面か
    Serial.println(wifi_sm) ;                  // SSID        for WiFi STA
    Serial.print("wifi_dns  : ") ;             // 設定画面か メイン画面か
    Serial.println(wifi_dns) ;                 // SSID        for WiFi STA
  # endif                                      //
}                                              //
                                               //
// ****************************************************************************
// * クライアントからのフォームデータを変数にセットし、設定ファイルに書き出す *
// ****************************************************************************
int  set_form2wifiparam(String &line) {        //
  String s_work ="" ;                          //
  int xhr = 0 ;                                //
                                               //
  if (line.indexOf("set=") != -1) {            // stamode set
    // クライアントからのデータを グローバル変数にセットする。 ------------------
    if (line.indexOf("ssid=") != -1) {         // wifi_ssid
      s_work = getvalue_s(line,"ssid=") ;      //  ssid に続く文字列を取得
      s_work.toCharArray(wifi_ssid,128) ;      //  変数にセット
    }                                          //
    if (line.indexOf("pass=") != -1) {         // wifi_pass
      s_work = getvalue_s(line,"pass=") ;      //  pass に続く文字列を取得
      s_work.toCharArray(wifi_pass,128) ;      //  変数にセット
    }                                          //
    if (line.indexOf("ip1=") != -1) {          // wifi_ip
      wifi_ip[0] = getvalue_i(line,"ip1=") ;   //  ip1 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("ip2=") != -1) {          // 
      wifi_ip[1] = getvalue_i(line,"ip2=") ;   //  ip2 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("ip3=") != -1) {          // 
      wifi_ip[2] = getvalue_i(line,"ip3=") ;   //  ip3 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("ip4=") != -1) {          // 
      wifi_ip[3] = getvalue_i(line,"ip4=") ;   //  ip4 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("gw1=") != -1) {          // wifi_gw
      wifi_gw[0] = getvalue_i(line,"gw1=") ;   //  gw1 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("gw2=") != -1) {          //                       
      wifi_gw[1] = getvalue_i(line,"gw2=") ;   //  gw2 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("gw3=") != -1) {          //                       
      wifi_gw[2] = getvalue_i(line,"gw3=") ;   //  gw3 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("gw4=") != -1) {          //                       
      wifi_gw[3] = getvalue_i(line,"gw4=") ;   //  gw4 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("sm1=") != -1) {          // wifi_sm
      wifi_sm[0] = getvalue_i(line,"sm1=") ;   //  sm1 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("sm2=") != -1) {          //                       
      wifi_sm[1] = getvalue_i(line,"sm2=") ;   //  sm2 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("sm3=") != -1) {          //                       
      wifi_sm[2] = getvalue_i(line,"sm3=") ;   //  sm3 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("sm4=") != -1) {          //                       
      wifi_sm[3] = getvalue_i(line,"sm4=") ;   //  sm4 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("dns1=") != -1) {         // wifi_dns
      wifi_dns[0] = getvalue_i(line,"dns1=") ; //  dns1 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("dns2=") != -1) {         //                       
      wifi_dns[1] = getvalue_i(line,"dns2=") ; //  dns2 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("dns3=") != -1) {         //                       
      wifi_dns[2] = getvalue_i(line,"dns3=") ; //  dns3 に続く数値を変数にセット
    }                                          //                       
    if (line.indexOf("dns4=") != -1) {         //                       
      wifi_dns[3] = getvalue_i(line,"dns4=") ; //  dns4 に続く数値を変数にセット
    }                                          //
    if (line.indexOf("stamode=") != -1) {      // stamode set
      s_work = getvalue_s(line,"stamode=") ;   //  ssid に続く文字列を取得
      s_work.toCharArray(wifi_mode,16) ;       //  変数にセット
      if (s_work == "sta") {                   //  設定値により、モードをセット
        stamode = true ;                       // 
        html_mode = MAIN ;                     // 
      } else {                                 // 
        stamode = false ;                      // 
        html_mode = MAIN  ;                    // 
      }                                        //
    }                                          //
    if (line.indexOf("rebt=") != -1) {         //                       
      s_work = getvalue_s(line,"rebt=") ;      //  ssid に続く文字列を取得
      if (s_work == "on") {                    //   rebt=on なら
        set_reboot = 1    ;                    //
        wifi_restartreq = true ;               //    WiFiリスタート要求
      } else {                                 //
        set_reboot = 0    ;                    //
      }                                        //
    }                                          //
    // グローバル変数を 設定ファイルに書き出す -----------------------------------
    wr_wificonfig() ;                          // 設定を初期設定ファイルに書き出し
  }                                            //
  // 戻る 場合 -------------------------------------------------------------------
  if (line.indexOf("rtn=") != -1) {            // return
    html_mode = MAIN ;                         //
  }                                            //
  // ダウンロード の場合 ---------------------------------------------------------
  if (line.indexOf("confld=") != -1) {         // config.txt のダウンロード時
    wifi_confdl = true ;                       //
    xhr = 1 ;                                  //
  }                                            //
                                               //
  return xhr ;                                 //
}                                              //
                                               //
// 要素名に続く値(文字列)を取得する --------------------------------------------
String getvalue_s(String &line,String param) { //
  String val="" ;                              //
  int pos = 0 ;                                //
                                               //
  if ((pos=line.indexOf(param)) != -1) {       // 要素名の位置を取得
    pos += param.length() ;                    // 
    while(   (line.charAt(pos) != '&')         //
           & (line.charAt(pos) != ' ')         //
           & (line.charAt(pos) != '\n')        //
           & (line.charAt(pos) != '\0')) {     //
      val += line.charAt(pos++) ;              //  '&' か 行末 まで文字を取得
    }                                          //
  }                                            //
  return val ;                                 //  取得した文字列を返す
}                                              //
                                               //
// 要素名に続く値(数値)を取得する ---------------------------------------------
int getvalue_i(String &line,String param) {    //
  String val="" ;                              //
  int pos = 0 ;                                //
                                               //
  if ((pos=line.indexOf(param)) != -1) {       // 要素名の位置を取得
    pos += param.length() ;                    //
    while(   (line.charAt(pos) >= '0')         //
           & (line.charAt(pos) <= '9')) {      //
      val += line.charAt(pos++) ;              //  数値でなくなるまで文字を
    }                                          //  取得
  }                                            //
  return val.toInt() ;                         //  数値に変換して返す
}                                              //
                                               //
// -----------------------------------------------------------------------------
// - グローバル変数の内容をString に出力                                       -
// -----------------------------------------------------------------------------
void getstr_wfconf(String &conf) {             //
  char s_work[128] ;                           //
  File fp ;                                    //
                                               //
  // String に 変数の内容を書き込む -------------------------------------------
  sprintf(s_work,"wifi_ssid : %s\n"            //
                ,wifi_ssid) ;                  //
  conf = String(s_work) ;                      //
  sprintf(s_work,"wifi_pass : %s\n"            //
                ,wifi_pass) ;                  //
  conf += String(s_work) ;                     //
  sprintf(s_work,"wifi_ip   : %3d.%3d.%3d.%3d\n" //
                ,wifi_ip[0],wifi_ip[1],wifi_ip[2],wifi_ip[3]) ; //
  conf += String(s_work) ;                     //
  sprintf(s_work,"wifi_sm   : %3d.%3d.%3d.%3d\n" //
                ,wifi_sm[0],wifi_sm[1],wifi_sm[2],wifi_sm[3]) ; //
  conf += String(s_work) ;                     //
  sprintf(s_work,"wifi_gw   : %3d.%3d.%3d.%3d\n" //
                ,wifi_gw[0],wifi_gw[1],wifi_gw[2],wifi_gw[3]) ; //
  conf += String(s_work) ;                     //
  sprintf(s_work,"wifi_dns  : %3d.%3d.%3d.%3d\n" //
                ,wifi_dns[0],wifi_dns[1],wifi_dns[2],wifi_dns[3]) ; //
  conf += String(s_work) ;                     //
  sprintf(s_work,"wifi_mode : %s\n"            //
                ,wifi_mode) ;                  //
  conf += String(s_work) ;                     //
  sprintf(s_work,"set_reboot: %d\n"            //
                ,set_reboot) ;                 //
                                               //
}                                              //
                                               //
// -----------------------------------------------------------------------------
// - グローバル変数の内容を設定ファイルに書き出す                              -
// -----------------------------------------------------------------------------
void wr_wificonfig() {                         //
  char s_work[128] ;                           //
  String conf ;                                //
  File fp ;                                    //
                                               //
  // String に 変数の内容を書き込む -------------------------------------------
  getstr_wfconf(conf) ;                        //
                                               //
  // 設定ファイルに書き込む ----------------------------------------------------
  fp = SPIFFS.open("/config.txt",FILE_WRITE) ; // 設定ファイルを開く
  if (!fp) {                                   //
    Serial.println(" 設定ファイル オープンエラー !!") ; //
  } else {                                     //
    // 初期設定ファイル書き込み -------------------------------------------------
    if(fp.print(conf)) {                       // ファイルに書き込み
      Serial.println("\"config.txt\" 書き込み終了") ; //   終了メッセージ
    } else {                                   //
      Serial.println("\"config.txt\" 書き込み失敗 !!") ; //   失敗メッセージ
    }                                          //
    fp.close() ;                               // ファイルクローズ
  }                                            //
                                               //
}                                              //
                                               //
// *****************************************************************************
// * HTML 送信処理                                                             *
// *****************************************************************************
                                               //
// ----------------------------------------------------------------------------
// - CONF_HTML 送信  -----------------------------------------------------------
// ----------------------------------------------------------------------------
void  send_wfCONF_html(WiFiClient client) {    //
  String htmlwk ;                              //
                                               //
  // 変数置換え処理 ------------------------------------------------------------
  htmlwk = html_wfCONF ;                       //
  htmlwk.replace("$ssid",String(wifi_ssid)) ;  //
  htmlwk.replace("$pass",String(wifi_pass)) ;  //
  htmlwk.replace("$ip1",String(wifi_ip[0])) ;  //
  htmlwk.replace("$ip2",String(wifi_ip[1])) ;  //
  htmlwk.replace("$ip3",String(wifi_ip[2])) ;  //
  htmlwk.replace("$ip4",String(wifi_ip[3])) ;  //
  htmlwk.replace("$sm1",String(wifi_sm[0])) ;  //
  htmlwk.replace("$sm2",String(wifi_sm[1])) ;  //
  htmlwk.replace("$sm3",String(wifi_sm[2])) ;  //
  htmlwk.replace("$sm4",String(wifi_sm[3])) ;  //
  htmlwk.replace("$gw1",String(wifi_gw[0])) ;  //
  htmlwk.replace("$gw2",String(wifi_gw[1])) ;  //
  htmlwk.replace("$gw3",String(wifi_gw[2])) ;  //
  htmlwk.replace("$gw4",String(wifi_gw[3])) ;  //
  htmlwk.replace("$dns1",String(wifi_dns[0])) ;//
  htmlwk.replace("$dns2",String(wifi_dns[1])) ;//
  htmlwk.replace("$dns3",String(wifi_dns[2])) ;//
  htmlwk.replace("$dns4",String(wifi_dns[3])) ;//
  if (stamode) {                               // ステーションモード時
    htmlwk.replace("$checked_sta","checked") ; // 
    htmlwk.replace("$checked_ap","") ;         //
  } else {                                     // アクセスポイントモード時
    htmlwk.replace("$checked_sta","") ;        //
    htmlwk.replace("$checked_ap","checked") ;  //
  }                                            //
  if (set_reboot != 0) {                       // ステーションモード時
    htmlwk.replace("$checked_rebt","checked") ;// 
  } else {                                     // アクセスポイントモード時
    htmlwk.replace("$checked_rebt","") ;       // 
  }                                            //
  htmlwk.replace("$footer",foot_msg ) ;        //
  // --------------------------------------------------------------------------
                                               //
  send_html(client,htmlwk) ;                   // HTML 送信処理
  # ifdef DEBUG_HTML                           //
    Serial.print("Send WiFi Config HTML | ") ; //
  # endif                                      //
}                                              //
                                               //
// ----------------------------------------------------------------------------
// - XHR RESPONSE_HTML 送信                                                   -
// ----------------------------------------------------------------------------
void send_wfRESP_html(WiFiClient client) {     //
  String htmlwk ;                              // HTML 編集用 ワーク
  String conf ;                                //
                                               //
  htmlwk = html_wfRESP ;                       // htmlwk に HTML をコピー
                                               //
  // 変数を値に変換----------------------------//
  if (wifi_confdl) {                           //  ダウンロードの場合 
    getstr_wfconf(conf) ;                      // 設定を文字列に変換
    htmlwk.replace("$conf_text",conf) ;        // 変数に 設定文字列をセット
    wifi_confdl = false ;                      //
  } else  {                                    //  アップロードの場合
    htmlwk.replace("$conf_text","") ;          //   変数に文字列(ダミー)をセット
  }                                            //
  // HTML を送信 ------------------------------//
  send_html(client,htmlwk) ;                   // HTML 送信処理
  # ifdef DEBUG_HTML                           //
    Serial.print("Send WiFi XHR Resp    | ") ; //
  # endif                                      //
  # ifdef DEBUG_WiFi                           //
    Serial.println("") ;                       //
    Serial.println(" response :") ;            //
    Serial.print(htmlwk) ;                     //
    Serial.println("") ;                       //
  # endif                                      //
                                               //
}                                              //
                                               //

// *****************************************************************************
// * SPIFFS ファイルを String に読み込む                                       *
// *****************************************************************************
int rd_SPIFFS(String fname, String &sname) {      //
  File fp = SPIFFS.open(fname,"r") ;              // ファイルをオープンする
  if (!fp) {                                      //  オープン失敗の場合
    Serial.print(fname) ;                         //
    Serial.println(" : file open error") ;        //
    return -1 ;                                   //    戻り値 -1
  }else {                                         //  オープン出来たら
    sname = fp.readString() ;                     //    String変数に読み込む
    fp.close() ;                                  //
  }                                               //
  return 0 ;                                      //  オープン出来た時のみ
}                                                 //    戻り値 0
                                                  //
// *****************************************************************************
// *  メイン用初期設定ファイル をリードし、グローバル変数に値をセットする      *
// *****************************************************************************
int rd_mainconf() {                               //
  String s_work ;                                 //
  File fp       ;                                 // ファイルポインタ
  int result = 0 ;                                // 戻り値
                                                  //
  if (SPIFFS.exists("/mconf.txt")) {              // 設定ファイル存在確認
    // ファイルがあった場合 --------------------- //
    result = rd_SPIFFS("/mconf.txt",s_work) ;     // mconf.txt を読み込み
  } else {                                        //
    // ファイルが無かった場合 ------------------- //
    Serial.println("メイン設定ファイルなし") ;    // 無かったら、APモード
    result = -1 ;                                 //
  }                                               //
                                                  //
  // ファイルが読み込めたら、グローバル変数にセットする
  if (result == 0)                                //
    # ifdef DEBUG                                 // デバッグ用表示
      Serial.println("--- mconf.txt --- ") ;      //
      Serial.println(s_work ) ;                   //
    # endif                                       //
    set_conf2pwpkparam(s_work) ;                  // 設定内容を変数に設定
                                                  //
  return result ;                                 //
}                                                 //
                                                  //
// ----------------------------------------------------------------------------
// - 設定ファイルの内容を グローバル変数に 設定する                           -
// ----------------------------------------------------------------------------
void set_conf2pwpkparam(String &conf) {           //
  int    pos = 0 ;                                //
  int    npos = 0 ;                               //
  String s_work ;                                 //
  String s_name ;                                 //
  String s_data ;                                 //
  // 記載内容を順次処理 ( 設定情報を取り出し、グローバル変数に設定する ) ------
  while(1) {                                      //
    if (conf.charAt(pos) == '/') {                // 先頭が '/' なら 次の行へ 
      npos = conf.indexOf('\n',pos) ;             //
      pos = npos + 1 ;                            //
    } else {                                      //
      npos = conf.indexOf(':',pos) ;              // ':' までの文字を取得
      if (npos == -1) break ;                     //   見つからなければ終了
      s_name = conf.substring(pos,npos) ;         //
      s_name.trim() ;                             //
      pos = npos+1 ;                              // ポインタを ':' の次へ
      npos = conf.indexOf('\n',pos) ;             // '\n' までの文字を取得
      if (npos == -1) npos = conf.length() ;      //
      s_data = conf.substring(pos,npos) ;         //
      s_data.trim() ;                             //
      pos = npos+1 ;                              //
      set_param(s_name,s_data) ;                  // 取得内容を変数に設定
    }                                             //
  }                                               //
}                                                 //
                                                  //
// - 設定内容を判定し、グローバル変数に設定 ------------------------------------
void set_param(String &name, String &data) {   //
  if (name.compareTo("prg_loop")==0) {         // 'prg_loop' の場合
    prg_tbl[0][0] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_loopnum")==0) {      // 'prg_loopnum' の場合
    prg_tbl[0][1] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_1_acc")==0) {        // 'prg_1_acc' の場合
    prg_tbl[1][2] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_1_spd")==0) {        // 'prg_1_spd' の場合
    prg_tbl[1][3] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_1_tim")==0) {        // 'prg_1_tim' の場合
    prg_tbl[1][4] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_2_en")==0) {         // 'prg_2_en'  の場合
    prg_tbl[2][0] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_2_trg")==0) {        // 'prg_2_trg' の場合
    prg_tbl[2][1] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_2_acc")==0) {        // 'prg_2_acc' の場合
    prg_tbl[2][2] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_2_spd")==0) {        // 'prg_2_spd' の場合
    prg_tbl[2][3] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_2_tim")==0) {        // 'prg_2_tim' の場合
    prg_tbl[2][4] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_3_en")==0) {         // 'prg_3_en'  の場合
    prg_tbl[3][0] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_3_trg")==0) {        // 'prg_3_trg' の場合
    prg_tbl[3][1] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_3_acc")==0) {        // 'prg_3_acc' の場合
    prg_tbl[3][2] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_3_spd")==0) {        // 'prg_3_spd' の場合
    prg_tbl[3][3] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_3_tim")==0) {        // 'prg_3_tim' の場合
    prg_tbl[3][4] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_4_en")==0) {         // 'prg_4_en'  の場合
    prg_tbl[4][0] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_4_trg")==0) {        // 'prg_4_trg' の場合
    prg_tbl[4][1] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_4_acc")==0) {        // 'prg_4_acc' の場合
    prg_tbl[4][2] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_4_spd")==0) {        // 'prg_4_spd' の場合
    prg_tbl[4][3] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_4_tim")==0) {        // 'prg_4_tim' の場合
    prg_tbl[4][4] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_5_en")==0) {         // 'prg_5_en'  の場合
    prg_tbl[5][0] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_5_trg")==0) {        // 'prg_5_trg' の場合
    prg_tbl[5][1] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_5_acc")==0) {        // 'prg_5_acc' の場合
    prg_tbl[5][2] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_5_spd")==0) {        // 'prg_5_spd' の場合
    prg_tbl[5][3] = data.toInt() ;             //
  }                                            //
  if (name.compareTo("prg_5_tim")==0) {        // 'prg_5_tim' の場合
    prg_tbl[5][4] = data.toInt() ;             //
  }                                            //
}                                              //
// ****************************************************************************
// * ステーションモードで サーバーを起動                                      * 
// ****************************************************************************
void start_STA_server() {
  int lpcnt = 0 ;                                    // ループカウント
  int lpcnt2 = 0 ;                                   // ループカウント2

  WiFi.config(wifi_ip, wifi_gw, wifi_sm, wifi_dns);  // Set fixed IP address
  delay(10) ;                                        //
  WiFi.begin(wifi_ssid, wifi_pass);                  // wifi 開始
  lpcnt = 0 ;                                        // 
  lpcnt2 = 0 ;                                       //
  while (WiFi.status() != WL_CONNECTED) {            // 接続確認
      lpcnt += 1 ;                                   //
      if (lpcnt > 4) {                               //
        WiFi.begin(wifi_ssid, wifi_pass);            // 4回目(2秒) で再度開始指示
        lpcnt = 0 ;                                  //
        lpcnt2 += 1 ;                                //
      }                                              //
      if (lpcnt2 > 5) {                              // 2秒x5 接続できなければ、
        stamode = false ;                            // APモードにして
        ESP.restart() ;                              // 再起動
      }                                              //
      Serial.print(".");                             //
      delay(500);                                    //   0.5秒毎にチェック
  }                                                  //
  server.begin();                                    // サーバー開始
}

// *****************************************************************************
// * HTML 送信処理                                                             *
// *****************************************************************************

// -----------------------------------------------------------------------------
// - メイン_HTML 送信                                                          -
// -----------------------------------------------------------------------------
void send_PWPK_html(WiFiClient client) {
  String htmlwk ;                               // HTML 編集用 ワーク
                                                //
  htmlwk = html_pwpk ;                          // HTML をワークにコピー
                                                //
  // 変数を値に変換-----------------------------//
  htmlwk.replace("$pwr",String(duty)) ;         //
  htmlwk.replace("$pwmin",String(base_val)) ;   //
  # ifdef DEBUG_WiFi                            //
    Serial.print("radio_local = ");             //
    Serial.println(local);                      //
    Serial.print("pwr  = ");                    //
    Serial.println(duty);                       //
  #endif                                        //
  if (local) {                                  //
    htmlwk.replace("$checked_lo","checked") ;   //
    htmlwk.replace("$checked_rm","") ;          //
  } else {                                      //
    htmlwk.replace("$checked_lo","") ;          //
    htmlwk.replace("$checked_rm","checked") ;   //
  }                                             //
  if (dir_bkwd) {                               //
    htmlwk.replace("$checked_fw","") ;          //
    htmlwk.replace("$checked_bw","checked") ;   //
  }else {                                       //
    htmlwk.replace("$checked_fw","checked") ;   //
    htmlwk.replace("$checked_bw","") ;          //
  }                                             //
                                                //
  // HTML 送信----------------------------------//
  send_html(client,htmlwk) ;                    // HTML 送信
  # ifdef DEBUG_HTML                           //
    Serial.print("Send Main HTML        | ") ; //
  # endif                                      //
}                                               //
                                                //
// ----------------------------------------------------------------------------
// - XHR RESPONSE_HTML 送信                                                   -
// ----------------------------------------------------------------------------
void send_pwpkRESP_html(WiFiClient client) {      //
  String htmlwk ;                                 // HTML 編集用 ワーク
                                                  //
  htmlwk = html_mRESP ;                           // htmlwk に HTML をコピー
                                                  //
  // 変数を値に変換-------------------------------//
  htmlwk.replace("$pwr",String(duty)) ;           //
  htmlwk.replace("$dir",String(dir_bkwd)) ;       //
  htmlwk.replace("$local",String(local)) ;        //
                                                  //
  send_html(client,htmlwk) ;                      // HTML 送信処理
  # ifdef DEBUG_HTML                              //
    Serial.print("Send Main XHR Resp    | ") ;    //
  # endif                                         //
  # ifdef DEBUG_WiFi                              //
    Serial.println("") ;                          //
    Serial.print(" dir :") ;                      //
    Serial.print(dir_bkwd) ;                      //
    Serial.println("|") ;                         //
    Serial.println(" response :") ;               //
    Serial.print(htmlwk) ;                        //
    Serial.println("") ;                          //
  #endif                                          //
  # ifdef DEBUG                                   //
    if (local) {                                  //
      Serial.print("local") ;                     //
    } else {                                      //
      Serial.print("remot") ;                     //
    }                                             //
  #endif                                          //
}                                                 //
                                                  //
// -----------------------------------------------------------------------------
// - メイン設定_HTML 送信                                                      -
// -----------------------------------------------------------------------------
void send_pwpkCONF_html(WiFiClient client) {    //
  String htmlwk ;                               // HTML 編集用ワーク
                                                //
  htmlwk = html_mCONF ;                         // html を ワークにコピー
                                                //
  // 変数を値に変換-----------------------------//
  if (prg_tbl[0][0]) {                          // loop チェックマーク
    htmlwk.replace("$checked_loop","checked") ; //
  } else {                                      //
    htmlwk.replace("$checked_loop","") ;        //
  }                                             //
  htmlwk.replace("$loopnum",String(prg_tbl[0][1])) ; // loop 間隔
  //    設定1 ----------------------------------//
  htmlwk.replace("$acc1",String(prg_tbl[1][2])) ;   // 設定1 加速度
  htmlwk.replace("$spd1",String(prg_tbl[1][3])) ;   // 設定1 加速度
  htmlwk.replace("$tim1",String(prg_tbl[1][4])) ;   // 設定1 加速度
  //    設定2 ----------------------------------///
  if (prg_tbl[2][0]) {                           // 設定 2 許可
    htmlwk.replace("$en2","checked") ;          //
  } else {                                      //
    htmlwk.replace("$en2","") ;                 //
  }                                             //
  if (prg_tbl[2][1] == 0) {                     // 設定 2 起点
    htmlwk.replace("$trg2_sel0","selected") ;   // 
    htmlwk.replace("$trg2_sel1","") ;           // 
  } else if (prg_tbl[2][1] == 1) {              //
    htmlwk.replace("$trg2_sel0","") ;           // 
    htmlwk.replace("$trg2_sel1","selected") ;   // 
  }                                             //
  htmlwk.replace("$acc2",String(prg_tbl[2][2])) ;   // 設定2 加速度
  htmlwk.replace("$spd2",String(prg_tbl[2][3])) ;   // 設定2 加速度
  htmlwk.replace("$tim2",String(prg_tbl[2][4])) ;   // 設定2 加速度
                                                //
  //    設定3 ----------------------------------///
  if (prg_tbl[3][0]) {                           // 設定 2 許可
    htmlwk.replace("$en3","checked") ;          //
  } else {                                      //
    htmlwk.replace("$en3","") ;                 //
  }                                             //
  if (prg_tbl[3][1] == 0) {                     // 設定 3 起点
    htmlwk.replace("$trg3_sel0","selected") ;   // 
    htmlwk.replace("$trg3_sel1","") ;           // 
  } else if (prg_tbl[3][1] == 1) {              //
    htmlwk.replace("$trg3_sel0","") ;           // 
    htmlwk.replace("$trg3_sel1","selected") ;   // 
  }                                             //
  htmlwk.replace("$acc3",String(prg_tbl[3][2])) ;   // 設定3 加速度
  htmlwk.replace("$spd3",String(prg_tbl[3][3])) ;   // 設定3 加速度
  htmlwk.replace("$tim3",String(prg_tbl[3][4])) ;   // 設定3 加速度
                                                //
  //    設定4 ----------------------------------///
  if (prg_tbl[4][0]) {                           // 設定 4 許可
    htmlwk.replace("$en4","checked") ;          //
  } else {                                      //
    htmlwk.replace("$en4","") ;                 //
  }                                             //
  if (prg_tbl[4][1] == 0) {                     // 設定 4 起点
    htmlwk.replace("$trg4_sel0","selected") ;   // 
    htmlwk.replace("$trg4_sel1","") ;           // 
  } else if (prg_tbl[4][1] == 1) {              //
    htmlwk.replace("$trg4_sel0","") ;           // 
    htmlwk.replace("$trg4_sel1","selected") ;   // 
  }                                             //
  htmlwk.replace("$acc4",String(prg_tbl[4][2])) ;   // 設定4 加速度
  htmlwk.replace("$spd4",String(prg_tbl[4][3])) ;   // 設定4 加速度
  htmlwk.replace("$tim4",String(prg_tbl[4][4])) ;   // 設定4 加速度
                                                //
  //    設定5 ----------------------------------///
  if (prg_tbl[5][0]) {                           // 設定 5 許可
    htmlwk.replace("$en5","checked") ;          //
  } else {                                      //
    htmlwk.replace("$en5","") ;                 //
  }                                             //
  if (prg_tbl[5][1] == 0) {                     // 設定 5 起点
    htmlwk.replace("$trg5_sel0","selected") ;   // 
    htmlwk.replace("$trg5_sel1","") ;           // 
  } else if (prg_tbl[5][1] == 1) {              //
    htmlwk.replace("$trg5_sel0","") ;           // 
    htmlwk.replace("$trg5_sel1","selected") ;   // 
  }                                             //
  htmlwk.replace("$acc5",String(prg_tbl[5][2])) ;   // 設定5 加速度
  htmlwk.replace("$spd5",String(prg_tbl[5][3])) ;   // 設定5 加速度
  htmlwk.replace("$tim5",String(prg_tbl[5][4])) ;   // 設定5 加速度
                                                //
  // HTML 送信----------------------------------//
  send_html(client,htmlwk) ;                    //  HTML 送信
  # ifdef DEBUG_HTML                           //
    Serial.print("Send Main Config HTML | ") ; //
  # endif                                      //
}                                               //
                                                //
// ----------------------------------------------------------------------------
// - PWPK XHR RESPONSE_HTML 送信                                              -
// ----------------------------------------------------------------------------
void send_pwpkCONF_RESP(WiFiClient client) {   //
  String htmlwk ;                              // HTML 編集用 ワーク
  String conf ;                                //
                                               //
  htmlwk = html_wfRESP ;                       // htmlwk に HTML をコピー
                                               //
  // 変数を値に変換----------------------------//
  if (pwpk_confdl) {                           //  ダウンロードの場合 
    getstr_pwpkconf(conf) ;                    // 設定を文字列に変換
    htmlwk.replace("$conf_text",conf) ;        // 変数に 設定文字列をセット
    pwpk_confdl = false ;                      //
  } else  {                                    //  アップロードの場合
    htmlwk.replace("$conf_text","") ;          //   変数に文字列(ダミー)をセット
  }                                            //
  // HTML を送信 ------------------------------//
  send_html(client,htmlwk) ;                   // HTML 送信処理
  # ifdef DEBUG_HTML                           //
    Serial.print("Send PWPK XHR Resp    | ") ; //
  # endif                                      //
  # ifdef DEBUG_WiFi                           //
    Serial.println("") ;                       //
    Serial.println(" response :") ;            //
    Serial.print(htmlwk) ;                     //
    Serial.println("") ;                       //
  # endif                                      //
                                               //
}                                              //
                                               //
// ----------------------------------------------------------------------------
// - HTML 送信処理                                                            -
// ----------------------------------------------------------------------------
void send_html(WiFiClient client, String &html ) {
    client.println("HTTP/1.1 200 OK");           //
    client.println("Content-type:text/html");    //
    client.println();                            //
                                                 //
    client.print(html) ;                         //
}                                                //
                                                 //
// *****************************************************************************
// * メイン処理                                                                *
// *****************************************************************************

// ----------------------------------------------------------------------------
// - PWPK メイン画面の クライアントからのフォームデータ 処理                  -
// ----------------------------------------------------------------------------
int  proc_main(String &line) {
  String s_work ="" ;
  bool workflg = false ;
  int val ;
  int xhr = 0 ;

  // remote/local のラジオボタンが設定された時の処理 --------------------------
  if (line.indexOf("GET /?remote") != -1) {       //
    s_work = getvalue_s(line,"?remote=") ;        //   remote に続く文字列を取得
    if (s_work=="local") {                        // local の場合
      local = true ;                              //   local フラグを ON
    } else {                                      // remote の場合
      local = false ;                             //   local フラグを OFF
      localen = false ;                           //   locavollen フラグを OFF
    }                                             //
  }                                               //
  // 進行方向が操作された場合の処理 -------------------------------------- 
  if (line.indexOf("GET /?dir") != -1) {          //
    s_work = getvalue_s(line,"?dir=") ;           // dir  に続く文字列を取得
    if (!local) {                                 //  遠隔時
      if (!running) {                             //    走行中でない時
        workflg = dir_bkwd ;                      //     現在の状態を一時保存
        if (s_work=="foward") {                   //     進行方向を設定
          dir_bkwd = false ;                      //
        } else {                                  //
          dir_bkwd = true ;                       //
        }                                         //
        if (workflg != dir_bkwd) {                //  状態が変わった場合に変更
          dir_change = true ;                     //
        }                                         //
      }                                           //
    }                                             //
  }                                               //
  // 出力値がセットされた場合の処理 -----------------------------------------
  if (line.indexOf("GET /?pwr_v") != -1) {        //
    val = getvalue_i(line,"?pwr_v=") ;            // gw2 に続く数値を変数にセット
    if (!local) {                                 //
      if (val>256) val = 255 ;                    // 255 にクリップ
      duty = val  ;                               // デューティを設定
    }                                             //
  }                                               //
  // スライダーが操作された場合の処理 -----------------------------------------
  if (line.indexOf("GET /?slid") != -1) {         //
    val = getvalue_i(line,"?slid=") ;             //   gw2 に続く数値を変数にセット
    if (!local) {                                 //
      if (val>256) val = 255 ;                    //
      duty = val  ;                               //
    }                                             //
    xhr = 1 ;                                     //   スライダー操作時は レスポンス
  }                                               //
  // 準備/解除がセットされた場合の処理 ----------------------------------------
  if (line.indexOf("GET /?ready") != -1) {        //
    if (!local) {                                 //
      if (ready == false) {                       //
        ready = true ;                            //
        base_val = base ;                         //  最低速度を 設定
        rdy_change = true ;                       //
        if (duty < base_val)                      //
          duty = base_val ;                       //
      }else{                                      //
        ready = false ;                           //
        base_val = 0 ;                            //  最低速度を 0 にする。
        rdy_change = true ;                       //
        duty = base_val ;                         //
      }                                           //
    }                                             //
  }                                           //
  // 発車/停車がセットされた場合の処理 ----------------------------------------
  if (line.indexOf("GET /?start") != -1) {        //
    if (!local) {                                 //
      start_sw = true ;                           //
    }                                             //
    xhr = 1 ;                                     // start 時は、XHR 応答
  }                                           //
  // ポーリング要求があった場合の処理 -----------------------------------------
  if (line.indexOf("GET /?pol") != -1) {          //
    xhr = 1 ;                                     // ポーリング時、XHR 応答
  }                                               //
  // 運行設定の処理 -----------------------------------------------------------
  if (line.indexOf("GET /?mconf") != -1) {        // 運行設定の場合、
    html_mode=MAIN_CONF;                          //   HTML を 運行設定用にする
  }                                               //
  // WiFi設定の処理 -----------------------------------------------------------
  if (line.indexOf("GET /?config") != -1) {       // WiFi設定の場合、
    html_mode=WIFI_CONF;                          //   HTML を WiFi設定 にする
  }                                               //
  // --------------------------------------------------------------------------
  return xhr ;                                    // 戻り値は、 xhr かどうか
}                                                 //
                                                  //
// ----------------------------------------------------------------------------
// - PWPK 設定画面の クライアントからのフォームデータ 処理                    -
// ----------------------------------------------------------------------------
int  set_mconf2param(String &line) {              //
  String s_work ="" ;                             //
  int xhr = 0 ;                                   //
                                                  //
  // クライアントからのデータを グローバル変数にセットする。 ------------------
  // 設定 の場合 ---------------------------------------------------------------
  if (line.indexOf("set=") != -1) {               // set
    if (line.indexOf("loop=") != -1) {            // prg_loop チェック有
      prg_tbl[0][0] = true  ;                     //  
    } else {                                      //          チェック無し
      prg_tbl[0][0] = false ;                     //
    }                                             //
    if (line.indexOf("loopnum=") != -1) {         // prg_loop_num
      prg_tbl[0][1] = getvalue_i(line,"loopnum=") ; //  loopnum 後の文字列を取得
    }                                             //
    if (line.indexOf("acc1=") != -1) {            // prg_1_acc
      prg_tbl[1][2] = getvalue_i(line,"acc1=") ;  //  acc1後の数値を変数にセット
    }                                             //
    if (line.indexOf("spd1=") != -1) {            // prg_1_spd
      prg_tbl[1][3] = getvalue_i(line,"spd1=") ;  //  spd1後の数値を変数にセット
    }                                             //
    if (line.indexOf("tim1=") != -1) {            // prg_1_tim
      prg_tbl[1][4] = getvalue_i(line,"tim1=") ;  //  tim1後の数値を変数にセット
    }
    if (line.indexOf("chk2=") != -1) {            // prg_2_en チェック有 
      prg_tbl[2][0] = true  ;                     // 
    } else {                                      //          チェック無し
      prg_tbl[2][0] = false ;                     //
    }                                             //
    if (line.indexOf("trg2=") != -1) {            // prg_2_trg
      prg_tbl[2][1] = getvalue_i(line,"trg2=") ;  //  trg2後の数値を変数にセット
    }                                             //
    if (line.indexOf("acc2=") != -1) {            // prg_2_acc
      prg_tbl[2][2] = getvalue_i(line,"acc2=") ;  //  acc2後の数値を変数にセット
    }                                             //
    if (line.indexOf("spd2=") != -1) {            // prg_2_spd
      prg_tbl[2][3] = getvalue_i(line,"spd2=") ;  //  spd2後の数値を変数にセット
    }                                             //
    if (line.indexOf("tim2=") != -1) {            // prg_2_tim
      prg_tbl[2][4] = getvalue_i(line,"tim2=") ;  //  tim2後の数値を変数にセット
    }                                             //
    if (line.indexOf("chk3=") != -1) {            // prg_3_en チェック有
      prg_tbl[3][0] = true  ;                     // 
    } else {                                      //          チェック無し
      prg_tbl[3][0] = false ;                     //
    }                                             //
    if (line.indexOf("trg3=") != -1) {            // prg_3_trg
      prg_tbl[3][1] = getvalue_i(line,"trg3=") ;  //  trg3後の数値を変数にセット
    }                                             //
    if (line.indexOf("acc3=") != -1) {            // prg_3_acc
      prg_tbl[3][2] = getvalue_i(line,"acc3=") ;  //  acc3後の数値を変数にセット
    }                                             //
    if (line.indexOf("spd3=") != -1) {            // prg_3_spd
      prg_tbl[3][3] = getvalue_i(line,"spd3=") ;  //  spd3後の数値を変数にセット
    }                                             //
    if (line.indexOf("tim3=") != -1) {            // prg_3_tim
      prg_tbl[3][4] = getvalue_i(line,"tim3=") ;  //  tim3後の数値を変数にセット
    }                                             //
    if (line.indexOf("chk4=") != -1) {            // prg_4_en チェック有
      prg_tbl[4][0] = true  ;                     // 
    } else {                                      //          チェック無し
      prg_tbl[4][0] = false ;                     //
    }                                             //
    if (line.indexOf("trg4=") != -1) {            // prg_4_trg
      prg_tbl[4][1] = getvalue_i(line,"trg4=") ;  //  trg4後の数値を変数にセット
    }                                             //
    if (line.indexOf("acc4=") != -1) {            // prg_4_acc
      prg_tbl[4][2] = getvalue_i(line,"acc4=") ;  //  acc4後の数値を変数にセット
    }                                             //
    if (line.indexOf("spd4=") != -1) {            // prg_4_spd
      prg_tbl[4][3] = getvalue_i(line,"spd4=") ;  //  spd4後の数値を変数にセット
    }                                             //
    if (line.indexOf("tim4=") != -1) {            // prg_4_tim
      prg_tbl[4][4] = getvalue_i(line,"tim4=") ;  //  tim4後の数値を変数にセット
    }                                             //
    if (line.indexOf("chk5=") != -1) {            // prg_5_en チェック有
      prg_tbl[5][0] = true  ;                     // 
    } else {                                      //          チェック無し
      prg_tbl[5][0] = false ;                     //
    }                                             //
    if (line.indexOf("trg5=") != -1) {            // prg_5_trg
      prg_tbl[5][1] = getvalue_i(line,"trg5=") ;  //  trg5後の数値を変数にセット
    }                                             //
    if (line.indexOf("acc5=") != -1) {            // prg_5_acc
      prg_tbl[5][2] = getvalue_i(line,"acc5=") ;  //  acc5後の数値を変数にセット
    }                                             //
    if (line.indexOf("spd5=") != -1) {            // prg_5_spd
      prg_tbl[5][3] = getvalue_i(line,"spd5=") ;  //  spd5後の数値を変数にセット
    }                                             //
    if (line.indexOf("tim5=") != -1) {            // prg_5_tim
      prg_tbl[5][4] = getvalue_i(line,"tim5=") ;  //  tim5後の数値を変数にセット
    }                                             //
    if (line.indexOf("save=") != -1) {            // prg_5_en チェック有
      // 設定をファイルに書き出す ----------------------------------------------
      Serial.println("write main config ") ;      //
      wr_pwpkconf() ;                             // 
    }                                             //
    Serial.println("set html_mode MAIN") ;        //
    html_mode = MAIN ;                            // 
  }                                               //
  // 戻る 場合 -----------------------------------------------------------------
  if (line.indexOf("rtn=") != -1) {               //  戻るの場合
    Serial.println("retuen MAIN mode") ;          //
    html_mode = MAIN ;                            // 
  }                                               //
  // ダウンロードの場合 --------------------------------------------------------
  if (line.indexOf("mcnfld=") != -1) {            //
    pwpk_confdl = true ;                          //
    xhr = 1 ;                                     //
  }                                               //
                                                  //
  return (xhr) ;                                  //
}                                                 //
                                                  //
// -----------------------------------------------------------------------------
// - グローバル変数の内容をString に出力                                       -
// -----------------------------------------------------------------------------
void getstr_pwpkconf(String &conf) {              //
  char s_work[1024] ;                             //
  File fp ;                                       //
                                                  //
  // String に 変数の内容を書き込む -------------------------------------------
  sprintf(s_work,"prg_loop         : %d\n",prg_tbl[0][0]  ) ;  //
  conf = String(s_work) ;                         //
  sprintf(s_work,"prg_loopnum      : %d\n",prg_tbl[0][1]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_1_acc        : %d\n",prg_tbl[1][2]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_1_spd        : %d\n",prg_tbl[1][3]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_1_tim        : %d\n",prg_tbl[1][4]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_2_en         : %d\n",prg_tbl[2][0]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_2_trg        : %d\n",prg_tbl[2][1]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_2_acc        : %d\n",prg_tbl[2][2]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_2_spd        : %d\n",prg_tbl[2][3]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_2_tim        : %d\n",prg_tbl[2][4]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_3_en         : %d\n",prg_tbl[3][0]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_3_trg        : %d\n",prg_tbl[3][1]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_3_acc        : %d\n",prg_tbl[3][2]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_3_spd        : %d\n",prg_tbl[3][3]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_3_tim        : %d\n",prg_tbl[3][4]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_4_en         : %d\n",prg_tbl[4][0]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_4_trg        : %d\n",prg_tbl[4][1]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_4_acc        : %d\n",prg_tbl[4][2]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_4_spd        : %d\n",prg_tbl[4][3]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_4_tim        : %d\n",prg_tbl[4][4]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_5_en         : %d\n",prg_tbl[5][0]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_5_trg        : %d\n",prg_tbl[5][1]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_5_acc        : %d\n",prg_tbl[5][2]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_5_spd        : %d\n",prg_tbl[5][3]  ) ;  //
  conf += String(s_work) ;                        //
  sprintf(s_work,"prg_5_tim        : %d\n",prg_tbl[5][4]  ) ;  //
  conf += String(s_work) ;                        //
                                                  //
}                                                 //
                                                  //
// -----------------------------------------------------------------------------
// - グローバル変数の内容を設定ファイルに書き出す                              -
// -----------------------------------------------------------------------------
void wr_pwpkconf() {                              //
  char s_work[1024] ;                             //
  String conf ;                                   //
  File fp ;                                       //
                                                  //
  // String に 変数の内容を書き込む -------------------------------------------
  getstr_pwpkconf(conf) ;                         //
                                                  //
  // 設定ファイルに書き込む ----------------------------------------------------
  fp = SPIFFS.open("/mconf.txt",FILE_WRITE) ;     // 設定ファイルを開く
  if (!fp) {                                      //
    Serial.println(" 設定ファイル オープンエラー !!") ;  //
  } else {                                        //
    Serial.println("/mconf.txt open complete ") ; //
    // 初期設定ファイル書き込み -------------------------------------------------
    if(fp.print(conf)) {                          // ファイルに書き込み
      Serial.println("m_config written") ;        //   終了メッセージ
    } else {                                      //
      Serial.println("m_config write error !!") ; //   失敗メッセージ
    }                                             //
    fp.close() ;                                  // ファイルクローズ
  }                                               //
}                                                 //
                                                  //
// ----------------------------------------------------------------------------
// - 定期割込み処理                                                           -
// ----------------------------------------------------------------------------
void execTicker() {                               //
  ticker_val = true ;                             // フラグを True にするだけ
}                                                 //
                                                  //
// 定期処理 (定期割込みフラグが立っていた時の処理) ------------------------
void do_JOB() {                                   //
  bool workflg = false ;                          //
  int  work = 0 ;                                 //
                                                  //
  // +--------------------------------------------------------------------------
  // | 制御盤情報の取得                                                        |
  // +--------------------------------------------------------------------------
                                                  //
  // loxal の場合 制御盤情報を取得する。----------------------------------------
  if (local == true) {                            //
    if (!digitalRead(MNT_MODE))  {                // MNT_MODE : Low Active
      // 準備/解除スイッチの 状態での処理 --------//
      if (!digitalRead(SW_READY))  {              // SW_READY 押下時(Low Active)
        if (pre_rdy == false ) {                  // 前回割り込み時、SW が OFF
          // wifiモード(stamode) に応じて AP か STA かを選択してサーバー起動 ---
          if (stamode == false) {                 // APモード時 softAP WiFi 開始
            start_AP_server() ;                   // APモードでサーバー起動
            sta_exec = false ;                    //
          } else {                                // STAモードの場合 Wifi 開始
            start_STA_server() ;                  // APモードでサーバー起動
            sta_exec = true ;                     //
          }                                       //
        }                                         //
        pre_rdy = true ;                          // 前回割り込み情報を更新
      } else {                                    //
        pre_rdy = false ;                         //
      }                                           //
    } else {                                      //
      // 設定モードではない場合 ------------------------------------------------
      // 出力用ボリューム値を取得 ----------------------------------------------
      vol_value  = analogRead(PWR_VOL);           //
                                                  //
      // 制御盤の操作が許可されていた場合 -----------------------------------
      if (localen == true) {
        // 進行方向スイッチのチェック ---------------------------------------
        if (!running) {                         // 走行中は切り替え不可
          workflg = dir_bkwd ;                  // 現在の状態を一時保存
          if (!digitalRead(SW_FWD))  {          // SW_FWD : Low Active
            dir_bkwd = 0 ;                      //
          }                                     //
          if (!digitalRead(SW_BWD))  {          // SW_BWD : Low Activ
            dir_bkwd = 1 ;                      //
          }                                     //
          if (workflg != dir_bkwd) {            // 状態が変わった場合に変更
            dir_change = true ;
          }                                     //
        }                                       //
                                                //
        // 準備/解除スイッチのチェック ---------------------------------------
        if (!digitalRead(SW_READY))  {              // SW_READY 押下時(Low Active)
          if (pre_rdy == false ) {                  // 前回割り込み時、SW が OFF
            if (ready == false) {                   //  かつ LED常時点灯でない場合
              ready = true ;                        //  ON にする
              base_val = base ;                     //  最低速度を 設定
              rdy_change = true ;                   //  準備要求状態変化フラグをON
            } else {                                // LED常時点灯中の場合
              ready = false ;                       //  OFF にする
              base_val = 0 ;                        //  最低速度を 0 にする。
              rdy_change = true ;                   //  準備要求状態変化フラグをON
            }                                       //
          }                                         //
          pre_rdy = true ;                          // 前回割り込み情報を更新
        } else {                                    //
          pre_rdy = false ;                         //
        }                                           //
                                                    //
        // 発車/停車が押下された場合の処理 ---------------------------------------
        if (!digitalRead(SW_START)) {               // SW_START押下時 (Low Active)
          if (pre_strt == false) {                  // 前回割込み時、SW が OFF
            start_sw = true ;                       //  発車要求フラグを ON
          }                                         //
          pre_strt = true ;                         // 前回割り込み情報を更新
        } else {                                    //
          pre_strt = false ;                        //
        }                                           //
                                                    //
        // PWM の デューティを設定 -----------------------------------------------
        if (prg_run == false)                       // 自動走行でない場合
          duty = ((vol_value*(255-base_val))/4095)+base_val  ; 
                                                    //  ボリュームの値で出力制御
      // Local だが、制御盤の操作が許可されていない場合 --------------------------
      } else  {                                     //
        duty = base_val ;                           // 出力 OFF
        // ボリューム が 0 の場合 ------------------------------------------------
        if (vol_value == 0)                         // ボリューム 0 に戻されたら
          localen = true ;                          //  制御盤操作を許可する。 
      }                                             //
    }
  // remote の場合  ------------------------------//
  } else {                                        //
    // 準備/解除スイッチのチェック 押されたら Local にする ---------------------
    if (!digitalRead(SW_READY)) {                 // SW_START押下時(Low Active)
      if (pre_rdy == false) {                     // 前回割込み時、SW が OFF
        local   = true ;                          // Local(制御盤操作)にする
 localen = false ;                         // 制御盤操作許可はOFF
      //wifi_restartreq = true ;                  // WiFiリスタート要求
      }                                           //
      pre_rdy = true ;                            // 前回割り込み情報を更新
    } else {                                      //
      pre_rdy = false ;                           //
    }                                             //
  }                                               //
                                                  //
  // +--------------------------------------------------------------------------
  // | 自動運転制御                                                            |
  // +--------------------------------------------------------------------------

  // 発車 処理 (自動 加減速 開始) REMOTE/LOCAL 共通処理 ------------------------
  if (start_sw == true) {               // 発車要求スイッチ押下状態 が 真の場合
    if (prg_run == false) {             //   自動運行中でなければ、
      prg_start = true ;                //     自動運行開始指示。
    } else {                            //   自動運行中の場合
      if (wait_sw) {                    //     STARTスイッチ待ちなら
        prg_next = true ;               //       次No.の実行を指示
        st_on = true ;                  //       ステータス LED ON (更新)
      } else {                          //     でなければ
        start = false ;                 //       発車要求フラグを OFF
        stop  = true  ;                 //       停車要求フラグを ON
      }                                 //
    }                                   //
    start_sw = false ;                  //   発車要求スイッチ状態を OFF
  }                                     //
                                        //
  // 自動運転開始指示時 ----------------//
  if (prg_start == true) {              //   自動運行開始時。(まだ動いていない時)
    prg_no = 1 ;                        //   実行する 運動設定 No. を '1' に設定。
    rate   = prg_tbl[1][2] ;            //     加速度設定
    target = prg_tbl[1][3] ;            //     目標速度設定
    prg_tim = prg_tbl[1][4] ;           //     運行時間設定
    prg_loopnum = prg_tbl[0][1] ;       //     ループ回数を取得。
    if (target < base_val)              //     目標速度が 最低速度より小さい時
        target = base_val ;             //       目標速度は 最低速度
    start  = true ;                     //     発車要求
    stop  = false ;                     //
    wait_sw = false ;                   //
    st_on  = true ;                     //     LED 点灯設定
    st_val       = prg_no ;             //     ステータス LED 表示番号設定
    prg_start = false ;                 //   
    prg_run = true ;                    //     自動運行フラグをセット。
    Serial.print("prg_no :") ;
    Serial.print(prg_no) ;
    start_time = millis();              //     開始時刻を取得 
  }                                     //
                                        //
  // 自動運行中の処理 ---------------------------------------------------------
  if (prg_run == true) {                // 自動運行実行中の場合
    now_time = millis() ;               //     現在時刻を取得
    if (prg_tim <=                      //   経過時間が運行時間以上になったら
             ((now_time - start_time)/100) ) { // 
      time_ovr = true ;                 //   運行時間オーバー
    }                                   // 
                                        //   
    // 運行時間経過時、次の運行No.を選択//
    if (!wait_sw & time_ovr)  {         //   運行時間オーバーでスイッチ待ちでない時
      nextno = prg_no + 1 ;             //     次の運行No.を設定。
      while (nextno != prg_no) {        //     No.が 一周するまでチェック
        if (nextno > 5) {               //     No.が上限を超えた場合、
          if (   (prg_tbl[0][0] == 1)   //       ループ En で
              && (prg_loopnum >  1)) {  //       繰り返し回数が 1以下でなければ
            nextno = 1 ;                //         No. を最初にする。
            prg_loopnum = prg_loopnum -1 ;//
            Serial.print("loopnum:") ;
            Serial.println(prg_loopnum) ;
          } else {                      //       ループ En でないか、繰り返し回数
                                        //       に達してれば
            nextno = prg_no ;           //         No.を現状にして
            now_time = millis() ;       //         現在時刻を取得
            now_time = now_time - start_time ;//   経過時間を取得
         // Serial.print(" | time : "); //
         // Serial.println(now_time) ;  //
            stop = true ;               //         停車要求
            break ;                     //         while を抜ける
          }
        }                               //
        if (prg_tbl[nextno][0] == 0) {  //      選択したNo.が有効(En)でなければ、
          nextno = nextno + 1 ;         //        次の No.
        } else {                        //      選択したNo.が有効(En)な時、
          prg_no = nextno ;             //        運行No.を選択したNo.に更新
          if (prg_tbl[prg_no][1] == 0)  //        起点が前No.の続きなら、
            prg_next = true ;           //          次No.の運行を指示
          else{                         //        起点がスイッチ押下なら、
            wait_sw = true ;            //          スイッチ待ちフラグを ON
            st_blink = true ;           //          ステータス点滅設定
            st_bl_intvl = 5 ;           //          点滅間隔設定
          }                             //
          break ;                       //        次No.選択を抜ける。   
        }                               //
      }                                 //
      time_ovr = false ;                //
    }                                   //
                                        //
    // 設定更新指示時 ------------------//
    if (prg_next == true) {             //   設定更新の場合
      rate   = prg_tbl[prg_no][2] ;     //     加速度設定
      target = prg_tbl[prg_no][3] ;     //     目標速度設定
      prg_tim = prg_tbl[prg_no][4] ;    //     運行時間設定
      if (target < base_val)            //     目標速度が 最低速度より小さい時
          target = base_val ;           //       目標速度は 最低速度
      start  = true ;                   //     発車要求
      stop  = false ;                   //
      wait_sw = false ;                 //
      st_on  = true ;                   //     LED 点灯設定
      st_val       = prg_no ;           //     ステータス LED 表示番号設定
      now_time = millis() ;             //     現在時刻を取得
      now_time = now_time - start_time ;//     経過時間を取得
      Serial.print(" | time : ") ;
      Serial.println(now_time) ;
      Serial.print("prg_no :") ;
      Serial.print(prg_no) ;
      start_time = millis();            //     開始時刻を取得 
      prg_next = false ;                //     設定更新終了
    }                                   //
                                        //
    // 発車要求時 ----------------------//
    if (start == true) {                //   発車要求フラグ が 真の場合
      autorun = true ;                  //     自動走行中のフラグを ON
      // 速度が目標値未満の時 ----------//
      if (duty < target) {              //     現状が目標デューティ未満の時
        if (duty < base)                //       LED常時点灯時の値未満の時は
          duty = base ;                 //         LED常時点灯時の値に更新
        if ((target - duty) < rate)     //       目標値との差分が増減値未満の時
          duty = target ;               //         出力を目標値に更新
        else                            //       増減値以上の場合
          duty = duty + rate ;          //         出力を 増減値分加算
      // 速度が目標値より大きい時 ------//
      } else if (duty > target){        //     現状が目標デューティより大きい時
        if ((duty - target) < rate)     //       目標値との差分が増減値未満の時
          duty = target ;               //         出力を目標値に更新
        else                            //       増減値以上の時
          duty = duty - rate ;          //         出力を 増減値分減算
      // 目標値となった時 --------------//
      } else {                          //     目標デューティに達したら
        now_time = millis() ;           //         現在時刻を取得
        now_time = now_time - start_time ;//   経過時間を取得
        Serial.print(" duty:") ;
        Serial.print(duty) ;
        Serial.print(" | time : ");     //
        Serial.print(now_time) ;        //
        Serial.print(" | ") ;           //
        start = false ;                 //       発車要求を解除
      }                                 //
    }                                   //
                                        //
    // 停車要求時 ----------------------//
    if (stop  == true) {                //   停車要求フラグが真の場合
      autorun = true ;                  //     自動走行中のフラグを ON
      if (duty > base_val){             //     現状が、最低速度より大きい時
        if ((duty - base_val) < rate)   //       最低速度との差分が増減値未満の時
          duty = base_val ;             //         出力を 最低族度に設定
        else                            //       増減値以上の時
          duty = duty - rate ;          //         出力を 増減値 分減算
      } else {                          //     最低速度以下の時
        stop  = false ;                 //       停車要求を OFF
        autorun = false;                //       自動走行中フラグを OFF
        prg_run = false;                //       自動運行中フラグを OFF
        if (ready == true) {            //       LED常時点灯の場合
          st_blink = true ;             //         ステータス点滅 ON
          st_val   = 7 ;                //         ステータス値を 7
          st_bl_intvl = 5 ;             //         点滅間隔 5
        } else {                        //       常時点灯ではない場合
          st_on    = true ;             //         ステータスLED 消灯
          st_val   = 0 ;                //
        }                               //
      }                                 //
    }                                   //
  // 自動運行中でない時 ----------------//
  } else {                              //  自動運行実行中ではない時
    if (rdy_change) {                   //    準備要求状態変化フラグがONの時
      if (ready) {                      //      常時点灯なら、
        st_blink = true ;               //        LED点滅を設定
        st_val   = 4 ;                  //
        st_bl_intvl = 5 ;               //
      } else {                          //      常時点灯でなければ
        st_on    = true ;               //        LED消灯を設定
        st_val   = 0 ;                  //
      }                                 //
      rdy_change = false ;              //
    }                                   //
  }                                     //
                                        //
  // 走行中フラグを更新 -------------------------------------------------------
  if (duty > base_val)                  //  最低速度より大きな Duty の時
    running = true ;                    //    走行中フラグを ON
  else                                  //  最低速度以下の時
    running = false ;                   //    走行中フラグを OFF
                                        //
  // +=========================================================================+
  // | 操作盤制御                                                              |
  // +=========================================================================+

  // 進行方向 設定 -------------------------------------------------------------
  if (dir_change) {                          // 進行方向が変化した時の設定
    ledcWrite(0, 0) ;                        // 切り替え前に PWM出力を停止 
    delay(10);                               // 
    digitalWrite(LED_FWD,!dir_bkwd) ;        // 前進用 LED 設定
    digitalWrite(LED_BWD,dir_bkwd) ;         // 後退用 LED 設定 
    digitalWrite(DIR_OUT,dir_bkwd) ;         // 進行方向用リレー 設定
    delay(10);                               // 
    ledcWrite(0, duty) ;                     // PWM出力を元に戻す。
    dir_change = false ;
  }                                          //
                                             //
  // ステータス LED 制御 -------------------------------------------------------
  //   ( ステータス LED を点灯 ) ------------//
  if (st_on == true) {                       // ステータス 点灯要求時
    digitalWrite(LED_ST3, (st_val & 0x01) != 0) ; // LED 設定
    digitalWrite(LED_ST2, (st_val & 0x02) != 0) ; //
    digitalWrite(LED_ST1, (st_val & 0x04) != 0) ; //
    st_on = false ;                          //
    st_blink = false ;                       //
  }                                          //
                                             //
  //   ( ステータス LED を点滅 ) ------------//
  if (st_blink == true) {                    // 点滅設定時
    if (st_bl_off == true) {                 //   消灯状態時
      if (st_bl_cnt == 0) {                  //     カウント値が 0 なら
        st_bl_cnt = st_bl_intvl ;            //       カウント値を初期値にする
        st_bl_off = false ;                  //       LED を点灯状態にする
        digitalWrite(LED_ST3, (st_val & 0x01) != 0) ; // LED 設定
        digitalWrite(LED_ST2, (st_val & 0x02) != 0) ; //
        digitalWrite(LED_ST1, (st_val & 0x04) != 0) ; //
      } else {                               //     カウント値が 0 でなければ
        st_bl_cnt = st_bl_cnt -1 ;           //       -1
      }                                      //
    } else {                                 //  点灯状態の時
      if (st_bl_cnt == 0) {                  //     カウント値が 0 なら
        st_bl_cnt = st_bl_intvl ;            //       カウント値を初期値にする
        st_bl_off = true  ;                  //       LED を消灯状態にする
        digitalWrite(LED_ST3,  0) ;          //
        digitalWrite(LED_ST2,  0) ;          //
        digitalWrite(LED_ST1,  0) ;          //
      } else {                               //     カウント値が 0 でなければ
        st_bl_cnt = st_bl_cnt -1 ;           //       -1
      }                                      //
    }                                        //
  }                                          //
                                             //
  // PWM デューティを設定 ----------------------------------------------------
  ledcWrite(0, duty) ;                       // PWM デューティを設定 
                                             //
  // +=========================================================================+
  // | 管理情報                                                                |
  // +=========================================================================+

  // 割り込み処理の実行回数をカウント ------------------------------------------
  loopcnt++ ;                                // ループ回数 インクリメント
                                             //
  // 実行回数が 10 回目の場合 (1秒毎) ------------------------------------------
  if (loopcnt ==10) {                        // 
    # ifdef DEBUG_Hard                       // デバッグ用出力
      Serial.println("") ;                   //
      Serial.print( "PWR_VOL :") ; Serial.println(vol_value) ;
      Serial.print( "duty     :") ; Serial.println(duty) ;
      Serial.print( "running  :") ; Serial.println(running) ;
      Serial.print( "autorun  :") ; Serial.println(autorun) ;
      Serial.print( "stop     :") ; Serial.println(stop ) ;
      Serial.print( "ready    :") ; Serial.println(ready) ;
      Serial.print( "start    :") ; Serial.println(start) ;
      Serial.print( "prg_start:") ; Serial.println(prg_start) ;
      Serial.print( "prg_next :") ; Serial.println(prg_next ) ;
      Serial.print( "prg_run  :") ; Serial.println(prg_run  ) ;
      Serial.print( "prg_tim  :") ; Serial.println(prg_tim  ) ;
    #endif                                   //

    loopcnt = 0;                             // ループ回数をクリア
  }                                          //
  ticker_val = false ;                       // 処理を実行したら、定期割込みフラグ
                                             // を false にする。
}                                            //
                                             //


 以下、メモ

  ( 詳細は、各スケッチのコメント参照。
  主に、パワーパック改2 からの変更箇所 について記す )

<PWPK MAIN HTML (pwpk.html)>
主な修正項目
   WiFi ハングアップ対策
   XHR 発行を 同時に 1つのみに制限。
   ポーリング内容に remote/local, forward, backward を追加。
   remote 時、ポーリング (低頻度) 実施
(行# 86,87,93,96,98,99,105)
  スライダー操作時のXHR発行時、発行を同時に1つのみにする。(WiFi ハング対策)
(行# 113,114,120,127-143 )
  ポーリング時のXHR発行時、発行を同時に1つのみにする。
  ポーリング内容に remote/local, forward, backward を追加。
(行# 158,159,160,166,173,179,182-185)
  発車/停止 押下時の XHR 発行を 他XHR と同時とならない様にする。
(行# 199,200)
  Remote 時、ポーリングを低頻度(1回/1秒) で発行する。
  Local 時は 1回/200m秒 にする。

<PWPK設定 HTML   (pwpk_conf.html)>

(行#  158-170)
  ダウンロード/アップロード用
(行#  241-272)
  ダウンロード用 javascript。
(行#  275-458)
  アップロード用 javascript。

<PWPK XHR 応答 HTML (pwpk_resp.html)>

(行#  6-7)
  forward/backward, remote/local 状態 出力

<WiFi設定 HTML (wifi_conf.html)>
"テキストファイルをアップロードする"と同じ

<WiFi XHR 応答 HTML (wifi_resp.html)>

"テキストファイルをアップロードする"と同じ




<MAIN (pwpk_4.ino : 変数定義, setup, loop 関数)>

(行#  34)
 WiFi 設定時、リブート有フラグ
(行#  36-40)
 WiFi 用 各種フラグ定義
(行#  57-67)
 HTML ファイル設定見直し
 XHR応答用を WiFI設定 用と pwpk設定 用 に分けた。
(行#  96)
 PWPK 用設定ダウンロードフラグ
(行#  144-145)
 SW2 用の IOポート定義
(行#  164-165)
 IO32,33 の ピンモード設定。 INPUT を設定が必用。
(行#  177-193)
 各 HTML ファイルの読み込み
 XHR応答用を WiFI設定 用と pwpk設定 用 に分けた。
(行#  241-246)
 各変数定義追加
 POST要求、アップロード、等の対応用。
(行#  267-276)
 GET 要求時の処理
 戻り値 (XHR がどうか) を追加。
(行#  277-285)
 POST 要求時は フラグセット。
 アップロードの場合はアップロードのフラグをセット。
(行#  289-324)
 POST 処理。
(行#  290-301)
   POST のデータを受信。
(行#  302-324)
   POST 処理。
   アップロード時、実際の処理は無し。
  ( HTML 上で 各要素に値をセットするのみの為 )
   アップロード以外の時は、GET と同じ処理。
  ( 処理関数を、 GET/POST の両方に対応させている )
(行#  342-359)
   各 HTML選択状態により、HTML を送信。
(行#  369,377)
   Client 切断後、5mS 待って client.stop() 。
 その後、50mS 待って クライアント処理終了。
   ポーリング時の hangup 対策の為。( HTML での XHR 応答後の待ち時間との兼ね合いがある? hangup の頻度が下がるだけかも。)
(行#  388-400)
  WiFi リスタート要求時の処理。


<WIFI設定 (wifi_config.ino : 主に WiFI 設定用 の関数)>

"テキストファイルをアップロードする"とほぼ同じ。
 若干の 変数、関数名見直し。

<PWPK処理 (pwpk.ino : 主に PWPK処理、PWPK 設定用 の関数)>

主に、設定のダウンロード、アップロードに対応
(行#  235-236)
 ポーリング時の応答に Local/Remote, foward/backward の状態を追加。
(行#  354-384)
  アップロード、ダウンロード時の XHR 応答 HTML 送信
(行#  601-606)
  フォームデータ処理で ダウンロード時、フラグセット
(行#  610-696)
  設定値のファイル書き込みを、 String への返還と設定ファイルの書き込みに分割。
(行#  716-733)
  MNT_MODE 時に準備/解除 スイッチで WiFiをリスタートする処理を追加。
  ( WiFi ハング時に備える )
(行#  976)
  LED常時点灯時は、LED_ST1 のみ点滅する様に変更。

0 件のコメント:

コメントを投稿