ESP8266のntpの設定は1行で


概要

ntp(ネットワーク・タイム・プロトコル)はネット経由で時計を合わせるための通信規約で、ネットワーク上のタイムサーバと同期させることで正確な時刻を簡単に得ることができる。

esp8266 Arduinoでも、利用することができるが、自前でntpのパケットの作成・送受信を行うスケッチの例が提供されているためか、自前でやらなければならないと誤解している人がいるかもしれません。

esp8266 Arduinoにはntpの機能が組み込まれていて、特にライブラリをインクルードしなくても、設定するだけでntp同期の時刻情報を得ることができます。

追記: 2020/05/24:

esp8266 arduino ver 2.7.0以降、従来説明した方法では時刻が18時間ずれるようになりました。従来は configTime()関数で設定を行ってましたが、2.7.0以降は configTzTime()関数を使用することをお勧めします。

使い方

configTzTime()関数で設定を行います。宣言はArduino.hにありますので、特にincludeする必要はありません。

Arduino.h
// esp32 api compatibility
inline void configTzTime(const char* tz, const char* server1,
    const char* server2 = nullptr, const char* server3 = nullptr)

日本で使う場合、tzは "JST-9"となります。tzに指定可能な文字列はTZ.hを参照してください。

ntpの同期が完了すると time()関数がUNIX時間(1970年1月1日0時0分0秒からのうるう秒を含まない秒数)を返すようになるので、これをlocaltime()関数などで変換し利用します。同期が完了するまではtime()関数は0を返すので、変な時刻を表示させたくない場合は、非0が返るまで待つようにすると良いでしょう。 time()やlocaltime()の使用にはtime.hをインクルードする必要があります。

サンプルプログラム

使用例を示します。

clock.ino
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <time.h>

#define WIFI_SSID   "your_wifi_ssid"
#define WIFI_PASSWORD   "your_wifi_password"
#define JST     3600*9

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.print("\n\nReset:\n");

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

  // configTime(  JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp"); // esp8266/arduino ver 2.6.3まで有効
  // configTime( -JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp"); // 2.7.0, 2.7.1ならこれでも可
  configTzTime("JST-9", "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");   // 2.7.0以降, esp32コンパチ
}

void loop() {
  time_t t;
  struct tm *tm;
  static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};

  t = time(NULL);
  tm = localtime(&t);
  Serial.printf("ESP8266/Arduino ver%s :  %04d/%02d/%02d(%s) %02d:%02d:%02d\n",
        __STR(ARDUINO_ESP8266_GIT_DESC),
        tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
        wd[tm->tm_wday],
        tm->tm_hour, tm->tm_min, tm->tm_sec);
  delay(1000);
}

実行時のシリアルモニタは次のようになります。

もう少し詳しい話

configTime(), time()等のソースを見ると、それぞれ sntp.cの対応関数を呼び出しているようで、sntp.cをみると1時間に1回 ntpパケットを投げているようです。