ESP32 の Wi-Fi アクセスポイントをスマホで選択できるようにしてみた

ESP32 ( ESP-WROOM-32 )

スケッチの入力

では、私が自作改良したスケッチを入力してみてください。
Arduino core for the ESP32 のサンプルスケッチ、WiFiClient や WiFiScan , SPIFFS_Test などを改変しています。
素人コードなので、無駄が多いと思います。
動作保証はしません。

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <WiFi.h>
#include "FS.h"
#include "SPIFFS.h"
#include "TimeLib.h" //Use Arduino time library ver1.5
#include "ESP32_WebGet.h" //mgo-tec library ver 1.12

const char *ap_ssid = "xxxxxxxxx"; //ESP32 softAP SSID
const char *ap_pass = "xxxxxxxxx"; //ESP32 softAP password

const char* AP_config_file = "/APconfig/APconfig.txt"; //ssid, password save file

IPAddress LIP; //Local IP address
WiFiServer server(80);
WiFiClient client;

ESP32_WebGet EWG;

uint8_t ssid_num;
String ssid_rssi_str[30];
String ssid_str[30];
String Selected_SSID_str = " ";
String Sel_SSID_PASS_str = " ";

uint32_t scanLastTime = 0;
boolean First_Scan_Set = true;
//*******************************************
void setup(){
  Serial.begin(115200);
  delay(500);

  if (!SPIFFS.begin()) {
    Serial.println("SPIFFS failed, or not present");
    return;
  }

  WiFi.mode(WIFI_AP_STA); //AP mode and STA mode
  WiFi.softAP(ap_ssid, ap_pass);
  delay(100);

  Serial.println("Setup done");

  server.begin();
  Serial.println(F("Server started"));

  scanLastTime = millis();
}
//*******************************************
void loop(){
  client_connect();
  wifi_scan(30000);
}
//*******************************************
void client_connect(){
  String html_res_head = "HTTP/1.1 200 OK\r\n";
         html_res_head += "Content-type:text/html\r\n";
         html_res_head += "Connection:close\r\n\r\n";
  String html_tag1 = "<!DOCTYPE html>\r\n<html>\r\n<head>\r\n";
         html_tag1 += "<meta name='viewport' content='initial-scale=1.5'>\r\n";
         html_tag1 += "</head>\r\n\r\n";
         html_tag1 += "<body style='background:#000; color:#fff; font-size:100%;'>\r\n";
         html_tag1 += "ESP32 (ESP-WROOM-32)<br>\r\n";
         html_tag1 += "Access Point Selector<br>\r\n";
  String html_tag2 = "\r\n</body>\r\n</html>\r\n\r\n";

  client = server.available();

  if (client) {
    Serial.println("new client");
    String req_str = "";
    while (client.connected()){
      while(client.available()){
        req_str =client.readStringUntil('\n');
        if(req_str.indexOf("\r") == 0) break;
        Serial.println(req_str);
        if(req_str.indexOf("GET / ") >= 0){
          Serial.println("--------------- GET Request Receive from Clinet");
          while(client.available()){
            char c = client.read();
            Serial.write(c);
          }
          Serial.println("--------------- GET Request Receive Finish");
          html_send(false, "ESP32 STA connection close", "ESP32 STA connection close", "#FFF", html_res_head, html_tag1, html_tag2);

          delay(10);
          client.stop();
          Serial.println("client disonnected");
          delay(10);
          req_str = "";
        }else if(req_str.indexOf("GET /?") >= 0){
          Serial.println("--------------- SUBMIT Receive from Clinet");
          int16_t getTXT_pass = req_str.indexOf("pass1=");
          int16_t getTXT_select = req_str.indexOf("ssid_select=");
          int16_t getTXT_close = req_str.indexOf("connection_close=");

          if(getTXT_pass > 0){
            Sel_SSID_PASS_str = req_str.substring(getTXT_pass + 6, req_str.indexOf("&ssid_sel_submit"));
          }
          if(getTXT_select > 0){
            Selected_SSID_str = req_str.substring(getTXT_select + 12, req_str.indexOf("&pass1"));
          }
          if(getTXT_close < 0){
            Serial.printf("Selected_SSID_str = %s\r\n", Selected_SSID_str.c_str());
            Serial.printf("Sel_SSID_PASS_str = %s\r\n", Sel_SSID_PASS_str.c_str());

            while(client.available()){
              char c = client.read();
              Serial.write(c);
            }

            Serial.println("-------------- SUBMIT Request Receive Finish");
            String str_w = Selected_SSID_str + "\r\n" + Sel_SSID_PASS_str;

            SPIFFS_writeFile(AP_config_file, str_w.c_str());
            delay(500);

            char spiffs_read_ssid[64] = {};
            char spiffs_read_pass[64] = {};

            SPIFFS_readFile(AP_config_file, spiffs_read_ssid, spiffs_read_pass);
            Serial.printf("SPIFFS read ssid = %s\r\n", (const char *)spiffs_read_ssid);
            Serial.printf("SPIFFS read pass = %s\r\n", (const char *)spiffs_read_pass);
            Serial.printf("\r\n%s Connecting ...\r\n", Selected_SSID_str.c_str());

            delay(10); // Important! This delay is necessary to connect to the Access Point.
            WiFi.begin(spiffs_read_ssid, spiffs_read_pass);
            uint32_t timeout = millis();
            while(1){
              if(WiFi.status() != WL_CONNECTED){
                delay(500);
                Serial.print(".");
                if(millis()-timeout > 20000){
                  html_send(false, "disconnected", "STA disconnected Time OUT", "#F00", html_res_head, html_tag1, html_tag2);
                  break;
                }
              }else{
                LIP = WiFi.localIP();
                Serial.println("\r\nWiFi connected");
                Serial.print("Local IP address: "); Serial.println(LIP);
                Serial.printf("\r\n-----------%s Connected!\r\n", spiffs_read_ssid);

                EWG.EWG_NTP_TimeLib_init(9, "time.windows.com"); //NTPサーバー取得初期化
                String NowTime = String(year()) + "/" + String(month()) + "/" + String(day()) + "/ " + String(hour()) + ":" + String(minute());
                html_send(true, Selected_SSID_str, "ESP32 STA connect OK!<br>"+ NowTime, "#0F0", html_res_head, html_tag1, html_tag2);
                Serial.printf("Now Time %s\r\n", NowTime.c_str());
                break;
              }
            }
          }else{
            html_send(false, "---", "ESP32 STA Closed!", "#F00", html_res_head, html_tag1, html_tag2);
            WiFi.disconnect(false); //false=WiFi_ON , true=WiFi_OFF
          }

          delay(10);
          client.stop();
          Serial.println("client disonnected");
          delay(10);
          req_str = "";
        }else if(req_str.indexOf("GET /favicon") >= 0){
          favicon_response();
          req_str = "";
        }
      }
    }
  }
}
//*******************************************
void html_send(boolean sta_connected, String message1, String message2, String color, String html_res_head, String html_tag1, String html_tag2){
  client.print(html_res_head);
  client.print(html_tag1);
  client.print(HTML_Select_Box_str("!xxxx", message1));
  client.printf("<p style='color:%s; font-size:80%%'>%s</p>\r\n", color.c_str(), message2.c_str());
  if(sta_connected == true){
    client.print("<span style='font-size:80%'>ESP32 STA IP = ");
    client.print(LIP);
    client.print("</span>\r\n");
  }
  client.print(html_tag2);

  Serial.print(html_res_head);
  Serial.print(html_tag1);
  Serial.print(HTML_Select_Box_str("!xxxx", message1));
  Serial.printf("<p style='color:%s; font-size:80%%'>%s</p>\r\n", color.c_str(), message2.c_str());
  if(sta_connected == true){
    Serial.print("<span style='font-size:80%%'>ESP32 STA IP = ");
    Serial.print(LIP);
    Serial.print("</span>\r\n");
  }
  Serial.print(html_tag2);
}
//*******************************************
String HTML_Select_Box_str(String button_id, String Sel_Ssid){
  String str = "";
  String selected_str = "";
  str += "<form name='F_ssid_select'>\r\n";
  str += "  <select name='ssid_select'>\r\n";
  for(int i=0; i<ssid_num; i++){
    if(Selected_SSID_str == ssid_str[i]){
      selected_str = " selected";
    }else{
      selected_str = "";
    }
    str += "    <option value=" + ssid_str[i] +  selected_str + ">" + ssid_rssi_str[i] + "</option>\r\n";
  }
  str += "</select><br>\r\n";
  str += "Password<br><input type='password' name='pass1'>\r\n";
  str += "<br><button type='submit' name='ssid_sel_submit' value='send' style='background-color:#AFA;' onclick='document.getElementById(\"ssid_sel_txt\").innerHTML=document.F_ssid_select.ssid_select.value;'>STA Connection GO!</button>\r\n";
  str += "</form><br>\r\n";
  str += "<form name='F_connection_close'>\r\n";
  str += "  <button type='submit' name='connection_close' value='send' style='background-color:#FAA;' onclick='document.getElementById(\"ssid_sel_txt\").innerHTML=\"ESP32 STA connection close\";'>STA Connection Close</button>\r\n";
  str += "</form>\r\n";
  str += "<br>  (Selected SSID)<br><span id='ssid_sel_txt'  style='font-size:80%;'>";
  str += Sel_Ssid;
  str += "</span>\r\n";
  return str;
}
//*******************************************
void wifi_scan(uint32_t scan_interval){
  if( (First_Scan_Set == true) || ((millis() - scanLastTime) > scan_interval) ){
    Serial.println("scan start");

    // WiFi.scanNetworks will return the number of networks found
    ssid_num = WiFi.scanNetworks();
    Serial.println("scan done\r\n");
    if (ssid_num == 0) {
      Serial.println("no networks found\r\n");
    } else {
      Serial.printf("%d networks found\r\n\r\n", ssid_num);
      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;
        Serial.printf("%d: %s\r\n", i, ssid_rssi_str[i].c_str());
        delay(10);
      }
    }
    Serial.println("");
    scanLastTime = millis();
    First_Scan_Set = false;
  }
}
//*******************************************
void SPIFFS_writeFile(const char * path, const char *c_ssid_pass){
    Serial.printf("SPIFFS writing file: %s\n", path);
    File file = SPIFFS.open(path, FILE_WRITE);
    if(!file){
      Serial.println("Failed to open file for writing");
      return;
    }
    file.seek(0);
    if(file.print(c_ssid_pass)){
      Serial.println(c_ssid_pass);
      Serial.println("SPIFFS file written\r\n");
    } else {
      Serial.println("SPIFFS write failed\r\n");
    }
    file.close();
}
//*******************************************
void SPIFFS_readFile(const char * path, char ssid_c[], char pass_c[]){
    Serial.printf("SPIFFS reading file: %s\n", path);

    File file = SPIFFS.open(path);
    if(!file || file.isDirectory()){
      Serial.println("SPIFFS Failed to open file for reading");
      return;
    }

    int i=0, j=0;
    while(file.available()){
      ssid_c[i] = file.read();
      if(ssid_c[i] == '\r'){
        ssid_c[i] = '\0';
        char c = file.read();
        if(c == '\n'){
          while(file.available()){
            pass_c[j++] = file.read();
          }
          break;
        }
      }
      i++;
    }
    if(i < 1) Serial.println("cannot read ssid");
    if(j < 1) Serial.println("cannot read password");
    file.close();
}
//*******************************************
void favicon_response(){
  Serial.println(F("-----------------------Favicon GET Request Received"));
  while(client.available()){
    Serial.write(client.read());
  }

  client.print(F("HTTP/1.1 404 Not Found\r\n"));
  client.print(F("Connection:close\r\n\r\n"));

  delay(10);
  client.stop();
  delay(10);

  Serial.println(F("-----------------Client.stop (by Favicon Request)"));
}

