漆黒な技術メモ

管理人が必要に応じて自分のメモを好き勝手に投下するたまり場的ブログ

【ちょっと技術的なことAdventCalender】 hackU実装振り返り-1. mbed×websocket-

この記事はちょっと技術的なことAdventCalenderの21日目です。 いよいよ最後も見えてきました。自分の担当分はこれを含めてあと4回です。

今日は、前回お話ししたhack Uで苦労したことにうちの1つをつらつら書いていこうと思います。

mbed × Webscoketは難しい

今回の残業防止IoT座布団では、座布団側のマイコンにmbedというARMマイコンを、サーバ側にnode.jsを利用しました。 この業界だと今流行りのマイコンは圧倒的にArduinoで、普通ならそっちを使うんですが、今回はチームメンバーが研究室でmbedを使っていること、 僕も昔に少し触れたことがあることから、mbedを採用しました。

で、mbedにもいくらかwebsoket関係のライブラリはあり、その中にはSocket.IOのラッパも存在したので、「これで問題なくSocket.IOを使ったwebSocketが使えるだろう」… って考えてた僕が馬鹿でした。 そのSokcet.IOのライブラリはどうもうまく動きませんでした。 パケットとライブラリ実装を確認すると、ライブラリのLast Commit Date が2012年になっており、そのころからSocket.IOの仕様が大きく変わってしまったため通信ができませんでした。 恐らくやるとしたらSocket.IOの仕様とライブラリの実装を読みくだきながらアレンジする必要があると思ったので、断念しました… 他にもWebsocketという名前そのままのライブラリがありました。しかしこれも最終コミットが2011年とまあまあ古い状態でした。ただRFC準拠実装みたいなので、何とかなるかなあと思い、サーバ(node.js)側でsocket.IO以外のライブラリを検討しました。

実装

結果node.js側で用意したのはwsというパッケージです。 インストールは $npm install ws で普通にインストールできます。 mbed側は先ほどお話ししたWebscoketライブラリを利用します。 フルソースは後日gitにアップするんで、一番要になる部分の実装だけ抜き出して…

mbed側

#include "mbed.h"
#include "Websocket.h"
#include "EthernetNetIf.h"

void init(char * hostAndPort){
    eth = new EthernetNetIf();
    EthernetErr ethErr = eth->setup();
    if (ethErr) {
        printf("\r\nERROR %d in setup.\r\n", ethErr);
    }
    printf("hostandport %s\n", hostAndPort);
    
    ws = new Websocket(hostAndPort, eth);
    printf("websock init\n");
}
    
bool connect(){
    printf("connecting...");
    int failcount=0;
    
    while(! ws->connect()) {
        failcount++;
        if(failcount>10){
            printf("cannot connect failed.\n");
            return false;
        }
        printf("cannot connect websocket, retrying...\n");
        wait(2);
    }
     return true;
}

int mess_send(char * msg){
    ws->send(msg);
    printf("data send\n");
    return 1;
} 

int mess_recv(char * msg){
    if (ws->read(msg)) {
        return 1;
    }else{
        return -1;
    }
}

int main() {
    init("ws://www.example.jp");
    printf("connect start\n");
    connect();
    printf("connect");

    char msg[64];
    while(1) {
        Net::poll();
        mess_send("hello");

        int ret = mess_recv(msg);
        if(ret)
            printf("recv: %s\n",msg);
        
        Net::poll();
    }
}

サーバ側

var wss = require('ws').Server;
var mport = 12020;
var ws_Mbed = new wss({ port: 12020 });

ws_Mbed.on('connection', function (ws) {
    wsm_groval = ws;
    countor = 0;
    console.log("connect from mbed");

    ws.on('message', function (message) {
        console.log('received:'+ message);
        var type;
    });
});

こんな感じで実装すればmbedでもwebsocketで通信できます!!

欠点

ただ、このmbedライブラリは大きな欠点を抱えています。イーサネットのライブラリに古いライブラリであるErthernetIFライブラリを利用しています。 現在はErthenetInterfaceというライブラリを利用するのが普通です。 古いErtherNetIFライブラリには大きな欠点があります。それはmbedのRTOS(非同期処理)ライブラリとすこぶる相性が悪いということです。 我々は新しいイーサネットライブラリへの付け替えにチームメンバが挑んでくれましたが、残念ながらうまくいきませんでした。
というわけで、たまたまmbedが2台あった我々は、mbed2台で通信側、センサ側と分け、2台のmbedをジャンパ線で直結してなんとか乗り切ろうという発想に至りました。
次回はそこでの苦労をお話ししたいと思います。

追記

mbed側のライブラリにWebSocketClientというライブラリがありました。こちらはErthenetInterfaceをライブラリに使っていたので、こちらを使えば次回に書くような苦労はしなくてもよかったのかもしれません。。。