前回の記事から発展して入出力を整えた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以外は弾くので大丈夫です。
実際のコード
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
入力
ボタンを押すと、
仕組みとしては以下のようになっているので、
Heroku側のログを
herolu logs --tail
で待っていると、
と表示されます。
出力
出力はブラウザで以下のように /output/1 と叩くと
仕組みとしては以下のようになっているので、
以下のようにLEDがつきます。
localhost.js側でも以下の様なログが出ていて成功です。
おわりに
ルールを整えたおかげで今回実装したかったSokcket部分に注力することが出来て無理なく対応できました。
今回のもので外部ネットワークにつなぎやすくなるので、いろいろとつなげてみようと思います!
それでは、よき NodeJS & Heroku & littleBits & Arduino Lifeを!