【とりあえず解説】

●4行:
Arduino IDE 標準の Time ライブラリのインクルードです。

●5行:
私の自作ライブラリのインクルードです。
このライブラリ内で Time ライブラリもインクルードしています。

●7-8行:
ESP32 の soft AP モードの SSID とパスワードを定義します。
パスワード等は文字数が少ないとエラーになるかも知れませんので、多めの文字数にしてください。

●10行:
SPIFFS フラッシュにアップロードした空のテキストファイルを定義します。

●12-14行:
LIP は STAモードでアクセスポイントに接続出来た場合のローカルIPアドレスを格納します。
WiFiServer は、ESP32 をサーバーにするクラス名定義です。
WiFiClient は、スマホなどのクライアントクラス名定義です。

●16行:
私の自作ライブラリのクラス名定義です。

●27-46行:
セットアップ関数です。
36行で ESP32 を soft AP モードと STAモード(ステーションAPモード)の両対応にします。
WIFI_AP_STA という定数を使えば良いです。
37行目で soft AP モードを起動します。
42行で ESP32 をWebサーバーとして起動します。

●48-51行:
メインループです。
49行でスマホなどのクライアントからの接続を受け付けます。
53-165行で関数化しています。
50行では、アクセスポイントを30秒毎にスキャンしています。

