Skip to main content
  1. Posts/

ESP32とBME280で、温湿度計を作った

ESP32(Arduino)とBME280(温湿度センサ)をつかって、部屋の温湿度をスマホに表示するダッシュボードを実装しました。

温湿度をスマホに表示

概要 #

ESP32にWebサーバを立てて、BME280で測定した部屋の温湿度をWebページとして表示し、スマホやPCで見られるようにします。

回路側 #

BME280とESP32を接続するだけの、シンプルな実装です。 センサーとの接続は、I2Cにしました。BME280とESP32の接続は、SCK - 22番、SDI - 21番で接続しています。

ESP32とBME280の接続

ESP32の開発ボードは、ESP32-DevKit-C-32E(ESP-WROOM-32E)を使用しています。 32D以下は、2021年現在、利用が推奨されていないそうです。

ESP32-WROOM-32Eについて、従来品との違い

プログラム側 #

Arduinoで動かしているプログラムには、大きく4つの機能を実装しました。

  • WebサーバとしてスマホやPCからのリクエストに応答(Webページのレンダリング)
  • mDNSに対応(.localのアドレスでアクセスできるようにする)
  • BME280からセンサーの値を取得
  • NTPで時刻を同期

こんな画面が表示されるようにしました。

スマホでアクセスしたときのスクショ

スマホからアクセスしたとき

PCでアクセスしたときのスクショ

PCからアクセスしたとき

実装 #

