2019年3月3日日曜日

ESP32/arduino:スマホからIPアドレス等のWiFi設定を行う

目的:

WiFi設定(接続先設定、IPアドレス 等)をスケッチには記述せず、スマホ等から設定できる様にする。

背景:

これまで WiFI設定をスケッチの中で定義していたが、これだと、WiFi設定変更の度にコンパイルをしなけらばならない。
 この為、APモード(アクセスポイントモード) で接続してWiFi接続先設定ができる様にし、スケッチに WiFI 環境の SSID や パスワードを書き込まなくて良くしたい。

 *APモード : APモードは、ESP32が無線LAN のアクセスポイント(親機)となり、スマホ等から直接接続できるモード。この場合、ESP32 と スマホの間は独立のネットワークとなり、スマホ等からは ESP32 への接続のみとなる。

動作概要:

・softAP で APモードでサーバ起動し、スマホ等から WiFi接続設定を行う。
  (APモードの時の ESP32 の SSID,パスワードは 固定値)
 ・設定したWiFi接続情報は、SPIFFS で 設定ファイルに保存する。
 ・設定ファイルが有り、接続先が設定されている場合は STA(ステーション)モードでサーバー起動する。 ・STAモードで 接続できなかった場合は APモードでサーバー起動する。
 ・APモードのまま、メインの動作も可能。


接続手順:

  1. 最初は、接続設定がないため、ESP32 は AP モードで起動。
  2. スマホ等から、WiFi接続先として
     SSID  : ESP32_AP
     パスワード : password
    で接続できる。 
  3. 接続後、ブラウザで "192.168.4.1" にアクセスすると、下記設定画面が表示される。
  4. WiFi接続設定(SSID, PASSWORD, IPアドレス等) を設定し、STA MODE を選択して "設定"ボタンを押下すると、 ESP32 がリブートして 設定した WiFi 親機に接続する。
  5. スマホ等から、ブラウザで設定した IPアドレスにアクセスすると、下記 LED調光画面が表示される。
  6. LED 調光画面から、WiFi 設定画面 ボタンを押下すると再度 WiFiの設定画面に遷移する。
  7. WiFi 親機に接続できなかった場合は、再度 AP モードで立ち上がる。
  8. 設定画面で、AP MODE を選択した場合は、AP モードのまま、LED調光画面に遷移する。

例:

LED をPWM で調光する。 をベースに WiFi 設定画面を追加。
 スケッチは、長くなった来た為、ファイルを .ino の拡張子で
1. メイン用  : 各変数等の設定, setup, loop  を記述
2. 設定用  :主にWiFi 設定に使用する処理を記述
3. LED調光用:主にLED調光に使用する処理を記述
の 3つに分割。( ファイル分割 参照 )
各処理の説明等は、コメントで記入。
その他 覚書などを、下部に記載。
< 各画面サンプル > 
 WiFi 設定画面
 接続先の SSID,パスワード, ESP32 の IPアドレス等を設定。
接続モード (AP/STA) を設定
 LED調光の画面
ON ボタンを押下する度に LED の明るさが増す。("LED を PWM で調光する" 同様 )

WiFi設定画面への移行用ボタンを追加


< WiFi 設定画面用 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; }
    #text_box     {height: 25px; font-size:12pt; float:left ; text-align:left; width:45%;}
    #input_box    {height: 25px; font-size:12pt; float:right; text-align:left; width:55%;}
    #ip_box       {height: 25px; font-size:12pt; float:right; text-align:left; width:15%;}
    #radio_box    {font-size:12pt; clear:both; margin : 0% 20% ; width : 60% ;}
    #button       {font-size:12pt; clear:both; width 50%}
    #foot         {font-size:16pt; clear:both;}
    input.val     {width: 90%;}
    input.ip      {width: 20%;}
    input.button  {margin:10px 10% ; width: 25%;}
    input.radio   {margin:10px 0px 0px 15% ; }
</style>

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

