kghr IT備忘録
HOME » IoT » Entry
ESP32のWiFi設定をWeb サーバで行ってみた
2020.08/22 (Sat)

ESP32はWiFi機能を持っているが、そのSSIDとパスワードをスケッチに直書きしてしまうと、設定を変更するたびにコンパイルしなおさなければならない。
やはりブラウザ経由だよねと、ということで有名どころの"WiFi Manager for ESP32"のライブラリを使ってみたものの、標準でない前提ライブラリがいくつかあったりでうまく動作せず。
そして、いくつか派生バージョンも試してみたものの、安定性に難があったりで使うのを断念。
結局、シンプルなものを作ったほうが早いのではないかということで、WiFi設定だけでなく比較的自由にパラメータを設定できるような仕組みを作ってみた備忘録。
参考にした記事:
https://qiita.com/nanbuwks/items/171d830c92617702ad7e
今回作成したものの仕組み
ESP32を起動したあと、設定ファイルを読み込んでWiFiに接続できなかったら以下の流れで設定する。
① ESP32をアクセスポイント化する
ESP32のアクセスポイントが立ち上がっているときは、BUILTINのLEDが点灯する。手元の開発キットだとBUILTINのLEDはGPIO2に設定されている。
WIFI_AP モードでWebサーバーを立ち上げたあとの”delay(200)”が重要。これがないとESP32がPanicを起こしてしまうようだ。
このアクセスポイントは一時的に立ち上がるものなので、パスワードなしにしておいた。
WiFi.mode(WIFI_AP);
WiFi.softAP(WIFIMGR_ssid); // no password
delay(200); // Important! This delay is necessary
WiFi.softAPConfig(apIP,apIP,IPAddress(255,255,255,0));
② 自動的にWiFiの設定画面に切り替わる
スマートフォンなどでESP32で立ち上げたアクセスポイント ”WIFIMGR_ESP32” に接続すると、自動的に設定のトップ画面が表示されるようにした。
DNSサーバーの機能を使って、wifimgr_top()の処理を実行。 wifimgr_top() では、WiFi設定画面のトップページを表示する。
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(53, "*", apIP);
webServer.on("/", wifimgr_top);
:
webServer.onNotFound(wifimgr_top);
webServer.begin();

③ WiFi接続のSSIDとパスワードを設定する
ESP32で周辺のWiFiアクセスポイントをスキャンし、プルダウンリストから選択できるようにする。パスワードを設定して[SET]ボタンを押せば設定ファイルに記録される。

設定ファイルは ”SPIFFS.h” を使って読み書きする。設定ファイルの中身は単純に、1行目がSSIDで2行目がパスワードとした。パスワードは平文だが、暗号化するならコードをちょっと足せばいい。
設定ファイル /config.itxt の中身:
myssid
12345678
④ ESP32をリブートする
トップ画面の[Reboot]ボタンを押せば、リブートの確認画面が表示されたあとESP32をリブートする。WiFiアクセスポイントとパスワードが設定されたので、起動時にはすんなり接続されるはず。