#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Wire.h>
#include "SparkFunBME280.h"
#include <time.h>
#define HTML "<!doctype html>"\
"<html lang=\"ja\">"\
"<head>"\
" <meta charset=\"UTF-8\" />"\
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />"\
" <meta http-equiv=\"refresh\" content=\"60; URL=./\">"\
" <link href=\"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css\" rel=\"stylesheet\">"\
" <link rel=\"stylesheet\" href=\"https://pro.fontawesome.com/releases/v5.10.0/css/all.css\" integrity=\"sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p\" crossorigin=\"anonymous\"/>"\
" <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">"\
" <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>"\
" <link href=\"https://fonts.googleapis.com/css2?family=Outfit:wght@600&display=swap\" rel=\"stylesheet\">"\
" <title></title>"\
"</head>"\
"<body class=\"p-4 bg-gray-200\">"\
" <main>"\
" <div class=\"flex flex-col md:flex-row md:flex-wrap\">"\
" <div class=\"flex-auto p-4 mb-4 md:mr-4 bg-gray-100 rounded-lg shadow-xl\">"\
" <h2 class=\"font-sans text-xl font-bold text-gray-800\"><i class=\"fas fa-thermometer-half mr-2\"></i>気温</h2>"\
" <p class=\"text-8xl text-gray-800\" style=\"font-family: 'Outfit', sans-serif;\">%0.1f<span class=\"text-6xl\">˚C</span></p>"\
" </div>"\
" <div class=\"flex-auto p-4 mb-4 md:mr-4 bg-gray-100 rounded-lg shadow-xl\">"\
" <h2 class=\"font-sans text-xl font-bold text-gray-800\"><i class=\"fas fa-water mr-2\"></i>湿度</h2>"\
" <p class=\"text-8xl text-gray-800\" style=\"font-family: 'Outfit', sans-serif;\">%0.0f<span class=\"text-6xl\">&#37;</span></p>"\
" </div>"\
" <div class=\"flex-auto p-4 mb-4 md:mr-4 bg-gray-100 rounded-lg shadow-xl\">"\
" <h2 class=\"font-sans text-xl font-bold text-gray-800\"><i class=\"fas fa-tachometer-alt mr-2\"></i>気圧</h2>"\
" <p class=\"text-8xl text-gray-800\" style=\"font-family: 'Outfit', sans-serif;\">%0.0f<span class=\"text-6xl\">hPa</span></p>"\
" </div>"\
" <div class=\"flex-auto p-4 mb-4 md:mr-4 bg-gray-100 rounded-lg shadow-xl\">"\
" "\
" <h2 class=\"font-sans text-xl font-bold text-gray-800\"> <i class=\"fas fa-tint mr-2\"></i>露点</h2>"\
" <p class=\"text-8xl text-gray-800\" style=\"font-family: 'Outfit', sans-serif;\">%0.1f<span class=\"text-6xl\">˚C</span></p>"\
" </div>"\
" </div>"\
" <p class=\"text-sm text-gray-800 tracking-widest\" style=\"font-family: 'Outfit', sans-serif;\">%s</p>"\
" </main>"\
"</body>"\
"</html>"
WebServer server(80);
// WiFi情報
const char* ssid = WIFI_SSID; // define by yourself
const char* pass = WIFI_PASS; // define by yourself
// センサ
BME280 sensor;
// NTP
#define TIMEZONE_JST "JST-9" // Arduino/cores/esp8266/TZ.h
#define NTP_SERVER1 "ntp.nict.jp" // NTPサーバー
#define NTP_SERVER2 "ntp.jst.mfeed.ad.jp" // NTPサーバー
// ダッシュボード画面を表示する
void handleRoot() {
// センサーから各データを取得
float humidity = sensor.readFloatHumidity();
float pressure = sensor.readFloatPressure();
float temp = sensor.readTempC();
float due = sensor.dewPointC();
// 現在時刻の取得
time_t timeNow = time(NULL);
struct tm* tmNow = localtime(&timeNow);
char szDateTime[32];
strftime(szDateTime, sizeof(szDateTime), "%Y/%m/%d %H:%M:%S", tmNow);
// レスポンスとなるHTMLを生成
char html[3000];
sprintf(html, HTML, temp, humidity, pressure / 100, due, szDateTime);
server.send(200, "text/html", html);
}
// 存在しないアドレスが指定された時の処理
void handleNotFound() {
server.send(404, "text/plain", "Not Found.");
}
void setup() {
// BME280とI2C接続する
Wire.begin(21, 22); // SDA, SCK
sensor.setI2CAddress(0x76);
if (sensor.beginI2C() == false) {
Serial.println("Sensor connect failed.");
}
// WiFiのアクセスポイントに接続する
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
delay(2000);
// NTP同期
configTzTime(TIMEZONE_JST, NTP_SERVER1, NTP_SERVER2);
// mDNS: esp32.local で接続できるようにする
MDNS.begin("esp32");
// リクエストに対する処理を登録
server.on("", handleRoot);
server.on("/", handleRoot);
server.onNotFound(handleNotFound);
// Webサーバーを起動
server.begin();
}
void loop() {
server.handleClient();
}

雑感と参考資料 #

Webサーバの実装 #

いくつかのサイトを参考にしながら実装しました。

mDNSが超便利 #

ESP32はマルチキャストDNSに対応しています。これにより、ESP32に割り振られたIPアドレスを調べずとも、ブラウザから「http://esp32.local」のようにアドレスを入力するだけでアクセスできます。

https://lipoyang.hatenablog.com/entry/2020/03/25/112815

ページスタイル #

以下のフレームワークを使いました。

  • Tailwind CSS
  • Font Awesome
  • Google Fonts

Tailwind CSSは初めて使用しましたが、ドキュメントがよく整備されているので、HTMLめったにやらない私でもサクッとスタイルをつけることができました。

ページの自動リロード #

1分で自動リロードするようにしたのですが、こちらのサイトが参考になりました。
https://designsupply-web.com/media/programming/4431/

<meta http-equiv="refresh" content="60; URL=./">

I2C #

Wire.begin()でピン番号を指定できるとは知らなかった……。

NTPで時刻同期 #


今回の記事とは関係ないけど、 https://tool-lab.com/ というサイトに遭遇した。 電子工作関連の情報がまとまっていて、勉強になりそう……。