<body>
<div id="base">
  <p>設定画面</p>
  <div id="text_box">
    <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="text_box">
    <span> WiFi 接続先 PASSWORD </span>
  </div>
  <div id="input_box">
      <input class='val' type='text' name='pass' id='pass' value=$pass>
  </div>
  <div id="text_box">
    <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="text_box">
    <span> WiFi 接続  サブネットマスク</span>
  </div>
  <div id="input_box">
      <input class='ip' type='number' name='sn1' id='sn1' min=0 max=255 value=$sm1 >
      <input class='ip' type='number' name='sn2' id='sn2' min=0 max=255 value=$sm2 >
      <input class='ip' type='number' name='sn3' id='sn3' min=0 max=255 value=$sm3 >
      <input class='ip' type='number' name='sn4' id='sn4' min=0 max=255 value=$sm4 >
  </div>
  <div id="text_box">
    <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="text_box">
    <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="button">
      <input class='button' type='submit' name='set' id='set' value='設定'>
  </div>
 </form>
 <div id="foot">
   <span>$footer</span>
 </div>
</div>
</body>
</html>


 < LED調光用 HTML >

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
    #base       {font-size:16pt; text-align:center; width:400px; border:solid 4px #93ff93; background-color:#f0f0f0; }
    #text_box   {height: 30px; font-size:12pt; float:left ; text-align:left; width:45%;}
    #button     {font-size:12pt; clear:both; width 50%}
    #foot       {font-size:16pt; clear:both;}
    input       {margin:8px;width:100px;}
    div         {font-size:16pt;text-align:center;}
</style>

<title>Color LED Controller</title></head>

<body>
<div id="base">
  <p>LED ON/OFF</p>
  <form method='get'>
    <input type='submit' name='on' value='ON' />
    <input type='submit' name='off' value='OFF' />
  </form>

  <form method="get">
    <div id="button">
      <input class='button' type='submit' name='config' id='config' value='WiFi設定画面'>
    </div>
   </form>
 <div id="foot">
   <span>$footer</span>
 </div>
</div>
</body>
</html>


 < スケッチ (メイン) >

#include <WiFi.h>                         // WiFi 使用の為
#include "FS.h"                           // SPIFFS 使用の為
#include "SPIFFS.h"                       // SPIFFS 使用の為

#define DEBUG_HTML                        // Debug用 Serial 表示制御用
#define DEBUG                             // Debug用 Serial 表示制御用

// AP モード用 WIFI 設定 ---------------------------------------------
const char ssid[] = "ESP32_AP";           // SSID for softAP
const char pass[] = "password";           // password for softAP
const IPAddress ip(192, 168, 4, 1);       // IPアドレス for softAP
const IPAddress subnet(255, 255, 255, 0); // サブネットマスク for softAP

// 動作モードフラグ ---------------------------------------------------
bool stamode = true ;                     // WiFi STA 接続モード : true
bool sta_exec = false ;                   // WiFi STA接続モード実行中フラグ
bool config_mode  = false ;               // 初期設定モード ; true
bool config_exec = false ;                // 初期設定実行中フラグ

// 設定ファイル用 -----------------------------------------------------
String s_config ;                         // 設定ファイル用 String
char wifi_ssid[128] = "" ;                // SSID        for WiFi STA
char wifi_pass[128] = "" ;                // password    for WiFi STA
IPAddress wifi_ip(192,168,1,66) ;         // IP Address  for WiFi STA
IPAddress wifi_gw(192,168,1,1)  ;         // gate way    for WiFi STA
IPAddress wifi_sm(255,255,255,0) ;        // subnet mask for WiFi STA
IPAddress wifi_dns(192,168,1,1) ;         // dns address for WiFi STA
char wifi_mode[16] = ""   ;               // wifi mode
String foot_msg ="" ;                     // リブートメッセージ用

WiFiServer server(80);

String html_CONF ;                        // 設定用 HTML
String html_MAIN ;                        // メイン HTML

// LED 調光処理用 ----------------------------------------------------------------
// -- for ledc
byte brightness = 0 ;                     // LED 明るさ設定 (0-255)

