入出力を整えたlittleBits ArduinoモジュールをHerokuとSocket通信で連携してみるメモ

前回の記事から発展して入出力を整えたlittleBits ArduinoモジュールをHerokuとSocket通信で連携してみるメモです。いままではPC内で動いているものなので、外部ネットワーク(今回の場合Horokuサーバー)と連携してみます。

これができるとHerokuを中継サーバーにして可能性が広がります。IFTTTのMaker Channnelや先日繋いだMilkcocoaにも連携しやすくなるので楽しみです。

目指すところ

  • 前回の記事の仕組みをベースにする。
    • ざっくりいうと、いままではPC内で動いているものなので、外部ネットワーク(今回の場合Horokuサーバー)と連携する
  • サーバー間(ローカルサーバーとHorokuサーバー)のSocket通信を行う仕組みを加える
  • ローカルサーバーでのURLで/output/1を叩く箇所はHerokuサーバーに移動する

あれれ、思ったよりTODOはすくない。

いろいろ調べておく

サーバー間通信(ローカルサーバーとHorokuサーバー)

なぜSocket通信が使うのかというと、Socket通信だと最初にコネクションを張っておけば送受信ともに気軽に行えるので扱いやすいためです。HTTP通信の場合はローカルサーバーへHerokuサーバーから受信するときにポートフォワードなど苦労したりします。

ということで、こちらの記事を参考にして、

あの技術の名前を僕たちはまだ知らない: Node.js+Socket.ioを利用してサーバ<->サーバ間通信

以下のようにローカルサーバーからHerokuへサーバー間通信をします。

// socket
var io = require('socket.io-client');

// HerokuのサーバーにSocket接続する
var socket_client = io('https://                          .herokuapp.com/');
socket_client.on('connect', function () {
    // 接続成功時にメッセージを送信する
    console.log('socket_client connect');
    socket_client.emit("message", 'connected');
});

応答が早いのも魅力的です。当然、コネクション増えて送信量も増えてくると、別の調整が必要ですが、まずは仕組みを優先します。

ローカルサーバーでのURLで/output/1を叩く箇所はHerokuサーバーに移動する

コードを移植するだけでは味気ないので、/output/以降をパラメータ受け取りにして、すこし短いコードで受け入れるようにしました。

// ローカルサーバーにlittleBits出力を送信する
// idが動作するポートを示す 1~3
app.get('/output/:id', function(req, res){
  res.send("{'request':'output' , 'id':'" + req.params.id + "'}");
  console.log("-> LittlebitsOutputAction req.params.id " + req.params.id );
  io.emit('LittlebitsOutputAction',{id:req.params.id});
});

これですと/output/hogeとか/output/781とかも受け入れてしまいますが、ローカルサーバーで受け付けた際に1~3以外は弾くので大丈夫です。

実際のコード

littlebits-arduino-heroku-socket-001

HerokuサーバーのNodeJS(index.js)

  • ローカルサーバーにlittleBits出力を送信するるすのはLittlebitsOutputActionメッセージで送るようにしています。
  • ローカルサーバーからのlittleBits入力の情報を受け取るときはLittlebitsInputActionメッセージで受け取るようにしています。
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.set('port', (process.env.PORT || 5000));
app.use(express.static(__dirname + '/public'));

app.get('/', function(request, response) {
  response.send('Hello World!');
});

http.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

// ローカルサーバーにlittleBits出力を送信する
// idが動作するポートを示す 1~3
app.get('/output/:id', function(req, res){
  res.send("{'request':'output' , 'id':'" + req.params.id + "'}");
  console.log("-> LittlebitsOutputAction req.params.id " + req.params.id );
  io.emit('LittlebitsOutputAction',{id:req.params.id});
});

// 接続設定
io.on('connection', function (socket) {
  console.log("connection");
  // ローカルサーバーからのlittleBits入力の情報を受け取る
  // idが動作したポートを示す 1~3
  socket.on('LittlebitsInputAction',function(data){
    console.log('[LittlebitsInputAction]');
    console.log(data);
  });
  // 通常メッセージを受け取る
  socket.on('message',function(data){
    console.log('[message]');
    console.log(data);
  });

});

ローカルサーバー側のNodeJS(localhost.js)

  • HerokuとはLittlebitsOutputAction・LittlebitsInputActionメッセージでやりとりしています。
// express
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
// socket
var io = require('socket.io-client');

// HerokuのサーバーにSocket接続する
var socket_client = io('https://                          .herokuapp.com/');
socket_client.on('connect', function () {
    // 接続成功時にメッセージを送信する
    console.log('socket_client connect');
    socket_client.emit("message", 'connected');
});

app.set('port', (process.env.PORT || 5000));
app.use(express.static(__dirname + '/public'));

app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

app.listen(app.get('port'), function() {
    console.log("Node app is running at localhost:" + app.get('port'));
});

// Serial Port ///////////////////////////////

var serialport = require('serialport');
var portName = 'COM4';
var sp = new serialport.SerialPort(portName, {
    baudRate: 9600,
    dataBits: 8,
    parity: 'none',
    stopBits: 1,
    flowControl: false,
    parser: serialport.parsers.readline("\n")
});

// シリアルポート入力からのデータ送信
sp.on('data', function(input) {
    var buffer = new Buffer(input, 'utf8');
    try {
        console.log('buffer: ' + buffer);
        if( buffer == 1 ){
            // 1番目のポートのlittleBits入力の情報を送る
            console.log('-> LittlebitsInputAction 1');
            socket_client.emit('LittlebitsInputAction',{id:1});
        } else if( buffer == 2 ){
            // 2番目のポートのlittleBits入力の情報を送る
            console.log('-> LittlebitsInputAction 2');
            socket_client.emit('LittlebitsInputAction',{id:2});
        } else if( buffer == 3 ){
            // 3番目のポートのlittleBits入力の情報を送る
            console.log('-> LittlebitsInputAction 3');
            socket_client.emit('LittlebitsInputAction',{id:3});
        }
    } catch(e) {
        return;
    }
});

// シリアルポートへのデータ出力
socket_client.on('LittlebitsOutputAction',function(data){
    console.log('[LittlebitsOutputAction]');
    console.log(data);
    // 出力ポートが反応する
    sp.write(data.id,function(err,results){
        console.log('-- socket.io output sp.write complete');
        console.log(err);
        console.log(results);
    })
});

// Server ////////////////////////////////////////////////

// root
app.get('/', function(req, res){
    res.send("Hello World!!!");
});

さっそく動かしてみる

HerokuサーバーのNodeJS(index.js)一式がアップロードされて動作している前提で進めます。

Arduinoをつないでローカルでlocalhost.jsを起動します。

node localhost.js

入力

ボタンを押すと、

littlebits-arduino-module-my-rule004

仕組みとしては以下のようになっているので、

littlebits-arduino-heroku-socket-002

Heroku側のログを

herolu logs --tail

で待っていると、

littlebits-arduino-heroku-socket-003

と表示されます。

出力

出力はブラウザで以下のように /output/1 と叩くと

littlebits-arduino-heroku-socket-004

仕組みとしては以下のようになっているので、

littlebits-arduino-heroku-socket-005

以下のようにLEDがつきます。

littlebits-arduino-module-my-rule007

localhost.js側でも以下の様なログが出ていて成功です。

littlebits-arduino-heroku-socket-006

おわりに

ルールを整えたおかげで今回実装したかったSokcket部分に注力することが出来て無理なく対応できました。

今回のもので外部ネットワークにつなぎやすくなるので、いろいろとつなげてみようと思います!

それでは、よき NodeJS & Heroku & littleBits & Arduino Lifeを!