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

2019年7月3日水曜日

ESP32/arduino : WiFi 接続不具合(Arduino core for the ESP32 Ver 1.0.2)

目的:

WEBからの XHR アクセス を繰り返していると ESP32 へ接続ができなくなることがある為、原因/対策を探る。

現象:

WEB から スライダーで値の送信 や、ESP32 の状態のポーリング を XHR を使用して行っていると、途中で ESP32 への接続ができなくなる。
 ( 例えば、 LED調光_WEBからと外付けのボリュームからを切り替えて調光 )  


原因:

原因は不明。

[2019/11/28 追記]
Arduino core for the ESP32 の Ver 1.0.2 の問題と推定。
Ver 1.0.3 では 問題は発生しない模様。

要因等:

短時間の確認しかできていないが、XHR で複数の要求が発生した場合に 接続できなくなる模様。XHR の発行を 1つだけに制限すると 現象が発生しない 頻度が下がる。

また、Arduino core for the ESP32 の Ver 1.0.2 で発生し、Ver 1.0.0, 1.01 では 継続して動作できる。
Arduino core for the ESP32 Ver 1.0.2 の問題と推定。

対応方法:


 (1) Ver 1.0.0 又は 1.01  1.03 以降で コンパイルする。
   但し、自分の環境では、1.0.1 は softAP での接続が できない。
 (2) XHR の発行を 1つに制限。(但し、頻度が下がるのみ)
    "LED調光_WEBからと外付けのボリュームからを切り替えて調光" の HTML での 修正例を以下に示す。

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
       #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:12pt;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>

<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)'>local<br>
      <input class='radio' type='radio' name='remote' id="rad_rm" value='remote' $checked_rm onclick='disp_ctrl(this.value); submit(this.value)'>remote<br>
    </form>
  </div>
  <div id="disp_box">
      <span> brightness value</span><br>
      <div id="brightness"><output id="o1">$led_brightness</output></div>
  </div>
  <div id="val_box">
    <form method="get">
      <span style="font-size:10pt;"> 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 ;
var sendbusy = 0 ;

function setval(ledval){
  if (sendbusy == 0) {
    sendbusy = 1 ;
    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').innerHTML = xhr.response.getElementById("output1").innerHTML;
        setTimeout( function() { sendbusy = 0 ; },100) ;
      }
    }

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

    ntimeout = function(e) {
      xhrget.abort() ;
      setTimeout( function() { sendbusy = 0 ; },100) ;
    }

    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>

XHR 送信中のフラグを用意し、送信中は新たな送信を行わない様にする。
XHR は スライダーでの送信と ボリューム値のポーリングの 2種を行っているが、フラグは共通で、常に 1つしか送信しない様にしている。
応答受信後、少しまってから フラグを解除する必要がある。(この例の待ち時間 100mS は暫定値。どの程度待てばよいかは未確認)




2019年6月23日日曜日

ESP32/arduino:テキストファイルをアップロードする

目的:

ESP32 へ 設定ファイル等のテキストファイルをアップロードできる様にする。
この時、ボタンではなく、リンク (a要素) の文字列をでダウンロードできる様にする。

方法:


  1. input タグ で type='file' を指定し、ファイル選択したら ファイル読み込みのjavascript を起動。
    この input は hidden 属性にして ボタンを表示させない。
  2. a要素でリンク文字列を設置し、押下されたら jabascript で 1の input を クリックする様にする。
  3. ファイル読み込みの javascript では、ファイルをテキストで読み込み、XMLHttpRequest を使って POST でファイル内容を送る。
    または、html 内で読み込んだファイルを処理する。
  4. ESP32側では、XMLHttpRequest で送信されたテキストを処理する。
XMLHttpRequest を POST で送信する場合は、xhr.send() の ()内に送信内容を記述する。
具体的な記述は、下記の例 を参照。

例:

(ファイル一式(.zip) は こちら)
softAPでWiFi設定 の 設定ファイル をアップロードできる様に修正。本例では、設定ファイルの内容を ESP32 に送信するが、ESP32 側では受信してシリアル出力のみ行う。
設定ファイルの内容で、WiFi設定画面の各要素の値の更新を行い、設定ボタンの押下で処理を行う。
<各画面サンプル>
 LED調光の画面 (メイン処理)

ダウンロードのリンクは設定画面に移動
WiFi設定画面

設定ボタンの下にリブート用チェックボックスを追加
その下に、ダウンロード、アップロードのリンクを追加

<html>


<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
    #base       {font-size:16pt; text-align:center; width:400px; border:solid  4px #8fbc8f; background-color:#f0f0f0; }
    #area_1     {font-size:12pt; text-align:center; width:100%;  border:dotted 0px #0000ff; }
    #area_1-1   {font-size:12pt; text-align:center; width:40%;   float:left; margin:0px 0px 0px 10%  ; border:dotted 0px #008080 }
    #area_1-2   {font-size: 8pt; text-align:center; width:40%;   float:left; margin:0px 10px 0px 0%  ; border:dotted 0px #800080 }
    #area_2     {font-size:12pt; text-align:center; width:100%;  clear:both; border:dotted 0px #00ff00; }
    #area_2-1   {font-size:12pt; text-align:left  ; width:25%;   float:left; margin:0px 0px 0px 15%  ; border:dotted 0px #008080 }
    #area_2-2   {font-size: 8pt; text-align:left  ; height:12pt; float:left; margin:10px 0px 0px 5%  ; border:dotted 0px #800080 }
    #foot       {font-size:14pt; clear:both; border:dotted 0px #808000 }
</style>

<title>Sample form</title></head>

<body>
<div id="base">
  <span> LED ON/OFF </span>
  <div id="area_1">
    <br>
    <form method="get">
      <div id="area_1-1">
      <input type='submit' name='on' value='LED_ON' />
      </div>
      <div id="area_1-2">
      <input type='submit' name='off' value='LED_OFF' />
      </div>
    </form>
    <br>
    <br>
    <br>
  </div>
  <div id="area_2">
    <form method="get">
      <div id="area_2-1">
        <input class='button' type='submit' name='config' id='config' value='WiFi 設定画面'>
      </div>
      <div id="area_2-2">
      </div>
     </form>
  </div>
 <div id="foot">
   <span>$footer</span>
 </div>
</div>

</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>

<ソース (.ino)>


#include <WiFi.h>                                 // WiFi 使用の為
#include "FS.h"                                   // SPIFFS 使用の為
#include "SPIFFS.h"                               // SPIFFS 使用の為
                                                  //
#define DEBUG_HTML                              // 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
                                                  //
// wifi_設定処理用 -------------------------------//
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
int  set_reboot = 0 ;                             // 設定時リブート
char wifi_mode[16] = ""   ;                       // wifi mode
String foot_msg ="" ;                             // リブートメッセージ用
bool  rebootreq = false ;                         // リブート要求フラグ
bool  wifi_confdl = false ;                       // wifi 設定ダウンロード
bool  wifi_conful = false ;                       // wifi 設定アップロード
                                                  //
WiFiServer server(80);                            //
                                                  //
// 動作モードフラグ ------------------------------//
#define WIFI_CONF   0                             // HTML選択用 定義
#define MAIN        1                             //
                                                  //
bool stamode = true ;                             // WiFi STAモード : true
bool sta_exec = false ;                           // WiFi STAモード実行中フラグ
int  html_mode = MAIN ;                           // HTML選択 : MAIN
int  html_exec = MAIN ;                           // 実行中 HTML : MAIN
                                                  //
