[ESP32] 内蔵RTCをディープスリープから復帰時にNTPサーバの時刻に合わせる

スポンサーリンク
電子工作

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");

}

参考にしたサイト

こちらの記事で解決方法を見つけました。

https://github.com/espressif/esp-idf/blob/master/examples/protocols/sntp/main/sntp_example_main.c#L138

コメント

タイトルとURLをコピーしました