WROOM-02相互間の通信でハマル

ESP-WROOM-02で構成したwifiアクセスポイント+webサーバーに、やはりESP-WROOM-02で構成したwifiステーション+httpクライアント( +Arduino ADK)から接続しようとすると、wifi-APに接続できないという問題に直面した。
なお、使用中のWROOM-02のsdkバージョンは1.3.0であり、その後のバージョンでは動作が異なるのかもしれない。

wroom02 x 2

現在の構想では、スマホからUSB経由でArduino ADKに指示をだし、Arduino ADKのSerial1(Tx1, Rx1)を介してステーション側のWROOM-02(STA-ESPと表記)にリクエスト文字列を送る。STA-ESPは、その内容を解釈してwifi AP接続とhttpリクエストを実行し、アクセスポイント側WROOM-02 (AP-ESPと表記)に接続した2つのサーボを駆動しようとしている。AP-ESP側の構成については ESP-WROOM-02のWEBサーバーで電動サーボ雲台 を参照。

すんなりできると思ったのだけど、STA-ESPからAP-ESPへのwifi接続が成功しないのである。STA-ESPからインターネットに出るためのwifiルーターへの接続や、インターネット上のwebサーバーに対するリクエストはすんなり成功してくれるが、STA-ESPからAP-ESPに接続しようとしてもつながらない。検証のため、AP側STA側ともに実験用のスケッチに戻して試してみた。

wifi-AP側(AP-ESP)

今回の検証時には、wifiアクセスポイントの構成およびスケッチとして、httpリクエストに応じてLEDをチカチカさせたときのものを使った(ESP-WROOM-02のWEBサーバーでLEDをチカチカさせてみる)。この構成は、
http://192.168.4.1/cmd?LED=led_options というリクエストを受け付けると、led_optionsの内容に応じてLEDのチカチカ具合を変えるだけのもの。

wifi-STA側(STA-ESP)

ESP-WROOM-02用開発環境でスケッチを動かす という項で使ったスケッチを若干手直しして以下のようなものを用意した。

#include <ESP8266WiFi.h>
extern "C" {
  #include "user_interface.h"
}

static const char* my_ssid     = "ESP8266AP2";
static const char* my_password = "password";
static const char* remote_host = "192.168.4.1";

void dead(const char* msg) {
  Serial.println(msg);
  for(;;) // stall..
    delay(10);
}

void setup() {
  Serial.begin(115200);
  delay(10);
  const char* cp = system_get_sdk_version();
  if (cp)
    Serial.println("\nsystem_get_sdk_version() returns " + String(cp) + "\n");
//  WiFi.begin("dummy-ssid-abcd", "password"); // connect to another esp8266
//  delay(500);
  Serial.println("\n\nConnecting to AP...");
  WiFi.begin(my_ssid, my_password); // connect to another esp8266
  WiFi.mode(WIFI_STA);
  int n = 0;
  int counter = 0;
  while ((n = WiFi.status()) != WL_CONNECTED) {
    Serial.print(String(n) + " ");
    delay(400);
    if (counter++ > 25) {
      Serial.println("\nwifi-connect failed\nprintDiag()\n");
      WiFi.printDiag(Serial);
      dead(WiFi.localIP().toString().c_str());
    }
  }
  Serial.println("\nconnected. WiFi.printDiag()\n");
  WiFi.printDiag(Serial);
  Serial.println(WiFi.localIP().toString());
  Serial.println("");
}

void loop() {
  delay(1);
  Serial.println("connecting to " + String(remote_host));
  WiFiClient client;
  if (!client.connect(remote_host, 80)) {
    dead("connection failed");
  }
  client.print("GET /cmd?LED=blink2  HTTP/1.1\r\nHost: " + String(remote_host) + "\r\nConnection: close\r\n\r\n");
  delay(10);
  while(client.available()){
    String line = client.readStringUntil('\n');
    Serial.println(line);
  }
  Serial.println();
  Serial.println("closing connection");
  client.stop();
  dead("finished.");
}

このスケッチは、コードの先頭付近にあるAPに接続できたらremote_hostにconnect()し、”/cmd?LED=blink2″というURIを投げますよ、というだけのもので、電源投入またはリセットによって動作を開始する。wifi APへの接続成功/失敗時ともに、WiFi.printDiag(Serial); によってステーションの状態をシリアルモニタに出力する。
検証の主眼は29~36行目のwhile()ループでの WiFi.status() の取得と表示にある。

実行結果1

STA-ESPのリセット後のシリアルモニタには、おおよそ以下のような内容が表示された。

system_get_sdk_version() returns 1.3.0

Connecting to AP...
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
wifi-connect failed
printDiag()

Mode: STA
PHY mode: N
Channel: 1
AP id: 0
Status: 0
Auto connect: 0
SSID (10): ESP8266AP2
Passphrase (8): password
BSSID set: 0
0.0.0.0

