Johnny-fiveからnode-serialportに

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などと同じ手順だと思いますが、コマンドプロントで

と入力すれば、インストールされます。

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行目 シリアルポートの指定

Macのサンプルコードだと「/dev/tty.usbmodemfd121」というような、Windowsユーザーの見慣れぬ文字列となっています。

Windowsの場合は、Arduinoと接続しているCOM番号を、コントロールパネルのデバイスマネージャーや、Arduino IDEで調べて指定します。

ただ、接続し直すとCOM番号が変わることがあるので、どうにかしたい・・・

11行目 改行パース

シリアル通信でデータで「AAA」と文字列を送ると、「A\nA\nA\n」というような改行が入る仕様(?)につまづきました。

文字列は1文字ずつしか送れず、必ず改行が入るようです。

送信したバラバラの文字列を、Arduino上で組み立てるコードも、この後掲載します。

18行目 IPアドレス表示

Wi-Fi接続の際、IPアドレスが変わっていることがあるので、コマンドプロントに出力することにしました。

実際の処理は、26行目以降のgetIP();です。

49行目 デバッグ用の受信コード

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に送る文字列

「LED点灯」ボタンを押したら、Node.jsを経由して、Arduinoにシリアル通信で文字列を送ります。

点灯の時は’l=1’、消灯の時は’l=0’としています。

 

また、逆にArduinoからNode.jsを経由して、文字列を受け取れるか確認するため、下記コードを一時的に追加していました。

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文字ずつ送られてきたデータを、Arduino上で組み立てていますが、17行目にdelay(1)を入れないと、繰り返し処理が正しく動作しませんでした。

1m秒遅らせたら、シリアルで送られてきたデータを正常に受信できました。

回路は単純なLED接続

説明不要かもしれないくらい単純な、13番PINでのLED接続です。

最後に動作確認

コマンドプロントでサーバーを立ち上げます。

サーバー立ち上げに成功すると、「192.168.XXX.XXX:3000 でサーバーを起動」とコマンドプロントに表示されます。

そのアドレスを、同じWi-Fiに接続された端末のブラウザで表示させます。

ブラウザでページを読み込むと、LED点灯ボタンが表示されます。

クリックすると・・・

ボタンも緑色になり、Arduinoに接続された緑色のLEDが光ります。

うまく動作しました。

Johnny-fiveからnode-serialportに移行成功です。

 

Johnny-fiveほど手軽ではないですが、応用は利くでしょう。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)