// -----------------------------------------------------------------------------
//  arduino 初期化処理                                                        --
// -----------------------------------------------------------------------------
void setup() {
  int res = 0 ;                                // 結果格納用 (ワーク)

  Serial.begin(115200);                        // シリアル 開始
  SPIFFS.begin(true) ;                         // SPIFFS 開始

  // 初期設定用 HTML ファイルの読み込み ---------------------------------------
  res = rd_SPIFFS("/Config.html",html_CONF) ;  // html_CONF に Config.html を格納

  // 本体 HTML ファイルの読み込み ----------------------------------------------
  res = rd_SPIFFS("/main.html",html_MAIN) ;    // html_MAIN に main.html を格納
  
  // 初期設定ファイルを読み込み、グローバル変数に設定し、wifi,画面モードを決定 ---
  res = rd_config() ;                          //

  // wifiモード(stamode) に応じて AP か STA かを選択してサーバー起動 -------------
  if (stamode == false) {                    // APモードの場合 softAP WiFi 開始
    start_AP_server() ;                      // APモードでサーバー起動
    sta_exec = false ;                       //
  } else {                                   // STAモードの場合 Wifi 開始
    start_STA_server() ;                     // APモードでサーバー起動
    sta_exec = true ;                        //
  }                                          //

  // 実行中のモード (設定かメインか) を設定 -------------------------------------
  if (config_mode) config_exec = true ;   // config_mode なら config_exec を '1'
  else config_exec = false ;              //

  Serial.println("Server start!");

  // メイン処理用 初期化 ------------------------------------------------------
  ledcSetup(0, 5000, 8);            // channel 0 周波数 5000 Hz, 8 bit 
  ledcAttachPin(2,0);               // attach pin 2 to channel 0
  ledcWrite(0, 0);                  // initialize channel 0 to off     


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


// -----------------------------------------------------------------------------
//  arduino メインループ処理                                                  --
// -----------------------------------------------------------------------------
void loop() {
  WiFiClient client = server.available();
  String htmlwk = "" ;                            // html 用 ワーク変数
  String line   = "" ;                            // クライアントからの入力用
 
  // 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 {                                  // メイン用 処理
     proc_main(line) ;                     //   フォームデータのメイン処理
   }                                       //
 }                                         //
        // 最終行(空行)を受信した時 -------------------------------------------
        if (line.length() == 1 && line[0] == '\r'){  // 最終行判定
   if (stamode != sta_exec) {         // sta_mode が変わった場合
     if (strcmp(wifi_mode,"ap") == 0){    //  リブートメッセージを設定
       foot_msg = "アクセスポイントモードに移行します。<br>" ;
       foot_msg += "この画面を閉じてアクセスポイントに接続して下さい。" ;
     }else{
       foot_msg = "ステーションモードに移行します。<br>"; 
       foot_msg += "この画面を閉じて 設定したアドレスに接続して下さい。" ;
     }
            if (config_exec) {                    //   初期設定中なら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
     } else {                              //   メイン画面なら
              send_MAIN_html(client) ;            //     メイン HTML 送信
     }                                     //    ( その後 リブート)
          } else {                                // sta_mode が変わらない場合 
            if (config_mode) {                    //   次に初期設定を表示するなら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
       config_exec = true ;                //
     } else {                              //   次にメイン画面なら
              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設定用) >

// *****************************************************************************
// *  初期設定ファイル をリードし、グローバル変数に値をセットする              *
// *****************************************************************************
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("設定ファイルなし") ;         // 無かったら、APモード
    result = -1 ;                                //
  }                                              //

  // ファイルが読み込めたら、グローバル変数にセットする
  if (result == 0)                               //
    # ifdef DEBUG                                // デバッグ用表示
      Serial.println("--- s_config --- ") ;      //
      Serial.println(s_config ) ;                //
    # endif                                      //
    set_conf2param() ;                           // 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" なら
        config_mode = false ;                  //   メイン画面
      else                                     // wifi_mode が "ap" でなければ
        config_mode = true ;                   //   初期設定画面
    } else {                                   // ssid,pass の設定があれば、
      config_mode = false ;                    //   メイン画面
      if ( strcmp(wifi_mode,"ap") == 0)        // wifi_mode が "ap" なら
        stamode = false ;                      //   APモード
      else                                     // "ap" でなければ
        stamode = true  ;                      //   STAモード
    }
  } else {                                     // 変数に設定できなかった場合
    stamode = false ;                          //   APモード
    config_mode  = true ;                      //   初期設定画面
  }

  return result ;                              //
}

