ラベル ESP32 の投稿を表示しています。 すべての投稿を表示
ラベル ESP32 の投稿を表示しています。 すべての投稿を表示

2020年10月25日日曜日

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

目的:

Nゲージ用 PWM制御パワーパックの改良/機能追加として、以下を行う。
(1) フォトリフレクタ TPR-105 による車両検出

追加機能:

レイアウトの主要地点に TPR-105 を設置し、車両検出を行う。
自動運転時、車両検出 (通過検出) により次の動作(設定)へ移行する。

 

車両検出ハードウェア:

 PWPK ハードウェア(回路) は こちら
 車両検出回路 は こちら

ESP32 に TPR-105 を接続できる端子が余っていない為、I2C接続の I/Oエキスパンダ(MCP23017) を使用して TPR-105 を接続している。
MCP23017 の使い方は こちら を参照。
TPR-105 の使い方は こちら を参照。
 

WEB画面:

車両検出対応の為、PWPK設定画面 ( 自動運行の設定画面 ) を下の様に変更。

 "設定更新契機" で、次の設定を開始するタイミングを
・時間経過
・地点通過
・スイッチ押下
から選択する。
地点通過で車両検出を行う。

その他の画面は WEBから制御可能なNゲージ用 PWM制御パワーパック_改3 と同じ。


スケッチ等:

スケッチ等は こちらのファイルを参照。(zipファイル)
スケッチ等の構成は、改3 と同様。
詳細はコメントを参照。
 

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_5.ino : 変数定義, setup, loop 関数)
WIFI設定 (wifi_config.ino : 主に WiFI 設定用 の関数)
PWPK処理 (pwpk.ino : 主に PWPK処理、PWPK 設定用 の関数)



車両位置検出概要:

車両位置検出は、以下の変数を使用する。

det_ariv : 到達フラグ。検出位置に到達したことを示す。定期割込み開始時にクリア。
pnt_extno : 車両位置。
最後に到達した位置を示す。初期値は 0xff

pnt_arrive[8] : 検出位置毎の到達フラグ。
検出位置に到達した事を示す。
検出位置 に 到達した時 '1', 処理終了か、別検出位置到達で '0'

pnt_mask[8] : 検出位置毎のマスクフラグ。
検出位置到達後、一定期間、到達の検出を行わない様にする
検出位置 に 到達した時 '1', 一定期間経過経過後に '0'

pnt_time[8] : 検出位置毎の到達時刻。
検出位置 に 到達した時の時刻を記憶する。


車両位置検出の処理の概要は以下の通り。
 (実際の処理は、スケッチ 参照)
  • 車両の位置情報取得
    1. 各検出位置毎に 到達後の経過時間をチェックし、一定時間経過していればマスクフラグを解除する。
    2. MC23017の割込み状態を取得 ( INTFA, INTCAPA をリードする )。
    3. 到達フラグをクリア。
    4. 割込み発生時、各変数更新。
      1. 到達位置を取得。
        マスクされていない位置に到達した時、到達フラグをセットして到達した位置を取得。
      2. 各変数更新。
        到達した位置の場合、到達フラグ(pnt_arrive[n]),マスクフラグ(pnt_mask[n]) をセットし、, 到達時刻(pnt_time[n])を更新。
        到達した位置ではない場合は、到達フラグ(pnt_arrine[n]) をクリア。

  • 自動運転制御中の 車両位置検出処理
    1. 自動運転開始時、全検出位置の変数を初期化する。
    2. 設定更新契機が地点通過の場合、待っている位置の到達フラグを確認。
      到達フラグが立っていた場合、自動運転設定の更新フラグをONし、全検出位置の到達フラグをクリア。