●53-165行:
スマホなどのクライアントからの接続を受け付けます。
スマホからの HTTP GET リクエストを受信したら、54-63行にあるような HTMLタグを、82行でスマホへ送信します。

次に、スマホのセレクトボックスで選択したアクセスポイントで 送信ボタンが押されると、89行目以降にあるように、受信したテキスト文字列から、ssid_select や pass1 という文字列を検知して、SSID とパスワードを抽出します。

111行目でSSID とパスワードの文字列を、キャリッジリターンとラインフィード記号の「¥r¥n」で連結して、113行目で SPIFFSフラッシュに書き込みます。

その後すぐに、119行目で SSID と パスワードをフラッシュから読み込み、125行目にあるように STAモード(ステーションAPモード)でアクセスポイントに接続します。

ここで、重要ポイント!

124行目のように、WiFi.begen関数の前に delay(10) を入れてください
これを入れないと、Wi-Fi接続してくれません。

128-134行で、アクセスポイントに接続できない場合、131行目で20秒ほどでタイムアウトして、スマホへ HTMLタグを送信します。

STAモードでインターネットに接続してあるアクセスポイントに接続出来た場合、141行で私の自作ライブラリを使って NTPサーバーに接続し、時刻を取得します。
そして、143行目でその時刻と共にスマホブラウザへ HTML タグを返します。

