この記事は、2021年の micro:bit のアドベントカレンダー の 6日目の記事です。
micro:bit を PC と USBケーブルでつないだ状態で、PC との間でやりとりをする方法の 1つである「シリアル通信」を扱います。
その中でも Node.js を用いたものについて、軽いお試しをしていく形です。
過去に micro:bit と PC との間の通信について書いた記事
過去にも、micro:bit と PC との間で何らか通信させる話を、以下のように複数書いています。
- Bluetooth関連
- Web Bluetooth API を使い micro:bit とブラウザの間で BLE通信
- あらためて noble でガジェットを扱う話に着手する: toio と micro:bit を複数混在させてスキャン、toio の複数制御(6台分) - Qiita
- Web Bluetooth API で BLE(Chrome と micro:bit をつなぐ)
- micro:bit と #obniz で BLE + UART を試そうとした話と Mac用ツール
- micro:bit で BLE + UART による文字列の送受信
- #obniz と micro:bit で obniz-noble を使った BLE通信
- シリアル通信関連
その中で、使ったという話は書いたけれど、ソースコードを公開したり等とはしていなかったと思われる
「Node.js でのシリアル通信を使う」
という形での PC連携について記事を書いていきます。
シリアル通信のためのライブラリ・ブロック
Node.js でシリアル通信
Node.js でシリアル通信を扱おうとした場合、使われる仕組みとして有名どころはこちらかと思います。
●Hello from Node Serialport | Node Serialport
https://serialport.io/
micro:bit以外のデバイスと組み合わせた話は、ちょっとしたコードを掲載したものは書いて出していました。
●GR-ROSE と Node.js のプログラムの間でシリアル通信(一方向) - Qiita
https://qiita.com/youtoy/items/460a9458309b0b4a7dbd
この時は、「シリアル通信で受信した内容を、1行ごとに読んでログに出すだけ」という感じでした。
const SerialPort = require('serialport')
const Readline = require('@serialport/parser-readline')
const port = new SerialPort('/dev/【自分の環境に合わせて変更】')
const parser = port.pipe(new Readline({ delimiter: '\r\n' }))
parser.on('data', console.log)
ソースコードも、かなりシンプル。
今回はもう少し、仕組みを足したものを作っていきます。
micro:bit でシリアル通信
過去の記事でも登場していますが、MakeCode for micro:bit で開発した場合に、シリアル通信を扱うブロックは以下になります。
(「高度なブロック」の中の「シリアル通信」の部分)
諸事情により、micro:bit でのシリアル通信(MakeCode for micro:bit を使うパターン)について、ちょっと見直してる。 pic.twitter.com/rfVq85G1bK
— you (@youtoy) December 4, 2021
このブロックを使うことで、読み書きを行うことができます。
簡単なシリアル通信を試す
まずは、簡単なシリアル通信を試します。
以下のシンプルなものを、動かしてみます。
- micro:bit のセンサーの値を、PC で受けとってログに出力(通信の向きは、micro:bit から PC)
- PC で Node.js のプログラムを実行したら、micro:bit側の LED が特定の表示になる(通信の向きは、PC から micro:bit)
ここで、PC と micro:bit は USBケーブルでつなぎ、その USBケーブルによるシリアル通信を行う形にします。
micro:bit のセンサーの値を PC でログに出力
micro:bit側のブロック
micro:bit側は、以下のような内容にしました。
加速度の X・Y・Z のそれぞれの値を、単純にカンマ区切りで書き出すだけのものです(出力間隔は 0.5秒)。
この時点で、プログラムを書き込んだ後に、「コンソールを表示 デバイス」という部分を押すと、シリアル通信で送られている値を見ることができます(また、グラフも表示されます)。
micro:bit側の準備は、これで完了です。
Node.js のプログラム
上記で micro:bit からシリアル通信で書き出すようにしたセンサーの値を、Node.js で受け取りログとして出力してみます。
なお、以下の内容は Mac で試しています(シリアルポートの指定の部分が、Mac用になってます)。
まず、USBケーブルで micro:bit を接続した状態で、以下のコマンドを実行します。
$ ls -l /dev/tty.*
実行結果で何行かの出力が得られたのですが、その中で自分の場合は /dev/tty.usbmodem●●●●●●●
というような名称のものが、micro:bit のシリアルポートでした(●の部分は数字が入ります)。
上記の確認が終わったら、Node Serialport を使えるように npm i serialport
を実行します。
その後、公式ドキュメントの以下や過去に使ったプログラムを元に、プログラムを作成します。
●SerialPort Usage | Node Serialport
https://serialport.io/docs/guide-usage
●What are Parsers? | Node Serialport
https://serialport.io/docs/api-parsers-overview
動作させるものは、以下になります。
const SerialPort = require("serialport");
const Readline = require("@serialport/parser-readline");
const port = new SerialPort("/dev/【自分の環境に合わせて変更】", {
baudRate: 115200,
});
const parser = port.pipe(new Readline());
parser.on("data", console.log);
上記を実行して、以下のような出力を得ることができました。
Node.js のプログラムで、それを受信して、ログに書き出し。
— you (@youtoy) December 4, 2021
↓こちらを使って。
●Hello from Node Serialport | Node Serialport
https://t.co/Kh7OHF51nK pic.twitter.com/qp1zRqmJCq
Node.js のプログラムで「split」を使う
3つのデータがカンマ区切りで出てきたので、それを 1つずつ取り出そうと、プログラムの最後の行を以下のように変更してみました。
parser.on("data", (data) => console.log(data.split(",")));
以下の処理を使っています。
●String.prototype.split() - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/split
そうすると、以下のツイートの画像の、上半分のほうの出力が得られました。
split() を使って分けようとしたら、こうなってた...
— you (@youtoy) December 4, 2021
下半分は、「delimiter: "\r\n"」を指定した後の出力。
●String.prototype.split() - JavaScript | MDN
https://t.co/09fsyjoaTf pic.twitter.com/8boaq27gVG
どうも、先ほどのログ出力をしていた時、出力の後ろには空白がたくさんついていたようでした。それと \r
も付いていたり...
とりあえず \r
を除いてしまおうかと、先ほどのプログラムの中で、 delimiter: "\r\n"
を指定しました。最後から 2行目の内容を、以下のように書きかえてます。
const parser = port.pipe(new Readline({ delimiter: "\r\n" }));
その結果が、上のツイートの画像の、下半分のほうです。
「さて、空白を除去するのは何だっけ?」と思って、ググろうとしたところ、Twitter でタイムリーにコメントをいただけました。
trim() してから split() してみるとよいのではないでしょうか
— ぺんた (@plageoj) December 4, 2021
以下を見ると、改行文字も除去してくれるらしく、 \r
を除く部分もやってくれそうです。
●String.prototype.trim() - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/trim
最終的に以下の内容にすることで、無事に x・y・z のそれぞれの値を、余計な文字が含まれない状態で得られました。
const SerialPort = require("serialport");
const Readline = require("@serialport/parser-readline");
const port = new SerialPort("/dev/【自分の環境に合わせて変更】", {
baudRate: 115200,
});
const parser = port.pipe(new Readline());
parser.on("data", (data) => console.log(data.trim().split(",")));
そして、無事に良い感じの結果を得られました。
改行文字も除去と書いてあったので、delimiter の指定はなくして、以下のきれいな結果を得られました。
— you (@youtoy) December 4, 2021
ありがとうございます。 pic.twitter.com/WnUPIks9yD
PC で Node.js のプログラムを実行したら micro:bit側の LED が特定の表示になる
micro:bit側のブロック
micro:bit側は、以下のような内容にしました。
micro:bit のシリアル通信で文字を受信する。 pic.twitter.com/I3RscMSWDL
— you (@youtoy) December 4, 2021
Node.js のプログラム
Node.js で作ったプログラムは以下のとおりです。
const SerialPort = require("serialport");
const port = new SerialPort("/dev/【自分の環境に合わせて変更】", {
baudRate: 115200,
});
const stringSent = "abc";
port.write(stringSent, function (err) {
if (err) {
return console.log("Error on write: ", err.message);
}
console.log("message written");
setTimeout(() => console.log("end"), 3000);
});
port.on("error", function (err) {
console.log("Error: ", err.message);
});
setTimeout(() => console.log("end"), 3000);
という処理を入れて、シリアル通信での PC からの書き込みを行った後、すぐにプログラムが終了しないようにしています。
これを入れている理由は、Node.js のプログラムがすぐに終了すると、それに連動して micro:bit 側のプログラムも終了させられるためです。今回の内容は、これを入れてないと LED が特定パターンで光る前に、micro:bit側のプログラムが終了させられて、LED の表示が出ないまま初期状態に戻る動作となりました。
なお、 const stringSent = "abc";
の部分を const stringSent = "123";
に変えて実行すると、micro:bit上の LED の点灯パターンが変わります。
おわりに
今回、PC と micro:bit との間で有線のシリアル通信を行いました。
そして、両方とも無事に想定通りの動作をさせることができました。
今回は軽いお試しのみでしたが、PC と連携をさせた部分を活用し、micro:bit単体では実現できない仕組みを、PC の力を借りて実行するようなものを試せればと思います。
余談
最後に書いていた件の 1つの方向性として、以下のように Groveモジュールを使えば実現できる IoT の仕組みを、PC の力を借りて実行するようなものは、試せればと思っています。
●【IoTLT 2020】 micro:bit を使った IoT(Grove - UART Wifi V2 との組み合わせ、IFTTT利用) - Qiita
https://qiita.com/youtoy/items/459289951134544e73eb