ESP32でwifi接続情報をブラウザから編集・保存して使うサンプル 接続できない時は自己AP化

Arduino/ESP32ESP32,nvs,WebServer,wifi,保存

今回はwifiの設定値を内蔵フラッシュメモリのnvs領域に書き込んで保持しておき、外部からこれを書き換えられる様にするテストをしてみようと思います。

また、接続出来なかった時は自己AP化して設定を促す感じに。

レシピはこんな感じ。

・nvs領域にwifi接続情報があればこれを使って接続する
・wifi接続情報が無い、または接続できなければ無線APモードで立ち上がる
・APモード、STAモードにかかわらず、ブラウザから設定情報を編集できる様にする

まずはブートから。

nvsに設定がある場合、nvsから読みだしてwifi接続し、nvsに設定が無い、または設定があっても接続出来なかった場合にはAPモードで起動するっと

#include <Arduino.h>
/*
    ・nvs領域にwifi接続情報があればこれを使って接続する
    ・wifi接続情報が無い、または接続できなければ無線APモードで立ち上がる
    ・APモード、STAモードにかかわらず、ブラウザから設定情報を編集できる様にする
*/
// WiFiライブラリ
#include <WiFi.h>
WiFiClass wifi;

// nvsライブラリ
#include <Preferences.h>
Preferences prefs;

// ----------------------------------------------------------------
// 無線APモード時のデフォルトSSID、パスワード
String ap_ssid = "ESP32AP";
String ap_pass = "12345678";
IPAddress ap_ip = IPAddress(192, 168, 4, 1);
IPAddress ap_gw = IPAddress(192, 168, 4, 1);
IPAddress ap_mask = IPAddress(255, 255, 255, 0);
uint8_t ap_ch = 5;
uint8_t ap_hidden = 0;        // SSIDを隠し属性にするか
uint8_t ap_maxconnection = 4; // 最大コネクション数(MAX=4)

// wifi接続用
uint8_t charrenge_retry_count = 3; // 何回接続試行するか
String wifi_ssid;
String wifi_pass;

// basic認証用
String auth_user = "basic";
String auth_pass = "basic";
// ----------------------------------------------------------------

// setup
void setup()
{

    // シリアル開始
    Serial.begin(115200);
    Serial.println();
    Serial.println("beginning AP..");

    // Wifi ------------------------------------------------------------
    // 既に接続中なら一旦切る
    wifi.setAutoConnect(false);
    if (wifi.isConnected())
    {
        wifi.disconnect();
        delay(3000);
    }

    //  ・nvs領域にwifi接続情報があればこれを使って接続する
    //  nvsシリアル開始 ReadOnly
    if (prefs.begin("config-wifi", true))
    {
        // コンフィグリセット
        if (prefs.getBool("reset_config"))
        {
            // Writeで開き直してリセット
            prefs.end();
            prefs.begin("config-wifi");
            prefs.clear();
            prefs.end();
            delay(5000);
        }
        else
        {
            // コンフィグロード
            if (prefs.getString("wifi_ssid").length() > 0)
                wifi_ssid = prefs.getString("wifi_ssid");
            if (prefs.getString("wifi_pass").length() > 0)
                wifi_pass = prefs.getString("wifi_pass");

            if (prefs.getString("ap_ssid").length() > 0)
                ap_ssid = prefs.getString("ap_ssid");
            if (prefs.getString("ap_pass").length() > 0)
                ap_pass = prefs.getString("ap_pass");

            if (prefs.getUInt("ap_ip") > 0)
                ap_ip = IPAddress(prefs.getUInt("ap_ip"));
            if (prefs.getUInt("ap_gw") > 0)
                ap_ip = IPAddress(prefs.getUInt("ap_gw"));
            if (prefs.getUInt("ap_mask") > 0)
                ap_ip = IPAddress(prefs.getUInt("ap_mask"));

            if (prefs.getUChar("ap_ch") > 0)
                ap_ch = prefs.getUChar("ap_ch");
            if (prefs.getUChar("ap_hidden") > 0)
                ap_hidden = prefs.getUChar("ap_hidden");
            if (prefs.getUChar("ap_maxconnection") > 0)
                ap_maxconnection = prefs.getUChar("ap_maxconnection");

            if (prefs.getUChar("charrenge_retry_count") > 0)
                charrenge_retry_count = prefs.getUChar("charrenge_retry_count");

            if (prefs.getString("auth_user").length() > 0)
                auth_user = prefs.getString("auth_user");
            if (prefs.getString("auth_pass").length() > 0)
                auth_pass = prefs.getString("auth_pass");
            // 閉じておく
            prefs.end();
        }

        Serial.println("configure wifi mode.");

        // STA-mode ssid, pass
        if (wifi_ssid.length() > 0 && wifi_pass.length() > 0)
        {
            // wifi通信開始
            wifi.mode(WIFI_STA);
            wifi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
            Serial.println("start wifi.");
            delay(6000);

            // 接続待ち
            int _count = 0;
            while (wifi.status() != WL_CONNECTED)
            {
                // 指定回数で抜ける
                if (_count >= charrenge_retry_count)
                {
                    Serial.println("wifi connection failed. booting AP mode.");
                    break;
                }
                Serial.println("wifi connecting...");
                Serial.println();
                delay(3000);
                if (wifi.status() == WL_CONNECT_FAILED)
                {
                    Serial.println("wifi connection failed. please check settings again.");
                    Serial.print("\n\n");
                    break;
                }
                _count++;
            }
        }
    }

    // リザルトメッセージ
    if (wifi.status() == WL_CONNECTED)
    {
        Serial.println("wifi connected.");
        Serial.println("IP address: ");
        Serial.println(wifi.localIP());
        Serial.println("GW address: ");
        Serial.println(wifi.gatewayIP());
        Serial.println("DNS address: ");
        Serial.println(wifi.dnsIP());
        Serial.println();
    }
    else
    {
        // nvs読み込み失敗 | コネクション確立不可
        // softAPモードで設定
        Serial.println("booting AP-mode.");
        wifi.mode(WIFI_AP);
        wifi.softAP(ap_ssid.c_str(), ap_pass.c_str(), ap_ch, ap_hidden, ap_maxconnection);
        delay(200);
        wifi.softAPConfig(ap_ip, ap_gw, ap_mask);

        Serial.print("AP-mode IP address: ");
        Serial.print(wifi.softAPIP());
        Serial.println();
    }
    // Wifi ------------------------------------------------------------

    delay(100);
}

