ESP32で作るIoT環境モニター:温湿度・気圧・CO2をWebダッシュボードで可視化
部品と用品
| ESP32開発ボード(DevKit V1等) | 1 |
| BME280 温湿度・気圧センサー | 1 |
| MH-Z19B CO2センサー(UART接続) | 1 |
| SSD1306 OLEDディスプレイ(128x64 I2C) | 1 |
| ブレッドボード・ジャンパーワイヤー | 1式 |
| USBケーブル・電源アダプター | 1 |
必要な工具
- PC(Arduino IDE / PlatformIO)
- WiFi環境
このプロジェクトについて
ESP32のWiFi/Bluetooth機能と豊富なGPIOを活かし、室内環境を総合的にモニタリングするIoTデバイスを作ります。温度・湿度・気圧・CO2濃度を測定し、ローカルWebダッシュボードとOLEDディスプレイの両方でリアルタイム表示。さらにデータをクラウド(InfluxDB + Grafana、またはThingsBoard)に送信して長期トレンド分析も可能です。
センサー構成
- BME280: I2C接続(デフォルトアドレス0x76)。温度(±0.5℃)、湿度(±3%)、気圧(±1hPa)を高精度測定
- MH-Z19B: UART接続(9600bps)。NDIR方式でCO2濃度(0-5000ppm)を測定。自動キャリブレーション機能付き
- SSD1306: I2C接続(0x3C)。現地確認用に現在値を常時表示
アーキテクチャ
[センサー群] → [ESP32] → [OLED表示]
↓
[WiFi] → [Web Server (ポート80)]
↓
[HTTP/MQTT] → [クラウド: InfluxDB/Grafana 等]
Webダッシュボード機能
- リアルタイム数値表示(ゲージ・数値カード)
- 過去24時間/7日/30日のトレンドグラフ
- 閾値超過アラート(CO2 > 1000ppm等)
- データCSVエクスポート
- レスポンシブデザイン(スマホ/PC両対応)
コード
メインスケッチ
#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ArduinoJson.h>
// WiFi設定
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// センサー
Adafruit_BME280 bme;
#define MH_Z19_RX 16
#define MH_Z19_TX 17
HardwareSerial mhz19(2);
// OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Webサーバー
WebServer server(80);
// データ構造体
struct SensorData {
float temperature;
float humidity;
float pressure;
int co2;
unsigned long timestamp;
} sensorData;
void setup() {
Serial.begin(115200);
// I2C初期化
Wire.begin(21, 22);
// BME280
if (!bme.begin(0x76)) {
Serial.println("BME280 not found!");
}
// MH-Z19B
mhz19.begin(9600, SERIAL_8N1, MH_Z19_RX, MH_Z19_TX);
// OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED init failed!");
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
// WiFi接続
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected: " + WiFi.localIP().toString());
// Webサーバールート設定
server.on("/", handleRoot);
server.on("/api/data", handleAPI);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
readSensors();
updateDisplay();
delay(2000);
}
void readSensors() {
// BME280読み取り
sensorData.temperature = bme.readTemperature();
sensorData.humidity = bme.readHumidity();
sensorData.pressure = bme.readPressure() / 100.0F; // hPa
// MH-Z19B読み取り
byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
mhz19.write(cmd, 9);
delay(100);
if (mhz19.available() >= 9) {
byte response[9];
mhz19.readBytes(response, 9);
if (response[0] == 0xFF && response[1] == 0x86) {
sensorData.co2 = (response[2] << 8) | response[3];
}
}
sensorData.timestamp = millis();
}
void updateDisplay() {
display.clearDisplay();
display.setCursor(0, 0);
display.printf("Temp: %.1f C", sensorData.temperature);
display.setCursor(0, 16);
display.printf("Hum: %.1f %%", sensorData.humidity);
display.setCursor(0, 32);
display.printf("Press: %.0f hPa", sensorData.pressure);
display.setCursor(0, 48);
display.printf("CO2: %d ppm", sensorData.co2);
display.display();
}
void handleRoot() {
String html = R"rawliteral(
ESP32 Environment Monitor
🌡️ Environment Monitor
--Temperature°C
--Humidity%
--PressurehPa
--CO2ppm
)rawliteral";
server.send(200, "text/html", html);
}
void handleAPI() {
StaticJsonDocument<256> doc;
doc["temperature"] = sensorData.temperature;
doc["humidity"] = sensorData.humidity;
doc["pressure"] = sensorData.pressure;
doc["co2"] = sensorData.co2;
doc["timestamp"] = sensorData.timestamp;
String output;
serializeJson(doc, output);
server.send(200, "application/json", output);
}
クラウド連携(InfluxDB + Grafana)
データを継続的に蓄積・可視化するには、以下の手順でセットアップ:
- DockerでInfluxDBとGrafanaを起動
- ESP32側でHTTP POSTまたはMQTTでInfluxDBに書き込み
- Grafanaでダッシュボード作成
// InfluxDB書き込み例(loop内で定期実行)
void sendToInfluxDB() {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
http.begin("https://YOUR_INFLUXDB:8086/api/v2/write?org=YOUR_ORG&bucket=YOUR_BUCKET&precision=ms");
http.addHeader("Authorization", "Token YOUR_TOKEN");
http.addHeader("Content-Type", "text/plain");
String line = "environment,device=esp32_01 temperature=" + String(sensorData.temperature, 2) +
",humidity=" + String(sensorData.humidity, 2) +
",pressure=" + String(sensorData.pressure, 2) +
",co2=" + String(sensorData.co2);
http.POST(line);
http.end();
}
筐体・設置
- 3Dプリントケース(Thingiverse等で「ESP32 BME280 case」で検索)
- センサー部は通気性を確保(スリットやメッシュ)
- 直射日光・エアコン吹き出し口を避け、室内中央付近に設置
- CO2センサーは人の呼気が直接当たらない高さ(1.5m程度)が理想
トラブルシューティング
| 症状 | 原因・対処 |
|---|---|
| BME280が見つからない | I2Cアドレス確認(0x76/0x77)、配線チェック、プルアップ抵抗 |
| MH-Z19Bが0ppm固定 | TX/RX逆接続、電源電圧不足(5V必要)、プレヒート未完了(3分待機) |
| OLED真っ白 | I2Cアドレス(0x3C/0x3D)、リセットピン配線 |
| WiFi接続失敗 | SSID/パスワード確認、2.4GHz帯のみ対応、電源容量不足 |
製造プロセス
- 家庭用の低コストワイヤレスセンサーネットワークを構築
- K30 CO2センサで室内空気質をリアルタイム監視する
- デジタル身長計の自作:Arduino Nanoとレーザー距離センサーで成長記録
- Arduinoでシンプルな障害物センサーを自作 – IR LEDとフォトダイオード入門
- DFRobot静電容量式指紋センサーをArduino/ESP8266で使う – 登録・認証・削除の完全ガイド
- Arduino Nanoで作るスマートな動体検知水鉄砲
- コンパクトなArduinoレーダーシステム – HC-SR04超音波センサー & 1.8インチST7735ディスプレイ搭載
- ArduinoセンサーデータをPHPMyAdmin経由でMySQLにアップロードする手順
- Arduino UNOで作る静電容量式タッチLEDスイッチ - 簡単DIY
- MKR GSM 1400で実現するリアルタイムIoT圧力監視:Arduino Cloud+Google Sheets連携
- Arduinoセンサー:種類、応用、実世界プロジェクト