ESP8266にWebSocketを喋らせてみる

世の中、同じようなことを考える方はいるようで、すでにESP8266+ArduinoIDEでWebSocketライブラリが存在しています。WebSocketが扱えるようになれば、(プログラミングスキルがあれば)クラウド側から好きなタイミングでESP8266に指示をおくることができるようになるはずです。(が、世の中そんなに甘くないんですねぇ〜)

で、今回見つけたWebSocketライブラリは ESP8266-Websocket です。

今回の記事はこれのサンプルを動かしてみよう、と苦戦した記録です。作業PCのOSはUbuntu14.10 LTS 64bit、ArduinoIDEのバージョンは1.6.5です。なお、Ubuntuで動かすにあたっては、

$ sudo addgroup foobar dialout
ユーザー `foobar' をグループ `dialout' に追加しています...
ユーザ foobar をグループ dialout に追加
完了。
$

として、/dev/ttyUSB0のアクセス権をユーザーにつけた後、再ログインしないと/dev/ttyUSB0へアクセスできないとしてエラーになります。

Arduinoのライブラリは ~/Arduino/librariesの下に置けばいいようですので、そこにgitでライブラリを引っ張ってきます。

~$ cd ~/Arduino/libraries
~/Arduino/libraries$ git clone https://github.com/morrissinger/ESP8266-Websocket
Cloning into 'ESP8266-Websocket'...
remote: Counting objects: 286, done.
remote: Total 286 (delta 0), reused 0 (delta 0), pack-reused 286
Receiving objects: 100% (286/286), 105.19 KiB | 0 bytes/s, done.
Resolving deltas: 100% (103/103), done.
Checking connectivity... done.
~/Arduino/libraries$ 

その後、ArduinoIDEで「スケッチ」→「Include Library」→「Manage Libraries」で右上の「Filter your search」で「ESP8266-Websocket」と入力して「INSTALLED」になっていることを確認します。

scrn1

次に、WebSocketClientのデモを動かしてみます。

「ファイル」→「スケッチの例」→「ESP8266-Websocket」→「WebSocketClient_Demo」を選択します。

scren2

ソースの冒頭の

const char* ssid     = "SSID HERE";
const char* password = "PASSWORD HERE";

の部分に自分の無線LANのESSIDとパスキーを入力してから、名前を付けて保存します。

このライブラリはおそらくWindows環境で作成されたのでしょう。Linux環境でビルドしようとすると、ファイル名の大文字小文字で失敗します。そのため、ライブラリの中の以下の部分を修正します。

WebSocketClient.h と WebSocketServer.h の

#include "String.h"

#include "string.h"

に、

WebSocketServer.cpp と WebSocketClient.cppの

#include "base64.h"

#include "Base64.h"

とします。

これでビルドが通るようになりましたが、それでもコネクションに失敗します。禁断のgoto文で接続しに行く部分のみを繰り返すと、何回目か以降は通るようです。ライブラリのWebSocketClient.cppの冒頭にデバッグのための#define文があるので、ここをコメントを外すとデバッグメッセージが出るのですが、これでもハンドシェークの途中で止まっていることはわかるものの、それ以上はなんともわかりません。
これ以上は間にLANアナライザを入れてみてパケット解析しつつ、RFCを読んで何かおかしいのか調べるしかなさそうです。

以下は、現時点でのサンプルプログラムを改造したものです。

#include <ESP8266WiFi.h>
#include <WebSocketClient.h>

const char* ssid     = "xxxxxxxxxxxxxxxx";
const char* password = "xxxxxxxxxxxxxxxx";
char path[] = "/";
char host[] = "echo.websocket.org";
  
WebSocketClient webSocketClient;

// Use WiFiClient class to create TCP connections
WiFiClient client;

void setup() {
  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network

  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  delay(5000);
  
RETRY:
  // Connect to the websocket server
  IPAddress remote_addr;
  if(!WiFi.hostByName(host,remote_addr)){
    Serial.println("Cannot resolve hostname");
    while(1){}
  } else {
    Serial.println("hostname resolved");
  }
  
  if (client.connect(remote_addr, 80)) {
    Serial.println("Connected");
  } else {
    Serial.println("Connection failed.");
    goto RETRY;
    while(1) {
      // Hang on failure
    }
  }

  // Handshake with the server
  webSocketClient.path = path;
  webSocketClient.host = host;
  if (webSocketClient.handshake(client)) {
    Serial.println("Handshake successful");
  } else {
    Serial.println("Handshake failed.");
    while(1) {
      // Hang on failure
    }  
  }

}


void loop() {
  String data;

  if (client.connected()) {
    
    webSocketClient.getData(data);
    if (data.length() > 0) {
      Serial.print("Received data: ");
      Serial.println(data);
    }
    
    // capture the value of analog 1, send it along
    pinMode(1, INPUT);
    data = String(analogRead(1));
    
    webSocketClient.sendData(data);
    
  } else {
    Serial.println("Client disconnected.");
    while (1) {
      // Hang on disconnect.
    }
  }
  
  // wait to fully let the client disconnect
  delay(3000);
  
}

WebSocketClient.cppをみると、サーバ側からのヘッダの解釈の実装がまだまだであることがわかります。ひとつの可能性として、どうやらここで止まっていそうです。もうひとつは、パケットの受信バッファの処理がどうも変な感じがします。

いずれにせよ、このライブラリはまだそのままでは使えるものではなさそうな感じです。(ま、よくみると色々と制約があるよ、って書いてあるんですけどね)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)