void loop()
{
    delay(15000);
}

なんかゴチャゴチャーっとしてきました。

更にWebServerを追加します、、、

// WebServerライブラリ
#include <WebServer.h>
#include <uri/UriRegex.h>
WebServer server;
TaskHandle_t *pWebServerPolling;

// wifiスキャン文字列を返すだけ
bool getWifiScanStr(String &_str, WiFiScanClass &_wifi)
{
    // wifiスキャン
    //  int16_t scan_res = wifi.scanNetworks();
    int16_t scan_res = _wifi.scanNetworks();
    if (scan_res > 0)
    {
        // 発見したwifiAPを表示
        _str += "- SCAN SSID RESULT -<br /><br />\n";
        _str += String(scan_res) + " of SSID found.<br /><br />\n";
        for (int i = 0; i < scan_res; ++i)
        {
            // SSIDとRSSIをprint
            _str += String(i) + ": " + _wifi.SSID(i) + " (RSSI:" + _wifi.RSSI(i) + " dBm)<br />\n";
        }
        return true;
    }
    else
    {
        //
        _str += "-- Wifi Not Found --<br />\n";
        return false;
    }
}

void response()
{
    // 処理内容モニタ
    Serial.println("Incomming Request.");
    Serial.print("request:");
    Serial.println(server.uri());

    // BASIC認証
    if (!server.authenticate(auth_user.c_str(), auth_pass.c_str()))
    {
        return server.requestAuthentication();
    }

    // リザルト文字列
    String str;

    // サーバーリセット
    if (server.uri() == "/factory_reset")
    {
        server.send(200, "text/plain", "Factory RESET at ESP32. after restart server at 10 sec..");
        prefs.begin("config-wifi");
        prefs.clear();
        prefs.end();
        delay(10000);
        ESP.restart();
    }
    else if (server.uri() == "/set_ssid")
    {
        // html作成
        str += "<html><head></head><body>\n";
        // 現在のSSIDを表示
        str += "Wifi Setting<br /><br />\n";

        // 設定受信した場合
        if (server.hasArg("_ssid") && server.hasArg("_pass") &&
            String(server.arg("_ssid")).length() > 0 && String(server.arg("_pass")).length() > 0)
        {
            // パスワード登録モード
            prefs.begin("config-wifi");
            // nvsに保存
            if (prefs.putString("wifi_ssid", (server.arg("_ssid")).c_str()) &&
                prefs.putString("wifi_pass", (server.arg("_pass")).c_str()))
            {
                delay(5000);
                prefs.end();
                str += "Success : SSID and Password saved.<br />\n";
                str += "SSID = " + String(server.arg("_ssid")) + "<br />Pass = " + String(server.arg("_pass")) + "<br /><br />\n";
                str += "server going reset after 10 sec.<br />\n";
                str += "</body></html>";
                server.send(200, "text/html", str);
                delay(10000);
                ESP.restart();
            }
            else
            {
                prefs.remove("wifi_ssid");
                prefs.remove("wifi_pass");
                prefs.end();
                delay(10000);
                str += "Error: not saved SSID and Password. plz retry.";
            }
        }
        else
        {
            // wifi scan print
            getWifiScanStr(str, wifi);

            // SSID,Pass設定用フォーム
            str += "<br /><form action='' method='post'>\n";
            str += "SSID : <input type='text' name='_ssid'><br />\n";
            str += "PASS : <input type='text' name='_pass'><br />\n";
            str += "<input type='submit' value='send'>\n";
            str += "</form>";
        }
    }
    else if (server.uri() == "/set_auth")
    {
        // html作成
        str += "<html><head></head><body>\n";
        // 現在のSSIDを表示
        str += "Auth Setting<br /><br />\n";

        // 設定受信した場合
        if (server.hasArg("_auth_user") && server.hasArg("_auth_pass") &&
            String(server.arg("_auth_user")).length() > 0 && String(server.arg("_auth_pass")).length() > 0)
        {
            // パスワード登録モード
            prefs.begin("config-wifi");
            // nvsに保存
            if (prefs.putString("auth_user", server.arg("_auth_user")) &&
                prefs.putString("auth_pass", server.arg("_auth_pass")))
            {
                delay(5000);
                prefs.end();
                str += "Success : User and Password saved.<br />\n";
                str += "User = " + String(server.arg("_auth_user")) + "<br />Pass = " + String(server.arg("_auth_pass")) + "<br /><br />\n";
                str += "server going reset after 10 sec.<br />\n";
                str += "</body></html>";
                server.send(200, "text/html", str);
                delay(10000);
                ESP.restart();
            }
            else
            {
                prefs.remove("auth_user");
                prefs.remove("auth_pass");
                prefs.end();
                delay(10000);
                str += "Error: not saved User and Password. plz retry.";
            }
        }
        else
        {
            // SSID,Pass設定用フォーム
            str += "<br /><form action='' method='post'>\n";
            str += "User : <input type='text' name='_auth_user'><br />\n";
            str += "Pass : <input type='text' name='_auth_pass'><br />\n";
            str += "<input type='submit' value='send'>\n";
            str += "</form>";
        }
    }
    else
    {
        // html作成
        str += "<html><head></head><body>\n";
        // 現在のSSIDを表示
        str += "Welcome ESP32 Server<br /><br />\n";
        // wifi scan print
        getWifiScanStr(str, wifi);
        str += "<br /><br />\n";
        str += "<a href='/set_ssid'>Wifi Setting</a> ";
        str += "<a href='/set_auth'>Auth Setting</a> ";
        str += "<a href='/factory_reset'>factory_reset</a><br />\n";
    }

    if (str.length() > 0)
    {
        str += "</body></html>";
        server.send(200, "text/html", str);
    }
    else
    {
        server.send(200, "text/plain", "Invalid Request");
    }
    delay(2);
}

// ポーリングなどを
void WebServerPolling(void *param)
{
    while (1)
    {
        // Webサーバー待ち受け
        server.handleClient();
        // resistance watchdog
        delay(2);
    }
}

void setup()
{
        :
        :
        :

    ~ wifi設定の後ろ辺りに追加 ~

    // WebServer ------------------------------------------------------------
    //  すべてのリクエストを取得
    server.on(UriRegex("^.*?$"), response);

    // WebServerスタート
    server.begin();

    // Core 0 でWebサーバーを起動
    //  xTaskCreatePinnedToCore
    //      コアを明確に指定しない場合は xTaskCreateUniversal ラッパーを使う方がいい
    xTaskCreateUniversal(
        WebServerPolling,   //  TaskFunction_t pvTaskCode,
        "WebServerPolling", //  const char * const pcName,
        20480,              //  const uint32_t usStackDepth,    // WebServerは結構メモリ使うので大き目に
        NULL,               //  void * const pvParameters,
        1,                  //  UBaseType_t uxPriority,
        pWebServerPolling,  //  TaskHandle_t * const pvCreatedTask,
        0);                 //  CPU Core

    Serial.print("WebServer Started. http://");
    if (wifi.getMode() == WIFI_MODE_AP)
    {
        Serial.print(wifi.softAPIP()); // AP-mode
    }
    else
    {
        Serial.print(wifi.localIP()); // STA-mode
    }
    Serial.println("/");
    // WebServer ------------------------------------------------------------

        :
        :
        :

}

void loop()
{
    // メインループは暇なので適当に何かさせておく
    Serial.print("Heap memory malloc: ");
    Serial.println(ESP.getFreeHeap());
    Serial.print("wifi connected client: ");
    Serial.println(wifi.softAPgetStationNum());
    delay(15000);
}

だいぶゴチャゴチャしてきましたね!

動けばいいんです動けば;

こんな感じで接続先のSSIDとパスワードをフォームから保存させる入口を設置。

ついでにBASIC認証のアイパスの変更、設定全クリアも追加。

うん、無駄にダラダラしたイイ感じのものに。