【micro:bit 2021】 PC と micro:bit を通信させる方法： Node.js・MakeCode でのシンプルなシリアル通信（USBケーブル接続）

この記事は、2021年の [micro:bit のアドベントカレンダー](https://qiita.com/advent-calendar/2021/microbit) の 6日目の記事です。

micro:bit を PC と USBケーブルでつないだ状態で、PC との間でやりとりをする方法の 1つである「シリアル通信」を扱います。
その中でも Node.js を用いたものについて、軽いお試しをしていく形です。

## 過去に micro:bit と PC との間の通信について書いた記事
過去にも、micro:bit と PC との間で何らか通信させる話を、以下のように複数書いています。

- Bluetooth関連
   - [Web Bluetooth API を使い micro:bit とブラウザの間で BLE通信](https://qiita.com/youtoy/items/c98c0996458a21fc1e67)
   - [あらためて noble でガジェットを扱う話に着手する： toio と micro:bit を複数混在させてスキャン、toio の複数制御（6台分） - Qiita](https://qiita.com/youtoy/items/15a743d692f348327ff9)
   - [Web Bluetooth API で BLE（Chrome と micro:bit をつなぐ）](https://qiita.com/youtoy/items/cd2c3d4770d4ad75a321)
   - [micro:bit と #obniz で BLE ＋ UART を試そうとした話と Mac用ツール](https://qiita.com/youtoy/items/34b70bb57c9f54c9e971)
   - [micro:bit で BLE ＋ UART による文字列の送受信](https://qiita.com/youtoy/items/46502b9bd17fad16d427)
   - [#obniz と micro:bit で obniz-noble を使った BLE通信](https://qiita.com/youtoy/items/bb24dd9051f3eb8a33e2)
- シリアル通信関連
   - [Web Serial API を使って micro:bit からセンサーの値（XYZ）を読み取る＆リアルタイムなグラフ化](https://qiita.com/youtoy/items/6394a5570ddd54a2ce87)
   - [Web Serial API を使って micro:bit からセンサーの値を読み取る（途中段階まで）](https://qiita.com/youtoy/items/9606c58369796a65f8f5)
   - [Web Serial API を使った micro:bit への値の書き込み](https://qiita.com/youtoy/items/f77dc205e817f31a4e18)
   - [Web Serial API を最短の手順で試したくてやったこと（micro:bit を利用）](https://qiita.com/youtoy/items/a0071a6d2ef7f6930d33)
   - [#toio を Alexa や micro:bit から操作する 〜概要編〜（JavaScriptライブラリを使ってみた）](https://qiita.com/youtoy/items/0ee56f5395e3f4c72c51)

その中で、使ったという話は書いたけれど、ソースコードを公開したり等とはしていなかったと思われる
　「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行ごとに読んでログに出すだけ」という感じでした。

```javascript
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 で開発した場合に、シリアル通信を扱うブロックは以下になります。
（「高度なブロック」の中の「シリアル通信」の部分）

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">諸事情により、micro:bit でのシリアル通信（MakeCode for micro:bit を使うパターン）について、ちょっと見直してる。 <a href="https://t.co/rfVq85G1bK">pic.twitter.com/rfVq85G1bK</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1467047523437408258?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

このブロックを使うことで、読み書きを行うことができます。

## 簡単なシリアル通信を試す
まずは、簡単なシリアル通信を試します。
以下のシンプルなものを、動かしてみます。

- 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側は、以下のような内容にしました。
![micro:bit　のセンサーの値を書き出す.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/97590e3d-1970-17e7-e8f0-f3f0aea07d5f.jpeg)
加速度の X・Y・Z のそれぞれの値を、単純にカンマ区切りで書き出すだけのものです（出力間隔は 0.5秒）。

この時点で、プログラムを書き込んだ後に、「コンソールを表示 デバイス」という部分を押すと、シリアル通信で送られている値を見ることができます（また、グラフも表示されます）。
![MakeCode上での確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f8ccc0d2-b1d2-ce9d-b09b-7ebcba958bae.jpeg)

micro:bit側の準備は、これで完了です。

#### Node.js のプログラム
上記で micro:bit　からシリアル通信で書き出すようにしたセンサーの値を、Node.js で受け取りログとして出力してみます。
なお、以下の内容は Mac で試しています（シリアルポートの指定の部分が、Mac用になってます）。

まず、USBケーブルで micro:bit を接続した状態で、以下のコマンドを実行します。

```bas
$ 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

動作させるものは、以下になります。

```javascript
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);
```

上記を実行して、以下のような出力を得ることができました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">Node.js のプログラムで、それを受信して、ログに書き出し。<br><br>↓こちらを使って。<br>●Hello from Node Serialport | Node Serialport<br>　<a href="https://t.co/Kh7OHF51nK">https://t.co/Kh7OHF51nK</a> <a href="https://t.co/qp1zRqmJCq">pic.twitter.com/qp1zRqmJCq</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1467063538477047809?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#### Node.js のプログラムで「split」を使う
3つのデータがカンマ区切りで出てきたので、それを 1つずつ取り出そうと、プログラムの最後の行を以下のように変更してみました。

```javascript
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

そうすると、以下のツイートの画像の、上半分のほうの出力が得られました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">split() を使って分けようとしたら、こうなってた...<br><br>下半分は、「delimiter: &quot;\r\n&quot;」を指定した後の出力。<br><br>●String.prototype.split() - JavaScript | MDN<br>　<a href="https://t.co/09fsyjoaTf">https://t.co/09fsyjoaTf</a> <a href="https://t.co/8boaq27gVG">pic.twitter.com/8boaq27gVG</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1467066382580064258?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

どうも、先ほどのログ出力をしていた時、出力の後ろには空白がたくさんついていたようでした。それと `\r` も付いていたり...
とりあえず `\r` を除いてしまおうかと、先ほどのプログラムの中で、 `delimiter: "\r\n"` を指定しました。最後から 2行目の内容を、以下のように書きかえてます。

```javascript
const parser = port.pipe(new Readline({ delimiter: "\r\n" }));
```

その結果が、上のツイートの画像の、下半分のほうです。

「さて、空白を除去するのは何だっけ？」と思って、ググろうとしたところ、Twitter でタイムリーにコメントをいただけました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">trim() してから split() してみるとよいのではないでしょうか</p>&mdash; ぺんた (@plageoj) <a href="https://twitter.com/plageoj/status/1467066879839973379?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

以下を見ると、改行文字も除去してくれるらしく、 `\r` を除く部分もやってくれそうです。

●String.prototype.trim() - JavaScript | MDN
　https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/trim

最終的に以下の内容にすることで、無事に x・y・z のそれぞれの値を、余計な文字が含まれない状態で得られました。

```javascript
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(",")));
```

そして、無事に良い感じの結果を得られました。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">改行文字も除去と書いてあったので、delimiter の指定はなくして、以下のきれいな結果を得られました。<br><br>ありがとうございます。 <a href="https://t.co/WnUPIks9yD">pic.twitter.com/WnUPIks9yD</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1467069165970558978?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### PC で Node.js のプログラムを実行したら micro:bit側の LED が特定の表示になる
#### micro:bit側のブロック
micro:bit側は、以下のような内容にしました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">micro:bit のシリアル通信で文字を受信する。 <a href="https://t.co/I3RscMSWDL">pic.twitter.com/I3RscMSWDL</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1467075644425392130?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#### Node.js のプログラム
Node.js で作ったプログラムは以下のとおりです。

```javascript
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