// -----------------------------------------------------------------------------
// | HTML ファイル名等 定義                                                   |
// -----------------------------------------------------------------------------
const char fname_wfCONF[] = "/wifi_conf.html" ;   // WiFi 設定用 HTMLファイル名
const char fname_wfRESP[] = "/wifi_resp.html" ;   // WiFi XHR応答用 HTMLファイル名
const char fname_MAIN[]   = "/main.html"   ;      // メイン HTML ファイル名
                                                  //
String html_wfCONF ;                              // WIFI設定用 HTML
String html_wfRESP ;                              // XHS レスポンス用 HTML
String html_MAIN ;                                // メイン HTML
                                                  //
// -----------------------------------------------------------------------------
                                                  //
// -----------------------------------------------------------------------------
//  MAIN 処理 定義                                                            --
// -----------------------------------------------------------------------------
// LED 調光処理用 --------------------------------//
byte brightness = 0 ;                             // LED 明るさ設定 (0-255)
                                                  //
// -----------------------------------------------------------------------------
                                                  //
// -----------------------------------------------------------------------------
//  arduino 初期化処理                                                        --
// -----------------------------------------------------------------------------
void setup() {                                    //
  int res = 0 ;                                   // 結果格納用 (ワーク)
                                                  //
  Serial.begin(115200);                           // シリアル 開始
  SPIFFS.begin(true) ;                            // SPIFFS 開始
                                                  //
  //HTML ファイルの読み込み ----------------------// HTML を変数に格納
  res = rd_SPIFFS(fname_wfCONF,html_wfCONF);      //  wifi設定用HTML
  res = rd_SPIFFS(fname_wfRESP,html_wfRESP) ;     //  wifi XHR応答用のHTML
  res = rd_SPIFFS(fname_MAIN,html_MAIN) ;         //  メイン処理のHTML
                                                  //
  // WiFi設定を読み込み、変数に設定しモードを決定 //
  res = rd_config() ;                             //
                                                  //
  // wifiモード(stamode) に応じてサーバ起動 ------//
  if (stamode == false) {                         // APモード時  softAP WiFi 開始
    start_AP_server() ;                           //   APモードでサーバー起動
    sta_exec = false ;                            //
  } else {                                        // STAモード時  Wifi 開始
    start_STA_server() ;                          //   STAモードでサーバー起動
    sta_exec = true ;                             //
  }                                               //
                                                  //
  // 実行中のモード (設定かメインか) を設定 ------//
  html_exec = html_mode ;                         // 実行中の html_mode を 設定
                                                  //
  Serial.println("Server start!");                //
                                                  //
  // メイン処理用 初期化 -----------------------------------------------------
  ledcSetup(0, 5000, 8);                          // channel:0,周波数:5000Hz, 8bit 
  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 line   = "" ;                            // クライアントからの入力用
  int xhr = 0 ;                                   // xhr 要求フラグ
  bool post_req = false ;                         // POST 要求フラグ
  char buf[257] ;                                 // 受信バッファ
  int  n ;                                        // 受信数用
                                                  //
  // HTML クライアント処理 -----------------------//
  if (client) {                                   //
    Serial.println(" +++++++++++++++++ new client! +++++++++++++++++ ");
    while (client.connected()) {                  // Client から接続されたとき
      if (client.available()) {                   // Client からのデータが有る間
        line = client.readStringUntil('\n');      // 1行分フォームデータを取得
        # ifdef DEBUG_HTML                        //
          Serial.println(line) ;                  //
        # endif                                   //
        if (line.indexOf("GET /?") != -1) {       // GET 処理
          if (html_mode == WIFI_CONF){            // 初期設定用 フォームデータ処理
            xhr = set_form2param(line) ;          //   フォームデータを変数に格納
          } else {                                // メイン用 処理
            xhr = proc_main(line) ;               //   フォームデータのメイン処理
          }                                       //
        }                                         //
        if (line.indexOf("POST /") != -1) {       // POST 要求の場合
          post_req = true ;                       //   POSTを受信したことを記憶
          if (line.indexOf("?conful") != -1) {    //     upload の場合  
            wifi_conful = true ;                  //       アップロードであることを記憶
          }                                       //
        }                                         //
        // 最終行(空行)を受信した時 --------------//
        if (line.length() == 1 && line[0] == '\r'){ // 空行の場合
          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 (html_mode == WIFI_CONF){          // wifi設定用 フォームデータ処理
              if (wifi_conful == true) {          //   アップロードの場合
                Serial.println(" アップロード :") ; //   内容をシリアルに表示
                Serial.print(line) ;              //
                Serial.println("") ;              //
                wifi_conful = false ;             //   アップロード処理終了
              } else {                            //
                xhr = set_form2param(line) ;      //   フォームデータを変数に格納
              }                                   //
            } else {                              // メイン用 処理
              xhr = proc_main(line) ;           // フォームデータ(POST)のメイン処理
            }                                     //
          }                                       //
          // -- 最終処理 -------------------------//
   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) {         //   初期設定中なら
              send_wfCONF_html(client) ;          //     初期設定 HTML 送信
     } else {                              //   メイン画面なら
              send_MAIN_html(client) ;            //     メイン HTML 送信
       html_exec = MAIN ;                  //
     }                                     //  
            rebootreq = true ;                    //   リブート要求セット
          } else {                                // sta_mode が変わらない場合 
            if (xhr) {                            //   XHR の場合 (WiFi設定のみ)
              send_wfRESP_html(client) ;          //     WiFi XHR応答HTML 送信
            } else if (html_mode == WIFI_CONF) {  //   次に初期設定を表示するなら
              send_wfCONF_html(client) ;          //     WiFi設定 HTML 送信
            } else {                              //    メイン設定画面A
              send_MAIN_html(client) ;            //     メイン HTML 送信
            }                                     //
     html_exec = html_mode ;               //   実行中の HTMLモードを更新
   }                                       //
   # ifdef DEBUG_HTML                      //
     Serial.print("Send HTML : ") ;        //
            Serial.println(html_mode) ;           //
   # endif                                 //
          break ;                                 // ループ終了 
        }                                         //
 //-------------------------------------------------------------------
      }                                           //
    }                                             //
    // 接続が切れた場合 ------------------------------------------------------
    client.stop();                                //
    Serial.println("client disonnected");         //
    Serial.println("----------------------------------------------------");
  }                                               //
                                                  //
  if (rebootreq) {                                // リブート要求時
    Serial.println("------------------- リブートします------------------- ");
    delay(500);                                   // 
    ESP.restart() ;                               // リブート
  } else {                                        //
  }                                               //
  // --------------------------------------------------------------------------
                                                  //
}                                                 //
                                                  //

// *****************************************************************************
// * 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
                                               //
// ****************************************************************************
// * ステーションモードで サーバーを起動                                      * 
// ****************************************************************************
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 送信処理                                                             *
// *****************************************************************************
                                               //
// ----------------------------------------------------------------------------
// - MAIN_HTML 送信                                                           -
// ----------------------------------------------------------------------------
void send_MAIN_html(WiFiClient client) {       //
  String htmlwk ;                              //
                                               //
  htmlwk = html_MAIN ;                         // htmlwk に HTML をコピー
  htmlwk.replace("$footer",foot_msg ) ;        // 変数を値に置き換える
                                               //
  send_html(client,htmlwk) ;                   // HTML 送信処理
                                               //
}                                              //
                                               //
// ---------------------------------------------------------------------------
// - 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) ;                       //
                                               //
    # ifdef DEBUG                              //
      Serial.println( " --- send html --- ");  //
    #endif                                     //
    # ifdef DEBUG_HTML                         //
    //  Serial.print(html) ;                   //
    #endif                                     //
}                                              //
                                               //
