目的:
ESP32 へ 設定ファイル等のテキストファイルをアップロードできる様にする。
この時、ボタンではなく、リンク (a要素) の文字列をでダウンロードできる様にする。
方法:
- input タグ で type='file' を指定し、ファイル選択したら ファイル読み込みのjavascript を起動。
この input は hidden 属性にして ボタンを表示させない。
- a要素でリンク文字列を設置し、押下されたら jabascript で 1の input を クリックする様にする。
- ファイル読み込みの javascript では、ファイルをテキストで読み込み、XMLHttpRequest を使って POST でファイル内容を送る。
または、html 内で読み込んだファイルを処理する。
- ESP32側では、XMLHttpRequest で送信されたテキストを処理する。
XMLHttpRequest を POST で送信する場合は、xhr.send() の ()内に送信内容を記述する。
具体的な記述は、下記の例 を参照。
例:
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>
全面的に見直し/修正を行っているが、主な変更は、
- POST 処理対応
POST処理については、POST受信 参照。
(行#131~134)
POST 受信時は フラグをセット。
アップロード要求時は、アップロードのフラグをセット。
(行#139~150)
POST受信時は、メッセージボディを取得
(行#141~164)
POST 要求の処理。
アップロード以外は 行#159 で GET 時と同じ処理を実行。
- アップロード対応
(行#153~157)
アップロードの場合、シリアルにデータを表示。
今回は、ESP32 では特に処理を行わない。
(クライアント側で 各要素の値に代入するだけ。)
<LED調光処理用スケッチ (led_ctrl.ino>
ダウンロード時の処理を削除 (WIFI設定に移動)
<WIFI 設定用スケッチ (wifi_config.ino>
全面的に見直し/修正を行っているが、主な変更は、
- フォームデータ処理を GET,POST どちらでも対応できる様に修正。
(行#209,210)
"GET /?" を判定から削除
- リブート指示時の処理追加
(行#106~109)
設定ファイル処理にリブートを追加。
(行#276~284)
フォームデータでリブート設定時の処理追加。(フラグセット)
(行#355)
リブート設定を設定ファイルに追記。
(行#411~414)
HTML送信時の リブート チェックボックスの値を設定。
- ダウンロード処理追加 ( LED調光画面からの移動)
(行#293~298)
フォームデータでダウンロード指定時の処理 (フラグセット)
(行#421~440)
XHR 応答送信処理追加 (LED調光画面からの移動)