Connecting to AP… 行の下に27個の 0 が並んでおり、約10秒間の間、WiFi.status()が0 ( WL_IDLE_STATUS ) を返し続けたことを示している。WL_IDLE_STATUS (SDKではSTATION_IDLE) というのは、AP接続要求が実施されていないのと同じ意味ではないのかな。
接続をあきらめた後のprintDiag()には設定意図と異なる項目はない。APに接続できていないのでIPアドレスは 0.0.0.0 のままであり、これは正しい。

この動作はSTA-ESPの電源を入れ直しても同じであり、考えられるいろいろなこと(WiFi.mode()をAPにしてからSTAにするとか、順序を変えてみるとか、staticアドレスにしてみるとか、スリープ状態を変えてみるとかなんとか)をやっても変化がなかった。

苦虫級の回避策

PCやスマホのブラウザからAP-ESPにリクエストを投げて、サーボの動きを試す分には問題なかったので、STA側のスケッチをサラサラ書いて、Arduino ADKに接続したシリアルモニタからリクエストコマンドを送信できるようになったところで、APに接続できない問題に直面した。他のwifiルーター等には接続できるのに。

このとき、一般的なwifiルーターに接続してインターネット上のwebサーバーにリクエストを投げ、その後に WiFi.disconnect(); によってAP接続を切ってAP-ESPへの接続を行うと成功することに気が付いた。こんな感じのスケッチを使った。

 if (WiFi.status() == WL_CONNECTED) {  // 接続済だが、別のところかもしれない。
   if (WiFi.SSID() != ssid)
     WiFi.disconnect();
   delay(250);
 }
 if (WiFi.status() != WL_CONNECTED) {
   WiFi.begin(ssid.c_str(), password.c_str()); 
 ....

このコードは、一度AP-ESPに接続済ならばwifi接続は試行せずにリクエストを行うが、別のAPに接続中ならばつなぎ直す、というつもりで書いている。どうやら、WiFi.begin()の時点で、status()が WL_IDLE_STATUSでなければ、begin()によって接続してくれるようだった。

ただ、いったん別のAPに接続してからならば、AP-ESPに接続できるというのは満足できる結果ではない。たとえば野外でサーボ雲台(または、別のWROOM-02相互接続アプリケーション)を使おうというとき、わざわざスマホのテザリングを有効にして、そこに接続してから使うというのは、まったく気が進まない。

そこでようやくたどり着いたのが、今回掲載したスケッチの22,23行目のコメント部分。このコメントを外してコンパイルしたスケッチを転送してやるとAP-ESPに接続できるようになった。かなり乱暴なやり方ではあるが、近所にいなさそうなSSIDを使ってbegin()しておき、その後に本来のssidを使って接続しようというものである。苦肉の策というより、苦虫を噛んだようなすっきりしない回避方法ではあるが、うまくいったので採用することにした。実行時のシリアルモニタは以下のような内容になった。

実行結果2

system_get_sdk_version() returns 1.3.0

Connecting to AP...
6 6 6 6 6 6 6 6 
connected. WiFi.printDiag()
Mode: STA
PHY mode: N
Channel: 1
AP id: 0
Status: 5
Auto connect: 0
SSID (10): ESP8266AP2
Passphrase (8): password
BSSID set: 0
192.168.4.2

connecting to 192.168.4.1
HTTP/1.1 200 OK
....
closing connection
finished.

Connecting to AP… 行の下に6 が8つならんでいるが、status()が返す6は切断済という状態を表している (WL_DISCONNECTED)。ありもしないSSIDを探索しているときには、WL_DISCONNECTEDになるのだろう。ホントのssidを使ってbegin()した約3秒後に接続が成功し、192.168.4.2 というIPアドレスを得ている。
Status: 5
となっているが、5 == WL_CONNECTEDである。

その後は予定通りにhttpリクエストを投げ、レスポンスをもらって終了している。

きょうのまとめ

今回の接続できない問題に伴う症状は妙な話に感じる。うちにあるWROOM-02の個体がおかしいのかもしれないし、SDKのバージョンアップによって解決するのかもしれない。ESP8266WiFiClassがラップしているsdkのapiレベルにも下り探してみたが、これという回避策は見つからなかった。

そういえば、1枚目の写真に写っているものを解説しておくと、奥にある中型のブレッドボードがAP-ESPを載せたもので、2台のサーボを駆動する回路入り。中央手前がSTA-ESPで、プログラミング時にはその奥にあるUSB-シリアルのFT-231Xの回路に接続している。
STA-ESPには、手前右側のArduino ADKから、+5VおよびGNDと、RESET信号を与えていて、ArduinoのRESETボタンでリセットがかかる。ある程度スケッチが固まったら、FT-231Xはお役御免で、Arduino ADKとシリアル接続することになる。
STA-ESPには、Arduino ADKではなくジョイスティックをA/Dコンバータ経由で接続する案もあり、まだまだ遊べそうではある。