M5StickC PlusとWebブラウザをBluetooth接続してChart.jsでグラフ表示する

先日、「M5StickC Plus」と「p5.js」をBluetoothで接続し、M5StickC Plusで取得したデータに基づいて、Webブラウザで図形をアニメーション表示してみました(記事は こちら)。
p5.jsとマイコンデバイスをBluetooth接続するために「p5.ble.js」というJavaScriptライブラリも使っています。

ところで、ここでできあがった画面(ボールが転がるアニメーション)を眺めているうちに、もっと実用的なもの、具体的にはマイコンデバイスから受信したデータに基づいてグラフ表示するようなものをつくってみたくなりました。
p5.jsを使って、グラフをゼロから描いてもいいのですが、「Chart.js」というグラフ描画のためのJavaScriptライブラリがあるので、今回はこれを活用してグラフ描画してみようと思います。
つまり今回は、「p5.ble.js」は使いますが「p5.js」は使いません。「p5.ble.js」でマイコンデバイスからデータをBluetooth受信し、「Chart.js」で受信したデータをグラフ表示します。


M5StickC Plus用のスケッチは以下のとおりです。

#include <M5StickCPlus.h>
#include "BLESerial.h"
BLESerial bleSerial;

float accX = 0.0f;
float accY = 0.0f;
float accZ = 0.0f;
float x = 120.0f;
float v = 0.0f;

void setup() {
  M5.begin();
  M5.IMU.Init();
  M5.Lcd.setRotation(1);
  bleSerial.begin("ESP32");
}

void loop() {
  M5.update();
  M5.IMU.getAccelData(&accX, &accY, &accZ);
  v += accY;
  x += v;
  if(x>=230.0) { v=0.0; x=230.0; }
  if(x<=10.0)  { v=0.0; x=10.0; }
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.fillCircle((int)x, 68, 10, YELLOW);
  bleSerial.print(accX);
  bleSerial.print(",");
  bleSerial.print(accY);
  bleSerial.print(",");
  bleSerial.println(accZ);
  delay(20);
}

こちら の記事で紹介したスケッチとほとんど同じです。今回はグラフ表示の際に複数のデータを表示させたかったため、3つのデータをコンマ区切りで送信するようにしました。

このスケッチをM5StickC Plusに書き込んでおきます。

HTMLファイルは以下のとおりです。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>chart.js & p5.ble.js demo</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.2.1"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@2.0.0"></script>
<script src="https://unpkg.com/p5ble@0.0.7/dist/p5.ble.js"></script>
<script src="ble.js"></script>
</head>
<body>

<button type="button" onclick="connectAndStartNotify()">START</button>
<button type="button" onclick="stopNotifications()">STOP</button>

<table style="width: 100%;">
<tr><td><canvas id="myChart" width=1000px height=200px></canvas></td></tr>
</table>

<script src="sketch.js"></script>

</body>
</html>

Chart.jsを利用するために「chart.js」ライブラリを、またChart.jsでリアルタイムチャートを描画するために「luxon」「chartjs-adapter-luxon」「chartjs-plugin-streaming」を読み込みます。Bluetooth通信のために「p5.ble.js」ライブラリも読み込みます。
また、後述する「ble.js」「sketch.js」というふたつのプログラムもここで読み込んでおきます。
Bluetooth通信を開始、終了するための「button」と、グラフ表示のための「canvas」を設置します。

「ble.js」の内容は以下のとおりです。

const serviceUuid = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
const txCharacteristic = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
const rxCharacteristic = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";

let myCharacteristic;
let myBLE;
let receiveText;

window.addEventListener("load", bleSetup);

function bleSetup() {
  myBLE = new p5ble();
}

function connectAndStartNotify() {
  myBLE.connect(serviceUuid, gotCharacteristics);
}

function gotCharacteristics(error, characteristics) {
  if (error) console.log("error: ", error);
  for(let i=0; i<characteristics.length; i++) {
    if(rxCharacteristic == characteristics[i].uuid) {
      myCharacteristic = characteristics[i];
      myBLE.startNotifications(myCharacteristic, handleNotifications, "string");
    }
  }
}

function handleNotifications(data) {
  receiveText += data;
  if (data === "\n") {
    getValue(receiveText);
    receiveText = "";
  }
}

function stopNotifications() {
  myBLE.stopNotifications(myCharacteristic);
  getValue();
}

こちら の記事で紹介したものとほとんど同じです。HTMLファイルが読み込まれたときの処理方法を変更、およびBluetooth通信を終了したときにデータを空にする処理を追加しています。

「sketch.js」の内容は以下のとおりです。

let values;

function getValue(value) {
  if(value) values = value.split(",");
  else      values = null;
}

function onRefresh(chart) {
  if(values) {
    chart.data.datasets.forEach((dataset, index) => {
      dataset.data.push({
        x: Date.now(),
        y: values[index]
      });
    });
  }
}

var data = {
  datasets: [
    { label: 'value0', fill: false, pointRadius: 0, lineTension: 0, borderJoinStyle: 'round', borderColor: '#00A0E9', backgroundColor: '#00A0E9', data: [] },
    { label: 'value1', fill: false, pointRadius: 0, lineTension: 0, borderJoinStyle: 'round', borderColor: '#E60012', backgroundColor: '#E60012', data: [] },
    { label: 'value2', fill: false, pointRadius: 0, lineTension: 0, borderJoinStyle: 'round', borderColor: '#F39800', backgroundColor: '#F39800', data: [] },
  ]
};

var options = {
  maintainAspectRatio: false,
  scales: {
    x: { type: 'realtime', realtime: { duration: 20000, refresh: 500, delay: 500, onRefresh: onRefresh } },
  }
};

var config = { type: 'line', data: data, options: options };
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, config);

Chert.jsを使って、リアルタイムチャートを描画しています。
このプログラム内の記載内容を変更することで、所望の形式のグラフを表示することができます。

HTMLファイル、「ble.js」、「sketch.js」の計3ファイルを同一フォルダに設置し、Webブラウザ(Google Chrome)でHTMLファイルを開きます。
M5StickC Plusの電源が入っている状態で「START」ボタンをクリックすると、接続先を選択するウィンドウが表示されます。
「ESP32」を選択して「ペア設定」をクリックすると、グラフ描画が始まります。

なお、私がM5Stack、M5StickCの使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。


ごく基本的なところから、かなり複雑なスケッチや、ネットワーク接続など、比較的高度なものまで、つまづかずに読み進めていけるような構成になっており、大変わかりやすい本です。


このサイトで書いている、M5Stackシリーズ(M5Stack、M5StickCなど)に関するブログ記事を、「さとやまノート」という別のブログページに、あらためて整理してまとめました。

他のM5Stackシリーズの記事にも興味のある方は「さとやまノート」をご覧ください。