SyntaxHighlighter

2016年11月16日水曜日

Node.js+WebSocketでEnOceanセンサを扱う

目的

Raspberry PiでEnOceanセンサデータを受信し、ブラウザ(localhost)上でリアルタイムに見れるようにしたい。
WebSocketを扱う場合、PythonよりもNode.jsの方が簡単そうだったので実装してみた。

Node.jsのバージョンはv6.9.1。今のところ、MacOS 10.12.1(Sierra)で動作を確認している。
EnOceanの受信にはnode-enoceanを利用。今回使っているのは以下4種類のセンサ。
  • ドア開閉 (STM431J or CS-EO429J)
  • 人感 (HM92-01WHC)
  • 温度 (STM429J or CS-EO429J)
  • ロッカースイッチ (ESM210R)

実行例

EnOceanのセンサが動作すると、リアルタイムにdatetimeとstate欄の表示が更新される。

ソースコード(サーバ側)

USBドングルのデバイス名(ここでは/dev/tty.usbserial-FTYKW2P5)とセンサIDは要変更。
また、EnOceanプロトコルについては以前の記事を参照。
// execute at node.js v6.9.1

// サーバ設定
var fs = require("fs");
var http = require("http");
var socketio = require("socket.io");
var server = http.createServer( function(req, res) {
    res.writeHead(200, {"Content-Type": "text/html" } );
    var output = fs.readFileSync("index.html", "utf-8");
    res.end(output);
}).listen(3000);
var io = socketio.listen(server);

// EnOcean設定
require("date-utils");
var enocean = require("node-enocean")();
enocean.listen("/dev/tty.usbserial-FTYKW2P5")

// サーバからデータ送信
io.sockets.on("connection", function (socket) {
    // EnOceanの電文を受信
    enocean.on("data", function (data) {
        console.log("============================");
        var dt = new Date();
        var datetime = dt.toFormat("YYYY/MM/DD HH24:MI:SS");
        console.log(datetime);
        // 電文からdata部を抽出し、センサIDを取得
        console.log("raw   : "+data["rawByte"]); // 電文本体
        body_length = parseInt(data["rawByte"].slice(4,6),16); // data部のbyte数
        body = data["rawByte"].slice(12,12+body_length*2); // data部
        sid = body.slice(2,10); // センサID
        console.log("data  : "+body);
        console.log("sid   : "+sid);
    
        // センサの種類を識別して状態を取得
        // ドア開閉 (STM431J or CS-EO429J)
        if(sid=="0a007cd0"){
            sType = "door";
            if(body.slice(10,12)=="08") state = "open";
            else if(body.slice(10,12)=="09") state = "close";
        // 人感 (HM92-01WHC)
        } else if(sid=="040150a5"){
            sType = "motion";
            if(body.slice(14,16)=="ff") state = "on";
            else if(body.slice(14,16)=="00") state = "off";
        // 温度 (STM429J or CS-EO429J)
        } else if(sid=="040177db"){
            sType = "temp";
            val = parseInt(body.slice(14,16),16);
            state = (255.0-val)/255.0*40.0;
        // ロッカースイッチ (ESM210R)
        } else if(sid=="002c865a"){
            sType = "SW";
            if(body.slice(10,12)=="84") state = "SW1-on";
            else if(body.slice(10,12)=="88") state = "SW2-on";
            else if(body.slice(10,12)=="04") state = "SW1-off";
            else if(body.slice(10,12)=="00") state = "SW2-off";
        } else {
            sType = "unknown";
            state = null
        }
        console.log("state : "+state);

        // 送信用JSON作成
        sensorStr = '{"datetime":"'+datetime+'","type":"'+sType+'","state":"'+state+'"}';
        sensorJson = JSON.parse(sensorStr);
        
        // WebSocketでJSONを送信
        io.sockets.emit("sendLog", sensorJson);

    });
});

ソースコード(クライアント側)

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Sample</title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>

<table class="table">
    <tr>
        <th>type</th>
        <th>datetime</th>
        <th>state</th>
    </tr>
    <tr>
        <td>door</td>
        <td><div id="door-dt"></div></td>
        <td><div id="door-state"></div></td>
    </tr>
    <tr>
        <td>motion</td>
        <td><div id="motion-dt"></div></td>
        <td><div id="motion-state"></div></td>
    </tr>
    <tr>
        <td>temp</td>
        <td><div id="temp-dt"></div></td>
        <td><div id="temp-state"></div></td>
    </tr>
    <tr>
        <td>SW</td>
        <td><div id="SW-dt"></div></td>
        <td><div id="SW-state"></div></td>
    </tr>
</table>

<script type="text/javascript">
var s = io.connect();
s.on("sendLog", function (data) {
    sType = data["type"];
    if(sType == "door"){
        tgtId = "#door";
    } else if(sType == "motion"){
        tgtId = "#motion";
    } else if(sType == "temp"){
        tgtId = "#temp";
    } else if(sType == "SW"){
        tgtId = "#SW";
    }
    $(tgtId+"-dt").html(data["datetime"]);
    $(tgtId+"-state").html(data["state"]);
});
</script>

</body>
</html>

参考にしたサイト