ESP32-DevKitCでNTPサーバと時刻同期して RTCモジュール DS3231SNの時刻合わせを行うLCD時計:環境モニタ(1)

ESP32-DevKitC ESP-WROOM-32開発ボード(以下、ESP32-DevKitC)を使ってインターネット上のNTP(Network Time Protocol)サーバと時刻同期してRTC(リアルタイムクロック)モジュールの時刻合わせを行うLCD時計を作った際の作業メモです。時刻の精度を高めた上で温度・湿度・気圧・CO2濃度を測る環境センサを加えていきます。

NTPサーバと時刻同期して RTCモジュール DS3231SNの時刻合わせを行うLCD時計(mp4、23秒、約8.6Mbyte)
目次

集めたパーツ

ネット通販(No.1、4、11は秋月電子通商、他はAmazon)でパーツを集めてブレッドボード上で結線しました。ESP32-DevKitCの基板は横幅が広いのでブレッドボードは6穴タイプです。

ESP32-DevKitCはRTCを内蔵していますが標準実装はRC発振。精度を上げるために水晶発振子をチップ内蔵したDS3231SNを使ったRTCモジュールをI2Cで外部接続しました。

LCDモジュールは今後環境モニタとして温湿度、気圧などを追加表示するので、20文字 x 4行の5V系の2004タイプを使っています。

#パーツ個数
1ESP32-DevKitC ESP-WROOM-32開発ボード1
2DS3231SN  I2C RTCモジュール1
32004 LCDモジュール 20×4キャラクタ 青
I2C I/F モジュール、バックライト付き、
バックライト調整用半固定ボリューム付き
1
4I2Cバス用双方向電圧レベル変換モジュール(PCA9306)
プルアップ抵抗付き(1kΩx4個)
1
5XL4015 可変DC-DCステップダウンコンバータ (3個入り)1
(手持ち)
6DCジャック DIP化モジュール1
7サンハヤト ニューブレッドボード SAD-011
8ジャンパーワイヤ適量
9USBケーブル(USB A オス to microB オス)1
(手持ち)
10コイン形リチウムイオン2次電池「LIR2032」(4個入り)1
11スイッチングACアダプター9V1.3A 100~240V1

モジュール間の結線図とLCD時計の組み立て(2022/4/21訂正)

結線図

各モジュール間をジャンパーワイヤで接続した際の結線図です。網掛け部分は温湿度センサやCO2センサ、microSDカードスロットなど拡張予定箇所です。

環境モニタ(1)の結線図
環境モニタ(1)の結線図(2022/4/21訂正)

組立て

ブレッドボード上に ESP32-DevKitC、DS3231SN RTC、20行 4桁の2004 LCDモジュールを実装してジャンパーワイヤで結線します。I2C(SCL、SDA)の2線と給電(5V or 3.3V、GND)の2線、計4本の配線ワイヤでモジュール間をつないでいくので組み立ても容易です。

スケッチ書き換え時などESP32-DevKitC とPCのシリアルポート接続はUSBケーブル(USB A オス to microB オス、データ通信用)を使います。

電源は、9V出力のACアダプタからXL4015 可変DC-DCステップダウンコンバータで降圧した5Vを使っています。

ESP32-DevKitCとDS3231SNを使ったLCD時計
ESP32-DevKitCとDS3231SNを使ったLCD時計

1602 LCDモジュール利用の場合

手持ちの16行、2桁表示の1602 LCDモジュールに交換してみました。表示文字数が16文字内に収まるように曜日表示を3文字+括弧にします。
スケッチ(3)の修正箇所は下記2箇所です。

LiquidCrystal_I2C lcd(0x27, 16, 2);  // 1602LCD利用時
const char* weekStr[7] = {"(Sun)","(Mon)","(Tue)","(Wed)","(Thu)","(Fri)","(Sat)"}; // 1602LCD利用時
ESP32-DevKitCとDS3231SNを使ったLCD時計
手持ちの1602 LCDモジュールに交換

バッテリーバックアップ

DS3231SNモジュールの背面にはバッテリーバックアップ用のコイン電池ホルダが付いているので、充電できるコイン形リチウムイオン2次電池「LIR2032」を入れておきます。

DS3231SNモジュールにセットした充電式の「LIR2032」
DS3231SNモジュールにセットした充電式の「LIR2032」

参考:
ESP32-DevKitC ESP-WROOM-32開発ボード回路図 | akizukidenshi.com
ESP32-DevKitC-1 pin-layout | espressif.com

各モジュールの電源電圧とI2C信号レベルの確認とI2C信号波形の観察(2021/12/6追記)

ESP32-DevKitCモジュールにはLDO(Low Dropout、NCP1117)が実装されているので、PCからのUSBバスパワー給電や5Vピンで5V外部電源にも直結できますが、ESP32チップ(GPIO)の動作電圧は3.3V動作です。
ESP32-DevKitCの5Vピンから給電する場合には3.3VはLDOが作り出すので、消費電力が大きいLCDモジュールやセンサ等の外付けモジュールは5Vの外部電源(DC-DCステップダウンコンバータ)からの給電で動作するものを選んでいます。