また、パワーパックの制御(定期割込み処理) の 概要は以下の通り。

  • 操作盤情報取得 : 操作盤の状態を取得し、状態により制御設定値を更新する。
    (Local(制御盤操作) 時)
    制御盤のスイッチ状態を取得する。
    (設定モード時)
    READYスイッチが押下ならWiFi再起動
    (通常モード時)
    出力用ボリューム値取得
    (制御盤操作許可時)
    (走行中ではない時)
    進行方向スイッチをチェックし、変化したらフラグをセット
    READYスイッチをチェックし、押下ならLED常時点灯設定を現在の反対にセット
    STARTスイッチをチェックし、押下ならスタートフラグをセット
    自動走行でない場合は、PWMduty値にボリューム値から計算した値を設定
    (制御盤操作不許可時)
    PWMduty値にOFF状態の値を設定
    ボリュームの値が'0'なら制御盤操作を許可にする。
    (Remote(WEB からの操作) 時)
    READYスイッチをチェックし、押下ならLocalにする。
  • 車両位置情報取得 : センサ()の状態を取得し、車両の位置情報を更新する。
    車両位置検出マスクフラグ更新
    MCP23017割込み状態取得(車両位置検出)
    車両位置検出用変数更新
  • 自動運転制御 : 自動運転設定により、制御盤設定値を自動で更新する。
    発車スイッチチェック
    (発車スイッチがONの時)
    (自動運行実行中ではない時)
    自動運行開始要求をON
    (自動運行実行中の時)
    (発車スイッチ待ちの時)
    設定更新要求をON
    (発車スイッチ待ちではない時)
    停車要求ON
    自動運転開始
    (自動運転開始要求がONの時)
    運転設定No.を1にし、各パラメータをセットする
    加減速要求ON
    通過位置情報をクリア
    ステータスLED値をNo.に設定して点灯ON
    自動運転中フラグをON
    開始時の時刻を取得
    自動運行中の処理
    (自動運転中フラグがONの時)
    (設定更新契機が時間経過の時)
    開始からの時間が運転設定時間以上なら設定更新要求ON
    (設定更新契機が地点通過の時)
    設定地点の到達フラグがONなら設定更新フラグをONし、全到達フラグをクリア
    (設定更新要求ONの時)
    次の有効な設定No.を探す。
    有効な設定No.が無い場合は停車要求をON
    (停車でない場合)
    設定を更新。
    ステータスLED値をNo.に設定して点灯ON
    (加速要求がONの時)
    (PWMduty値が目標値でない場合)
    PWMduty値に加速度設定値を加減算する
    (PWMduty値が目標値の場合)
    加速要求をOFFする
    (停車要求の時)
    (PWMduty値が停車時のdutyより大きい場合)
    PWMduty値から加速度設定値を減算する
    (PWMduty値が停車時のduty以下の場合)
    停車要求をOFFする
    自動運転中フラグをOFFする
    ステータスLED値を停車時の値に設定して点灯ON
    (自動運転中フラグがOFFの時)
    (停車時設定変化時)
    ステータスLED値を停車時の値に設定して点灯ON
    走行中フラグ更新
    PWMdutyの値から走行中の判定をしてフラグをON/OFF
    操作盤制御:更新された制御盤設定値により、走行設定を行う。
    進行方向設定
    ステータスLED制御
    PWMduty設定
    デバッグ用シリアル出力
    デバッグ用にシリアル画面に各種情報を出力する。



2020年9月21日月曜日

ESP32/arduino:WiFi接続時のスマホ画面とPC画面の切り替え

目的:

WiFi接続時、スマホからのアクセス画面をスマホ用にする。(スマホ対応)

背景:

WiFi接続時、スマホから接続した場合に ボタン間隔が狭い等、操作しにくい場合がある。
この為、PCからアクセスした場合とスマホからアクセスした場合で画面表示を変えて操作しやすくする。

方法:

HTML を レスポンシブデザインとして、ウィンドウ幅によりデザインを分ける。
  1. viewportを設定する。
    <head> ~ </head> 部分に、以下の様な metaタグ を記述する。
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
    
  2. ウィンドウ幅に応じて styleタグ を分ける。
    以下の様に、styleタグを 分ける。
    <style media="screen and (min-width: 769px)">
     ....  (PC用 設定)
    </style>
    <style media="screen and (max-width:768px)">
     ....  (スマホ用 設定)
     </style>
  3. 必要に応じて、HTML を修正する。 
* ウィンドウ幅で切り替えているため、PC でも ブラウザの幅を狭めれば スマホ用表示になる。

 例:

ESP32/arduino:LED調光_WEBからと外付けのボリュームからを切り替えて調光
をスマホ対応にした 時の画面 と HTML を示す。(スケッチ、レスポンス用HTML は変更なし)
PC用 画面
スマホ用画面
*スマホ時は、全体に間隔を広げ、ラジオボタンの形状を変更している。