// ----------------------------------------------------------------------------
// - 設定ファイルの内容を グローバル変数に 設定する                           -
// ----------------------------------------------------------------------------
void set_conf2param() {
  int    pos = 0 ;
  int    npos = 0 ;
  String s_work ;
  String s_name ;
  String s_data ;
  // 記載内容を順次処理 ( 設定情報を取り出し、グローバル変数に設定する ) ------
  while(1) {
    if (s_config.charAt(pos) == '/') {         // 先頭が '/' なら 次の行へ 
      npos = s_config.indexOf('\n',pos) ;      //
      pos = npos + 1 ;                         //
    } else {                                   //
      npos = s_config.indexOf(':',pos) ;       // ':' までの文字をs_name に取得
      if (npos == -1) break ;                  //   見つからなければ終了
      s_name = s_config.substring(pos,npos) ;  //
      s_name.trim() ;                          //
      pos = npos+1 ;                           // ポインタを ':' の次へ
      npos = s_config.indexOf('\n',pos) ;      // '\n' までの文字をs_data に取得
      if (npos == -1) npos = s_config.length() ;  //
      s_data = s_config.substring(pos,npos) ;  //
      s_data.trim() ;                          //
      pos = npos+1 ;                           //
      set_param(s_name,s_data) ;               // 取得した内容をグローバル変数に設定
    }                                          //
  }                                            //
}                                              //

// - 設定内容を判定し、グローバル変数に設定 ------------------------------------
void set_param(String &name, String &data) {   //
  if (name.compareTo("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) ;           //
  }                                            //
}                                              //

// - 文字列 から 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 (config_mode)                              // 変数の状態と実行状況を表示
      Serial.print("Config MODE - ") ;            //
    else                                          //
      Serial.print("Main MODE - ") ;              //
    if (config_exec)                              //
      Serial.println("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 set_form2param(String &line) {
  String s_work ="" ;

  // クライアントからのデータを グローバル変数にセットする。 ------------------
  if (line.indexOf("GET /?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 ;                            // 
      config_mode = false ;                       // 
    } else {                                      // 
      stamode = false ;                           // 
      config_mode = false ;                       // 
    }
  }

  // グローバル変数を 設定ファイルに書き出す -----------------------------------
  wr_config() ;                           // 設定を初期設定ファイルに書き出し
}

// 要素名に続く値(文字列)を取得する --------------------------------------------
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) != '\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() ;                            //  数値に変換して返す
}

// -----------------------------------------------------------------------------
// - グローバル変数の内容を設定ファイルに書き出す                              -
// -----------------------------------------------------------------------------
void wr_config() {
  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) ;                    //

  // 設定ファイルに書き込む ----------------------------------------------------
  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_CONF_html(WiFiClient client) {
  String htmlwk ;

  // 変数置換え処理 ------------------------------------------------------------
  htmlwk = html_CONF ;
  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") ;   //
  }
  htmlwk.replace("$footer",foot_msg ) ;
  // --------------------------------------------------------------------------

  send_html(client,htmlwk) ;                       // HTML 送信処理
}

 < スケッチ (LED調光用) >

// *****************************************************************************
// * 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("$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                                       //
}

