気の向くままに辿るIT/ICT/IoT
IoT・電子工作

ESP32とTFT液晶で温湿度計付き時計を作る

ホーム前へ次へ
ESP8266って?

ESP32とTFT液晶で温湿度計付き時計を作る

ESP32とTFT液晶で温湿度計付き時計を作る

2018/08/28

 Wi-Fi(wifi)モジュールESP8266の内、ESP32/ESP-WROOM-32の開発ボードとTFT 1.8インチ液晶、DHT11モジュールを使って温度計・湿度計付きデジタル時計を自作してみるページ。

 ESP8266の内、ESP-WROOM-02とESP32は日本の技適を通ったものがあり、安心して使えるし、ESP32は、GPIOピンも多く、実はBluetooth/BLEも搭載するスグレモノ。

 尚、ESP32は温度センサーも備わっていますが、今回は、温度・湿度センサーモジュールであるDHT11を使いました。

 当初、単にArduinoで作った温湿度計付き時計のESP32版にするつもりでしたが、ESP32内蔵時計をNTPで時計合わせすることにしたため、別途RTCモジュールを用意する必要はありません。

 Arduino版だとNTPを使おうと思うとイーサネットシールドなり、Wifiモジュールなり別途用意しないとコンセントに挿して使うことになってしまいます。

 が、WiFiモジュールでありながら、マイコンを持ち、Arduino感覚でプログラムを書き込むことができるESP8266、ESP32であれば、WiFi環境さえあれば、無線でOKなので何もなくてもNTPで時計合わせできる...

 と思ったのですが、PCのUSBポートやACアダプタ付きUSB充電器へ接続すれば問題なく動作しますが、WiFiは一時的にでも多くの電流を要するからか、電池駆動しようとすると...後述。

 それは、さておき、話を進めます。

前提

 ここでは、Arduino IDEを使うのでArduino IDEの[ツール] => [ボード]から[espressif/arduino-esp32]を選択、ESPにスケッチをアップロードできる状態であること。

 Arduino IDE 1,8,6で追加された[ツール] => [ライブラリを管理...]メニューか、従来の[スケッチ] => [ライブラリをインクルード...] => [ライブラリを管理...]メニューを辿ってライブラリ管理画面を開き、TFT 1.8液晶用に[Adafruit GFX Library]、[Adafruit ST7735 and ST7789 Library]、DHT11/DHT22センサーモジュール用に[DHT sensor library for ESPx]を検索、インストールしておくこと。

 参考までに自身の使用しているOSは、Debian(Linux)、Arduino IDEのバージョンは、1.8.6(1.8.6を素直に起動できない場合、起動方法参照)。

必要なモノ

 今回は、WiFiモジュールの載ったESP32の開発ボードを使いましたが、技適はさておき、GPIOピンの数は足りるはずなのでESP12あたりでもできるでしょう。

必要な電子部品類

 ここでは、ESP32開発ボードを使いましたが、ESP-WROOM-32モジュール単体の場合には、ピンホールが半円だし、ブレッドボードとピッチも合わないため、別途、市販のブレイクアウトボードを買うか、自作でごにょごにょする必要があるでしょう。

 尚、ESP32は、入力電圧定格3.3V、今回使ったTFT1.8インチもDHT11センサーも3.3Vでいけました。

ESP32開発ボードへのスケッチのアップロード

 ESP32開発ボードの内、DOIT製DevKit V1及び互換ボードをArduino IDEで使う場合、スケッチのアップロード時、書き込む際にボード上のRESET/RST押したまま、BOOTボタンを押し、RSTを放す、もしくは、BOOTボタンのみを押して放す必要がありましたが、Arduino IDE 1.8.6では、その必要がなくなったようです。

 少なくとも後者の操作が必要だった自身のボードにおいては、この必要がなくなったことを確認済み。

 よってArduino IDE 1.8.6を使っている場合、特記すべきことはありません。

回路

ESP32TFT1.8DHT11
19MISO-
5CS-
18SCL-
23SDA-
17A0-
(16)(RESET)-
4-DATA
3.3VVCC
LED+-
GNDGND
LED--

 配線は、目的は違えど流れ着いたMhageGH/esp32_ST7735_Movieを参考にさせて頂きましたが、スケッチと回路を一致させれば、特殊なピンを避ければ、なお面倒がありませんが、どのピンでも問題ないでしょう。

 今回、ESP32開発ボードには、GPIOが38ピンあるEspressif DevKitC V4と同じっぽい一方、4隅の穴からDOIT DevKit風にも見えるものを使いましたが、30ピンのDOIT DevKit V1や互換機には、GPIO17はないようなので他の空いたピンを使い、スケッチにも反映させればよいでしょう。

 注意すべきは、このTFT 1.8(KMR-1.8 SPI)は、なぜか、SPIというかI2Cっぽく、ケーブル2本は、スケッチとは、別の配線をする必要がある点。(ちなみにArduinoでは同様に4本別の配線を要しました)

 具体的には、スケッチ上では、KMR-1.8液晶のSCLK/MOSIに接続することになっているESP32のピン(今回は18/23)を回路上では、SCL/SDAに配線する必要があります。

 そうしないと液晶に何も表示されず戸惑うことになります。(バックライトは点くし、もにょもにょ何やら映そうとはしますが、結果何も表示されない。)

 ちなみに、このTFT液晶、購入当時、そうとは知らず、返送不要で返金してもらったものの、後で使えることがわかり、再支払いした経緯があります。

