node.jsでマイコンボード(ARDUINO UNO)と会話

node.jsやelectronを使うとそれなりのアプリが作れそうなので勉強がてらに進めていきます。 作ろうと思っているのは

  1. Arduino UNOとBME280をつなぎ温度、気圧、湿度を取得
  2. Arduino UNOとPCをシリアルポートで通信してデータを取り込む
  3. PC側のシリアル通信プログラムはnode.jsで作る
  4. 上記で作成した通信プログラムとREST APIで通信してnode.js+VueでBME280のデータを表示する

結構面倒くさいような気がしますが将来的にセンサーと表示部が別々な場所になってもいいように各機能ごとに分けてみました。

まずはPC側のシリアル通信プログラムを作ってみようと思います。

node.js+expressの導入


どうもnode.jsでREST APIを作るにはexpressというものを使えば大丈夫というのをググったら出てきました。まずはそれから...

このサイトを参考にさせていただいた。

qiita.com

> mkdir serial_api_bme280
> cd serial_api_bme280
> npm init
> npm i express --save

npm initで質問が出てきますので"entry point:"だけmain.jsに変えてみました。

APIのコマンド待ち受け


次に以下のURLでBME280のダミーデータを受け取れるようにします。

http://localhost:3000/api/bme280

上記の参考サイトからソースをいただいてまいります。 そして写真のサンプルデータとなっているところをダミーデータをいれて返します。

/* 1. expressモジュールをロードし、インスタンス化してappに代入。*/
var express = require("express");
var app = express();

/* 2. listen()メソッドを実行して3000番ポートで待ち受け。*/
var server = app.listen(3000, function(){
    console.log("Node.js is listening to PORT:" + server.address().port);
});

/* 3. 以後、アプリケーション固有の処理 */

// BME280のダミーデータ
var bme280_dummy = 
    {
        temperature: "23.05",
        pressure: "1007.54",
        humidity: "40.99",
    };

// dataを取得するAPI
app.get("/api/bme280", function(req, res, next){
    res.json(bme280_dummy);
});

コンソールより次のコマンドをたたくとサーバーが起動します。

> node main.js

ブラウザで以下を見てみましょう。

http://localhost:3000/api/bme280

chromeでは次のように表示されました。

{"temperature":"23.05","pressure":"1007.54","humidity":"40.99"}

いやはやめっちゃ簡単です。素晴らしいです。ありがとうございます。

Arduino側のプログラム

BME280をAruduinoにつないでデータを取得します。これはスイッチサイエンスさんが素晴くわかりやすくなおかつソースコードまでおいていてくれています。ので参照してください。

www.switch-science.com

ソースコードは以下に置いています。

github.com

このまま動作させるてIDEのコーンソールで見ると次のようになっています。

f:id:mechagocha:20190331164135j:plain
arduinoコンソールログ(BME280)

この時COMポートの番号は記録しておいてくださいね。node側でこのポートにアクセスしますので。

シリアポートからの読み込み


まずはserialportのインストールです。

> npm i serialport --save

つぎに下記のサイトの"Control your Creations"って書いているところからソースをコピーしてきます。

serialport.io

できたらソースコード中の"path"って書いているとを実際に接続しているポート名に変えます。私の場合はwindows10を使っていてArduinoと接続されているのが”COM4"になっていますのでそのように変更しています。ファイル名を"serial.js"として下記のファイルを作りました。

const SerialPort = require('serialport')
const Readline = require('@serialport/parser-readline')
const port = new SerialPort('COM4', { baudRate: 9600 })

const parser = new Readline()
port.pipe(parser)

parser.on('data', line => console.log(`> ${line}`))

そしてこのコードを動かします。

> npm serial.js

Arduinoのコンソールは閉じておかないとエラーが出ます。 動かしたときのShellのキャプチャになります。

f:id:mechagocha:20190331164346j:plain
node.js + serialport で Arduino + BME280 のデータ表示

Arduino IDEと同じような値が出ていますか?

本当のデータの読み込み


いやはやここからが考えないといけません。なぜなら上記で作成した"main.js"と"serial.js"をつなぎ方がどこのWebサイトにも載っていないと思います。たぶん。頑張って考えてみます。 まずは " TEMP: 23.20 DegC ...”のようなデータを受け取りますのでこれを数値データとして各値ばらばらにします。

思考結果...

serial.js側のコードです。

const SerialPort = require('serialport')
const Readline = require('@serialport/parser-readline')
const port = new SerialPort('COM4', { baudRate: 9600 })

const parser = new Readline()
port.pipe(parser)


let startParser = function(){
  parser.on('data', line => bme280Data(line));
}
exports.startParser=startParser;

let bmeDataJson=null;

let bme280Data = function(line){
  // console.log(`> ${line}`);
  let reg = /TEMP\s:\s(\d+\.\d+)\sDegC\s.PRESS\s:\s(\d+\.\d+)\shPa\s.HUM\s:\s(\d+\.\d+)\s%/;
  let result = line.match(reg);
  if(result!=null){
    bmeDataJson = {
      temperature: result[1],
      pressure: result[2],
      humidity: result[3]
    }
    console.log(bmeDataJson);
  };
}

let getBme280Data = function(){
  return bmeDataJson;
}
exports.getBme280Data=getBme280Data;

sartParserという関数を作ってmain.jsから呼ぶことにします。 bme280Data関数でシリアルポートから読み取ったデータを正規表現をつかって分解し各値をbmeDataJsonのグローバル変数に代入します。 そしてgetBme280Data関数でそのグローバル変数をmain.jsから読み取れるようにします。

main.js側のコードです。

/* 1. expressモジュールをロードし、インスタンス化してappに代入。*/
var express = require("express");
var app = express();


/* 2. listen()メソッドを実行して3000番ポートで待ち受け。*/
var server = app.listen(3000, function(){
    console.log("Node.js is listening to PORT:" + server.address().port);
});

/* 3. 以後、アプリケーション固有の処理 */

const uartData = require("./serial.js");
uartData.startParser();

// dataを取得するAPI

app.get("/api/bme280", function(req, res, next){
    let bme280=uartData.getBme280Data();
    res.json(bme280);
});

require("./serial.js")でserial.jsの関数を呼べるようにします。uartData.startParser関数で通信開始です。"/api/bme280"がgetされたら、getBme280Dataを呼び出してデータを返します。

http://localhost:3000/api/bme280

を呼び出すとログと同じデータがブラウザに表示されていると思います。

ソースコードまとめ

ソースコードは下記のGitHubに置いています。Tag名:V0.0にこのブログで書いているものはしました。

github.com


ひとまずここまで、次は今回作成したAPIを別から呼び出して表示されるようにしたいと思います。 ではでは!