LoginSignup
8
2

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-02-09

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

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

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

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

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

前の記事で発生していた問題を解決する

Webサイト側の内容修正

前の記事で発生していた問題に関し、以下のサイトの情報を見つつ対応しました。

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

以下に、改善後のソースコードを掲載します。
前の記事の内容と差があるところにコメントを入れました。

<!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

プログラムを実行してみる

シリアル通信で読み出された値は、コンソールに以下のように出力されました。

出力(改善版).jpg

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

センサーの値をグラフ化

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

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

ソースコードと実行結果

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

<!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

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2