ESP32の内蔵RTC(Real-Time Clock, つまり時計)は精度が悪く、1日に数分は余裕でズレていきます。
なので、外部RTCや水晶発振器を使ったり、定期的に公開NTPサーバの時刻と同期させるといった対策が必要です。
この記事ではNTPサーバと同期させる方法、特に、ディープスリープモードから復帰したときにも同期が可能な方法を説明しています。
問題点
ESP32を1日1回、ディープスリープモードからタイマー起動させる使い方をするため、ディープスリープモードからの復帰時にNTPの時刻に合わせたいと考えました。
そこで、少し検索したところconfigTzTime()してからgetLocalTime()する方法が一般的なようだったので、最初はwhile (!getLocalTime()) {}としてgetLocalTime()が取得できるまで待機する処理を書いていました。
しかし数日使っているうちに、ESP32の初回起動時のみ正しい時刻が取得できるものの、2回目以降はNTP同期されていない時刻が取得されていることに気づきました。
原因
原因は、内蔵RTCの時刻が以前に一度NTP同期済みの場合は、NTPサーバとの時刻同期処理の完了を待たずにgetLocalTime()によって内蔵RTCの時刻が取得されていたためです。
初回起動時は内蔵RTCがリセットされているので、NTPサーバの時刻を取得して内蔵RTCに設定されるまではgetLocalTime()が取得できず、getLocalTime()が可能になるまで待機する処理(while (!getLocalTime()) {})を書けば、内蔵RTCからNTPと同期済みの時刻が得られますが、
2回目以降の起動(ディープスリープからの復帰)時には既に内蔵RTCは時を刻み続けているので、getLocalTime()はNTP同期の成否関係なく、完了を待たずに内蔵RTCの時刻を取得してしまっていました。
解決方法
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {}とすることで、NTPの時刻に合うまで待機できます。
NTP同期後はgetLocalTime()を確実に取得できるはずなので、getLocalTime()の成否判定はしていません。
#include <esp_sntp.h>
void setup() {
// 省略:Wi-Fi接続処理
// NTPサーバ接続
struct tm localTime;
configTzTime("JST-9", "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
Serial.print("[NTP Svr] Connecting.");
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {
Serial.print(".");
delay(1000); // 1秒毎にリトライ
}
getLocalTime(&localTime);
Serial.println("\n[NTP Svr] Connected!");
Serial.print("[System] Local time: ");
Serial.println(&localTime, "%Y/%m/%d(%a) %H:%M:%S");
}
参考にしたサイト
こちらの記事で解決方法を見つけました。
コメント