はじめに
前回(#4 LCD表示編)でLCDにCO2濃度を表示できるようになりました。
今回はESP32でWebサーバーを立てて、ブラウザで CO2濃度をグラフ表示 します。
なぜWebサーバー・グラフをつけるのか
LCDでは「今の数値」しかわかりません。
CO2濃度が時間とともにどう変化したかをグラフで見られれば、分析がしやすくなると考えました。
例えば「何時頃に濃度が上がるのか」「換気でどれくらい下がるのか」が一目でわかります。
仕組み
ESP32自身がWebサーバーになり、ブラウザからアクセスするとグラフページを返します。
1. [ブラウザ] からESP32にアクセス
2. [ESP32] がCO2データ付きHTMLを返す
3. [ブラウザ] がグラフを表示
グラフの描画には Chart.js という無料のライブラリを使います。
使用ライブラリ
-
WiFi(ESP32標準) -
WebServer(ESP32標準)
Chart.jsはインターネットから読み込むのでインストール不要です。
コード
#include <HardwareSerial.h>
#include <MHZ19.h>
#include <WiFi.h>
#include <WebServer.h>
#include "secrets.h"
MHZ19 myMHZ19;
HardwareSerial mySerial(2);
int co2Value = 0;
int co2History[20] = {0};
int historyIndex = 0;
unsigned long lastTime = 0;
const char* ssid = SECRET_SSID;
const char* password = SECRET_PASSWORD;
WebServer server(80);
void handleRoot() {
String dataPoints = "";
for (int i = 0; i < 20; i++) {
int index = (historyIndex + i) % 20;
dataPoints += String(co2History[index]);
if (i < 19) dataPoints += ",";
}
String html = "<!DOCTYPE html><html>";
html += "<head><meta charset='UTF-8'>";
html += "<script src='https://cdn.jsdelivr.net/npm/chart.js'></script>";
html += "</head><body>";
html += "<h1>CO2 Monitor</h1>";
html += "<p>現在のCO2: " + String(co2Value) + "ppm</p>";
html += "<canvas id='chart'></canvas>";
html += "<script>";
html += "var ctx = document.getElementById('chart').getContext('2d');";
html += "var chart = new Chart(ctx, {";
html += "type: 'line',";
html += "data: { labels: Array.from({length: 20}, (_, i) => i + 1),";
html += "datasets: [{ label: 'CO2 (ppm)', data: [" + dataPoints + "],";
html += "borderColor: 'rgb(75, 192, 192)', tension: 0.1}] },";
html += "options: { scales: { y: {beginAtZero: false } } }";
html += "});";
html += "setInterval(function() { window.location.reload(); }, 5000);";
html += "</script>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void setup() {
Serial.begin(115200);
mySerial.begin(9600, SERIAL_8N1, 13, 14);
myMHZ19.begin(mySerial);
myMHZ19.autoCalibration(false);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Wi-Fi接続成功!");
Serial.print("IPアドレス:");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.begin();
}
void loop() {
server.handleClient();
unsigned long currentTime = millis();
if (currentTime - lastTime >= 5000) {
lastTime = currentTime;
co2Value = myMHZ19.getCO2();
co2History[historyIndex] = co2Value;
historyIndex = (historyIndex + 1) % 20;
Serial.print("CO2: ");
Serial.println(co2Value);
}
}
使い方
- 書き込んだらSerial Monitorを開く
- 表示されたIPアドレス(例:192.168.1.50)を確認
- 同じWi-FiのスマホやpcのブラウザでそのIPアドレスにアクセス
グラフが表示されます。
一番ハマったポイント
ずっとリロードされてグラフが表示されない
最初、ブラウザがずっと再読み込みを繰り返すだけで、グラフがいつまでも表示されませんでした。
原因は loop() の中で delay(5000) を使っていたことでした。
delay() の間、ESP32は完全に停止します。その間ブラウザからのアクセスに応答できず、ページが正しく返せていなかったのです。
解決方法:delay()をやめてmillis()を使う
// 悪い例:delay()で5秒止まる
delay(5000);
// 良い例:millis()で時間を測る
unsigned long currentTime = millis();
if (currentTime - lastTime >= 5000) {
lastTime = currentTime;
// 5秒ごとの処理
}
millis() はESP32が起動してからの時間をミリ秒で返します。
これを使えばESP32を止めずに「5秒経ったか」を判定できるので、その間もブラウザに応答し続けられます。
この修正でグラフが無事表示されました。
コードのポイント
① 過去のデータを配列に保存
int co2History[20] = {0};
過去20件のCO2データを配列に保存し、グラフに表示しています。
② リング状にデータを保存
historyIndex = (historyIndex + 1) % 20;
% 20 で、20件を超えたら古いデータから上書きしていきます。
まとめ
- ESP32でWebサーバーを立てた
- Chart.jsでCO2をグラフ表示できた
- delay()をやめてmillis()にしたらリロード問題が解決した
delay() の落とし穴は初心者がハマりやすいポイントだと思います。同じ問題で困っている人の参考になれば嬉しいです。
次回はNTPで時刻を取得してグラフのX軸に表示します!
#6 NTP時刻編へ続く