Web Serial API を使って micro:bit からセンサーの値（XYZ）を読み取る＆リアルタイムなグラフ化

この記事は、以下の内容の続きです。

●Web Serial API を使って micro:bit からセンサーの値を読み取る（途中段階） - Qiita
　https://qiita.com/youtoy/items/9606c58369796a65f8f5

ちなみに、Web Serial API 関連の話は、上記の記事とは別に以下の記事も書いています。

* [Web Serial API を最短の手順で試したくてやったこと（micro:bit を利用） - Qiita](https://qiita.com/youtoy/items/a0071a6d2ef7f6930d33)
* [Web Serial API を使った micro:bit への値の書き込み - Qiita](https://qiita.com/youtoy/items/f77dc205e817f31a4e18)

冒頭に記載した記事の内容では、micro:bit から値を読み取れてはいたものの、意図したとおりには読み取れていない問題があったため、それを解決するための対応をしたのがこの記事の内容です。
また、それと合わせて読み取った値のグラフ化も行ってみました。

最終的な完成形は以下となります。

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">Web Serial API を使った、シリアル通信による値の読み取りができた！<br><br>micro:bit の加速度センサーの値（XYZ）をブラウザで直接読み取って、3種類の値をそれぞれ別の色に対応づけてリアルタイムにグラフ化。<br><br> <a href="https://twitter.com/hashtag/IoTLT?src=hash&amp;ref_src=twsrc%5Etfw">#IoTLT</a> <a href="https://t.co/TEjDV7HTCU">pic.twitter.com/TEjDV7HTCU</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1359209878741348352?ref_src=twsrc%5Etfw">February 9, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 前の記事で発生していた問題を解決する
### Webサイト側の内容修正
前の記事で発生していた問題に関し、以下のサイトの情報を見つつ対応しました。

●Read from and write to a serial port
　https://web.dev/serial/

以下に、改善後のソースコードを掲載します。
[前の記事](https://qiita.com/youtoy/items/9606c58369796a65f8f5)の内容と差があるところにコメントを入れました。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Web Serial（Read）</title>
  </head>

  <body>
    <h1>Web Serial（Read）</h1>
    <button onclick="onStartButtonClick()">接続</button>

    <script>
      // ▼追加した部分1
      class LineBreakTransformer {
          constructor() {
            this.chunks = "";
          }

          transform(chunk, controller) {
            this.chunks += chunk;
            const lines = this.chunks.split("\r\n");
            this.chunks = lines.pop();
            lines.forEach((line) => controller.enqueue(line));
          }

          flush(controller) {
            controller.enqueue(this.chunks);
          }
      }

      async function onStartButtonClick() {
        try {
          const port = await navigator.serial.requestPort();
          await port.open({ baudRate: 115200 });

          while (port.readable) {
            // ▼追加した部分2
            const textDecoder = new TextDecoderStream();
            const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
            const reader = textDecoder.readable
            .pipeThrough(new TransformStream(new LineBreakTransformer()))
            .getReader();

            try {
              while (true) {
                const { value, done } = await reader.read();
                if (done) {
                  console.log("Canceled");
                  break;
                }
                // ▼ここでデコードの処理をしていたのを削除
                console.log(value);
              }
            } catch (error) {
              console.log("Error: Read");
              console.log(error);
            } finally {
              reader.releaseLock();
            }
          }
        } catch (error) {
          console.log("Error: Open");
          console.log(error);
        }
      }
    </script>
  </body>
</html>
```

### micro:bit のプログラム（少し手を加えてみる）
micro:bit のプログラムは前回と同じものでも良かったのですが、加速度センサーの値を XYZ の 3種類全て書きだすように変えてみました。

![マイクロビットのプログラム（XYZ）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/0b57170d-47ab-2ae6-2fd8-f06bb4190929.jpeg)

### プログラムを実行してみる
シリアル通信で読み出された値は、コンソールに以下のように出力されました。

![出力（改善版）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/5a1f0b01-3fc3-3269-853c-d4246cd549bf.jpeg)

micro:bit側で 1行ずつ書きだした内容が、読み取り側でも 1行ずつ表示されるようになりました。

## センサーの値をグラフ化
以前、以下の記事を書いた際に使った Chart.js とプラグインを使い、加速度センサーの値をグラフ化してみます。

●【JavaScript 2020】 MQTT で受信したデータを Smoothie Charts（smoothie.js）以外でリアルタイムにグラフ化： Chart.js とプラグインを利用 - Qiita
　https://qiita.com/youtoy/items/252f255c9d794bf3d964

### ソースコードと実行結果
グラフ化のための処理を加えたソースコードは以下のとおりです。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Web Serial（グラフ化）</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@1.8.0/dist/chartjs-plugin-streaming.min.js"></script>
  </head>

  <body>
    <h1>Web Serial（グラフ化）</h1>
    <button onclick="onStartButtonClick()">接続</button>
    <br>
    <canvas id="myChart"></canvas>

    <script>
      class LineBreakTransformer {
          constructor() {
            this.chunks = "";
          }

          transform(chunk, controller) {
            this.chunks += chunk;
            const lines = this.chunks.split("\r\n");
            this.chunks = lines.pop();
            lines.forEach((line) => controller.enqueue(line));
          }

          flush(controller) {
            controller.enqueue(this.chunks);
          }
      }

      const ctx = document.getElementById("myChart").getContext("2d");

      let chart = new Chart(ctx, {
        type: "line",
        data: {
          datasets: [
            {
              label: 'X',
              borderColor: 'rgb(200, 50, 50)',
              backgroundColor: 'rgba(200, 50, 50, 0.2)',
              data: [],
            },
            {
              label: 'Y',
              borderColor: 'rgb(50, 50, 200)',
              backgroundColor: 'rgba(50, 50, 200, 0.2)',
              data: [],
            },
            {
              label: 'Z',
              borderColor: 'rgb(50, 200, 50)',
              backgroundColor: 'rgba(50, 200, 50, 0.2)',
              data: [],
            },
          ],
        },
        options: {
          scales: {
            xAxes: [
              {
                type: "realtime",
                realtime: {
                  delay: 500,
                },
              },
            ],
          },
        },
      });

      async function onStartButtonClick() {
        try {
          const port = await navigator.serial.requestPort();
          await port.open({ baudRate: 115200 });

          while (port.readable) {
            const textDecoder = new TextDecoderStream();
            const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
            const reader = textDecoder.readable
            .pipeThrough(new TransformStream(new LineBreakTransformer()))
            .getReader();

            try {
              while (true) {
                const { value, done } = await reader.read();
                if (done) {
                  console.log("Canceled");
                  break;
                }
                console.log(value);

                if(value.slice(0,1)==="X") {
                  chart.data.datasets[0].data.push({
                    x: Date.now(),
                    y: value.slice(2),
                  });
                } else if(value.slice(0,1)==="Y") {
                  chart.data.datasets[1].data.push({
                    x: Date.now(),
                    y: value.slice(2),
                  });
                } else if(value.slice(0,1)==="Z") {
                  chart.data.datasets[2].data.push({
                    x: Date.now(),
                    y: value.slice(2),
                  });
                }
                chart.update({
                  preservation: true,
                });
              }
            } catch (error) {
              console.log("Error: Read");
              console.log(error);
            } finally {
              reader.releaseLock();
            }
          }
        } catch (error) {
          console.log("Error: Open");
          console.log(error);
        }
      }
    </script>
  </body>
</html>
```

このグラフ化の処理を加えたものを実行すると、冒頭に掲載した動画のようなグラフ化が行えます。

このように、Web Serial API を使って micro:bit からセンサーの値を読み取り、それを Webサイト上でリアルタイムにグラフ化することができました。

## 追記
ソースコードなどは以下の GitHub にも置きました。
　https://github.com/yo-to/WebSeriaAPI/tree/main/examples/02_read_microbit_and_graph_drawing