ESP32:Cart.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>
)=====";
コメント