Arduino互換ESP32マイコンボードをNTPクライアント化

ESP32マイコンボードではNTPサーバから時刻を所得してマイコンボードの内部時計に適用するconfigTime()が用意されていて、使うのもとても簡単。Unix系OSのntpdateで時刻合わせをする感じ?ただし、バツンと時刻を合わせるようなので時間が飛んだり戻ったりというのはイヤかも。そして継続して時刻合わせするにはどうするのか、適度な間隔で勝手に時刻を取り続けてくれるのかな?
configTime()は時刻取得が簡単とはいえ、できればNTPクライアントとしてずれを調整しながら時刻を合わせて欲しいところ。なのでもう少しNTPクライアントっぽい処理をして欲しい。そこで使ってみようと思ったのがNTPクライアント用ライブラリ。これが期待しているNTPクライアントとしての動きをしてくれるのかは不明だけど。

Arduino互換マイコンでNTP時計を作る 1
Arduino IDEのメイン画面のメニューから「ツール」を開く。
「ライブラリを管理」をクリックする。(ここからハングアップしたかと思うほど時間がかかるかも)

Arduino互換マイコンでNTP時計を作る 2
ライブラリマネージャが開いたら右上の「検索をフィルタ」のテキストボックスに「ntp」と入力。(入力後の反応がないかもなので暫く待ってやる)
しばらく待つとNTPClientがリストに出る(リストの一番上に表示される筈だが、多少上下にスクロールが必要かも)。
NTPClientの行のどこかにポインタを合わせるとバージョンのドロップダウンメニューと「インストール」ボタンが表示される。バージョンは基本最新が選択されている筈なので触らずに「インストール」ボタンを押す。

Arduino互換マイコンでNTP時計を作る 3
NTPClientの行に「INSTALLED」が表示されたら (画像の赤色下線部分)、右下の「閉じる」をクリック。
ここでインストールされたNTPClientライブラリはhttps://github.com/arduino-libraries/NTPClientのものだと思われるのでそこで使い方等の情報を得る。

やりたいこと。
configTime()でNTPサーバから時刻を取得してローカルタイムを設定する(ESP32マイコンの内部時計の時刻を設定する)のではなく、NTPClientのtimeClient()でNTPサーバから時刻を取得する。(ESP32マイコンの内部時計の時刻は設定しない)
取得したNTP時刻をlocaltime()に入れてstrftime()で日付と曜日を得る。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include "time.h"

#define SSID "MyAP-SSID"             //Wi-Fiルーターなど無線LANのAPのSSID
#define WIFIKEY "MyAP-Key"           //上のAPのKey(パスワード)
#define ntpServer "192.168.128.1"    //NTPサーバのIPアドレスやホスト名
#define tzOffset 32400               //UTCからの時差 日本だと32400秒(マイナスになる)

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, ntpServer, tzOffset, 60000);

void setup() {
  setenv("TZ", "JST-9", 1);
  tzset();

  Serial.begin(115200);             //シリアル通信速度を115200bpsに指定して通信開始
  WiFi.begin(SSID, WIFIKEY);        //Wi-Fi接続開始
  //configTime(tzOffset, 0, ntpServer);  //NTPサーバへの接続

  
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println(WiFi.localIP());

  timeClient.begin();
}

void loop() {
  timeClient.update();     //NTP時刻の更新
  Serial.print("NTP Time: ");
  Serial.println(timeClient.getFormattedTime());   //NTPで取得した時刻のみ表示
  Serial.println(timeClient.getEpochTime());       //NTP時刻(≒UnixTime)を表示

  time_t epTime = timeClient.getEpochTime() - tzOffset; //NTP時刻(≒UnixTime)から時差分(オフセット)を差し引く
  struct tm  ts;
  char buf[32];
  ts = *localtime(&epTime);
  strftime(buf, sizeof(buf), "%Y.%m.%d (%a) %H:%M:%S %Z", &ts);
  Serial.printf("%s\n", buf);              //整形したNTP時間(JST)を表示

  time_t loTime = time(nullptr);   //内部時計の日時を取得して
  Serial.print("LocalTime: ");
  Serial.print(String(ctime(&loTime)));    //内部時計を日時表示
  Serial.println(loTime);                  //内部時計のUnixTimeを表示

 
  delay(1000);
}

20行目をコメントにしたり有効にすることでESP32マイコンが持つ内部時間とNTP時間が同じになったり異なることがわかります。configTime()ではNTPサーバから時間を取ってその時間をESP32マイコンの内部時計に適用するようです。今回はESP32マイコンの時計にはNTP時間を適用せずにNTP時刻を表示する方法を採用します。(それが良いか悪いかは不明)

Arduino互換マイコンでNTP時計を作る 4
20行目の行頭に//を付けてコメントにてマイコンボードに書き込んだ。これにより内部時計には時刻を設定することがないためプログラムの起動開始によりエポック秒が0から始まる。つまり1970年1月1日の零時から時を刻む。NTP時刻は(おそらく)非同期でNTPサーバの取りに行った時刻が入る。以後はマイコンボードの時計の制度が酷いものでも徐々に補正される?

Arduino互換マイコンでNTP時計を作る 5
20行目の行頭の//を取って非コメント化してマイコンボードに書き込んだ。これによりプログラム開始時にNTPサーバから取得した時刻を内部時計に書き込まれる。それと共に内部時計とは非同期と思われるNTP時刻も持つ。当然だけど同じ時刻を表示する。ただし、内部的には日本時間のオフセット分の32400秒ズレている。(内部時計はUTCじゃなくてJSTで設定されてるから)

内部時計がいきなり補正されると嫌。そのせいでNTP時刻に影響するかもしれないので内部時計は設定しないでNTP時計だけを利用するつもり。
次回は表示側。USBシリアルで表示するんじゃ意味ないし。

関連記事: