2012年5月20日日曜日

ArduinoからWebSocketサーバへ、socket.ioをつかって接続するには

ArduinoにEthernet Shieldを積んで、WebSocketを使うときのKUFU。

WebSocketをつかうには

WebSocketClientというライブラリが公開されているので、それをつかって。Zipファイルをダウンロードしたら、Arduino.app/Contents/Resources/Java/librariesに展開。Arduino.appを再起動すると、Scketch→Import LibraryとFile→ExamplesにWebSocket関係の項目が追加されます。Mac以外の場合は知りません。然るべきディレクトリに解凍してください。

サンプルをうごかしてみる

#include "Arduino.h"
#include <Ethernet.h>
#include <SPI.h>
#include <WebSocketClient.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 2, 100 };
char server[] = "echo.websocket.org";
WebSocketClient client;

void setup() {
  Serial.begin(9600);
  Ethernet.begin(mac, ip);
  client.connect(server);
  client.setDataArrivedDelegate(dataArrived);
  client.send("Hello World!");
}

void loop() {
  client.monitor();
}

void dataArrived(WebSocketClient client, String data) {
  Serial.println("Data Arrived: " + data);
}

もともとのサンプルから、7行目にbyte ip〜を追加、13行目の引数をmac→mac, ipに変更しています。ipは適宜変更していただいて。ipが指定去れていない場合はDHCPとなりますが、試したArduino DuemilanoveとEthernet Shieldの組み合わせせはIPアドレスの取得に時間がかかり、echoサーバにつながっていないようでした。固定IPアドレスにすることでDHCPの時間をカットできます。

socket.ioをつかったWebSocketサーバにつなぐ

WebSocketサーバでsocket.ioをつかっている場合、これはちょっと特殊。そもそもWebSocket = socket.ioではなく、WebSocket ⊂ socket.ioといった具合。WebSocketが使えない・対応していない状況ではXMLHttpRequestやCometなどの代替手段でWebSocketふうの接続をまかなうのがsocket.ioなので、微妙に変えてあげる必要があります。

// Arduino (Client)
#include "Arduino.h"
#include <Ethernet.h>
#include <SPI.h>
#include <WebSocketClient.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 2, 100 };
char server[] = "192.168.2.1";

WebSocketClient client;

void setup(){
  Serial.begin(9600);
  
  Ethernet.begin(mac, ip);
  delay(1000);
  if(client.connect(server, "/socket.io/1/websocket/", 3000)){
    client.setDataArrivedDelegate(dataArrived);
    //client.subscribe("atime");
    delay(1000);
  } else { while(1){} }
}

void dataArrived(WebSocketClient client, String data) {
  Serial.println("Data Arrived: "+data);
}

void loop(){
  client.monitor();
  
  //send a message
  String message = "{";
  message += "\"millis\":";
  message += millis();
  message += "}";
  
  client.send(message);
  delay(1000);
}

// Node.js (Server)
var io = require('socket.io').listen(3000);

io.sockets.on('connection', function(socket){
  socket.on('atime', function(data){
    console.log(data);
  });
});

クライアント側はこのスライドを参考に。でもコレ、ちょっと怪しい箇所があるので、動くものに書き直ししたのが上のもの。サーバ側のNode.jsは基本的にサイトのサンプルのまま。重要なのはクライアント側のclient.connectの部分。第2の引数をsocket.ioの仕様にて定義されているとおりに指定するだけ。プロトコルのバージョンを示している1と通信方法を示しているwebsocketの部分は基本的に変えません。socket.ioの部分が名前空間を示しているので、任意の名前空間を使用する場合には適宜書き換えとなりますここは試していますがうまくいきません。destroying non-socket.io upgradeというメッセージがコンソールに表示されているところ。socket.ioを使うサーバとWebSocketでやりとりするならコレでOK。

問題点

何らかの原因でサーバとクライアントとの接続が切れたとき、ブラウザ上のsocket.ioでは再接続のタイミングをねらって待機しますが、この実装ではそういった点は考慮していません。この実装ではリセット以外に再接続の方法はないので、必要ならうまいことスケッチに追加する必要があります。

もうひとつ。最後のdelay(1000)は1秒待てですが、これがないと3〜5msおきにメッセージを投げ続けます。このときに、Ethernet Shield上のチップが結構熱くなるのが心配。常温環境でもパッケージに触るといい具合に熱が伝わってくるので、あったかいところでは気がかりです。

0 件のコメント:

コメントを投稿