ESP32-DevKitCモジュールのI2C信号レベルは3.3Vなので、5V系のモジュールとの接続は「I2Cバス用双方向電圧レベル変換モジュール(PCA9306)、プルアップ抵抗付き(1kΩx4個)」でレベル変換しました。

秋月電子通商のよくある質問(Q&A)にI2Cバス用双方向電圧レベル変換モジュール(PCA9306)は、「Vref2側が高電圧側」との記載があります。

#モジュール名モジュールへの
給電電圧
I2C
信号レベル
I2C
SCL
I2C
SDA
1ESP32-DevKitC5V
※5V0ピンから給電、LDOで5V → 3.3V。内部は3.3V動作。
5V
※双方向電圧 レベル変換モジュールで 5V <—> 3.3V変換。内部は3.3V動作。
GPIO22GPIO21
2DS3231SN
RTC
5V
(仕様:3.3~5.5V)
5V
(仕様:3.3~5.5V)
印字印字
32004 LCD I2C I/F5V5V印字印字

「I2Cバス用双方向電圧レベル変換モジュール(PCA9306)、プルアップ抵抗付き(1kΩx4個)」のI2C波形をデジタルオシロスコープでモニタすると、ESP32-DevKitC側の3.3V信号が5Vに変換されていることが確認できました。