スケッチ

#include <WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <DHTesp.h>
 
DHTesp DHT;
 
#define DHT11_PIN 4
 
// 任意の4〜5つのGPIOピンを使うことができる
#define sclk 18  // この定義のまま、実際には、SCL
#define mosi 23  // この定義のまま、実際には、SDA
#define cs 5   // CS
#define dc 17   // A0
#define rst 16  // ESP32のRESETに接続してもよい
 
Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);
 
// WiFi設定
#define WIFI_SSID  "SSID"
#define WIFI_PASSWORD  "PASSWORD"
#define JST   3600*9
 
void setup(void) {
 Serial.begin(115200);
 Serial.println("initialize");
 // Use this initializer if you're using a 1.8" TFT
 tft.initR(INITR_BLACKTAB);  // ST7735Sチップ/ブラックタブの初期化
 
 uint16_t time = millis();
 tft.fillScreen(ST77XX_BLACK);
 time = millis() - time;
 
 Serial.println(time, DEC);
 delay(500);
 
 Serial.println("done");
 delay(1000);
 
 // WiFi開始
 Serial.println("WiFi connecting...");
 WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
 while(WiFi.status() != WL_CONNECTED) {
  Serial.print('.');
  delay(500);
 }
 Serial.println();
 Serial.printf("Connected, IP address: ");
 Serial.println(WiFi.localIP());
 Serial.println("WiFi connected!");
 
 // NTP開始
// configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
 configTime( 9 * 3600L, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
 
 DHT.setup(DHT11_PIN, DHTesp::DHT11);
 
 delay(1000);
}
 
void loop() {
 time_t t;
 struct tm *tm;
 static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
 char rdate[30], rday[30], rtime[30], rsec[30];
 
 t = time(NULL);
 tm = localtime(&t);
 
 // 日付時刻取得値を出力値に成形
 Serial.printf(" %04d/%02d/%02d(%s) %02d:%02d:%02d\n",
    tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
    wd[tm->tm_wday],
    tm->tm_hour, tm->tm_min, tm->tm_sec);
 sprintf(rdate, "%04d/%02d/%02d",
    tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
 sprintf(rday, " (%s)",
    wd[tm->tm_wday]);
 sprintf(rtime, " %02d:%02d",
    tm->tm_hour, tm->tm_min);
 sprintf(rsec, ":%02d",
    tm->tm_sec);
 delay(1000 - millis()%1000);
 
 // 温湿度取得
 float humidity = DHT.getHumidity();
 float temperature = DHT.getTemperature();
 
 // シリアルモニタ出力
 Serial.print("Date : ");
 Serial.print(rdate);
 Serial.println(rday);
 Serial.print("Time : ");
 Serial.print(rtime);
 Serial.println(rsec);
 
 // TFT 1.8液晶出力
 // 背景色:黒 文字色:白
 tft.fillScreen(ST77XX_BLACK);
 tft.setTextColor(ST77XX_WHITE);
 // 日付
 tft.setCursor(0, 20);
 tft.setTextSize(2);
 tft.println(rdate);
 // 曜日
 tft.setCursor(0, 40);
 tft.println(rday);
 // 時刻(時:分)
 tft.setCursor(0, 70);
 tft.setTextSize(3);
 tft.print(rtime);
 // 時刻(秒)
 tft.setTextSize(1);
 tft.println(rsec);
 // 温度
 tft.setCursor(0, 110);
 tft.setTextSize(2);
 tft.print(" ");
 tft.print(temperature, 1);
 tft.setTextSize(1);
 tft.println(" C");
 // 湿度
 tft.setCursor(0, 130);
 tft.setTextSize(2);
 tft.print(" ");
 tft.print(humidity, 1);
 tft.setTextSize(1);
 tft.println(" %");
}

 スケッチは、以前作ったArduino時計、ESPスマートリモコンやESPスマートコンセントのWiFi部分、Adafruit ST7735ライブラリ、DHT sensor library for ESPxのヘッダファイルやサンプルスケッチ、ピンアサイン部分は、先のリンク先を、NTP設定については、ESP8266のntpの設定は1行でを、[delay(1000 - millis()%1000);]については、申し訳ないことにサイトURLは失念しましたが、別件をぐぐっている最中に、たまたま見かけた情報を参照させて頂いたものから成る合作。

 そのまま使えますが、少なくとも自身で利用可能なWiFiルーター、アクセスポイント用にSSID/PASSWORDは書き換える必要があります(WiFi接続できないと時計も表示されない)。

#include <WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_BME280.h"
 
#define I2C_SDA 32
#define I2C_SCL 33
#define SEALEVELPRESSURE_HPA (1013.25)
#define BME280_ADD 0x76
 
Adafruit_BME280 bme(I2C_SDA, I2C_SCL);
 
// 任意の4〜5つのGPIOピンを使うことができる
#define sclk 18  // この定義のまま、実際には、SCL
#define mosi 23  // この定義のまま、実際には、SDA
#define cs 5   // CS
#define dc 17   // A0
#define rst 16  // ESP32のRESETに接続してもよい
 
Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);
 
// WiFi設定
#define WIFI_SSID  "SSID"
#define WIFI_PASSWORD  "PASSWORD"
#define JST   3600*9
 
void setup(void) {
 Serial.begin(115200);
 Serial.println("initialize");
 // Use this initializer if you're using a 1.8" TFT
 tft.initR(INITR_BLACKTAB);  // ST7735Sチップ/ブラックタブの初期化
 
 uint16_t time = millis();
 tft.fillScreen(ST77XX_BLACK);
 time = millis() - time;
 
 Serial.println(time, DEC);
 delay(500);
 
 Serial.println("done");
 delay(1000);
 
 // WiFi開始
 Serial.println("WiFi connecting...");
 WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
 while(WiFi.status() != WL_CONNECTED) {
  Serial.print('.');
  delay(500);
 }
 Serial.println();
 Serial.printf("Connected, IP address: ");
 Serial.println(WiFi.localIP());
 Serial.println("WiFi connected!");
 
 // NTP開始
// configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
 configTime( 9 * 3600L, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
 
 delay(1000);
 
 bool status;
 
 status = bme.begin(BME280_ADD);
 if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
 }
 delay(1000);
}
 
void loop() {
 time_t t;
 struct tm *tm;
 static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
 char rdate[30], rday[30], rtime[30], rsec[30];
 
 t = time(NULL);
 tm = localtime(&t);
 
 // 日付時刻取得値を出力値に成形
 Serial.printf(" %04d/%02d/%02d(%s) %02d:%02d:%02d\n",
    tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
    wd[tm->tm_wday],
    tm->tm_hour, tm->tm_min, tm->tm_sec);
 sprintf(rdate, "%04d/%02d/%02d",
    tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
 sprintf(rday, " (%s)",
    wd[tm->tm_wday]);
 sprintf(rtime, " %02d:%02d",
    tm->tm_hour, tm->tm_min);
 
 // シリアルモニタ出力
 Serial.print("Date : ");
 Serial.print(rdate);
 Serial.println(rday);
 Serial.print("Time : ");
 Serial.println(rtime);
 Serial.print("Temp : ");
 Serial.print(bme.readTemperature());
 Serial.println(" C");
 Serial.print("Humi : ");
 Serial.print(bme.readHumidity());
 Serial.println(" %");
 
 // TFT 1.8液晶出力
 // 背景色:黒 文字色:白
 tft.fillScreen(ST77XX_BLACK);
 tft.setTextColor(ST77XX_WHITE);
 // 日付
 tft.setCursor(0, 20);
 tft.setTextSize(2);
 tft.println(rdate);
 // 曜日
 tft.setCursor(0, 40);
 tft.println(rday);
 // 時刻(時:分)
 tft.setCursor(0, 70);
 tft.setTextSize(3);
 tft.println(rtime);
 // 温度
 tft.setCursor(0, 110);
 tft.setTextSize(2);
 tft.print(" ");
 tft.print(bme.readTemperature(), 1);
 tft.setTextSize(1);
 tft.println(" C");
 // 湿度
 tft.setCursor(0, 130);
 tft.setTextSize(2);
 tft.print(" ");
 tft.print(bme.readHumidity(), 1);
 tft.setTextSize(1);
 tft.println(" %");
}
[2019/02/15]

 以前、買って動作確認済みのBME280に差し替えてみたスケッチがこれ。

 秒やDHT11関連の削除した部分は明示していませんが、BME280用に追加したのは、太字部分。

 やはり、温度と湿度だけでBME280では他に標高や気圧も簡単に取得できますが、今回は使っていません(ちなみにBMP280だと温度・気圧のみで湿度は計測できません)。

[2020/04/27]

 あれ、WiFi接続とNTPクライアントの実験によるとNTPのconfigTime()の第一引数が違ったらしい...エラーも出ずに使えてるのに...他で試そうとしたら、コンパイルエラーに。

 正しくは、[9 * 3600L](時差9h x 3600sec)のようで間違えた第一引数JSTは、configTzTime()のものらしい...しかもクォートもいるっぽい。

実行

ESP32で温度計・湿度計付き時計

 社内、宅内無線LANルーターまたは、アクセスポイントにアクセスできるよう準備・確認し、スケッチをアップロードすれば、TFT液晶に表示されるはずですが、そうでない場合は、ESP32ボード上のRST/RESETボタンを押下、それでも表示されない場合は、配線を見直します。

 カラーなので定義さえしてあれば、色も任意ですが、今回は、背景色を黒、文字色を白、スペースの関係で曜日は別の行とし、秒と温度、湿度の単位は、なんとなくフォントを小さくしてみましたが、温湿度はもう少しフォントを大きくしてもよかったかもしれません。

 ん?眺めている限りにおいて温度と湿度の小数点以下は常にゼロで意味がないように見受けられますが、気にしないことにします。

[2019/02/15]

 先のBME280とそのスケッチでは、小数点以下も正常に表示されました。

省電力化・スリープモード

 https://www.espressif.com/sites/default/files/documentation/9b-esp8266-low_power_solutions_en.pdf => ESP8266 Low-Power Solutions(...パスは同じ、ファイル名末尾のsolutionsとen.pdfの間のアンスコが1つ増えただけ...)によれば、ESP8266/ESP-WROOM-02/ESP-WROOM-32には、3種類のスリープモードがありますが、電池駆動を考えている為、最も省電力な数μAのDeep Sleepモードを使いたいところですが、未実装。

 ただ...、電池駆動の場合、プラス/マイナスをESP32開発ボードの5V/GNDピンに接続すればよいのですが、単3電池x2(eneloop)昇圧+USBモバイルバッテリでも、単3電池x4(eneloop)+電池ボックスでもWiFi用には、電流が不足するようで無負荷で入力電圧5V以上はありますが、接続すると2.6V台あたりまで電圧降下、液晶上にも表示しようという努力は認められますが、表示しきれない...。

 スリープ以前に電池駆動にできるのだろうか...?できなければ、NTPなしでRTCモジュール併用?それか、コンセント接続前提?

 ESP8266の動作が不安定なときの対策案は、電池駆動においても解決策になり得るか?

 いや、それはそれとして乾電池で連続340日間動作するESP8266搭載ワイヤレスセンサESP32 WiFi 温湿度計の長期間動作が、電池駆動を含めた解決策なのか?

 ただ、データロガーではなく、モニタ付き温湿度計付き時計なのでどこまで取り入れられるかは未知数ですが、中でもULP、これを使った場合、時計も秒を出力せず、温湿度も1分ごとに計測、Deep Sleepから復帰、表示、Deep Sleep...なんてこともできたりするのだとすれば、1筋の光明が...。

USB充電器仕様で常用

ケーシングしたESP32/BME280/TFT1.8温湿度計付き時計
[2019/04/28]

 電源をAC接続USB充電器からとることにし、適度なサイズが好みの100均セリアのトレカケースに入れて常用してみることにしました。

 ユニバーサルボードにはんだ付けするまでもないか...とブレッドボード版のまま入れました。

 また、温度計、湿度計としては、こもると何なのでBME280は、ニョキッと外に出すことにしました。

[2020/01/17]

 一応の完成をみてから、キッチンで使うにあたり、何気なく、炊飯器と同じ電源タップにつないでずっと平置きしていて、時折、温湿度がおかしなことになってるなと思っていたところ、今更ながら、炊飯中に、表示が激しく点滅したり、乱れたりと目まぐるしい状態になっていることに気づきました...。

 そこで、別系統で電源をとりつつ、より電源(スイッチ付きACタップ+USB充電器)をとりやすい、自作ラズパイスマートスピーカーも載っている冷蔵庫の上に置いたみたところ、ディスプレイサイズからして視認性がイマイチということで100均の約200mm幅のマグネットバーをケースの幅の長さに切ってグルーガンで固定、冷蔵庫側面にペタっとくっつけてみたら、磁力も程よく、良い感じになりました。

[2024/03/07]

 製作から5年半、NTPの時刻取得に失敗する現象が時々見受けられるな...と思ってたら、先日、液晶に何も映らなくなり、外してみたら、USBポートがもげて、ホールもメッキがなく再ハンダできず、ボードを変更...。

 当時のスケッチそのままにアップロードすると書き込めたり、書き込めなかったりしつつ、書き込めても[rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)]でリセットし続けたり、稀にWi-Fi接続できたかと思ったらBME280を見失ったり、ESP32ボードもいくつか替えてみたりしたものの、解決に至らず、諦めることに。

 お疲れ様。

ホーム前へ次へ