ArduinoをJavaScriptで動かす「Johnny-five」。
魅力的ではあるものの、対応している電子部品に限りがあり、開発が行き詰ってしまいます。
Arduinoの開発言語はライブラリが充実しているC++を使った方が良さそうです。
でもNode.jsは使いたい
IoT実現のため、Arduinoの制御にJavaScriptを使うことは断念しましたが、ブラウザとの連携はNode.jsサーバーを使って、JavaScriptで行いたいです。
そうなるとArduinoとNode.jsサーバー間のデータ送受信で、Johnny-fiveに代わる仕組みが必要になります。
シリアル通信で何かしらの文字列を送受信できれば良いのですが・・・
node-serialportを使う
シリアル通信ができるNode.jsライブラリがありました!
ArduinoとNode.jsサーバー間で、文字列の送受信を行います。
例えば「LED=H」という文字列をNode.jsからArduinoにシリアル通信で送ると、Arduinoに接続されたLEDが光るというような想定です。
ちょこちょこハマる
結果的にはできましたが、ハマりどころが多かったです。
シリアル通信もあまり理解していないところから始めたので、簡単なところでつまずいています。
参考になるか分かりませんが、コードを掲載していきます。
開発環境は、この系統の情報が意外と少ない(?)、Windows 10です。
ディレクトリ構成
Cドライブ直下に「node-arduino」というディレクトリを作り、ここで開発を行います。
「node-serialport」インストール
すでにNode.jsとExpress、soket.ioはインストールされているとします。
Expressなどと同じ手順だと思いますが、コマンドプロントで
1 |
npm install serialport |
と入力すれば、インストールされます。
Node.jsプログラムの制作
続いてNode.jsサーバーのapp.jsを書きます。
以前書いたものに、飯谷健太さん、sifueさん、_shimizuさんの記事を参考に手を加えています。
var TCP_PORT = 3000, DIRECTORY = 'htdocs', SERIAL_PORT = 'COM20', BAUD_RATE = 57600, express = require('express'), app = express(), http = require('http').Server(app), io = require('socket.io')(http), serialPort = require('SerialPort'), serial = new serialPort(SERIAL_PORT,{ parser: serialPort.parsers.readline('\n'), baudrate: BAUD_RATE }); var init = function () { app.use(express.static(DIRECTORY)); http.listen(TCP_PORT, function(){ console.log(getIP() + ':' + TCP_PORT + ' でサーバーを起動'); }); serial.on('open',function(){ console.log('シリアルポート ' + SERIAL_PORT + ' と接続'); }); setIo(); }, //ローカルIPアドレス取得 getIP = function(){ var os = require('os'), ip, interfaces = os.networkInterfaces(); var getIPv4 = function(){ interfaces[dev].forEach(function(details){ if (details.family === 'IPv4' && details.address.indexOf('192.') > -1){ ip = details.address; } }); }; for (var dev in interfaces) { getIPv4(dev); } return ip; }, //ソケットIO設定 setIo = function(){ serial.on('data',function(data){ io.emit('recvmsg',data.toString()); }); io.on('connection',function(socket){ socket.on('led',function(msg){ serial.write(new Buffer(msg),function(err,results){ if(err) { console.log('Err: ' + err); console.log('Results: ' + results); } }); }); }); }; init();
ハマりどころとしては・・・
4行目 シリアルポートの指定
1 |
SERIAL_PORT = 'COM20', |
Macのサンプルコードだと「/dev/tty.usbmodemfd121」というような、Windowsユーザーの見慣れぬ文字列となっています。
Windowsの場合は、Arduinoと接続しているCOM番号を、コントロールパネルのデバイスマネージャーや、Arduino IDEで調べて指定します。
ただ、接続し直すとCOM番号が変わることがあるので、どうにかしたい・・・
11行目 改行パース
1 |
parser: serialPort.parsers.readline('\n'), |
シリアル通信でデータで「AAA」と文字列を送ると、「A\nA\nA\n」というような改行が入る仕様(?)につまづきました。
文字列は1文字ずつしか送れず、必ず改行が入るようです。
送信したバラバラの文字列を、Arduino上で組み立てるコードも、この後掲載します。
18行目 IPアドレス表示
1 2 |
console.log(getIP() + ':' + TCP_PORT + ' でサーバーを起動'); }); |
Wi-Fi接続の際、IPアドレスが変わっていることがあるので、コマンドプロントに出力することにしました。
実際の処理は、26行目以降のgetIP();です。
49行目 デバッグ用の受信コード
1 |
io.emit('recvmsg',data.toString()); |
Node.jsでシリアルポートを使っていると、Arduinoのシリアルモニタは使えません。
Arduinoのデバッグをする際は、確認したい値をブラウザに表示させます。
(コマンドプロントに表示でもよいのですが)
LED操作画面のindex.html
続いてNode.jsと連携するindex.htmlの制作です。
LED点灯ボタンがあるだけです。
index.htmlファイルに、HTML、CSS、JavaScriptをまとめて記述しています。
<!doctype html <html> <head> <meta charset="utf-8"> <title>Node.jsでシリアル通信</title> <script src="/socket.io/socket.io.js"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <style> body { padding-top: 500px; background: #000; text-align: center; } button { padding: 20px 40px; border-bottom: #999 solid 20px; border-radius: 15px; background: #EEE; font-size: 100px; } button.on { margin-top: 15px; border-bottom: #095E13 solid 5px; background: #3C9053; color: #FFF; } </style> </head> <body> <button id="ledBtn">LED点灯</button> <script type="text/javascript"> var SWITCH_CLASS = "on", socket = io(), $ledBtn = $("#ledBtn"), ledSwitch = 0; var init = function(){ setBrowser(); setMicom(); } //ブラウザ設定 var setBrowser = function(){ $ledBtn.on("click", function(){ if(ledSwitch === 0){ ledSwitch = 1; //true $(this).addClass(SWITCH_CLASS); }else{ ledSwitch = 0; //false $(this).removeClass(SWITCH_CLASS); } socket.emit('led', 'l=' + ledSwitch); }); }, //マイコン設定 setMicom = function(){ socket.on("buttonSwitch", function(buttonSwitch){ if(buttonSwitch === true){ ledSwitch = 1; $ledBtn.addClass(SWITCH_CLASS); }else{ ledSwitch = 0; $ledBtn.removeClass(SWITCH_CLASS); } }); socket.on('recvmsg',function(data){ $ledBtn.text(data); }); }; init(); </script> </body> </html>
52行目 Arduinoに送る文字列
1 |
socket.emit('led', 'l=' + ledSwitch); |
「LED点灯」ボタンを押したら、Node.jsを経由して、Arduinoにシリアル通信で文字列を送ります。
点灯の時は’l=1’、消灯の時は’l=0’としています。
また、逆にArduinoからNode.jsを経由して、文字列を受け取れるか確認するため、下記コードを一時的に追加していました。
1 2 3 |
socket.on('recvmsg',function(data){ $ledBtn.text(data); }); |
Arduinoからシリアル送信した文字を、LED点灯ボタンに表示させます。
前途したように、デバッグ用に使いました。
Arduinoのシリアル送受信プログラム
まだ終わりません。Arduinoにプログラムを書き込みます。
#define LED_PIN 13 int recieveByte = 0; String instruction = ""; void setup(){ Serial.begin(57600); pinMode(LED_PIN,OUTPUT); } void loop(){ instruction = ""; while (Serial.available() > 0){ recieveByte = Serial.read(); if(recieveByte == (int)'\n') break; instruction.concat((char)recieveByte); delay(1);//必要 } if(instruction.length() > 0){ if(instruction == "l=1"){ digitalWrite(LED_PIN,HIGH); }else if(instruction == "l=0"){ digitalWrite(LED_PIN,LOW); } } }
17行目: 処理に余裕を持たせる
1 |
delay(1) |
シリアルポートから1文字ずつ送られてきたデータを、Arduino上で組み立てていますが、17行目にdelay(1)を入れないと、繰り返し処理が正しく動作しませんでした。
1m秒遅らせたら、シリアルで送られてきたデータを正常に受信できました。
回路は単純なLED接続
説明不要かもしれないくらい単純な、13番PINでのLED接続です。
最後に動作確認
コマンドプロントでサーバーを立ち上げます。
1 |
node app.js |
サーバー立ち上げに成功すると、「192.168.XXX.XXX:3000 でサーバーを起動」とコマンドプロントに表示されます。
そのアドレスを、同じWi-Fiに接続された端末のブラウザで表示させます。
ブラウザでページを読み込むと、LED点灯ボタンが表示されます。
クリックすると・・・
ボタンも緑色になり、Arduinoに接続された緑色のLEDが光ります。
うまく動作しました。
Johnny-fiveからnode-serialportに移行成功です。
Johnny-fiveほど手軽ではないですが、応用は利くでしょう。