148-150行では、スマホのブラウザ側で「STA Connection Close」ボタンが押されたら、STAモードでのアクセスポイントを切断します。

158-160行では、ブラウザが Google Chrome の場合、favicon をリクエストしてくるので、404エラーコードを返して正常終了させます。
これは、288-302行で関数化しています。

●167-189行:
スマホなどのブラウザに HTML タグを送信する関数です。
確認の為、クライアントへ送信したものと同じものをシリアルモニターにも表示させています。

●191-215行:
ブラウザ上でアクセスポイントのセレクトボックスを作成するための関数です。
一応、選択した SSID で再びブラウザ表示させた場合、そのアクセスポイントを選択状態「selected」させています。

●217-240行:
これは、Arduino core for the ESP32 のサンプルスケッチ、WiFiScan を少々改変させています。

●242-257行:
ESP-WROOM-32 の SPIFFSフラッシュへ文字列を書き込む関数です。
サンプルスケッチの SPIFFS_Test を改変しています。

●259-286行:
ESP-WROOM-32 の SPIFFS フラッシュにあるファイルから文字列を読み込んでいます。
先に説明した、SSID と パスワードを「¥r¥n」で連結した文字列から、SSID とパスワードを再抽出しています。

●288-302行:
ブラウザが Google Chrome の場合、GET リクエストを受信して、client.stop() で切断すると、直ぐに favicon リクエストが送信されてきます。
ここで、アイコンを送信しても良いのですが、面倒なので、404エラーを返して、正常にコネクション切断するようにしています。

では、次では実際にコンパイル書き込み実行を説明します。