<!DOCTYPE html><html lang='ja'>
<head> 
  <meta charset='UTF-8'>
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
  <style media="screen and (min-width: 769px)">
       #base          {font-size:16pt;text-align:center;width:300px;border:solid 4px #93ff93; }
       #radio_box     {font-size:12pt;float:left ;text-align:left; width:45%; }
       #disp_box      {font-size:12pt;float:right;text-align:left; width:50%; }
       #brightness    {font-size:12pt;text-align:right; }
       #val_box       {font-size:10pt;text-align:center; clear:both ;}
       #ctl_box       {text-align:center; }
       input.radio    {margin-left:8px; width:30px;}
       input.value    {margin-left:8px; width:30px;}
       input.setbutton{margin-left:8px; width:40px;}
       input.slider   {margin-left:8px; width:250px;}
       input.button   {margin:0px 15px; width:100px;}
  </style>
  <style media="screen and (max-width:768px)">
       #base             {font-size:20pt;text-align:center; width:97%; border:solid 4px #008000; }
       #radio_box        {font-size:18pt;float:left ;text-align:left; width:45%; }
       #disp_box         {font-size:18pt;float:right;text-align:left; width:50%; line-height:30pt; }
       #brightness       {font-size:18pt;text-align:right; }
       #val_box          {font-size:16pt;text-align:center; clear:both ; width:100% ; }
       #ctl_box          {text-align:center; height: 100px }
       input.radio       {margin-left:8px; width:30px; vertical-align:middle ; display:none; }
       input.radio:checked + label.lbl { background: #00FF00 ; color: #FF0080 ; }
       label.lbl         {display:block ; margin: 0px calc(90% - 120px) 10px 10% ; text-align:center ; height:50px; line-height:50px; border: 1px solid #006DD9; border-radius:8px;}
       output.brightness {margin-right:50px }
       input.value       {margin-left:8px; margin-top:30px; width:50px; height:30px ; font-size:16pt; }
       input.setbutton   {margin-left:8px; width:50px; height:30px ; font-size:16pt; }
       input.slider      {margin-top:20px; margin-bottom:20px; width:95%; height:80px }
       input.button      {margin:0px 5% ; width:30%; height:50px ; font-size:18pt; background: #008000 ; color:#ffffff ; }
  </style>
  <title>Color LED Controller</title>
</head>

<body>
<div id="base">
  <p>LED ON/OFF</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)'>
      <label for="rad_lo" class="lbl">Local<br></label>
      <input class='radio' type='radio' name='remote' id="rad_rm" value='remote' $checked_rm onclick='disp_ctrl(this.value); submit(this.value)'>
      <label for="rad_rm" class="lbl">Remote<br></label>
    </form>
  </div>
  <div id="disp_box">
      <span> brightness value</span><br>
      <div id="brightness"><output class='brightness' id="o1">$led_brightness</output></div>
  </div>
  <div id="val_box">
    <form method="get">
      <span > LED brightness (0-255)</span> 
      <input class='value'  type='text' name='led_v' value=$led_brightness id='led_v' disabled>
      <input class='setbutton' type='submit' name='set' id='set' value='SET' disabled>
    </form> 
    <form method='get'> 
      <input class="slider" type="range" name='led_s' id='led_s' value=$led_brightness min="0" 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="ctl_box">
    <form method='get'>
      <input class='button' type='submit' name='on' id="on" value='ON' disabled >
      <input class='button' type='submit' name='off' id="off" value='OFF' disabled><br>
    </form>
  </div>
</div>


<script>
var polling = null ;

function setval(ledval){
 var xhr = new XMLHttpRequest();
        xhr.open("get", "?slid="+ledval );
        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) ) {
            document.getElementById('led_v').value = xhr.response.getElementById("output1").innerHTML;
            document.getElementById('o1').value = xhr.response.getElementById("output1").innerHTML;
          }
        }

        ntimeout = function(e) {
          xhr.abort() ;
        }
        xhr.send();
}


function getval(){
 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.onreadystatechange = function() {
          if( (xhrget.readyState == 4) && (xhrget.status == 200) ) {
            document.getElementById('o1').value = xhrget.response.getElementById("output1").innerHTML;
          }
        }

        ntimeout = function(e) {
          xhrget.abort() ;
        }
        xhrget.send();
}

function disp_ctrl( radioid ) {
   if(radioid == 'remote') {
      document.getElementById('led_v').disabled = false;
      document.getElementById('set').disabled = false;
      document.getElementById('led_s').disabled = false;
      document.getElementById('on').disabled = false;
      document.getElementById('off').disabled = false;
      clearInterval(polling);
   } else {
      document.getElementById('led_v').disabled = true;
      document.getElementById('set').disabled = true;
      document.getElementById('led_s').disabled = true;
      document.getElementById('on').disabled = true;
      document.getElementById('off').disabled = true;
      polling = setInterval(getval,100) ;
   }

}

window.onload = function() {
    if(document.getElementById("rad_rm").checked) {
      document.getElementById('led_v').disabled = false;
      document.getElementById('set').disabled = false;
      document.getElementById('led_s').disabled = false;
      document.getElementById('on').disabled = false;
      document.getElementById('off').disabled = false;
      clearInterval(polling);
    } else if(document.getElementById("rad_lo").checked) {
      document.getElementById('led_v').disabled = true;
      document.getElementById('set').disabled = true;
      document.getElementById('led_s').disabled = true;
      document.getElementById('on').disabled = true;
      document.getElementById('off').disabled = true;
      polling = setInterval(getval,100) ;
    }
}

</script>

</body>
</html>