ESP32-DevKitC側(3.3V系)の I2Cバスレベル変換モジュールのI2C信号波形(
ESP32-DevKitC側(3.3V系)の I2Cバスレベル変換モジュールのI2C信号波形(上段;SCL、下段;SDA)
2004LCDやDS3231SN側(5V系)の I2Cバスレベル変換モジュールのI2C信号波形
2004LCDやDS3231SN側(5V系)の I2Cバスレベル変換モジュールのI2C信号波形(上段;SCL、下段;SDA)

開発ツールarduino-esp32をインストール(2021/12/21更新)

Arduino IDEのセキュリティアップデート版v1.8.19が提供されたのでアップデートしました(2021/12/21)

arduino-esp32のインストール手順を別ページに纏めました。

あわせて読みたい
ESP32-DevKitCの開発ツール arduino-esp32 のインストール手順 Arduino IDE上で動作する ESP32-DevKitC の開発ツール arduino-esp32(Arduino core for the ESP32)をインストールした際の手順メモです。 arduino-esp32は、Seeed Stu...

ESP32-DevKitC上でArduino IDE(arduino-esp32)のスケッチを実行

スケッチ1:I2c Scannerで各モジュールのI2Cアドレスを確認

ArduinoサイトにあるI2CアドレスをスキャンするスケッチArduino Playground – I2cScannerを実行すると3つのI2Cアドレスが検出されます。
0x27はLCDモジュール(I2C I/F モジュール)のアドレス。0x68がRTCモジュールDS3231SNのアドレス。0x57はRTCモジュールのAtmel社のAT24C32 EEPROMのアドレスでした。

i2c_scannerスケッチの実行結果
i2c_scannerスケッチの実行結果
DS3231SNモジュール基板上の AT24C32 EEPROM(下のチップ)
DS3231SNモジュール基板上の AT24C32 EEPROM(下のチップ)

各モジュールのI2Cアドレスと、モジュール上のコネクタに割り当てられたSCL信号とSDA信号のピン番号のメモです。

#モジュールI2C
アドレス
I2C
SCL
I2C
SDA
1ESP32-DevKitCマスタ22番 21番
2 2004 LCD
+I2C I/Fモジュール
0x27印字印字
3DS3231SN
RTC
AT24C32 EEPROM

0x68
0x57

印字

印字

スケッチ2:WiFi接続してNTPサーバから時刻を取得してシリアルモニタに表示

ESP32開発元 Espressif Systems提供の「SimpleTime.ino」をArduino IDE(arduino-esp32)で動作確認しました。このスケッチをGitHub「arduino-esp32/SimpleTime.ino at master」からダウンロードします。
Time.h、TimeLib.hライブラリは、GitHub「PaulStoffregen/Time」サイトの「Code」プルダウンから「Download ZIP」でPCに「Time-master.zip」をダウンロード。Arduino IDEのメニュー「スケッチ」–>「ライブラリをインクルード」–>「zip形式のライブラリをインクルード」で「Time-master.zip」を指定します。

ntpServerにはNTPサーバ(ntp.nict.jp)を、gmtOffset_secはローカル時刻とGMTとの差:9時間を秒(32400)で、daylightOffset_secは日本ではサマータイムは無いので(0)を設定します。
シリアルモニタへの日時の出力形式を
“%Y/%m/%d(%a) %H:%M:%S”
に変更しています。

設定変更したスケッチの実行結果
設定変更したスケッチの実行結果。スケッチ内のssidとpassword、シリアルモニタCOM9のssidは画像処理で消しています。

スケッチ3:WiFi接続してNTPサーバから時刻を取得、DS3231SNと時刻同期してLCD表示

RTCモジュールに必要なライブラリ「DS3232RTC.h」を取り込みます。名称はDS3232ですがライブラリには「Arduino Library for Maxim Integrated DS3232 and DS3231 Real-Time Clocks」の記載があり、DS3231SNでも使えます。
GitHub「JChristensen/DS3232RTC」サイトの「Code」プルダウンから「Download ZIP」で、PCに「DS3232RTC-master.zip」をダウンロード。Arduino IDEのメニュー「スケッチ」–>「ライブラリをインクルード」–>「zip形式のライブラリをインクルード」でダウンロードした「DS3232RTC-master.zip」を指定します。
同様に、今回使った2004 LCDモジュール表示に必要なライブラリ「LiquidCrystal_I2C.h」は、GitHub「johnrickman/LiquidCrystal_I2C」サイトからダウンロードします。

esp_sntp.h」は arduino-esp32同梱モジュールです。詳細は Official develooment framework for ESP32 (ESP-IDF) に記述があります。mulong.meサイトを参考にESP32内蔵RTCの時刻が NTPで取得した時刻に一致しているかの判定に使っています。

下記のスケッチでは起動時にNTPとの時刻合わせを行いますが、定期的なNTPサーバとの時刻合わせ機能は未実装です。NTPとの時刻合わせは、ESP32-DevKitC基板上の「ENボタン(Enableボタン)」を押して再起動することで行っています。

電源を入れて時計表示までの間に少し待ち時間ある(その間LCDには表示なしで寂しい)ので無線LAN接続やJSTとの時刻合わせ状況のメッセージ表示を加えています。

ESP32_wifi_NTP_DS3232RTC_LCD.ino
※ここをクリックするとコード表示を開閉できます。
#include <WiFi.h>
#include <time.h>              // Timeライブラリ
#include <LiquidCrystal_I2C.h> // LCD用ライブラリ
#include <DS3232RTC.h>         // DS3232、DS3231用ライブラリ
#include <esp_sntp.h>

DS3232RTC myRTC(false);
// LiquidCrystal_I2C lcd(0x27, 16, 2);  // 1602LCD利用時
LiquidCrystal_I2C lcd(0x27, 20, 4);
// const char* weekStr[7] = {"(Sun)","(Mon)","(Tue)","(Wed)","(Thu)","(Fri)","(Sat)"}; // 1602LCD利用時
// 2022/03/27:曜日の文字数を9文字固定に変更
const char* weekStr[7] = {"Sunday   ","Monday   ","Tuesday  ","Wednesday","Thursday ","Friday   ","Saturday "};
const char* ssid      = "your ssid";
const char* password  = "your password";
const char* ntpServer = "ntp.nict.jp";
const long  gmtOffset_sec = 32400;
const int   daylightOffset_sec = 0;

void setup() {
struct tm timeInfo;
Serial.begin(115200);
myRTC.begin();
lcd.init();
lcd.backlight();
//WiFi接続
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
lcd.print("."); // 進捗表示
delay(500);
}
// WiFi接続の表示
lcd.clear();
lcd.print("WiFi connected");
delay(2000);
lcd.clear();
// NTPサーバからJST取得
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
lcd.clear();
lcd.print("JST synchro.");
delay(2000);
lcd.clear();
// 内蔵RTCの時刻がNTP時刻に合うまで待機
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {
lcd.print(">"); // 進捗表示
delay(1000); 
}
//内蔵RTC時刻 = NTP時刻の表示
lcd.clear();
lcd.print("Time matched");
delay(2000);
lcd.clear();
// 内蔵RTCの時刻の取得
getLocalTime(&timeInfo);
// 内蔵RTCの時刻をDS3231に時刻設定
// setTime(12, 40, 0, 14, 11, 2021);  // 手動設定・動作確認用(時、分、秒、日、月、年)
setTime(timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec, timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900);
myRTC.set(now());
//WiFi切断
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}

void loop(void) {
dispLCD();
delay(1000);
}

void dispLCD(void) {
// RTCから時刻取得
tmElements_t tm;
myRTC.read(tm);
// LCD表示
lcd.setCursor(0,0);
lcd.print(tm.Year + 1970);
lcd.print("/");
lcdzeroSup(tm.Month);
lcd.print("/");
lcdzeroSup(tm.Day);
lcd.print(" ");
lcd.setCursor(11,0);
lcd.print(weekStr[tm.Wday - 1]);
lcd.setCursor(0,1);
lcdzeroSup(tm.Hour);
lcd.print(":");
lcdzeroSup(tm.Minute);
lcd.print(":");
lcdzeroSup(tm.Second);
}

// 先頭のゼロ(0)を空白に置換
void lcdzeroSup(int digit) {
if(digit < 10)
lcd.print(' ');
lcd.print(digit);
}
よかったらシェアしてね!
  • URLをコピーしました!
目次