// *****************************************************************************
// * メイン処理                                                                *
// *****************************************************************************
// クライアント要求処理 --------------------------------------------------------
int  proc_main(String &line) {                 //
  String s_work ="" ;                          //
  int xhr = 0 ;                                //
                                               //
  if (line.indexOf("config=") != -1) {         // config
    html_mode=WIFI_CONF;                       //   HTML を WiFi設定 にする
  }                                            //
  if (line.indexOf("on=LED_ON") != -1) {       // "on"が押下された時 
    brightness +=25 ;                          // 明るさを +25 
    // -- for ledc                             //
    ledcWrite(0, brightness) ;                 // チャネル#0 に Duty値設定
  }                                            //
  if (line.indexOf("off=LED_OFF") != -1) {     // "off"が押下された時
    brightness = 0 ;                           // 明るさを "0" 
    // -- for ledc                             //
    ledcWrite(0, brightness) ;                 // チャネル#0 に Duty値設定
  }                                            //
                                               //
  return xhr ;                                 //
}                                              //
                                               //
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
//  WiFi 設定関連 定義                                                       --
// -----------------------------------------------------------------------------
// *****************************************************************************
// *  初期設定ファイル をリードし、グローバル変数に値をセットする              *
// *****************************************************************************
int rd_config() {                              //
  File fp       ;                              // 設定ファイル用ファイルポインタ
  int result = 0 ;                             // 戻り値
                                               //
  if (SPIFFS.exists("/config.txt")) {          // 設定ファイル存在確認
    // ファイルがあった場合 -------------------//
    result = rd_SPIFFS("/config.txt",s_config) ; // s_config に config.txt を格納
  } else {                                     //
    // ファイルが無かった場合 -----------------//
    Serial.println("WiFi設定ファイルなし") ;   // 無かったら、APモード
    result = -1 ;                              //
  }                                            //
                                               //
  // ファイルが読み込めたら、グローバル変数にセットする
  if (result == 0)                             //
    # ifdef DEBUG                              // デバッグ用表示
      Serial.println("--- s_config --- ") ;    //
      Serial.println(s_config ) ;              //
    # endif                                    //
    set_conf2wifiparam(s_config) ;             // s_config の内容を変数に設定
  // 初期設定ファイルの状態で 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 = stoip(data) ;                 //
  }                                            //
//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() ;                        // 数値に変換して 第1オクテット に 代入
  pos++ ;                                      // '.' の次から
  num="";                                      //
  while(data.charAt(pos) != '.') {             // '.' までの文字を取得
    num += data.charAt(pos++) ;                //
  }                                            //
  ip[1] = num.toInt() ;                        // 数値に変換して 第2オクテットに代入
  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() ;                        // 数値に変換して 第4オクテットに代入
                                               //
  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.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.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_form2param(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    ;                    //
        rebootreq = true ;                     //    リブート要求
      } 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() ;                         //  数値に変換して返す
}                                              //
                                               //
// -----------------------------------------------------------------------------
// - グローバル変数の内容を設定ファイルに書き出す                              -
// -----------------------------------------------------------------------------
void wr_wificonfig() {                         //
  char s_work[128] ;                           //
  File fp ;                                    //
                                               //
  // String に 変数の内容を書き込む -------------------------------------------
  sprintf(s_work,"wifi_ssid : %s\n",wifi_ssid) ;  //
  s_config = String(s_work) ;                     //
  sprintf(s_work,"wifi_pass : %s\n",wifi_pass) ;  //
  s_config += 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]) ;
  s_config += 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]) ;
  s_config += 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]) ;
  s_config += 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]) ;
  s_config += String(s_work) ;                    //
  sprintf(s_work,"wifi_mode : %s\n",wifi_mode) ;  //
  s_config += String(s_work) ;                    //
  sprintf(s_work,"set_reboot: %d\n",set_reboot) ; //
                                                  //
  // 設定ファイルに書き込む ----------------------------------------------------
  fp = SPIFFS.open("/config.txt",FILE_WRITE) ; // 設定ファイルを開く
  if (!fp) {                                   //
    Serial.println(" 設定ファイル オープンエラー !!") ; //
  } else {                                     //
    // 初期設定ファイル書き込み -------------------------------------------------
    if(fp.print(s_config)) {                   // ファイルに書き込み
      Serial.println("\"s_config\" written") ; //   終了メッセージ
    } else {                                   //
      Serial.println("\"s_config\" write error !!") ; //   失敗メッセージ
    }                                          //
    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 送信処理
}                                              //
                                               //
// ----------------------------------------------------------------------------
// - XHR RESPONSE_HTML 送信                                                   -
// ----------------------------------------------------------------------------
void send_wfRESP_html(WiFiClient client) {     //
  String htmlwk ;                              // HTML 編集用 ワーク
                                               //
  htmlwk = html_wfRESP ;                       // htmlwk に HTML をコピー
                                               //
  // 変数を値に変換----------------------------//
  if (wifi_confdl) {                           //  ダウンロードの場合 
    htmlwk.replace("$conf_text",s_config) ;    // 変数に 設定文字列をセット
    wifi_confdl = false ;                      //
  } else  {                                    //  アップロードの場合
    htmlwk.replace("$conf_text","") ;          //   変数に文字列(ダミー)をセット
  }                                            //
  // HTML を送信 ------------------------------//
  send_html(client,htmlwk) ;                   // HTML 送信処理
                                               //
}                                              //
                                               //

  以下、覚書など。

< メイン HTML (LED調光用 HTML) >

 ダウンロード用リンク、javascript は削除。

< WiFi 設定画面用 HTML >