以下、覚書など。
  ( 行# 4 )
viewportの設定。

  ( 行# 5 ~ 17 )
styleタグに メディア属性を設定。769px 以上の幅の場合は PC画面とし、従来の設定のままとする。但し、行#10 は html 内で font-size を変更していた為、style の設定を変更。

  ( 行# 18 ~ 33 )
スマホ用の styleタグ。
styleタグに メディア属性を設定。768px 以下の幅の場合は スマホ画面とし、スマホ用の設定を記述。
全体に 間隔が広がるように margin、font-size 等の設定を変更。
ラジオボタンの形状変更の為、通常のラジオボタンの表示を消し ( display:none; )、
Label タグ を設定して、"input.radio:checked + label.lbl" (行#26) で、chkeck 時の動作 (Label の 色を変更)

  ( 行# 42 ~ 45 )
スマホ用に ラジオボタンの形状を変更する為、labelタグを追加し、表示文字列は Label で設定。

  ( 行# 50 )
表示位置をずらすため、class の設定を追加。(PC画面には影響なし)

  ( 行# 50 )
font-size を style で設定する為に削除。


2020年5月6日水曜日

ESP32/arduino:NTPサーバーとの時刻同期と時刻表示

目的:

NTPサーバーと時刻同期を行い、現在時刻を取得する。

手順:

  1. 時刻同期
    wifi 設定後、configTime() により、NTPサーバーを設定する。
    書式:
      void configTime(long gmtOffset_sec, int daylightOffset_sec,
             const char* server1, const char* server2, const char* server3);

    パラメータ :
      gmtOffset_sec      GMTとローカル時刻との差(秒)。日本では 3600*9 (9時間)
     daylightOffset_sec 夏時間設定(秒)。日本では 0
     server1 ~ server3 NTPサーバ。最低一つ設定する。

    記述例 :
       configTime(3600 * 9, 0, "ntp.nict.jp", "time.google.com",
                 "ntp.jst.mfeed.ad.jp" );
     
    1. 時刻取得
      時刻の取得は、getLocalTime() で取得する。
      getLocalTime()
      書式 :
         bool getLocalTime(struct tm *info, uint32_t ms);
      パラメータ:
        info 
      取得する時刻情報を格納する領域。
        ms    タイムアウト時間。省略した場合は5000。

      記述例:
        struct tm timeInfo;
        getLocalTime(&timeInfo, 1);
        Serial.printf(" %04d/%02d/%02d %02d:%02d:%02d\n",
                       timeInfo.tm_year + 1900,
                       timeInfo.tm_mon + 1,
                       timeInfo.tm_mday,
                       timeInfo.tm_hour,
                       timeInfo.tm_min,
                       timeInfo.tm_sec) ;

      注:

       getLocalTime は、取得した年が 116(2016年) 以下の場合、タイムアウトになるまで取得し直す。NTPサーバーと同期がとれない場合、タイムアウト値を指定しないと、5秒間、リトライする(処理が待たされる) こととなる。

       NTPサーバーと同期がとれない要因の 1つとして、WIFI 固定IP設定時の DNSアドレス が正しく設定されていないことが考えられる。( やらかしてました。 )
       

例:

 「SPIFFSのテキストファイルをダウンロードする 」の シリアル出力にタイムスタンプを追加した例を以下に示す。
(メインスケッチ以外のファイルは 「SPIFFSのテキストファイルをダウンロードする 」を参照 )
<表示例>
     |
 2020/05/06 17:08:03
 +++++++++++++++++ new client! +++++++++++++++++
GET / HTTP/1.1
     |

#include <WiFi.h>                         // WiFi 使用の為
#include "FS.h"                           // SPIFFS 使用の為
#include "SPIFFS.h"                       // SPIFFS 使用の為
#include "time.h"                         // getLocalTime 使用の為
 
#define DEBUG_HTML                        // Debug用 Serial 表示制御用
#define DEBUG                             // Debug用 Serial 表示制御用
 
// 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
 
// 動作モードフラグ ---------------------------------------------------
bool stamode = true ;                     // WiFi STA 接続モード : true
bool sta_exec = false ;                   // WiFi STA接続モード実行中フラグ
bool config_mode  = false ;               // 初期設定モード ; true
bool config_exec = false ;                // 初期設定実行中フラグ
 
// 設定ファイル用 -----------------------------------------------------
String s_config ;                         // 設定ファイル用 String
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
String foot_msg ="" ;                     // リブートメッセージ用

#define JST 3600*9                        // 日本の GMT との時間差(秒)
#define CLIENT_TIMEOUT 3000               // クライアントタイムアウト (mS)
WiFiServer server(80);
 
String html_CONF ;                        // 設定用 HTML
String html_MAIN ;                        // メイン HTML
String html_resp ;
 
// LED 調光処理用 ----------------------------------------------------------------
// -- for ledc
byte brightness = 0 ;                     // LED 明るさ設定 (0-255)
 
// -----------------------------------------------------------------------------
//  arduino 初期化処理                                                        --
// -----------------------------------------------------------------------------
void setup() {
  int res = 0 ;                                // 結果格納用 (ワーク)
 
  Serial.begin(115200);                        // シリアル 開始
  SPIFFS.begin(true) ;                         // SPIFFS 開始
 
  // 初期設定用 HTML ファイルの読み込み ---------------------------------------
  res = rd_SPIFFS("/WiFi_Config.html",html_CONF) ;  // html_CONF に Config.html を格納
 
  // 本体 HTML ファイルの読み込み ----------------------------------------------
  res = rd_SPIFFS("/main.html",html_MAIN) ;         // html_MAIN に main.html を格納
   
  // レスポンス用HTML ファイルの読み込み ----------------------------------------
  res = rd_SPIFFS("/resp_cnfld.html",html_resp) ;   // html_resp に resp_confld.html を格納
   
  // 初期設定ファイルを読み込み、グローバル変数に設定し、wifi,画面モードを決定 ---
  res = rd_config() ;                          //
 
  // wifiモード(stamode) に応じて AP か STA かを選択してサーバー起動 -------------
  if (stamode == false) {                    // APモードの場合 softAP WiFi 開始
    start_AP_server() ;                      // APモードでサーバー起動
    sta_exec = false ;                       //
  } else {                                   // STAモードの場合 Wifi 開始
    start_STA_server() ;                     // STAモードでサーバー起動
    sta_exec = true ;                        //
    configTime(JST, 0, "ntp.nict.jp",        // NTPサーバー設定
               "time.google.com",            //
               "ntp.jst.mfeed.ad.jp");       //
  }                                          //

  // 実行中のモード (設定かメインか) を設定 -------------------------------------
  if (config_mode) config_exec = true ;   // config_mode なら config_exec を '1'
  else config_exec = false ;              //
 
  Serial.println("Server start!");
 
  // メイン処理用 初期化 ------------------------------------------------------
  ledcSetup(0, 5000, 8);            // channel 0 周波数 5000 Hz, 8 bit 
  ledcAttachPin(2,0);               // attach pin 2 to channel 0
  ledcWrite(0, 0);                  // initialize channel 0 to off     
 
 
}
// -----------------------------------------------------------------------------
 
 
// -----------------------------------------------------------------------------
//  arduino メインループ処理                                                  --
// -----------------------------------------------------------------------------
void loop() {
  WiFiClient client = server.available();
  String htmlwk = "" ;                            // html 用 ワーク変数
  String line   = "" ;                            // クライアントからの入力用
  int xhr = 0 ;                                   // xhr 要求フラグ
  unsigned long start_time = 0 ;                  // タイムアウト監視開始時間
  unsigned long wait_time  = 0 ;                  // 開始/前回受信からの経過時間
  struct tm timeInfo;                             // 時刻を格納するオブジェクト
  
  // HTML クライアント処理 ------------------------------------------------------
  if (client) {
    start_time = millis() ;                       // タイムアウト監視開始時間を更新
    getLocalTime(&timeInfo, 10);                  // 時刻を取得
    Serial.printf(" %04d/%02d/%02d %02d:%02d:%02d\n", // タイムスタンプ表示
                  timeInfo.tm_year + 1900,        //      年 (西暦)
                  timeInfo.tm_mon + 1,            //      月
                  timeInfo.tm_mday,               //      日
                  timeInfo.tm_hour,               //      時
                  timeInfo.tm_min,                //      分
                  timeInfo.tm_sec  )  ;           //      秒
    Serial.println(" +++++++++++++++++ new client! +++++++++++++++++ ");
    while (client.connected()) {                  // クライアントから接続されたとき
      if (client.available()) {                   //
        start_time = millis() ;                   // タイムアウト監視開始時間を更新
        line = client.readStringUntil('\n');      // 1行分取得
        # ifdef DEBUG_HTML                        //
          Serial.println(line) ;                  //
        # endif                                   //
        if (line.indexOf("GET /?") != -1) {       // GET 処理
          if (config_mode)                        // 初期設定用 フォームデータ処理
            set_form2param(line) ;                //   フォームデータを変数に格納
          else {                                  // メイン用 処理
            xhr = proc_main(line) ;               //   フォームデータのメイン処理
          }                                       //
        }                                         //
        // 最終行(空行)を受信した時 -------------------------------------------
        if (line.length() == 1 && line[0] == '\r'){  // 最終行判定
          if (stamode != sta_exec) {         // sta_mode が変わった場合
            if (strcmp(wifi_mode,"ap") == 0){    //  リブートメッセージを設定
              foot_msg = "アクセスポイントモードに移行します。<br> " ;
              foot_msg += "この画面を閉じてアクセスポイントに接続して下さい。" ;
            }else{
              foot_msg = "ステーションモードに移行します。<br>"; 
              foot_msg += "この画面を閉じて 設定したアドレスに接続して下さい。" ;
            }
            if (config_exec) {                    //   初期設定中なら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
            } else {                              //   メイン画面なら
              send_MAIN_html(client) ;            //     メイン HTML 送信
            }                                     //    ( その後 リブート)
          } else {                                // sta_mode が変わらない場合 
            if (config_mode) {                    //   次に初期設定を表示するなら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
              config_exec = true ;                //
            } else {                              //   次にメイン画面なら
              if (xhr) {                          //     XHR 応答の場合
                send_resp_html(client) ;          //       XHR 応答 送信
              } else {                            //     HTML 送信なら
                send_MAIN_html(client) ;          //     メイン HTML 送信
                config_exec = false ;             //
              }                                   //
            }                                     // 
          }                                       //
          # ifdef DEBUG_HTML                      //
            Serial.println("Send HTML") ;         //
          # endif                                 //
          break ;                                 // ループ終了 
        }                                         //
        //-------------------------------------------------------------------
      } else {                                    // 文字列受信していない時
        wait_time = millis() - start_time ;       //
        if ((wait_time) > CLIENT_TIMEOUT) {       // 文字列受信 タイムアウト
          Serial.print("タイムアウト :") ;        //
          Serial.print(wait_time) ;               //
          Serial.println(" mS") ;                 //
          break ;                                 // ループ終了
        }                                         //
      }
    }
    // 接続が切れた場合 ------------------------------------------------------
    client.stop();
    Serial.println("client disonnected");
    Serial.println("----------------------------------------------------");
  }
 
  if (stamode != sta_exec) {                      // sta_mode が変わった場合
    Serial.println("------------------- リブートします------------------- ");
    delay(500);                                   // 
    ESP.restart() ;                               // リブート
  } else {
  }
  // --------------------------------------------------------------------------
 
}

(行#4)
getLocalTime を使用する為に必要な include

(行#32)
日本時間(GMTとの差) の定義。

(行#72~74)
STAモードにする場合、NTPサーバー設定を行う。

(行#103)
時刻を格納するオブジェクトの定義。

(行#108)
時刻を取得。

(行#109~115)
時刻(タイムスタンプ)の表示。
Serial 表示に printf が使用できる模様。

2020年5月2日土曜日

ESP32/arduino:クライアントのタイムアウト

目的:

WEBブラウザからの文字列受信にタイムアウト処理を追加する。

問題点:

「SPIFFSのテキストファイルをダウンロードする 」等 WEBサーバー動作で、クライアント接続後 に client.connected() のまま何も文字列を受信しなかった場合に while 文のループを抜けなくなってしまう。
 文字列受信開始後に 最終行(空行) を受信せずに client.available() == 0 となった場合も同様。( clientclient.connected() のまま で )

対策:

  一定時間 client.available() == 0 の状態が続いた場合、タイムアウトとして、ループを抜けて client.stop() を行う。タイムアウト時間は状況に応じて設定。

start_time = millis() ;         // 開始前の時刻を取得
while (client.connected()) {   
  if (client.available()) {

    start_time = millis() ;     // 文字列受信時の時刻に更新
      |
  } else {
    if ( (millis() - start_time) > 3000 ) {
      break ;                   // 3000 mS 受信がなければループを抜ける
    }
  }


例:

「SPIFFSのテキストファイルをダウンロードする 」の メインスケッチにタイムアウト処理を追加した例を示す。
(メインスケッチ以外のファイルは 「SPIFFSのテキストファイルをダウンロードする 」を参照 )


#include <WiFi.h>                         // WiFi 使用の為
#include "FS.h"                           // SPIFFS 使用の為
#include "SPIFFS.h"                       // SPIFFS 使用の為
 
#define DEBUG_HTML                        // Debug用 Serial 表示制御用
#define DEBUG                             // Debug用 Serial 表示制御用
 
// 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
 
// 動作モードフラグ ---------------------------------------------------
bool stamode = true ;                     // WiFi STA 接続モード : true
bool sta_exec = false ;                   // WiFi STA接続モード実行中フラグ
bool config_mode  = false ;               // 初期設定モード ; true
bool config_exec = false ;                // 初期設定実行中フラグ
 
// 設定ファイル用 -----------------------------------------------------
String s_config ;                         // 設定ファイル用 String
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
String foot_msg ="" ;                     // リブートメッセージ用

#define CLIENT_TIMEOUT 3000               // クライアントタイムアウト (mS)
WiFiServer server(80);
 
String html_CONF ;                        // 設定用 HTML
String html_MAIN ;                        // メイン HTML
String html_resp ;
 
// LED 調光処理用 ----------------------------------------------------------------
// -- for ledc
byte brightness = 0 ;                     // LED 明るさ設定 (0-255)
 
// -----------------------------------------------------------------------------
//  arduino 初期化処理                                                        --
// -----------------------------------------------------------------------------
void setup() {
  int res = 0 ;                                // 結果格納用 (ワーク)
 
  Serial.begin(115200);                        // シリアル 開始
  SPIFFS.begin(true) ;                         // SPIFFS 開始
 
  // 初期設定用 HTML ファイルの読み込み ---------------------------------------
  res = rd_SPIFFS("/WiFi_Config.html",html_CONF) ;  // html_CONF に Config.html を格納
 
  // 本体 HTML ファイルの読み込み ----------------------------------------------
  res = rd_SPIFFS("/main.html",html_MAIN) ;         // html_MAIN に main.html を格納
   
  // レスポンス用HTML ファイルの読み込み ----------------------------------------
  res = rd_SPIFFS("/resp_cnfld.html",html_resp) ;   // html_resp に resp_confld.html を格納
   
  // 初期設定ファイルを読み込み、グローバル変数に設定し、wifi,画面モードを決定 ---
  res = rd_config() ;                          //
 
  // 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 ;                        //
  }                                          //
 
  // 実行中のモード (設定かメインか) を設定 -------------------------------------
  if (config_mode) config_exec = true ;   // config_mode なら config_exec を '1'
  else config_exec = false ;              //
 
  Serial.println("Server start!");
 
  // メイン処理用 初期化 ------------------------------------------------------
  ledcSetup(0, 5000, 8);            // channel 0 周波数 5000 Hz, 8 bit 
  ledcAttachPin(2,0);               // attach pin 2 to channel 0
  ledcWrite(0, 0);                  // initialize channel 0 to off     
 
 
}
// -----------------------------------------------------------------------------
 
 
// -----------------------------------------------------------------------------
//  arduino メインループ処理                                                  --
// -----------------------------------------------------------------------------
void loop() {
  WiFiClient client = server.available();
  String htmlwk = "" ;                            // html 用 ワーク変数
  String line   = "" ;                            // クライアントからの入力用
  int xhr = 0 ;                                   // xhr 要求フラグ
  unsigned long start_time = 0 ;                  // タイムアウト監視開始時間
  unsigned long wait_time = 0 ;                   // 開始/前回受信からの経過時間

  // HTML クライアント処理 ------------------------------------------------------
  if (client) {
    start_time = millis() ;                       // タイムアウト監視開始時間を更新
    Serial.println(" +++++++++++++++++ new client! +++++++++++++++++ ");
    while (client.connected()) {                  // クライアントから接続されたとき
      if (client.available()) {                   // 受信文字列がある事
        start_time = millis() ;                   // タイムアウト監視開始時間を更新
        line = client.readStringUntil('\n');      // 1行分取得
        # ifdef DEBUG_HTML                        //
          Serial.println(line) ;                  //
        # endif                                   //
        if (line.indexOf("GET /?") != -1) {       // GET 処理
          if (config_mode)                        // 初期設定用 フォームデータ処理
            set_form2param(line) ;                //   フォームデータを変数に格納
          else {                                  // メイン用 処理
            xhr = proc_main(line) ;               //   フォームデータのメイン処理
          }                                       //
        }                                         //
        // 最終行(空行)を受信した時 -------------------------------------------
        if (line.length() == 1 && line[0] == '\r'){  // 最終行判定
          if (stamode != sta_exec) {         // sta_mode が変わった場合
            if (strcmp(wifi_mode,"ap") == 0){    //  リブートメッセージを設定
              foot_msg = "アクセスポイントモードに移行します。<br> " ;
              foot_msg += "この画面を閉じてアクセスポイントに接続して下さい。" ;
            }else{
              foot_msg = "ステーションモードに移行します。<br>"; 
              foot_msg += "この画面を閉じて 設定したアドレスに接続して下さい。" ;
            }
            if (config_exec) {                    //   初期設定中なら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
            } else {                              //   メイン画面なら
              send_MAIN_html(client) ;            //     メイン HTML 送信
            }                                     //    ( その後 リブート)
          } else {                                // sta_mode が変わらない場合 
            if (config_mode) {                    //   次に初期設定を表示するなら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
              config_exec = true ;                //
            } else {                              //   次にメイン画面なら
              if (xhr) {                          //     XHR 応答の場合
                send_resp_html(client) ;          //       XHR 応答 送信
              } else {                            //     HTML 送信なら
                send_MAIN_html(client) ;          //     メイン HTML 送信
                config_exec = false ;             //
              }                                   //
            }                                     // 
          }                                       //
          # ifdef DEBUG_HTML                      //
            Serial.println("Send HTML") ;         //
          # endif                                 //
          break ;                                 // ループ終了 
        }                                         //
        //-------------------------------------------------------------------
      } else {                                    // 文字列受信していない時
        wait_time = millis() - start_time ;       //   前回受信からの時間を取得
        if ((wait_time) > CLIENT_TIMEOUT) {       //   文字列受信タイムアウト判定
          Serial.print("タイムアウト :") ;        //   
          Serial.print(wait_time) ;               //
          Serial.println(" mS") ;                 //
          break ;                                 // ループ終了
        }                                         //
      }                                           //
    }
    // 接続が切れた場合 ------------------------------------------------------
    client.stop();
    Serial.println("client disonnected");
    Serial.println("----------------------------------------------------");
  }
 
  if (stamode != sta_exec) {                      // sta_mode が変わった場合
    Serial.println("------------------- リブートします------------------- ");
    delay(500);                                   // 
    ESP.restart() ;                               // リブート
  } else {
  }
  // --------------------------------------------------------------------------
 
}

(行#31)
タイムアウト時間 の define 文

(行#96,97)
タイムアウト処理用ローカル変数

(行#101,105)
開始時、文字列受信時の 監視開始時間を取得

(行#151~158)
client.available() == 0 時の処理。
タイムアウト時間を超えていたら break ;を実行




2019年7月21日日曜日

ESP32/arduino:GPIOについて

目的:

ESP32-WROOM32 の GPIO 設定について纏める。

GPIO 一覧:

GPIO 一覧を下表に纏める。


ピン名称 No. GPIO ADC その他 主な 用途 リセット時状態 備考
入力EN PU/PD
IO0 25 GPIO0         TOUCH1 EN PU ブートストラップ
TXD0 35 GPIO1     TXD     EN PU UART に使用
IO2 24 GPIO2 ADC2 A12 HSPIWP HS2_DATA0 TOUCH2 EN PD ブートストラップ
RXD0 34 GPIO3     RXD     EN PU UART に使用
IO4 26 GPIO4 ADC2 A10 HSPIHD HS2_DATA1 TOUCH0 EN PD  
IO5 29 GPIO5     SS     EN PU ブートストラップ
SCK/CLK 20 GPIO6     SCK     EN PU 使用不可
SDO/SD0 21 GPIO7     SDO     EN PU 使用不可
SDI/SD1 22 GPIO8     SDI     EN PU 使用不可
SHD/SD2 17 GPIO9     SHD     EN PU 使用不可
SWP/SD3 18 GPIO10     SWP     EN PU 使用不可
SCS/CMD 19 GPIO11     SCS     EN PU 使用不可
IO12 14 GPIO12 ADC2 A15 HSPIQ HS2_DATA2 TOUCH5 EN PD ブートストラップ
IO13 16 GPIO13 ADC2 A14 HSPID HS2_DATA3 TOUCH4 EN    
IO14 13 GPIO14 ADC2 A16 HSPICLK HS2_SCK TOUCH6 EN    
IO15 23 GPIO15 ADC2 A13 HSPICS0 HS2_CMD TOUCH3 EN PU ブートストラップ
IO16 27 GPIO16           EN    
IO17 28 GPIO17           EN    
IO18 30 GPIO18     VSPICLK     EN    
IO19 31 GPIO19     VSPIQ     EN    
IO21 33 GPIO21     VSPIHD SDA   EN    
IO22 36 GPIO22     VSPIWP SCL   EN    
IO23 37 GPIO23     VSPID     EN    
IO25 10 GPIO25 ADC2 A18 DAC1     DIS    
IO26 11 GPIO26 ADC2 A19 DAC2     DIS    
IO27 12 GPIO27 ADC2 A17     TOUCH7 EN    
IO32 8 GPIO32 ADC1 A4     TOUCH9 DIS    
IO33 9 GPIO33 ADC1 A5     TOUCH8 DIS    
IO34 6 GPIO34 ADC1 A6       DIS   入力のみ, PU/PD 無し
IO35 7 GPIO35 ADC1 A7       DIS   入力のみ, PU/PD 無し
SENSOR_VP 4 GPIO36 ADC1 A0 SVP     DIS   入力のみ, PU/PD 無し
SENSOR_VN 5 GPIO39 ADC1 A3 SVN     DIS   入力のみ, PU/PD 無し

*No. は ESP32-WROOM32 の ピンNo.
*GPIO6~11 は使用不可 (ESP32-WROOM-32内で SPIフラッシュメモリに使用)
*GPIO 0,2,5,12,15 は 起動モード設定に使用される為、外部での プルアップ、プルダウン時は注意する必要がある。
*GPIO 34~39 は 入力専用で、内部プルアップ/プルダウンは無い。


GPIO設定方法:

ピンモード設定

pinMode( GPIO No. ,  モード ) ;

モード : OUTPUT      出力に設定
      INPUT        入力に設定
      INPUT_PULLUP   入力 (内臓プルアップ付き) に設定

例 :
GPIO13 を プルアップ付き 入力 に設定
pinMode (13, INPUT_PULLUP) ;
GPIO27 を出力に設定し、'High' を出力
pinMode (27,OUTPUT) ;
digitalWrite (27, HIGH) ;

内臓プルアップ/プルダウン 設定

プルアップ設定は pinMode の INPUT_PULLUP 設定でできるが、プルダウンはできない。
プルダウン設定は、以下の方法で設定可能。

gpio_set_pull_mode( GPIO_NUM , PULL_MODE) ;

GPIO_NUM :  GPIO_NUM_0 ~ GPIO_NUM_39
PULL_MODE : GPIO_PULLUP_ONLY      プルアップ
       GPIO_PULLDOWN_ONLY    プルダウン
       GPIO_PULLUP_PULLDOWN   プルアップ、プルダウン 両方
       GPIO_FLOATING        プルアップ/プルダウン 無し
プルアップ/プルダウン の抵抗値は  20KΩ~50KΩ。

例 :
GPIO13 を プルダウン付き 入力 に設定
pinMode (13, INPUT) ;
gpio_set_pull_mode( GPIO_NUM_13, GPIO_PULLDOWN_ONLY) ;


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 のみ点滅する様に変更。