1. WiFiルータにつなぐ
ESP32をWiFiルータに接続する時、マニュアルに倣い下記のようなコードを書きますが、なぜか全然つながらないことがあります。開発ボードのリセットスイッチを押して起動し直すと すんなりつながったりして、なんなんだろう?と思った経験がありませんか?
Serial.print("WiFi connecting");
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print('.');
}
Serial.println("\nWiFi connected.");
今回は結論を先に書きます。今後は下記のようなコードに変えましょう。つながらないイライラが解消します。
bool done = true;
WiFi.begin(ssid, pass);
while (done) {
Serial.print("WiFi connecting");
auto last = millis();
while (WiFi.status() != WL_CONNECTED && last + 5000 > millis()) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
done = false;
} else {
Serial.println("retry");
WiFi.disconnect();
WiFi.reconnect();
}
}
Serial.println("\nWiFi connected.");
2. つながらない時は何が起こっているのだろう?
冒頭のコードの実行ログをシリアルモニタで確認してみました。
STA_CONNECTED
イベントが発生していながらも、statusがWL_CONNECTED
にならないため、ずーっと.
が続きます。
20:55:54.382 -> [ 3][D][esp32-hal-cpu.c:244] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz
20:55:54.825 -> [ 450][I][esp32-hal-psram.c:96] psramInit(): PSRAM enabled
20:55:54.892 -> [ 483][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 0 - WIFI_READY
20:55:54.960 -> WiFi connecting[ 574][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 2 - STA_START
20:55:54.994 -> [ 616][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 4 - STA_CONNECTED
20:55:55.476 -> ..........................................................................................
下記は数秒でつながった時のログ。比較すると、上は、IPアドレスの払い出しを待っているように見えます。(固定IPならこんなことにならないのかも?)
20:58:32.511 -> [ 3][D][esp32-hal-cpu.c:244] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz
20:58:32.955 -> [ 450][I][esp32-hal-psram.c:96] psramInit(): PSRAM enabled
20:58:32.955 -> [ 483][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 0 - WIFI_READY
20:58:33.056 -> WiFi connecting[ 572][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 2 - STA_START
20:58:33.089 -> [ 615][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 4 - STA_CONNECTED
20:58:33.572 -> ......[ 3989][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 7 - STA_GOT_IP
20:58:36.509 -> [ 3989][D][WiFiGeneric.cpp:991] _eventCallback(): STA IP: 192.168.21.114, MASK: 255.255.255.0, GW: 192.168.21.1
20:58:36.577 -> .
20:58:36.577 -> WiFi connected.
つながらない時は、DHCPプロトコルのシーケンスのどこか途中で止まっているのでしょうか? TCP/IPレベルのパケットトレースを確認すればより詳しくわかると思いますが、今回はそこまでしていません。
今回、この記事にまとめるにあたり、ESP32のハードリセットによりWiFi接続までの所要時間を測ってみました。(WiFi connecting
からWiFi connected
までのシリアルモニタのタイムスタンプ時間[秒])
回 | 所要時間 | 回 | 所要時間 | 回 | 所要時間 | ||
---|---|---|---|---|---|---|---|
1 | 0.519 | 21 | 3.530 | 41 | 7.521 | ||
2 | 0.508 | 22 | 7.509 | 42 | 135.502 | ||
3 | 1.529 | 23 | 0.508 | 43 | 0.489 | ||
4 | 0.521 | 24 | 3.521 | 44 | 3.527 | ||
5 | 0.515 | 25 | 0.487 | 45 | 0.515 | ||
6 | 0.510 | 26 | 3.509 | 46 | 0.489 | ||
7 | 3.000 | 27 | 0.515 | 47 | 0.517 | ||
8 | 0.490 | 28 | 0.485 | 48 | 0.512 | ||
9 | 0.511 | 29 | 3.535 | 49 | 3.488 | ||
10 | 0.506 | 30 | 3.521 | 50 | 0.483 | ||
11 | 0.513 | 31 | 30.514 | 51 | 0.513 | ||
12 | 0.518 | 32 | 0.514 | 52 | 0.520 | ||
13 | 3.512 | 33 | 7.510 | 53 | 0.517 | ||
14 | 3.508 | 34 | 7.516 | 54 | 0.511 | ||
15 | 45.520 | 35 | 60.502 | 55 | 0.487 | ||
16 | 0.501 | 36 | 60.489 | 56 | 0.519 | ||
17 | 0.517 | 37 | 120.526 | 57 | 0.510 | ||
18 | 0.524 | 38 | 150.539 | 58 | 7.505 | ||
19 | 0.511 | 39 | 0.514 | ||||
20 | 0.495 | 40 | 0.522 |
ほとんどが数秒ですが、少ないながら長時間を要す時もありました。
所要時間 | 回数 | 割合 | 備考 |
---|---|---|---|
1秒以下 | 35 | 60% | 平均0.508秒 |
3秒以下 | 2 | 3% | 平均2.265秒 |
5秒以下 | 9 | 16% | 平均3.517秒 |
8秒以下 | 5 | 9% | 7.505秒、7.509秒、7.510秒、7.516秒、7.521秒 |
10秒以上 | 7 | 12% | 30.514秒、45.520秒、60.489秒、60.502秒、 120.526秒、135.502秒、150.539秒 |
全体 | 58 | 平均11.985秒 | 10秒以上の7回が全体平均を押し上げている |
10秒以上の7回は永久につながらないのかと思うほどでしたが、内部でリトライしているのでしょうか、いつかはつながる結果となりました。でも、2分半(150秒)はかかりすぎです。
さらに要約すると『およそ8割は5秒以下でつながっている』ということで、冒頭の5秒の根拠としました。
所要時間 | 回数 | 割合 | 備考 |
---|---|---|---|
5秒以下 | 46 | 79% | 平均1.173秒 |
5秒超え | 12 | 21% | 平均53.429秒 |
58回とサンプル数は少ないですが、『5秒待ってつながらない時は、reconnectした方が早くつながる。』が今回の結論。
3. 先人に学ぶ
WiFiにつながらないこの現象に遭遇したとき、回避策をネットで検索したところ、次の二つの記事を見つけました。
一つ目の『ソフトウェアリセット』の記事は、何回かループしても接続できない場合は、プログラムからリセット
する内容です。
int lpcnt=0 ;
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
lpcnt +=1 ;
if (lpcnt>10) { ESP.restart(); }
Serial.print(".");
}
手動でリセットスイッチを押す手間が省けますね。
二つ目の『WiFiが切れている時に再接続する』記事は、一定時間後に再接続する
という内容です。
冒頭のコードは、この記事を参考にさせていただきました。ありがとうございます。
unsigned long currentMillis = millis();
// if WiFi is down, try reconnecting
if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
Serial.print(millis());
Serial.println("Reconnecting to WiFi...");
WiFi.disconnect();
WiFi.reconnect();
previousMillis = currentMillis;
}
4. 検証
今回のコードがどれほど有効なのか検証しました。
所要時間 | 回数 | 割合 | 備考 |
---|---|---|---|
1秒以下 | 55 | 59% | 平均0.507秒 |
4秒以下 | 16 | 17% | 平均2.998秒 |
6秒以下 | 22 | 24% | 平均5.502秒 全数がリトライによるもの |
全体 | 93 | 平均2.117秒 |
・1秒以下が全体の6割という点は、前回と同じ。
・5秒を超えた22回の全数がリトライによるものですが、6秒以下となっています。
つまりリトライ後は1秒でつながったとう結果で、効果絶大であると思います。
リトライが発生した22回のログを検証しましたが、リトライ後はすべて.
が1個しか出ていませんでした。
21:24:19.128 -> [ 3][D][esp32-hal-cpu.c:244] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz
21:24:19.571 -> [ 450][I][esp32-hal-psram.c:96] psramInit(): PSRAM enabled
21:24:19.606 -> [ 482][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 0 - WIFI_READY
21:24:19.684 -> [ 574][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 2 - STA_START
21:24:19.718 -> WiFi connecting[ 620][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 4 - STA_CONNECTED
21:24:20.214 -> ..........retry
21:24:24.730 -> WiFi connecting[ 5585][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 5 - STA_DISCONNECTED
21:24:24.730 -> [ 5585][W][WiFiGeneric.cpp:950] _eventCallback(): Reason: 8 - ASSOC_LEAVE
21:24:24.730 -> [ 5616][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 4 - STA_CONNECTED
21:24:24.770 -> [ 5656][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 7 - STA_GOT_IP
21:24:24.811 -> [ 5657][D][WiFiGeneric.cpp:991] _eventCallback(): STA IP: 192.168.21.114, MASK: 255.255.255.0, GW: 192.168.21.1
21:24:25.206 -> .
21:24:25.206 -> WiFi connected.
これなら、閾値を5秒ではなく、1秒にした方が全体平均は2秒以下になるのでは?と推定し、実験してみました。コードは5秒を1秒に変えるだけです。
bool done = true;
WiFi.begin(ssid, pass);
while (done) {
Serial.print("WiFi connecting");
auto last = millis();
- //while (WiFi.status() != WL_CONNECTED && last + 5000 > millis()) {
+ while (WiFi.status() != WL_CONNECTED && last + 1000 > millis()) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
done = false;
} else {
Serial.println("retry");
WiFi.disconnect();
WiFi.reconnect();
}
}
Serial.println("\nWiFi connected.");
所要時間 | 回数 | 割合 | 平均 |
---|---|---|---|
1秒以下 | 26 | 68% | 0.504秒 |
1秒超え | 12 | 32% | 1.503秒 全数がリトライによるもの |
全体 | 38 | 0.819秒 |
・1秒以下が全体の7割弱という傾向は、これまでと同じ。
・1秒を超えた12回の全数がリトライによるものですが、2秒未満となっています。
サンプル数が38回と少ないですが、全体平均が1秒以下になり、これが本当の正解か?
5. さらなる改善?
もしかしたら、ステータスWL_CONNECTED
を確認する前に、reconnect
したら、もっと改善できるのかも?と考え、コードを以下のように変更して実験しました。
bool done = true;
WiFi.begin(ssid, pass);
while (done) {
Serial.print("WiFi connecting");
WiFi.disconnect();
WiFi.reconnect();
auto last = millis();
while (WiFi.status() != WL_CONNECTED && last + 1000 > millis()) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
done = false;
} else {
Serial.println("retry");
}
}
Serial.println("\nWiFi connected.");
実は、1度もリトライが発生しなくなるのでは?と想定していましたが、結果は違いました。
所要時間 | 回数 | 割合 | 平均 |
---|---|---|---|
1秒以下 | 26 | 74% | 0.499秒 |
2秒以下 | 9 | 26% | 1.508秒 全数がリトライによるもの |
全体 | 35 | 0.758秒 |
全35回のうち、9回リトライが発生しました。リトライの割合は減りましたが、全体平均はさほど改善が見られません。
また、いきなりdisconnect
、reconnect
も不自然ですし、この方法は却下します。
6. 最終結論
以上の実験から、今回は『1秒待ってつながらない時は、reconnectした方が早くつながる。』を最終結論とします。(冒頭の5秒説を訂正いたします。)
通常は0.5秒でつながり、たまにリトライが発生しても、1.5秒でつながります。劇的な改善です。
bool done = true;
WiFi.begin(ssid, pass);
while (done) {
Serial.print("WiFi connecting");
auto last = millis();
while (WiFi.status() != WL_CONNECTED && last + 1000 > millis()) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
done = false;
} else {
Serial.println("retry");
WiFi.disconnect();
WiFi.reconnect();
}
}
Serial.println("\nWiFi connected.");
ただし、使用しているWiFiルータにも依ると思われますので、閾値は参考とお考えください。
(今回は、Aterm WG1200HS4 PA-WG1200HS4、 IEEE802.11g(2.4GHz帯)を使用)
以上、何かの参考になれば嬉しいです。