コメント

  1. けり より:

    こんにちは!
    僕もこのような試みをESP8266でやったことがあります。htmlを作ったり結構めんどくさいんですよね ^^;

    最近はsmartConfigという機能を使っています。かなり楽です。
    https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WiFiSmartConfig/WiFiSmartConfig.ino
    ESP-IDFでもサポートされていますので、割と安心して使えると思います。
    もしすでにご存知でしたら失礼しました。

    • mgo-tec mgo-tec より:

      けりさん

      いつも Twitter ではお世話になっております。
      記事をご覧いただき、ありがとうございます。
      m(_ _)m

      smartConfig は使ったことが無いですね。
      これは、公共Wi-Fi などのオープンなアクセスポイントに自動接続するライブラリでしょうか?
      あまり詳しく調べてないのですが、このサンプルスケッチだけ見ても、イマイチ使い方がわかりません。。

      WiFiSTA ライブラリには、setAutoConnect などの関数があって、使い易そうですね。
      これらを使えば、もっと簡略化できるかもしれませんね。
      今度挑戦してみたいと思います。

      いつも有益な情報、ありがとうございます。
      感謝! 感謝!

  2. cypris より:

    こんにちは!
    こちらの記事を参考にさせていただいて,スマートフォンからESPの設定を行えるようにしました.しかし,androidなら動作するのですが,iphoneだとWi-Fiに接続できても,ブラウザにアクセスできません.
    そちらはiphoneでも動作していますでしょうか?
    ちなみにブラウザはsafari,google chrome等一通り試してみました.

  3. mgo-tec mgo-tec より:

    cyprisさん

    大変申し訳ございません。
    只今、当方の不具合により、過去記事全てのソースコードの’¥’マークと、改行が消えてしまいました。

    ブログを全面修正して、現在復旧しました。

    ただ、その時のコメント投稿も消えてしまいました。
    大変申し訳ございませんでした。
    このコードで試していただくよう、よろしくお願いいたします。
    m(_ _)m

  4. Tom より:

    cyprisさん

    こんにちは、教えてほしいのですが、スマホ(Android又はiPhone)から
    ESP32の設定変更の書換ができたそうですが、私は、訪問先のWiFiの
    ルータのSSIDとPassを書き換えられるようにしたいと思います。
    この辺のインターフェースの簡易版を作りたいのですが、注意点など
    教えて頂けませんか。
    宜しくお願いします。

    • mgo-tec mgo-tec より:

      Tomさん

      管理人の mgo-tec です。
      当ブログにお越しいただき、ありがとうございます。

      この質問は cypris さん宛てということで承りました。
      cyprisさん、ご覧でしたらご回答よろしくお願いいたします。

  5. juchang より:

    mgo-tec 様

    本テキストにチャレンジしています。
    設定したアクセスポイントへの接続まではできたのですが、ESP32 の soft IP アドレスへの接続ができません。
    Android 7.1.1 を使用、「接続できません」のメッセージとなります。
    アドバイスの程お願いいたします。

    • mgo-tec mgo-tec より:

      juchangさん

      いつも記事のコードを試していただき、ありがとうございます。

      当方で確認したところ、特に問題無く接続できました。
      Android 7.0 です。

      スマホのWi-Fi画面スイッチか、またはWi-Fi設定画面でsoft APへのアクセスは問題ないということですね。
      とすると、ブラウザのURL入力欄のURLはどのように入力されていますか?
      192.168.4.1
      と入力してアクセスできなかったら、
      http://192.168.4.1
      と入力してみてください。
      因みに、ブラウザはGoogle Chromeを使ってみて下さい。

      また、セキュリティーソフトで引っかかっている可能性があります。
      その場合は、一時的にセキュリティーソフトや、Wi-Fiセキュリティーソフトを解除してみてください。

      • juchang より:

        mgo-tec 様

        いつもご面倒をお掛けします。
        Google Chromeを使っていて、http://192.168.4.1 とも入力していますが、いずれも「応答時間が長すぎます。」とのメッセージとなります。
        「空の設定ファイルを SPIFFS フラッシュへアップロードする」というところが今一理解できていないのですが本トラブルと関係ありますでしょうか。
        smartConfig も閲覧しましたが、やはり関連が理解できません。
        ご指導の程よろしくお願い致します。

        • mgo-tec mgo-tec より:

          juchangさん

          そうですか。。。
          APconfig.txt は、station mode のアクセスポイント一覧を保存するためのファイルです。
          soft APのアクセスとは関係ありません。

          ならば、ブラウザのURL欄に192.168.4.1 を入力した後のシリアルモニターはどう表示されるか教えてください。

          • juchang より:

            mgo-tec 様

            シリアルモニターの表示を確認しましたところ、スマホのアクセスポイント選択で表示されるはずの「 15 – AP_STACONNECTED 」の表示がありませんでした。
            パスワードの変更(長めに)、ライブラリーの整理等試してみましたが効果がありませんでした。

          • mgo-tec mgo-tec より:

            juchangさん

            なるほど。
            Arduino IDE の「ツール」で「Core Debug Level」をverboseにしているということで良いでしょうか?
            それは外部Wi-FiルーターにSTA(stationモード)で接続できていないということのようです。

            でも残念ながら、情報が少なすぎて、どこが悪いのか全く判断できません。
            もっと情報をいただきたいので、それ以前のシリアルモニター表示はどのように表示されていますでしょうか?
            scan start
            scan done
            networks found
            などが表示されていますか?
            また、スキャンされたアクセスポイントのSSID一覧は表示されていますでしょうか?

  6. juchang より:

    mgo-tec 様

    「 Core Debug Level 」の設定が違っていました。
    verbose に設定し書き込みをしたところ下記のメッセージとなりました。
    [D][WiFiGeneric.cpp:336] _eventCallback():Event:15-AP_STACONNECTED
    dhcps:send_offer>udp_sendto result 0
    [D][WiFiGeneric.cpp:336] _eventCallback():Event:17-AP_STAIPASSIGNED
    scan start
    [D][WiFiGeneric.cpp:336] _eventCallback():Event:1-SCAN_DONE
    scan done

    9 networks found
    0:Buffalo-*-****
    1:**********

    接続の瞬間のみ、AP_STACONNECTED となりますが、その後表示しなくなります。

    • mgo-tec mgo-tec より:

      juchangさん

      私は今、最新版環境で動作確認しています。
      Arduino IDE ver 1.8.9
      Arduino core for the ESP32 1.0.2

      気になったのが、

      [D][WiFiGeneric.cpp:336] _eventCallback():Event:15-AP_STACONNECTED
      dhcps:send_offer>udp_sendto result 0
      [D][WiFiGeneric.cpp:336] _eventCallback():Event:17-AP_STAIPASSIGNED
      scan start
      [D][WiFiGeneric.cpp:336] _eventCallback():Event:1-SCAN_DONE
      

      という一連のところです。
      AP_STACONNECTED
      AP_STAIPASSIGNED
      というのは、なぜかSTAモードでアクセスポイントに接続したということになります。
      最初はSoft AP モードで起動せねばならず、STAモードで起動して、外部ルーターへ接続されてしまっていますね。
      これはおかしいですね。

      このメッセージの前に
      AP_START
      というメッセージがなければ、Soft AP モードで起動できていません。

      私の場合は以下のように表示されます。

      [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 0 - WIFI_READY
      [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 13 - AP_START
      [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 13 - AP_START
      

      この記事を書いた当初とArduino core の最新版とではメッセージの出方が違いますが、これは、Soft AP モードがスタートしたというメッセージで、これが正しい動作です。

      今一度、ソースコードの36行目、37行目が正しく入力されているか確認してみてください。
      また、Soft AP モードのSSID とパスワードは半角9文字以上にして、文字以外の記号は入れないで試してみて下さい。

      • juchang より:

        mgo-tec 様

        下記の動作環境となっています。
        Arduino IDE ver 1.8.9
        Arduino core for the ESP32 1.0.2

        Arduino IDE ver 1.8.5 でも試してみましたが同じ結果となります。

        36行目: WiFi.mode(WIFI_AP_STA);
        37行目: WiFi.softAP(ap_ssid, ap_pass);
        としていますが正しいですか。

        SSID とパスワードも半角9文字以上としています。

        ご確認の程お願いいたします。

      • juchang より:

        mgo-tec 様

        その後の調査で、書き込み後シリアルモニターを立ち上げた時点では、[D][WiFiGeneric.cpp:336] _eventCallback(): Event: 0 – WIFI_READY
        [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 13 – AP_START
        [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 13 – AP_START
        と表示されていることを確認しました。
        その後の、スマホのアクセスポイントへの接続後に、AP_STACONNECTED
        AP_STAIPASSIGNED
        となっています。

        • mgo-tec mgo-tec より:

          juchangさん

          なるほど。
          AP_START が出て、その後、スマホからのWi-Fiアクセスポイント接続で、ESP32 の Soft AP モードとの接続は出来ているようですね。
          因みに、シリアルモニターには
          Server started
          という文字は表示されていますか?

          Soft APモード接続は出来ているとすると、あとはブラウザとの通信がうまく行っていません。
          Server started という文字が表示された後、ESP32 はサーバーとして動作します。

          その文字が表示されているならば、ブラウザのURL入力欄に 192.168.4.1 を入力してエンターを押した後、シリアルモニターに
          new client
          という文字は表示されますか?

          • juchang より:

            mgo-tec 様

            [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 0 – WIFI_READY
            [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 13 – AP_START
            [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 13 – AP_START
            Setup done
            Server started
            scan start
            [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 1-SCAN_DONE
            scan done
            ここまでテキストと同じ表示となっています。

            ブラウザのURL入力欄に「 192.168.4.1 」を入力してエンターを押してもシリアルモニターには何の変化もありません。

          • mgo-tec mgo-tec より:

            juchangさん

            それは謎ですね。
            ということは、ソースコードの67行目を通過できていないということです。
            ESP32がclientを認識できないようです。
            ソースコードの50行と51行の間に
            delay(1);
            を入れてみて下さい。

            また、Arduino IDE 「ツール」のボード設定は私は以下のようにしています。

            ボード:  ESP32 Dev Module
            Upload Speed:  921600
            CPU Frequency:  240MHz (WiFi/BT)
            Flash Frequency:  80MHz
            Flash Mode:  QIO
            Flash Size:  4MB (32Mb)
            Partition Scheme:  Huge APP (3MB No OTA/1MB SPIFFS)
            Core Debug Level:  なし
            PSRAM:  Disabled
            シリアルポート:  ※ご自分の M5Camera のUSBポート
            ————————————-
            書込装置: USBasp
            

            Partition Scheme の設定を換えたら、新たにSPIFFSにファイルを書き込む必要がありますので、ご注意ください。
            Partition Schemeは基本的に変えなくても良いと思います。

            以上で試してダメならば、もしかしたら Android のバージョンが影響しているのかも知れません。
            私のAndroidはバージョンが低いために、セキュリティーが甘いのかも知れません。
            もしかしたら、Android7.1の場合は動作しないのかもです。
            私はスマホを買い替える予算が無くて、今は試せません。
            申し訳ありません。

          • mgo-tec mgo-tec より:

            あ!
            ちょっと待ってください。
            調査中~!

          • mgo-tec mgo-tec より:

            juchangさん

            当方のAndroidバージョンは8.0.0の間違いでした。
            大変失礼いたしました。
            m(_ _)m
            それでも一世代前の端末ですが。。。

            いずれにしても、Android 7.1.1の端末は持っていないので、if(client)文を通過できないのは原因不明です。
            今日中には調査できないと思いますので、今後分かり次第お知らせしたいと思います。

  7. juchang より:

    mgo-tec 様

    50行と51行の間に
    delay(1);
    を入れることにより、「 AP_STAIPASSIGNED 」の表示は出なくなりましたが、「 192.168.4.1 」を入力してもシリアルモニターの変化はありませんでした。
    Setup done
    Server started
    scan start
    [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 1 – SCAN_DONE
    scan done

    9 networks found

    0: *************(-84dBm)*
    1: ********** (-85dBm)*
    省略
    8: *********** (-93dBm)*

    [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 15 – AP_STACONNECTED
    scan start
    [D][WiFiGeneric.cpp:336] _eventCallback(): Event: 1 – SCAN_DONE
    scan done
    *networks found
    繰り返し

    貴重なお時間を割いてお付き合いいただきありがとうございました。
    今後ともよろしくお願い致します。

    • mgo-tec mgo-tec より:

      juchangさん

      そうですか。。。
      たぶん、私は原因究明できそうもないです。
      ソースコードの65行、67行で、
      client = server.available();
      if(client){
      のところを通過できないということは、ESP32のサーバープログラムが動作しないということです。

      この記事を書いた当初は Android7.0 で動作確認しているので、Android7.1.1も同様かと思われます。
      juchangさんの端末で何か他のWi-Fiセキュリティーソフトが働いませんでしょうか?

      ソースコードの転記で誤りが無ければ、私には原因究明できそうもありません。
      お手上げです。
      申し訳ございません。
      m(_ _)m

      • mgo-tec mgo-tec より:

        今後何か分かったらお知らせしたいと思います。
        お役に立てず、申し訳ございません。
        m(_ _)m

        • juchang より:

          mgo-tec 様

          諦めきれずに試行錯誤を繰り返しています。
          Webで検索している中で、WiFi接続時の電圧降下が原因ではとのやり取りがあり、独立電源が良いという情報がありました。
          具体的なやり方が理解できないため、mgo-tec さんにお伺いを立てたいと思います。
          又、「ツール」のボード設定で、Core Debug Level は「なし」と「 Verbose 」のどちらが良いのかご教示の程お願いいたします。

          • mgo-tec mgo-tec より:

            juchangさん

            すみません。
            当方では症状が出ないため、検証ができないでいます。
            ESP32-DevKitC か ESPr Developer 32 または M5Stack をお使いでしたら、私の方で症状が出ないので、どうやって解決したら良いのか分からない状態です。
            その他のボードでしたら、もしかしたら電源の可能性もありますね。

            電源はUSBハブを使わないことが重要です。
            また、PCのUSBポート(できればUSB3.0)に直挿しして、USBケーブルは太く短く、良質のケーブルを使用してください。
            格安USBケーブルは要注意です。

            また、長い間、ESP32 の大電流によって、PCのUSBポートが破壊されている場合があります。
            その場合はモバイルUSB電源等で試してみるというのも手です。

            Core Debug Level は「なし」にしていた方が、シリアルモニターへの文字出力が減るために安定動作します。
            「Verbose」はどこでプログラムがストップしたかを探るのに有効で、問題無い時は「なし」にする方が良いです。

          • mgo-tec mgo-tec より:

            juchangさん

            そういえば、ひとつ心当たりがありました。
            ソースコードの36行目の前に

            WiFi.disconnect(true, true);

            を追記してコンパイル書き込みしてみてください。
            これは、WiFi設定を全てリセットする関数です。
            昔、私のESP32がWiFiに繋がらなくなった時に、WiFi.begin関数の前にこれを試したら、無事接続できました。

            それでもダメなら、Arduino core for the ESP32 の「スケッチの例」にあるサンプルスケッチ
            SimpleWiFiServer
            の WiFi.begin 関数の1行を以下の2行に書き換えます。

            WiFi.mode(WIFI_AP_STA); //AP mode and STA mode
            WiFi.softAP(ap_ssid, ap_pass);
            

            ap_ssid と ap_pass はそれぞれ半角9文字以上にしてください。
            それでコンパイル書き込みしてみてください。
            それでもうまく行かない場合は、ESP32が故障しているか、USBポートが壊れているという原因になると思います。

            以上、ご確認くださいませ。

  8. 匿名 より:

    mgo-tec 様

    接続成功!
    大変ご面倒をお掛け致しました。
    softAP のアクセスポイントをタップしパスワードを入力すると、「接続先にインターネット接続がありません」というところまではテキスト通りだったのですが、その後「このネットワークはインターネットに接続していません。接続を維持しますか?」というメッセージが出ていたのに気付きませんでした。
    「はい」をタップし、ブラウザの URL 入力欄に「 192.168.4.1 」と入力すると無事接続できるようになりました。
    その後の操作もテキスト通りとなります。
    いつも単純なミスでご迷惑をお掛けし申し訳ありません。
    これでカメラモジュールに集中して取り組むことができます。
    ありがとうございました。

    • mgo-tec mgo-tec より:

      juchangさん

      そういうことでしたか。
      そういえば、この記事には実際の動画を掲載していなかったので、スマホからどういうメッセージが出るか分からないですね。
      この記事の後、セキュアなSSL通信を使ったアクセスポイントセレクターを作った時には動画をアップしていたので、そちらを見ればスマホ側のメッセージ表示が見られました。
      Arduino – ESP32 で SSLサーバーを構築し、セキュアな Wi-Fi アクセスポイントセレクターを作ってみました

      できるだけ動画をアップした方が良いのかな? と、今回、私自身も学びました。
      何にしても、ちゃんと動作してホッとしました。
      こちらこそ、いつもいろいろ試していただき、ありがとうございました。

  9. tama より:

    スマホorノートPCでAPを設定したくてこのページに辿りました。

    早速、掲載されているソースを実行したところ、 SSIDとPASSはSPIFFSに
    保存できたのですが、143行目の内容がスマホに表示されない時があります。
    (例えば、同じSSIDとPASSを入力した時等)

    おそらく、125行目のWiFi.beginの影響があるかなと思うのですが
    どのように対処したらよろしいでしょうか?
    ArduinoのVerは1.8.12です。

    • mgo-tec mgo-tec より:

      tamaさん

      記事をご覧いただき、ありがとうございます。

      この記事も随分前に作成して、すっかり忘れていました。
      久々に動かしてみたところ、私の場合は特に問題無く動作しました。
      私の環境は以下です。
      Arduino core for the ESP32 ver 1.0.4
      Arduino IDE 1.8.13
      Windows 10

      143行目の内容が表示されない時があるということは、緑色のNTP時刻表示のところのことですね。
      他は表示されているということは、WiFiアクセスポイントに接続はできていて、WiFiルーターからNTPサーバーへ接続がうまくいっていないと考えられます。
      表示されるときと表示されない時があるということならば、NTPサーバーを変えてみるという手もあります。
      今現在が、time.windows.com ならば、
      ntp.nc.u-tokyo.ac.jp
      に変えてみるとか、
      time.nist.gov
      に変えてみるとかでしょうか。
      これで試してみて下さい。

  10. tama より:

    mgo-tec 様

    ご回答ありがとうございます。

    NTPサーバーを、time.windows.com、ntp.nc.u-tokyo.ac.jp、time.nist.govに
    変えてみましたが、表示されない時がありました。
    (追記:表示されない時というのは、Firefoxの画面に
    「192.168.4.1 のサーバーからの応答が一定時間以内に返ってきませんでした。」
    というメッセージが出てしまい、以後ESPからは切断された状態になります。)

    なお、シリアルモニタのログを見ると、
    [SSID] Connecting …
    …….
    WiFi connected
    Local IP address: 192.168.0.177
    ———–[SSID] Connected!
    time.nist.gov: 132.163.97.3
    Transmit NTP Request
    Receive NTP Response
    HTTP/1.1 200 OK
    Content-type:text/html
    Connection:close

    143行で出力されるhtmlソース

    Now Time 2020/10/19/ 10:11
    client disonnected

    上記のように、ESPからNTPサーバーに接続して時刻は取得できているようなのですが、
    時々、その結果を端末側に送信してくれない事があります。

    ちなみに、ハードはTTGO T-Cameraというのを使っています。

    • mgo-tec mgo-tec より:

      tamaさん

      なるほど、そういうことだったんですね。
      ちょっとまだ情報が少なくて判断できませんので、次のことを教えてください。

      1.スマホのOS種類(iOSなのかAndroidなのか)とバージョン
      2.Arduino core for the ESP32 のバージョン
      3.「STA Connection GO!」ボタンは毎回確実に表示されていて、それを押した後に192.168.4.1からの応答が無いことがあるという認識でよろしいでしょうか?
      4.この記事は、ブラウザはGoogle Chromeでしか実験しておりません。Google Chromeで試されましたでしょうか? 

  11. tama より:

    mgo-tec 様

    すみません、こちらのミスが原因で今回の不具合が起きたようです。
    というのも、昨日から見てるソースの一部に改変した所があって
    これはまずいと思い、もう一度掲載されたソースを
    Android端末(Ver10、Chrome)で実行すると、
    今度は143行目の内容が毎回正常に表示されるようになりました。

    下手にカスタマイズしていたのを見落としていたようです。
    お手数おかけしてすみませんでした。

    • mgo-tec mgo-tec より:

      tamaさん

      そうだったんですか。
      原因が分かって良かったですね。

      実は、この質問を検証していて、久々にsoftAPモードを動かしていて、全然つながらなくてハマりました。
      Androidスマホの画面にポップアップメッセージが出ているのを無視していたことが原因だったのです。
      ここのコメント投稿で、以前、juchangさんが悩んでいたことを私がすっかり忘れていたんです。
      これはハマる人もいるだろうなと思ったので、トラブルシューティング記事に追記しました。

      このキッカケがなかったらスルーしていたので、逆に質問頂いて気付けて良かったです。
      ありがとうございました。
      m(_ _)m

タイトルとURLをコピーしました