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> )=====";
コメント
[…] […]