// *****************************************************************************
// * メイン処理                                                                *
// *****************************************************************************
void proc_main(String &line) {
  String s_work ="" ;

  if (line.indexOf("GET /?config=") != -1) {        // config
    Serial.println(" -- Set config_mode truw--") ;
    config_mode = true ;
  }
  if (line.indexOf("GET /?on") != -1) {             // "on"が押下された時 
    brightness +=25 ;                               // 明るさを +25 
    Serial.print("brightness value : ");
    Serial.println(brightness);
    // -- for ledc
    ledcWrite(0, brightness) ;                      // チャネル#0 に Duty値設定
  }
  if (line.indexOf("GET /?off") != -1) {            // "off"が押下された時
    brightness = 0 ;                                // 明るさを "0" 
    Serial.print("brightness value : ");
    Serial.println(brightness);
    // -- for ledc
    ledcWrite(0, brightness) ;                      // チャネル#0 に Duty値設定
  }
}

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

以下、覚書など。

 <メイン スケッチ>

(行#8~12)
APモード用 WiFi 設定。
APモードで立ち上がった場合 、スマホから、ここに設定した SSID が見える。
パスワードは、この値 ("password") で接続。
接続後、設定した IPアドレス (192.168.4.1) にアクセスすると、設定画面が表示される。
(行#14~18)
接続モード等のフラグ定義
(行#20~29)
WiFi接続設定用変数。
この変数を、SPIFFS で 設定ファイルに保存する。
初期値は、仮の値。
(行#33,34)
HTML 格納用 String変数
(行#36 ~ 39)
LED調光用変数
(行#43~79)
Arduino setup 関数
内容は、コメント参照。
LED調光の ledc の dutyビット数は、8bit とした。
(行# 86~143)
Arfuino loop 関数
内容はコメント参照。
APかSTAか、初期設定がメイン処理かで、処理を選択。
(行#145~159)
WiFi 接続モードが変わった場合、リブートする。

<WiFi 設定用 スケッチ >

(行#1~134)
初期設定ファイルをリードして、変数に設定。
初期設定ファイルのリード結果により、WiFi接続モードや画面モードを選択する。
SFPFFS ファイルリードの関数は、LED調光用 のファイルに記述。
  (行#135~145)
APモードでサーバー起動
以下の手順で、サーバーを開始する。
 WiFi.softAP(ssid,password)   : AP としての SSID と パスワードを設定
 WiFi.softAPConfig(ip adr, gateway adr, subnetmask) : サーバーのIPアドレスの設定
 WiFi.softAPIP()                : サーバーをAPモードで起動
 server.begin()                 : サーボー開始
(行#174~319)
クライアントからのフォームデータを解析して変数に設定し、内容を初期設定ファイルに書き込む。
(行#320~361)
 WiFI設定用 HTML の変数部分を 値の置換え、送信する。
 送信の関数は、LED調光用 のファイルに記述。

<LED調光用 スケッチ >

 (行#1~15)
 SFPFFS ファイルリードの関数。
HTML 初期設定ファイルのリードに使用。
  (行#17~45)
STAモードでサーバー起動。
 一定時間(約2秒)以内に接続できなければ、再度 WiFi 開始を実行。
 (リセット時等、1回置きに接続できない現象 (原因不明) の対策。)
 約 10秒以内に接続できなければ、APモードにして再起動。
(行#46~61)
LED調光用 HTML の変数部分を 値の置換え、送信する。
 (行#63~76)
HTML 送信処理
 (行#78~104)
LED調光処理
クライアントからのフォームデータを解析してLEDの調光を行う。
”LED をPWM で調光する” から少し変更。
1. PWM の DUTY設定を 8bit にし、 0~255 の範囲とした。
2. ON ボタン 押下毎に 明るさを +25づつ加算する様にした。



3 件のコメント:

  1. 私にとって、とっても 分かりやすい うまく書かれたソースです。
    公開、ありがとうございます。
    ひとつだけ、教えてください。
    rd_SPIFFS() は どこに存在している Function なのでしょうか?

    返信削除
    返信
    1. すみません、私の 見落としでした。
      m(__)m

      削除
    2. 参照ありがとうございます。
      記事が参考になれば幸いです。

      削除