ラズパイPicoWを固定IPで使おうとしたら不思議な挙動をした話
はじめに
- 内容はタイトルの通りです。この挙動があると困る、解決したいという方のために残しておきます。
- 開発環境はArduino IDEです。
- デバイスはRaspberry Pi Pico Wです。WiFiアクセスポイントに接続し、固定IPで使用します。
- Arduino IDEでラズパイPicoWを使えるようにする方法は割愛します。
この記事で扱う不思議な挙動
ネットで調べながら、
- ラズパイPicoWをWiFiアクセスポイントに接続する。
- 固定IPで使用する。
を実現しようとした場合、概ね、以下のようなコードを書くことになると思います。
#include <WiFi.h>
const char* ssid = "アクセスポイントのSSID";
const char* password = "パスワード";
IPAddress myIP(192,168,1,10); //希望する固定IP
void setup() {
Serial.begin(115200);
delay(1000);
//一旦切断
WiFi.disconnect(true);
delay(500);
//設定
WiFi.mode(WIFI_STA);
WiFi.config(myIP);
//WiFiモジュール起動
WiFi.begin(ssid, password);
//接続待ち
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(500);
}
//接続されたらIPを表示する(デバッグ用)
Serial.println();
Serial.println(WiFi.localIP());
}
void loop() {
//任意の処理
}
指定したアクセスポイントが存在する場合、上記は(一見)正常に動作します。
指定したアクセスポイントが存在しない場合、不思議な挙動を示します。
アクセスポイントがないにもかかわらず、接続待ちをスルーして、その後の処理が実行されてしまうのです。
IPを固定しない(WiFi.configをコールしない)場合は、この挙動は発生しません。
期待する挙動と解決策
ここで期待するのは、アクセスポイントを見つけて接続するまではwhileを抜けない、という挙動のはずです。
とにかく解決策が知りたい方のために、先に結論を書きます。
WiFi.beginとwhileの間に、2秒程度のdelayを入れてください。以上。
何が起きているのか
WiFi.statusの値が想定と違うのだろうと予想を立てて、WiFi.beginと接続待ちの間に確認のコードを追加してみます。
//WiFiモジュール起動
WiFi.begin(ssid, password);
+ for(int i = 0; i < 1000; i++) {
+ Serial.println();
+ Serial.printf("%03d : %d", i, WiFi.status());
+ delay(10);
+ }
//接続待ち
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(500);
}
10msに1回、iとWiFi.statusを表示します。
結果は以下のようになりました。
-
WiFi.configを呼び、アクセスポイントがない場合
i = 66程度まで、WiFi.status = 3(= WL_CONNECTED)
以降、WiFi.status = 4(= WL_CONNECT_FAILED) -
WiFi.configを呼ばず、アクセスポイントがない場合
i = 66程度まで、WiFi.status = 6(= WL_DISCONNECTED)
以降、WiFi.status = 4(= WL_CONNECT_FAILED)
以上の結果から、WiFi.configを呼んでからWiFi.beginした(=固定IPで使おうとした)場合、WiFi.begin直後は(アクセスポイントがないため、絶対に接続できていないにもかかわらず)接続成功のステータスが返ってくるということが分かります。
解決策を考える
ラズパイPicoWは、固定IPで使おうとした場合、WiFi起動から660ms程度の間、アクセスポイントにつながっていなくてもつながっていると主張することが分かりました。
もっとも簡単な解決策は、ラズパイPicoWが「俺、つながってないな?」と気づくまでdelayで待ってあげることでしょう。
ここで、delayではなく以下のようにしたらどうか?と思う方もいるのではないでしょうか。
//WiFiモジュール起動
WiFi.begin(ssid, password);
+ while(WiFi.status() == WL_CONNECTED){
+ //ラズパイがアクセスポイントにつながっていないことに気づくまで待つ
+ }
//接続待ち
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(500);
}
一見、固定のdelayを入れるより効率的に思えますが、結論から言うとNGです。
これはアクセスポイントがある場合にWiFi.statusが以下のように変化することが確認できるためです。
-
WiFi.configを呼び、アクセスポイントがある場合
WiFi.begin直後からずっとWiFi.status = 3(= WL_CONNECTED) -
WiFi.configを呼ばず、アクセスポイントがある場合
アクセスポイントに接続するまで、WiFi.status = 6(= WL_DISCONNECTED)
接続後、WiFi.status = 3(= WL_CONNECTED)
固定IP設定でWiFi.beginした場合、追加したwhileを抜けないことが分かります。
まとめ
- Raspberry Pi Pico Wを固定IPで使用した場合、WiFi.begin直後は、アクセスポイントにつながっていなくてもつながっていると思い込む。
- アクセスポイントがない場合のことを考えて、つながっていないと気づくまで待つ処理(ただのdelayですが)を入れましょう。
- アクセスポイントがある場合は、つながっていないのに気づく間もなく、本当につながってしまうので、whileで気づくのを待つことはできません。おとなしく固定のdelayを入れて我慢しましょう。
Discussion