(行#82~85)
設定後のリブート指示用のチェックボックスを追加。
( リブートしないと設定が反映されない為 )


(行#91~95)
ダウンロード、アップロードのリンクを設定。
 クリックで javascript を実行する為、
  href='javascript:selfile();'
の様に href に javascript を設定する。

(行#96~98)
type='file' の input。
 属性を hidden にし、WEB 画面には表示しない。


(行#107~138)
ダウンロード用の javascript。
メイン HTML にあったものと同じ。

(行#140~143)
 アップロード用にリンクをクリックした場合に実行する javascript。
行#97 の input をクリックする。


(行#144 ~ 230)
行#97 の input を クリックした時に実行する javascript。
行#97 の input (ファイル選択) でのファイルオブジェクトを取得。
ファイルオブジェクトをテキストとして読み込んだら、内容を解析して HTML の各要素の値をアップロードした値で更新し、XHR を使用して ファイル内容を ESP32 へ送信。
XHR 送信時、 xhr_ul.open("post", "?conful");
で、 POST コマンドを指定し、URL に ?conful を指定
 xhr_ul.send(reader.result);
で、ファイルの内容 (reader.result) を指定。
詳細は、HTML 内のコメントを参照。

<XHR 応答用 HTML>

XHR 応答時に送信するが、特に送信するものは無し。

<メインスケッチ (test_AP3.ino>

全面的に見直し/修正を行っているが、主な変更は、
  1. POST 処理対応
    POST処理については、POST受信 参照。
    (行#131~134)
    POST 受信時は フラグをセット。
    アップロード要求時は、アップロードのフラグをセット。
    (行#139~150)
    POST受信時は、メッセージボディを取得
    (行#141~164)
    POST 要求の処理。
    アップロード以外は 行#159 で GET 時と同じ処理を実行。
  2. アップロード対応
    (行#153~157)
    アップロードの場合、シリアルにデータを表示。
    今回は、ESP32 では特に処理を行わない。
    (クライアント側で 各要素の値に代入するだけ。)

<LED調光処理用スケッチ (led_ctrl.ino>

ダウンロード時の処理を削除 (WIFI設定に移動)

<WIFI 設定用スケッチ (wifi_config.ino>

 全面的に見直し/修正を行っているが、主な変更は、
  1. フォームデータ処理を GET,POST どちらでも対応できる様に修正。
    (行#209,210)
    "GET /?" を判定から削除
  2. リブート指示時の処理追加
    (行#106~109)
    設定ファイル処理にリブートを追加。
    (行#276~284)
    フォームデータでリブート設定時の処理追加。(フラグセット)
    (行#355)
    リブート設定を設定ファイルに追記。
    (行#411~414)
    HTML送信時の リブート チェックボックスの値を設定。
  3. ダウンロード処理追加 ( LED調光画面からの移動)
    (行#293~298)
    フォームデータでダウンロード指定時の処理 (フラグセット)
    (行#421~440)
    XHR 応答送信処理追加 (LED調光画面からの移動)

2019年5月19日日曜日

ESP32/arduino : POST受信

目的: 

クライアントからの POST 要求で LED ON/OFF できるようにする。

POST 受信処理:

クライアントからのメッセージは、
 リクエスト行 (GET / POST 等 )
 リクエストヘッダ
 空行
 メッセージボディ (POST のデータ等)
の順に送られ、GET要求の場合は メッセージボディが無く、POST要求ではメッセージボディに送信データが入る。
POST受信処理では、空行の後のメッセージボディを受信して解析する必要がある。

リクエスト行から空行の間は、行単位(最後が '\r\n'で終了) の為、GET要求の受信処理では行単位で処理 ( readStringUntil('\n') で 行末('\n') までを種付く) していたが、メッセージボディの最後は ('\r\n') になるとは限らない為、readStringUntil('\n')で データを取得すると、('\n') が見つからずに タイムアウト (Default 1秒) まで待たされる。この為、受信バイト数分を取得 (メッセージボディ全てを一度に取得) して処理することとした。

例:

LED ON/OFF (固定 IPアドレス) の スケッチ に POST 受信処理を追加した例を以下に示す。

 WEB画面:

GET 用の ON, OFF ボタン と
POST 用 の ON, OFF ボタン を 設置

POST_ON, POST_OFF 押下時、POST要求を発行

スケッチ:

// +==========================================================================+
// |  WiFi LED ON/OFF TEST ( GET / POST )                                     |
// +==========================================================================+
                                                 //
#include <WiFi.h>                                // WiFi 用
                                                 //
// ----------------------------------------------------------------------------
// 各種設定                                                                  --
// ----------------------------------------------------------------------------
const char* ssid = "hogehoge" ;                  // WiFi AP  SSID
const char* password = "hogehogepassy";          // WiFi AP  パスワード
                                                 //
String html;                                     // HTML 格納用
                                                 //
IPAddress ip(192, 168, 1, 32);                   // ESP32 IPアドレス
IPAddress gateway(192,168, 1, 1);                //       ゲートウェイアドレス
IPAddress subnet(255, 255, 255, 0);              //       サブネットマスク
IPAddress DNS(192, 168, 1, 90);                  //       DNS アドレス
                                                 //
WiFiServer server(80);                           //
                                                 //
// ----------------------------------------------------------------------------
// 初期設定                                                                  --
// ----------------------------------------------------------------------------
void setup()                                     //
{                                                //
    Serial.begin(115200);                        // シリアル出力開始
    pinMode(2, OUTPUT);                          // LED 用 PIN 設定
                                                 //
    WiFi.config(ip, gateway, subnet, DNS);       // ESP32 WiFi 設定
    delay(10);                                   //
                                                 //
    Serial.println();                            // デバッグ用出力
    Serial.print("Connecting to ");              //
    Serial.println(ssid);                        //
                                                 //
    WiFi.begin(ssid, password);                  // WiFi 開始
                                                 //
    while (WiFi.status() != WL_CONNECTED) {      //
        delay(500);                              //
        Serial.print(".");                       //
    }                                            //
                                                 //
    Serial.println("");                          // デバッグ用出力
    Serial.println("WiFi connected.");           //
    Serial.println("IP address: ");              //
    Serial.println(WiFi.localIP());              //
                                                 //
    server.begin();                              // WiFi サーバ 開始
                                                 //
}                                                //
                                                 //
// ----------------------------------------------------------------------------
// メインループ                                                              --
// ----------------------------------------------------------------------------
void loop(){                                     //
  String line = "" ;                             // データ格納用
  char buf[257] ;                                //
  int  n ;
  bool   post_req = false ;                      // ポスト要求フラグ
  WiFiClient client = server.available();        // listen for incoming clients
                                                 //
  if (client) {                                  // if you get a client,
    Serial.println("***** Client access start *****"); // デバッグ用出力
    while (client.connected()) {                 // client が接続されている間
      if (client.available()) {                  //  client からのデータがある間
        line = client.readStringUntil('\n');     //   データを1行分取得
        Serial.println(line);                    //   デバッグ用出力
        if (line.indexOf("POST /") != -1) {      //  "POST /" 受信の場合
          post_req = true ;                      //     LED on
        }                                        //
        if (line.indexOf("GET /?on") != -1) {    //  "GET /?on" 受信の場合
          digitalWrite(2, HIGH);                 //     LED on
        }                                        //
        if (line.indexOf("GET /?off") != -1) {   // "GET /?off" 受信の場合
          digitalWrite(2, LOW);                  //     LED off
        }                                        //
        // 空行を受信した場合 ---------------    //
        if (line.length() == 1 && line[0] == '\r'){ // 空行の場合
          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 データ処理 -------------     //
            Serial.println(line);                //   デバッグ用出力
            if (line.indexOf("post_on") != -1) { //  "on" の場合
              digitalWrite(2, HIGH);             //     LED on
            }                                    //
            if (line.indexOf("post_off") != -1) { // "off" の場合
              digitalWrite(2, LOW);              //     LED off
            }                                    //
          }                                      //
          // 終了処理 ---------------------------//
          send_response(client) ;                //
          Serial.println("Send HTML !!");        //
          break;                                 // break while loop
        }                                        //
      }                                          //
    }                                            //
    delay(1);                                    // give the web browser time to receive the data
    // close the connection: --------------------//
    client.stop();                               //
    Serial.println("Client Disconnected.");      //
    Serial.println("--------------------------------------------------");
  }                                              //
}                                                //
// ------------------------------------------------------------------
void send_response(WiFiClient client) {
    // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
    // and a content-type so the client knows what's coming, then a blank line:
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type:text/html");
    client.println();
    // the content of the HTTP response follows the header:
    client.print("<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>" ) ;
    client.print("<style>input {margin:8px;width:100px;}div {font-size:16pt;text-align:center;width:250px;border:solid 4px #93ff93;}</style>" ) ;
    client.print("<title>Color LED Controller</title></head>" ) ;
    client.print("<body><div><p>LED ON/OFF</p>" ) ;
    client.print("<form method='get'>" ) ;
    client.print("<input type='submit' name='on' value='GET_ON' /><input type='submit' name='off' value='GET_OFF' />" ) ;
    client.print("</form>" ) ;
    client.print("<form method='post'>" ) ;
    client.print("<input type='submit' name='post_on' value='POST_ON' /><input type='submit' name='post_off' value='POST_OFF' />" ) ;
    client.print("</form>" ) ;
    client.print("</div></body></html>" ) ;
    client.println();
    Serial.println( " --- send responce --- ");
}

 以下、メモ

・詳細は コメント参照。
(行#58~60)
 POST処理用 変数定義。
(行#69~71)
 リクエスト行が POST の場合に POST処理フラグを立てる。
(行#80~100)
 POST 処理フラグが 立っていた場合のPOST処理。
  (行#81~91) で メッセージボディのデータを取得。
  readStringUntil('\n') や readString() を使用すると、タイムアウト(Default 1000mS) まで待ってしまい、ボタンを押下してから LED の ON/OFF まで 1秒かかる。
 この為、残りデータ数 (client.available() の戻り値) 分を client.readBytes() で取得している。
  (行#92~98) で メッセージボディのデータを処理。
 処理内容は、GET と同様。

 

2019年4月21日日曜日

ESP32/arduino:I2C 転送速度設定

目的:

I2C アクセスの転送速度設定を行い、アクセス時間を確認する

I2C 転送速度設定方法:

Wire.bigin() ; の後で、
Wire.setClock(転送速度);
で行う。
設定を行わない場合(default)の転送速度は 100Kbit/S。
例えば、400Kbit/S に設定する場合は
Wire.setClock(400000);

アクセス時間は、アクセス前後で micros() ; で時間を測定する。
実行結果は概ね、

  100Kbit/S 400Kbit/S
単発リード 597 uS 307 uS
単発ライト 400 uS 191 uS
ダンプ 553 uS 268 uS

 単発リードとダンプ(22回の平均)で 40uSぐらいの差がある。
400Kbit/S では 周波数は 4倍だが、測定結果は 2倍程度。

結構、ESP32での処理にも時間がかかっているのかな?

例:

MCP23017 レジスタアクセスのスケッチに 転送速度設定、アクセス速度計測を追加する。

WEB画面サンプル:

使用方法等

 設定ファイルが無い状態では APモードで立ち上がるため、
 SSID : ESP32_AP
   ( パスワード : password )
に接続し、ブラウザで IPアドレス 192.168.4.1 に
アクセスして、WiFi 設定を行う。

WiFi設定変更で IPアドレス等を変更した場合は、
再起動で 変更した IPアドレス等が有効となる。

転送速度は ラジオボタンで選択。
"rd", "wr", "dmp" を行うと、1アクセス当たりの
アクセス速度が表示される。

<HTML, スケッチ>

構成は、MCP23017 レジスタアクセスと同じ。

<HTML> : 3種。 rdataフォルダの下に保存し、SPIFFSにアップロードする。
  • MAIN HTML (main.html)
  • WiFi設定 HTML (wifi_conf.html)
  • XHR 応答 HTML (resp_cnfld.html)

<スケッチ> : 4種。拡張子は .ino
  • MAIN (変数定義, setup, loop 関数) 
  • WIFI設定 (主に WiFI 設定用 の関数) 
  • WIFI処理 (主に MAIN処理(レジスタアクセス)の WEB画面処理 )
  • MAIN処理 (主に I2Cアクセス の関数)
以下、変更した HTML, スケッチを記す。

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
    #base         {font-size:16pt; text-align:center; width:410px; border:solid 4px #93ff93; background-color:#f0f0f0; }
    #set_box1     {height:19pt;font-size:10pt; text-align:left; }
    #set_box2     {height:15pt;font-size:10pt; text-align:left; }
    #text_box     {font-size:10pt; margin : 3px 0px 0px 0px ;float:left ; text-align:left; width:150px;}
    #regname      {font-size:10pt; margin : 0px 0px 0px 0px ;float:left ; text-align:left; width:80px;border:solid 1px #000000;}
    #regadr       {font-size:10pt; margin : 0px 0px 0px 0px ;float:left ; text-align:left; width:40px;border:solid 1px #000000;}
    #regwdt       {font-size:10pt; margin : 0px 0px 0px 0px ;float:left ; text-align:left; width:100px;}
    #regrdt       {font-size:10pt; margin : 0px 3px 0px 0px ;float:left ; text-align:left; width:75px;border:solid 1px #000000;}
    #button       {font-size:12pt; margin : 10px 0px 10px 0px ;clear:both; }
    #btn_conf     {font-size:12pt; margin : 10px 0px 10px 0px ;text-align:left  ; width:83%; }
    #text_in      {height: 12pt ; margin:14px 0px 0px 5px ; font-size:8pt; float:right; }
    #radio_box    {font-size:10pt; float:left ;text-align:left; width:120px; margin: 0px 0px  0px 0px ; }
    input         {margin:0px 8px 0px 8px ;width:50px;}
    input.button  {margin:0px 8px 0px 8px ;width:100px;}
    input.btncnf  {margin:0px 0px 0px 28px ;width:100px;}
    input.radio   {margin:0px 8px 0px 0px ;width:10px;}
    output        {margin:0px 8px 0px 8px ;}
</style>

<title>MCP23017 Access</title></head>

<body>
<div id="base">
  <p>MCP23017 レジスタ アクセス</p>
  <form method='get'>
    <div id=set_box1>
      <div id=text_box>スレーブアドレス</div>
      <div id=regwdt> <span>0x</span><input type='text' name='sadr' value=$s_adr > </div>
    </div>
    <div id=set_box1>
      <div id=text_box>転送速度</div>
      <div id="radio_box">
        <input class="radio" type="radio" name="spd" id="std" value="std" $checked_std onclick="submit(this.value)">100Kbit/s  
      </div>
      <div id="radio_box">
        <input class="radio" type="radio" name="spd" id="fst" value="fst" $checked_fst onclick="submit(this.value)">400Kbit/s  
      </div>
    </div>
  </form>
  <form method='get'>
    <div id=set_box1>
      <div id=text_box>レジスタアドレス</div>
      <div id=regwdt> <span>0x</span><input  type='text' name='regadr' value=$reg_adr > </div>
    </div>
    <div id=set_box1>
      <div id=text_box>ライトデータ</div>
      <div id=regwdt> <span>0x</span><input  type='text' name='wrdt' value=$wr_dt > </div>
    </div>
    <div id=set_box1>
      <div id=text_box>リードデータ</div>
      <div id=regwdt> <span>0x</span><output  name='rddt' >$rd_dt</output>  </div>
    </div>
    <div id=set_box1>
      <div id=text_box>アクセス速度</div>
      <div id=regwdt> <output  name='acs_spd' >$acs_spd</output><span> uS</span>  </div>
    </div>
    <div id=button>
      <input class=button type='submit' name='read' value='rd' >
      <input class=button type='submit' name='write' value='wr' >
    </div>
  </form>
      <span>レジスタダンプ</span>
    <div id=set_box2>
      <div id=regname>レジスタ名</div>
      <div id=regadr>adr</div>
      <div id=regrdt>データ</div>
      <div id=regname>レジスタ名</div>
      <div id=regadr>adr</div>
      <div id=regrdt>データ</div>
    </div>
    <div id=set_box2>
      <div id=regname> IODIRA</div>
      <div id=regadr> 00</div>
      <div id=regrdt> <span>0x</span><output name='rddt00' >$rdt00</output> </div>
      <div id=regname> IODIRB</div>
      <div id=regadr> 01</div>
      <div id=regrdt> <span>0x</span><output name='rddt01' >$rdt01</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> IPOLA </div>
      <div id=regadr> 02</div>
      <div id=regrdt> <span>0x</span><output name='rddt02' >$rdt02</output> </div>
      <div id=regname> IOOLB</div>
      <div id=regadr> 03</div>
      <div id=regrdt> <span>0x</span><output name='rddt03' >$rdt03</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> GPINTENA </div>
      <div id=regadr> 04</div>
      <div id=regrdt> <span>0x</span><output name='rddt04' >$rdt04</output> </div>
      <div id=regname> GPINTENB</div>
      <div id=regadr> 05</div>
      <div id=regrdt> <span>0x</span><output name='rddt05' >$rdt05</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> DEFVALA </div>
      <div id=regadr> 06</div>
      <div id=regrdt> <span>0x</span><output name='rddt06' >$rdt06</output> </div>
      <div id=regname> DEFVALB</div>
      <div id=regadr> 07</div>
      <div id=regrdt> <span>0x</span><output name='rddt07' >$rdt07</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> INTCONA </div>
      <div id=regadr> 08</div>
      <div id=regrdt> <span>0x</span><output name='rddt08' >$rdt08</output> </div>
      <div id=regname> INTCONB</div>
      <div id=regadr> 09</div>
      <div id=regrdt> <span>0x</span><output name='rddt09' >$rdt09</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> IOCON   </div>
      <div id=regadr> 0a</div>
      <div id=regrdt> <span>0x</span><output name='rddt0a' >$rdt0a</output> </div>
      <div id=regname> IOCON  </div>
      <div id=regadr> 0b</div>
      <div id=regrdt> <span>0x</span><output name='rddt0b' >$rdt0b</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> GPPUA   </div>
      <div id=regadr> 0c</div>
      <div id=regrdt> <span>0x</span><output name='rddt0c' >$rdt0c</output> </div>
      <div id=regname> GPPUB  </div>
      <div id=regadr> 0d</div>
      <div id=regrdt> <span>0x</span><output name='rddt0d' >$rdt0d</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> INTFA   </div>
      <div id=regadr> 0e</div>
      <div id=regrdt> <span>0x</span><output name='rddt0e' >$rdt0e</output> </div>
      <div id=regname> INTFB  </div>
      <div id=regadr> 0f</div>
      <div id=regrdt> <span>0x</span><output name='rddt0f' >$rdt0f</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> INTCAPA </div>
      <div id=regadr> 10</div>
      <div id=regrdt> <span>0x</span><output name='rddt10' >$rdt10</output> </div>
      <div id=regname> INTCAPB </div>
      <div id=regadr> 11</div>
      <div id=regrdt> <span>0x</span><output name='rddt11' >$rdt11</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> GPIOA </div>
      <div id=regadr> 12</div>
      <div id=regrdt> <span>0x</span><output name='rddt12' >$rdt12</output> </div>
      <div id=regname> GPIOB</div>
      <div id=regadr> 13</div>
      <div id=regrdt> <span>0x</span><output name='rddt13' >$rdt13</output> </div>
    </div>
    <div id=set_box2>
      <div id=regname> OLATA </div>
      <div id=regadr> 14</div>
      <div id=regrdt> <span>0x</span><output name='rddt14' >$rdt14</output> </div>
      <div id=regname> OLATB</div>
      <div id=regadr> 15</div>
      <div id=regrdt> <span>0x</span><output name='rddt15' >$rdt15</output> </div>
    </div>
    <div id=set_box1>
      <div id=text_box>アクセス速度</div>
      <div id=regwdt> <output  name='dmp_spd' >$dmp_spd <span> uS</span></output> </div>
    </div>
    <form method='get'>
      <div id='button'>
        <input class='button' type='submit' name='dump' value='dmp' >
      </div>
    </form>
  <div id="btn_conf">
    <form method="get">
        <input class='btncnf' type='submit' name='config' id='config' value='WiFi設定画面'>
        <div id="text_in">
          <a href="#" onclick='cnfdl();'>設定をダウンロードする</a>
        </div>
     </form>
  </div>
</div>

<script>
  function cnfdl() {
    "use strict";
  
    var xhr = new XMLHttpRequest();
    xhr.open("get", "?cnfdl");
    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();
  }
</script>

</body>
</html>

WiFi設定 HTML (wifi_conf.html)  は、MCP23017 レジスタアクセスと同じ。
XHR 応答 HTML (resp_cnfld.html) は、MCP23017 レジスタアクセスと同じ。

/* +========================================================================+ */
/* | MCP23017 TEST                                                          | */
/* +========================================================================+ */
                                              //
#include <Wire.h>                             //
#include <WiFi.h>                             //
#include "FS.h"                               // SPIFFS 用
#include "SPIFFS.h"                           // SPIEES 用
                                              //
// +------------------------------------------------------------------------+
// | 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
// AP/STA 動作モードフラグ --------------------------------------------------
bool stamode = true ;                         // WiFi STA 接続モード : true
bool sta_exec = false ;                       // WiFi STA接続モード実行中フラグ
bool config_mode  = false ;                   // 初期設定モード ; true
bool config_exec = false ;                    // 初期設定実行中フラグ
// WiFi 設定ファイル用 ------------------------------------------------------
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 ="WiFi設定を変更した場合、反映するには再起動して下さい。" ;
                                              // リブートメッセージ用
bool config_dl = false ;                      // 設定ファイルダウンロード要求フラグ

WiFiServer server(80);

String html_CONF ;                            // 設定用 HTML
String html_MAIN ;                            // メイン HTML
String html_resp ;                            // XHR レスポンス用

// --------------------------------------------------------------------------

// +------------------------------------------------------------------------+
// | MCP23017 用定義                                                        |
// +------------------------------------------------------------------------+
bool wadr      = false ;                      // 2B モードフラグ
bool rd_req    = false ;                      // リード要求フラグ
bool wr_req    = false ;                      // ライト要求フラグ
bool dmp_req   = false ;                      // ダンプ要求フラグ
bool spdchg_req   = false ;                   // 速度変更要求フラグ
byte sadr      = 0x21 ;                       // slave address
byte regadr    = 0x0  ;                       // regadr
byte wrdt      = 0x00 ;                       // write data
byte rddt      = 0x00 ;                       // read date
byte dmpdt[32] = {0,0,0,0,0,0,0,0,            // dump data
                  0,0,0,0,0,0,0,0,            //          
                  0,0,0,0,0,0,0,0,            //          
                  0,0,0,0,0,0,0,0 } ;         //          
bool fstmod    = false ;                      // 転送速度 400Kモード(fst_mod)

float acstim = 0 ;                            // アクセス時間
float dmptim = 0 ;                            // ダンプ時アクセス時間
unsigned long start_tim ;                     // 開始時間
unsigned long end_tim ;                       // 終了時間
unsigned long exe_tim ;                       // 実行時間

/* +========================================================================+ */
/* | 初期設定                                                               | */
/* +========================================================================+ */
void setup()
{
  // +------------------------------------------------------------------------+
  // | Serial 出力用 SetUp                                                    |
  // +------------------------------------------------------------------------+
  Serial.begin(115200);                        // シリアル 開始
  SPIFFS.begin(true) ;                         // SPIFFS 開始
                                               //
  // +------------------------------------------------------------------------+
  // | WiFi 用 SetUp                                                          |
  // +------------------------------------------------------------------------+
  int res = 0 ;                                // 結果格納用 (ワーク)
                                               //
  // 初期設定用 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!");             //
                                               //
  // +------------------------------------------------------------------------+
  // | メイン処理用設定                                                       |
  // +------------------------------------------------------------------------+
  Wire.begin();
  Wire.setClock(100000);

  // set direction IOA,IOB
  //I2C_BWR(MCP23017,IODIRA,0xFE) ;   // set IOA0 : output
  //I2C_BWR(MCP23017,IODIRB,0x7F) ;   // set IOB7 : output
  //I2C_BWR(MCP23017,GPPUA,0x80) ;    // set IOA7 : PullUp

}


/* +========================================================================+ */
/* | メインループ                                                           | */
/* +========================================================================+ */
void loop()
{
  // +------------------------------------------------------------------------+
  // | WiFi 処理                                                              |
  // +------------------------------------------------------------------------+
  WiFiClient client = server.available();
  String htmlwk = "" ;                            // html 用 ワーク変数
  String line   = "" ;                            // クライアントからの入力用
  int xhr = 0 ;                                   // xhr 要求フラグ
 
  // HTML クライアント処理 ------------------------------------------------------
  if (client) {
    Serial.println(" +++++++++++++++++ new client! +++++++++++++++++ ");
    while (client.connected()) {                  // クライアントから接続されたとき
      if (client.available()) {                   //
        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_form(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 (rd_req) {                       //     リード要求フラグが ON なら
                start_tim = micros() ;            //     開始時刻を取得
                rddt = I2C_BRD(sadr,regadr) ;     //     リードを実行
                end_tim = micros() ;              //     終了時刻を取得
                exe_tim = (end_tim - start_tim) ; //     実行時間を取得
                acstim = (float)(exe_tim) ;        //     アクセス時間を取得
                rd_req = false ;                  //
              }                                   //
              if (wr_req) {                       //     ライト要求フラグが ON なら
                start_tim = micros() ;            //     開始時刻を取得
                I2C_BWR(sadr,regadr,wrdt) ;       //      ライトを実行
                end_tim = micros() ;              //     終了時刻を取得
                rddt = I2C_BRD(sadr,regadr) ;     //      リードを実行
                exe_tim = (end_tim - start_tim) ; //     実行時間を取得
                acstim = (float)(exe_tim) ;       //     アクセス時間を取得
                wr_req = false ;                  //
              }                                   //
              if (dmp_req) {                      //     ダンプ要求フラグが ON なら
                start_tim = micros() ;            //     開始時刻を取得
                reg_dmp() ;                       //      ダンプ動作を実行
                end_tim = micros() ;              //     終了時刻を取得
                exe_tim = (end_tim - start_tim) ; //     実行時間を取得
                dmptim = (float)(exe_tim)/22 ;    //     アクセス時間を取得
                dmp_req = false ;                 //
              }                                   //
              if (spdchg_req) {                   //     速度変更要求フラグが ON なら
                if (fstmod) {                     //       fstmod なら
                  Wire.setClock(400000);          //         400K
                } else {                          //       違えば
                  Wire.setClock(100000);          //         100K
                }                                 //
                spdchg_req = false ;              //
              }                                   //
              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 ;                                 // ループ終了 
        }                                         //
        //-------------------------------------------------------------------
      } 
    }
    // 接続が切れた場合 ------------------------------------------------------
    client.stop();
    Serial.println("client disonnected");
    Serial.println("----------------------------------------------------");
  }

  if (stamode != sta_exec) {                      // sta_mode が変わった場合
    Serial.println("------------------- リブートします------------------- ");
    delay(500);                                   // 
    ESP.restart() ;                               // リブート
  } else {
  }
  // --------------------------------------------------------------------------
}

WIFI設定 (主に WiFI 設定用 の関数)  は、MCP23017 レジスタアクセスと同じ。

// *****************************************************************************
// * 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
                                                 //
// ****************************************************************************
// * ステーションモードで サーバーを起動                                      * 
// ****************************************************************************
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 送信処理                                                             *
// *****************************************************************************

// ----------------------------------------------------------------------------
// - CONF_HTML 送信                                                           -
// ----------------------------------------------------------------------------
void send_MAIN_html(WiFiClient client) {
  String htmlwk ;

  htmlwk = html_MAIN ;                          // htmlwk に HTML をコピー
  htmlwk.replace("$s_adr",String(sadr,HEX)) ;       // 変数を値に置き換える
  htmlwk.replace("$reg_adr",String(regadr,HEX)) ;   // 変数を値に置き換える
  htmlwk.replace("$wr_dt",String(wrdt,HEX)) ;       // 変数を値に置き換える
  htmlwk.replace("$rd_dt",String(rddt,HEX)) ;       // 変数を値に置き換える
  htmlwk.replace("$rdt00",String(dmpdt[0],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt01",String(dmpdt[1],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt02",String(dmpdt[2],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt03",String(dmpdt[3],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt04",String(dmpdt[4],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt05",String(dmpdt[5],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt06",String(dmpdt[6],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt07",String(dmpdt[7],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt08",String(dmpdt[8],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt09",String(dmpdt[9],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt0a",String(dmpdt[10],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt0b",String(dmpdt[11],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt0c",String(dmpdt[12],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt0d",String(dmpdt[13],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt0e",String(dmpdt[14],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt0f",String(dmpdt[15],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt10",String(dmpdt[16],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt11",String(dmpdt[17],HEX)) ;  // 変数を値に置き換える
  htmlwk.replace("$rdt12",String(dmpdt[18],HEX)) ; // 変数を値に置き換える
  htmlwk.replace("$rdt13",String(dmpdt[19],HEX)) ; // 変数を値に置き換える
  htmlwk.replace("$rdt14",String(dmpdt[20],HEX)) ; // 変数を値に置き換える
  htmlwk.replace("$rdt15",String(dmpdt[21],HEX)) ; // 変数を値に置き換える
  htmlwk.replace("$footer",foot_msg ) ;         // 変数を値に置き換える

  if (fstmod) {                                 // fstmod(400K)の時
    htmlwk.replace("$checked_std","") ;         //
    htmlwk.replace("$checked_fst","checked") ;  //
  } else {                                      //
    htmlwk.replace("$checked_std","checked") ;  //
    htmlwk.replace("$checked_fst","") ;         //
  }                                             //
  htmlwk.replace("$acs_spd",String(acstim)) ;   // 変数を値に置き換える
  htmlwk.replace("$dmp_spd",String(dmptim)) ;   // 変数を値に置き換える
                                                //
  send_html(client,htmlwk) ;                    // HTML 送信処理

}

// ----------------------------------------------------------------------------
// - 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) ;                         //

    # ifdef DEBUG                                //
        Serial.println( " --- send html --- ");  //
    #endif                                       //
}

// ----------------------------------------------------------------------------
// - XHR RESPONSE_HTML 送信                                                   -
// ----------------------------------------------------------------------------
void send_resp_html(WiFiClient client) {        //
  String htmlwk ;                               // HTML 編集用 ワーク
                                                //
  htmlwk = html_resp ;                          // htmlwk に HTML をコピー
                                                //
  // 変数を値に変換-----------------------------//
  htmlwk.replace("$cnf_text",s_config) ;        // 変数に 設定文字列をセット
                                                //
  send_html(client,htmlwk) ;                    // HTML 送信処理
      Serial.print("xhr html  :"); Serial.println(htmlwk);
# ifdef DEBUG_WiFi                              //
      Serial.print("xhr value :"); Serial.println(duty);
#endif                                          //
}                                               //
                                                //

// *****************************************************************************
// * フォームデータ処理                                                        *
// *****************************************************************************
int  proc_form(String &line) {
  String s_work ="" ;
  int xhr = 0 ;

  if (line.indexOf("GET /?") != -1) {            // "form" が押下された時
    if (line.indexOf("config=") != -1) {         // wifi 設定なら
      config_mode = true ;                       //   設定モードにする
    }                                            // 
    if (line.indexOf("cnfdl") != -1) {           // config.txt のダウンロード時
      config_dl = true ;                         //   設定ダウンロード要求フラグ ON
      xhr = 1 ;                                  //   XHR 応答にする。
    }                                            //
    if (line.indexOf("read") != -1) {            // "read" が押下された時
      if (line.indexOf("sadr=") != -1) {         //   sadr
        sadr   = getvalue_h(line,"sadr=") ;      //     sadr に続く数値を変数にセット
      }                                          //
      if (line.indexOf("regadr=") != -1) {       //   regadr
        regadr = getvalue_h(line,"regadr=") ;    //     sadr に続く数値を変数にセット
      }                                          //
      rd_req = true ;                            //   リード要求フラグを ON
    }                                            //
    if (line.indexOf("write") != -1) {           // "write" が押下された時
      if (line.indexOf("sadr=") != -1) {         //   sadr
        sadr   = getvalue_h(line,"sadr=") ;      //     sadr に続く数値を変数にセット
      }                                          //
      if (line.indexOf("regadr=") != -1) {       //   regadr
        regadr = getvalue_h(line,"regadr=") ;    //     sadr に続く数値を変数にセット
      }                                          //
      if (line.indexOf("wrdt=") != -1) {         //   wrdt
        wrdt   = getvalue_h(line,"wrdt=") ;      //     wrdt に続く数値を変数にセット
      }                                          //
      wr_req = true ;                            //   ライト要求フラグをON
    }                                            //
    if (line.indexOf("spd") != -1) {             // "転送速度" が選択された時
      s_work = getvalue_s(line,"spd=") ;         //     spd に続く文字列を変数にセット
      if (s_work == "std") {                     //     値が "std" なら
        fstmod = false ;                         //       fstmod は false
      } else {                                   //     違えば
        fstmod = true ;                          //       fstmod は true
      }                                          //
      spdchg_req = true ;                        //     速度変更要求
    }                                            //
    if (line.indexOf("dump") != -1) {            // "dump" が押下された時
      dmp_req = true ;                           //    ダンプ動作要求フラグを ON
    }                                            //
  }                                              //
  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')) {        //
      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() ;                            //  数値に変換して返す
}

// 要素名に続く値(数値(HEX))を取得する ----------------------------------------
int getvalue_h(String &line,String param) {
  String val="" ;
  int pos = 0 ;
  byte c= 0 ;
  int dt = 0 ;

  if ((pos=line.indexOf(param)) != -1) {          // 要素名の位置を取得
    pos += param.length() ;                       //
    do {                                          //
      c = line.charAt(pos++) ;                    //
      if ((c>='0') & (c<='9')) {                  // 数値の場合
        dt = dt*16 + ( c - 0x30) ;                   //
      } else if ((c>='a') & (c<='f')) {           //
        dt = dt*16 + (c - 87) ;                      // 小文字 a~f の場合
      } else if ((c>='A') & (c<='F')) {           // 大文字 A~F の場合
        dt = dt*16 + (c - 55) ;                      //
      } else {                                    // 16進数値以外
        c = 0xff ;                                //
      }                                           //
    } while (c != 0xff) ;                         //
  }                                               //
  return dt ;                                     //  数値を返す
}

// -----------------------------------------------------------------------------


/* +=======================================================================+ */
/* | MAIN 処理                                                             | */
/* +=======================================================================+ */

// I2C WR/READ ----------------------------------------------------------------
void I2C_BWR(byte TGT, byte REGADR, byte DATA) {
  Wire.beginTransmission(TGT);         // transmit to device #TGT 
  Wire.write(REGADR);                  // send REG address (REGADR)
  Wire.write(DATA);                    // send write data (DATA)
  Wire.endTransmission();              // end transmit
}

byte I2C_BRD(byte TGT,byte REGADR) {
  byte data = 0 ;
  Wire.beginTransmission(TGT);         //  transmit to device #TGT
  Wire.write(REGADR);                  //  send REG address (REGADR)
  Wire.endTransmission();              //  end transmit
  Wire.requestFrom(TGT, 1);            // request 1 byte from slave device #TGT
  data = Wire.read();                  // receive 1 byte 
  
  return data ;
}

void reg_dmp() {
                                                 //
  dmpdt[0]  = I2C_BRD(sadr,0) ;                  // REG0 リード
  dmpdt[1]  = I2C_BRD(sadr,1) ;                  // REG0 リード
  dmpdt[2]  = I2C_BRD(sadr,2) ;                  // REG0 リード
  dmpdt[3]  = I2C_BRD(sadr,3) ;                  // REG0 リード
  dmpdt[4]  = I2C_BRD(sadr,4) ;                  // REG0 リード
  dmpdt[5]  = I2C_BRD(sadr,5) ;                  // REG0 リード
  dmpdt[6]  = I2C_BRD(sadr,6) ;                  // REG0 リード
  dmpdt[7]  = I2C_BRD(sadr,7) ;                  // REG0 リード
  dmpdt[8]  = I2C_BRD(sadr,8) ;                  // REG0 リード
  dmpdt[9]  = I2C_BRD(sadr,9) ;                  // REG0 リード
  dmpdt[10]  = I2C_BRD(sadr,10) ;                // REG0 リード
  dmpdt[11]  = I2C_BRD(sadr,11) ;                // REG0 リード
  dmpdt[12]  = I2C_BRD(sadr,12) ;                // REG0 リード
  dmpdt[13]  = I2C_BRD(sadr,13) ;                // REG0 リード
  dmpdt[14]  = I2C_BRD(sadr,14) ;                // REG0 リード
  dmpdt[15]  = I2C_BRD(sadr,15) ;                // REG0 リード
  dmpdt[16]  = I2C_BRD(sadr,16) ;                // REG0 リード
  dmpdt[17]  = I2C_BRD(sadr,17) ;                // REG0 リード
  dmpdt[18]  = I2C_BRD(sadr,18) ;                // REG0 リード 18 : 0x12
  dmpdt[19]  = I2C_BRD(sadr,19) ;                // REG0 リード 19 : 0x13
  dmpdt[20]  = I2C_BRD(sadr,20) ;                // REG0 リード 20 : 0x14
  dmpdt[21]  = I2C_BRD(sadr,21) ;                // REG0 リード 21 : 0x15

}

  以下、メモ

<HTML>
  • MAIN HTML (main.html)
    (行#32~40)
     転送速度(100Kbit/S, 400Kbit/S) の選択ラジオボタン
     どちらをチェックするかは 変数 で指定
    (行#55~58)
     単発リード/ライトでのアクセス時間表示
    (行#161~164)
     ダンプ時のアクセス時間表示
     22レジスタリードの平均値
<スケッチ>
  • MAIN (変数定義, setup, loop 関数) 
    (行#51,60~66)
     速度設定、アクセス時間測定用の変数定義
    (行#115)
     初期の速度設定(100Kbit/S, なくても可)
    (行#175,177~179)
     単発リード時のアクセス時間測定
    (行#183,185,187,188)
     単発ライト時のアクセス時間測定
    (行#192,194~196)
     ダンプ時のアクセス時間測定
     22レジスタリードの為、その平均を算出
    (行#199~206)
     転送速度設定時の処理
     転送速度に応じて設定
    (行#236~)
     不要なコメント削除
  • WIFI処理 (主に MAIN処理(レジスタアクセス)の WEB画面処理 )
    (行#85~94)
     HTML送信時の処理で速度設定、アクセス速度の変数を値に置き換える。
    (行#169~177)
     フォームデータ処理で、転送速度が設定された時の処理を追加。
    (行#192,193,194,195)
     フォームデータの要素名の後の文字列を取得する関数で、区切り文字として ' '(スペース) を追加。
  • MAIN処理 (主に I2Cアクセス の関数)
    (行#11,20)
     シリアル出力(デバッグ用)を削除。
     アクセス速度の測定に影響する為。