ESP32: Webサーバ上でリアルタイムグラフ表示(Chart.js)

Arduino開発

ESP32:Chart.jsを使ったグラフ表示

公開講座(春夏)「AI/IoTセンサのしくみを知ろう」の補足です。

ESP32のウェブサーバ上で,センサで取得したデータをグラフ表示します。グラフの描画には,Chart.jsというグラフ描画用javascriptライブラリを使用します。グラフの更新には,WebSocketプロトコルを使用して,リアルタイムでセンシングしたデータの表示を行います。

1.ファイルの準備

まず,次のライブラリをArduinoにインストールし,必要なファイルを準備します。

<インストールするライブラリ> Arduinoライブラリに追加

arduinoWebSockets ,  elapsedMillis

<ダウンロードするファイル>  dataフォルダ内に配置し,ESP32 Sketch Data Uploadにより書き込み

Chart.min.js (画面の下の方:Chart.jsから空白・改行を除いたファイル)

favicon.ico(ブラウザのタイトル横のアイコンファイル:無いとChromeでエラーがでます。) 各自で作成

2.Arduinoプログラム

グラフの表示例として,温度センサHTU21Dのセンシング結果を表示するプログラムを示します。

基本的に,このひな形のプログラムの(1)~(4)を,各センサのヘッダファイル,初期化設定,センシング手順に変更することで,グラフ表示に対応できます。

*Wi-FiアクセスポイントのSSID, パスワードは各自で設定をお願いします。

*サンプリング周期は,1 秒(1000 ms)に設定しています。センサの応答時間にあわせて,調整してください。

*スマートフォンやパソコン,タブレットから,最大5台まで同時に表示ができます。テストをするには,ブラウザのタブを複数開くと良いでしょう。

#include <WiFi.h>
#include <WebServer.h>
#include <WebSocketsServer.h> // arduinoWebSocketsライブラリ
#include <elapsedMillis.h> // elapsedMillisライブラリ
#include <SPIFFS.h>
#include "index_html.h" // web server root index

//===============================-
// (1) センサライブラリのヘッダファイル
#include "SparkFunHTU21D.h"

//===============================-
// (2) センサの定義
HTU21D htu21d; // temperature & humidity sensor

// Webサーバー 192.168.4.1:80
WebServer webServer(80); // 80番ポート
// Websocketサーバー 192.68.4.1:81
WebSocketsServer webSocket = WebSocketsServer(81); // 81番ポート

// WiFi設定
const char *ssid = ""; // 各自のSSIDを入力
const char *password = ""; //各自のパスワードを入力
const IPAddress ip(192, 168, 4, 1);
const IPAddress subnet(255, 255, 255, 0);

// サンプリング周期
elapsedMillis sensorElapsed;
const unsigned long DELAY = 1000; // ms

// Webコンテンツのイベントハンドラ
void handleRoot() {
  String s = INDEX_HTML; // index_html.hより読み込み
  webServer.send(200, "text/html", s);
}
void handleNotFound() {
  webServer.send(404, "text/plain", "File not found.");
}

//==========================================
// (3) センサの初期化
//==========================================
void sensor_init() {
  htu21d.begin(); // 温度・湿度センサの初期化
}

// センサのデータ(JSON形式)
const char SENSOR_JSON[] PROGMEM = R"=====({"val1":%.1f})=====";

// データの更新
void sensor_loop() {
  char payload[16];
//=============================================
// (4) センシング
  float temp = htu21d.readTemperature();
  snprintf_P(payload, sizeof(payload), SENSOR_JSON, temp);
//============================================= 

  // WebSocketでデータ送信(全端末へブロードキャスト)
  webSocket.broadcastTXT(payload, strlen(payload));
//  Serial.println(payload);
}

void setup() {
  // シリアル通信設定
  Serial.begin(115200);
  delay(100);

  // Wi-Fi設定
  WiFi.disconnect(true);
  delay(1000);
  WiFi.softAP(ssid, password);
  delay(100);
  WiFi.softAPConfig(ip, ip, subnet);
  IPAddress myIP = WiFi.softAPIP();

  sensor_init(); // センサの初期化

  // Webサーバーのコンテンツ設定
  // favicon.ico, Chart.min.jsは dataフォルダ内に配置
  SPIFFS.begin();
  webServer.serveStatic("/favicon.ico", SPIFFS, "/favicon.ico");
  webServer.serveStatic("/Chart.min.js", SPIFFS, "/Chart.min.js");
  webServer.on("/", handleRoot);
  webServer.onNotFound(handleNotFound);
  webServer.begin();

  // WebSocketサーバー開始
  webSocket.begin();
}

void loop(void) {
  webSocket.loop();
  webServer.handleClient();

  // 一定の周期でセンシング
  if (sensorElapsed > DELAY) {
    sensorElapsed = 0;
    sensor_loop();
  }
}

3.プログラム(index_html.h)

グラフを描画するためのヘッダファイル(index_html.h)で,ESP32のウェブページのindex.htmlに相当します。

このファイルは,センサが変わっても,特に書き換える必要はありません。

タイトルと凡例を変えたい場合,

・<title>Sensor graph</title>,

・<b>Sensor graph</b>,

・label: “Sensor-01”

などを変更した後,再度,書き込みましょう。

// index_html.h
const char INDEX_HTML[] PROGMEM = R"=====(
<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Sensor graph</title>
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<div style="text-align:center;"><b>Sensor graph</b></div>
<div class="chart-container" position: relative; height:350px; width:100%">
  <canvas id="myChart" width="600" height="400"></canvas>
</div>
<br><br>
<script src = "/Chart.min.js"></script>  
<script>
var graphData = {
  labels: [],  // X軸のデータ (時間)
  datasets: [{
        label: "Sensor-01",
        data: [], // Y軸のデータ(センシング結果)
        fill: false,
        borderColor : "rgba(254,97,132,0.8)",
        backgroundColor : "rgba(254,97,132,0.5)",
  }]
};
var graphOptions = {
  maintainAspectRatio: false,
  scales: {
    yAxes: [{
      ticks: {beginAtZero:true}
    }]
  }
};

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

var ws = new WebSocket('ws://' + window.location.hostname + ':81/');
ws.onmessage = function(evt) {
  var Time = new Date().toLocaleTimeString();
  var data_x1 = JSON.parse(evt.data)["val1"];
  console.log(Time);
  console.log(data_x1);

  chart.data.labels.push(Time);
  chart.data.datasets[0].data.push(data_x1);
  chart.update();
};
ws.onclose = function(evt) {
  console.log("ws: onclose");
  ws.close();
}
ws.onerror = function(evt) {
  console.log(evt);
}
</script>
</body></html>
)=====";

 

コメント

  1. […] […]

タイトルとURLをコピーしました