作成したスケッチ
以下に今回作成したスケッチを記載する。
なお、設定ファイルの保管にSPIFFSを使用するため、コンパイル時のオプションで Pertirion Scheme: "Default with spiffs" を選択しておくこと。
/*
WIFI_MGR for ESP32
by kghr labo 2020
*/
// include the library code:
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include "SPIFFS.h"
//#define LED_BUILTIN 2;
IPAddress apIP(192,168,1,100);
WebServer webServer(80);
const char* WIFIMGR_ssid = "WIFIMGR_ESP32";
const char* WIFIMGR_pass = "xxxxxxxx";
DNSServer dnsServer;
// parameters setting
const String defaultSSID = "myssid";
const String defaultPASSWD = "12345678";
String ssid = defaultSSID;
String passwd = defaultPASSWD;
// scan SSID
#define SSIDLIMIT 30
String ssid_rssi_str[SSIDLIMIT];
String ssid_str[SSIDLIMIT];
// SPIFFS config filename
const char* configfile = "/contig.txt";
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
// init filesystem
if(!SPIFFS.begin(true)){
Serial.println("SPIFFS Mount Failed");
}
// read config from SPIFFS
readConfigFile();
// wifi connect
uint8_t retry = 0;
WiFi.begin(ssid.c_str(), passwd.c_str());
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(200); // 200ms
retry ++;
if (retry > 50) { // 200ms x 50 = 10 sec
Serial.println("wifi connection timeout");
webconfig(); // enter webconfig
}
}
Serial.println();
Serial.printf("Connected, IP address: ");
Serial.println(WiFi.localIP());
delay(500);
}
void loop() {
// put your main code here, to run repeatedly:
delay(100);
}
//*******************************************
void webconfig() {
Serial.println("WebConfig mode: ");
digitalWrite(LED_BUILTIN, HIGH);
configserver();
uint8_t configloop = 1;
while (configloop == 1) {
dnsServer.processNextRequest();
webServer.handleClient();
}
digitalWrite(LED_BUILTIN, LOW);
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
void configserver() {
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
delay(100);
WiFi.mode(WIFI_AP);
WiFi.softAP(WIFIMGR_ssid); // no password
// WiFi.softAP(WIFIMGR_ssid,WIFIMGR_pass); // with password
delay(200); // Important! This delay is necessary
WiFi.softAPConfig(apIP,apIP,IPAddress(255,255,255,0));
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(53, "*", apIP);
webServer.on("/", wifimgr_top);
webServer.on("/wifiinput", HTTP_GET, wifiinput);
webServer.on("/wifiset", HTTP_GET, wifiset);
webServer.on("/reboot", reboot);
webServer.on("/doreboot", doreboot);
webServer.onNotFound(wifimgr_top);
webServer.begin();
}
String maskpasswd(String passwd){
String maskpasswd = "";
for (int i=0; i<passwd.length(); i++) maskpasswd = maskpasswd + "*";
if (passwd.length() == 0) maskpasswd = "(null)";
return maskpasswd;
}
void wifimgr_top() {
String html = Headder_str();
html += "<a href='/wifiinput'>WIFI setup</a>";
html += "<hr><h3>Current Settings</h3>";
html += "SSID: " + ssid + "<br>";
html += "passwd: " + maskpasswd(passwd) + "<br>";
html += "<hr><p><center><a href='/reboot'>Reboot</a></center>";
html += "</body></html>";
webServer.send(200, "text/html", html);
}
String Headder_str() {
String html = "";
html += "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1.3'>";
html += "<meta http-equiv='Pragma' content='no-cache'>";
html += "<meta http-equiv='Cache-Control' content='no-cache'></head>";
html += "<meta http-equiv='Expires' content='0'>";
html += "<style>";
html += "a:link, a:visited { background-color: #009900; color: white; padding: 5px 15px;";
html += "text-align: center; text-decoration: none; display: inline-block;}";
html += "a:hover, a:active { background-color: green;}";
html += "bo32{ background-color: #EEEEEE;}";
html += "input[type=button], input[type=submit], input[type=reset] {";
html += "background-color: #000099; border: none; color: white; padding: 5px 20px;";
html += "text-decoration: none; margin: 4px 2px;";
html += "</style>";
html += "<body>";
html += "<h2>WIFIMGR</h2>";
return html;
}
void InitialConfigFile(){
Serial.printf("SPIFFS initial file: %s\n", configfile);
ssid = defaultSSID;
passwd = defaultPASSWD;
WriteConfigFile();
}
//*******************************************
void WriteConfigFile(){
ssid.trim();
passwd.trim();
Serial.printf("SPIFFS writing file: %s\n", configfile);
File fw = SPIFFS.open(configfile, "w");
fw.println(ssid);
fw.println(passwd);
fw.close();
delay(100);
}
//*******************************************
void readConfigFile(){
String numstr;
Serial.printf("SPIFFS reading file: %s\n", configfile);
File fr = SPIFFS.open(configfile, "r");
if (fr) {
ssid = fr.readStringUntil('\n');
ssid.trim();
if (ssid =="") ssid = defaultSSID;
passwd = fr.readStringUntil('\n');
passwd.trim();
if (passwd == "" && ssid == defaultSSID) passwd = defaultPASSWD;
fr.close();
} else {
//InitialConfigFile
Serial.println("read open error");
Serial.print("SPIFFS data seems clash. Default load...");
InitialConfigFile();
}
}
void wifiinput() {
String html = Headder_str();
html += "<a href='/'>TOP</a> ";
html += "<hr><p>";
html += "<h3>WiFi Selector</h3>";
html += WIFI_Form_str();
html += "<br><hr><p><center><a href='/'>Cancel</a></center>";
html += "</body></html>";
webServer.send(200, "text/html", html);
}
//*******************************************
String WIFI_Form_str(){
Serial.println("wifi scan start");
// WiFi.scanNetworks will return the number of networks found
uint8_t ssid_num = WiFi.scanNetworks();
Serial.println("scan done\r\n");
if (ssid_num == 0) {
Serial.println("no networks found");
} else {
Serial.printf("%d networks found\r\n\r\n", ssid_num);
if (ssid_num > SSIDLIMIT) ssid_num = SSIDLIMIT;
for (int i = 0; i < ssid_num; ++i) {
ssid_str[i] = WiFi.SSID(i);
String wifi_auth_open = ((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
ssid_rssi_str[i] = ssid_str[i] + " (" + WiFi.RSSI(i) + "dBm)" + wifi_auth_open;
ssid_rssi_str[i] = ssid_str[i] + wifi_auth_open;
Serial.printf("%d: %s\r\n", i, ssid_rssi_str[i].c_str());
delay(10);
}
}
String str = "";
str += "<form action='/wifiset' method='get'>";
str += "<select name='ssid' id ='ssid'>";
for(int i=0; i<ssid_num; i++){
str += "<option value=" + ssid_str[i] + ">" + ssid_rssi_str[i] + "</option>";
}
str += "<option value=" + ssid + ">" + ssid + "(current)</option>";
if (ssid != defaultSSID){
str += "<option value=" + defaultSSID + ">" + defaultSSID + "(default)</option>";
}
str += "</select><br>\r\n";
str += "Password<br><input type='password' name='passwd' value='" + passwd + "'>";
str += "<br><input type='submit' value='set'>";
str += "</form><br>";
str += "<script>document.getElementById('ssid').value = '"+ ssid +"';</script>";
return str;
}
void wifiset(){
ssid = webServer.arg("ssid");
passwd = webServer.arg("passwd");
ssid.trim();
passwd.trim();
WriteConfigFile();
// 「/」に転送
webServer.sendHeader("Location", String("/"), true);
webServer.send(302, "text/plain", "");
}
void reboot() {
String html = Headder_str();
html += "<hr><p>";
html += "<h3>reboot confirmation</h3><p>";
html += "Are you sure to reboot?<p>";
html += "<center><a href='/doreboot'>YES</a> <a href='/'>no</a></center>";
html += "<p><hr>";
html += "</body></html>";
webServer.send(200, "text/html", html);
}
void doreboot() {
String html = Headder_str();
html += "<hr><p>";
html += "<h3>rebooting</h3><p>";
html += "The setting WiFi connection will be disconnected...<p>";
html += "<hr>";
html += "</body></html>";
webServer.send(200, "text/html", html);
// reboot esp32
Serial.println("reboot esp32 now.");
digitalWrite(LED_BUILTIN, LOW);
delay(2000); // hold 2 sec
ESP.restart(); // restart ESP32
}
あとがき
設定パラメータの追加とかスケッチにページを足していけば自由に拡張していくことができる。ラジオボタンやチェックボックスなどレイアウトもしやすいので、これをベースに広げていけそうな予感。
GPIOにつないだスイッチを押したら設定画面に移行するなどの動きも、スケッチを足していけば自由にできるしいいんじゃないだろうか。
なにせESP32のアクセスポイントに接続したら自動的に設定画面が表示されるところが気に入っている。参考になれば幸い。
画面幅に合わせるなどcssをもうちょっとちゃんと書けばよさそうだけど、まあいいか。
今日はここまで。
スポンサーサイト
