WeMosを使ってみる

NTPクライアント


こ ちらにESP8266のNTPクライアントのサンプルスケッチが公開されています。
また、こちらに UNO+イーサネットシールドを使ったNTPクライアント(日本時間)のサンプルスケッチが公開されています。
Timeライブラリで簡単にグリニッジ標準時間→日本時間に変換してるところが素晴らしいです。
そこで、この2つのサンプルスケッチを合体し、日本時間を戻すNTPクライアントのスケッチを紹介します。

<追記>
こちらに もっと簡単な時刻合わせの方法が紹介されていました。
NTPではなく、SNTPの機能を使っています。
</追記>

Timeライブラリはこちらのラ イブラリを使いました。
UNOのEthernetライブラリにはDNSClientオブジェクトが有って、ホスト名→IPアドレスの名前解決ができるのですが、
ESP8266 Coreには(いまのところ)ありません。
/*

 Udp NTP Client

 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket
 For more on NTP time servers and the messages needed to communicate with them,
 see http://en.wikipedia.org/wiki/Network_Time_Protocol

 This code is in the public domain.

 */

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <TimeLib.h>     // https://github.com/PaulStoffregen/Time

const char* ssid = "SSID";
const char* password = "PASSWORD";

#define INTERVAL     60      // データ取得間隔(秒)

// UDPローカルポート番号
unsigned int localPort = 2390;
// NTPタイムサーバIPアドレス(ntp.nict.jp NTP server)
IPAddress timeServer(133, 243, 238, 164);
// NTPパケットバッファサイズ
const int NTP_PACKET_SIZE= 48;
// NTP送受信用パケットバッファ
byte packetBuffer[NTP_PACKET_SIZE];
// 最後にパケットを送信した時間(ミリ秒)
unsigned long lastSendPacketTime = 0;

// Udpクラス
WiFiUDP udp;

// dow_char() 曜日文字を戻す [Sun,Mon....]
char * dow_char_EN(byte days) {
  char *you[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  return you[days];
}

// dow_char() 曜日文字を戻す [日曜,火曜....]
char * dow_char_JP(byte days) {
  char *you[] = {"日曜","月曜","火曜","水曜","木曜","金曜","土曜"};
  return you[days];
}

// dow() 曜日を示す数値を戻す[0-Sunday, 1-Monday etc.]
uint8_t dow(unsigned long t) {
    return ((t / 86400) + 4) % 7;
}

void showTime(char * title, time_t timet, char * dow) {
   Serial.print(title);
   Serial.print(year(timet));
   Serial.print("/");
   Serial.print(month(timet));
   Serial.print("/");
   Serial.print(day(timet));
   Serial.print(" ");
   Serial.print(hour(timet));
   Serial.print(":");
   Serial.print(minute(timet));
   Serial.print(":");
   Serial.print(second(timet));
   Serial.print(" [");
   Serial.print(dow);
   Serial.println("]");
}


void setup()
{
  Serial.begin(9600);
  delay(500);
  Serial.println();
  Serial.println();
  Serial.print("Wait for WiFi...");

  // We start by connecting to a WiFi network
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.println("Starting UDP");
  udp.begin(localPort);

#if 0
  // タイムサーバーの名前解決
  DNSClient dns;
  WiFi.begin(Ethernet.dnsServerIP());
//  if(dns.getHostByName("ntp.jst.mfeed.ad.jp",timeServer) == 1) {
  if(dns.getHostByName("ntp.nict.jp",timeServer) == 1) {
    Serial.print(F("ntp = "));
    Serial.println(timeServer);
  } else {
    Serial.print(F("dns lookup failed"));
    while(1) { }
  }
#endif
 
  // 最初の時刻リクエストを送信
  sendNTPpacket(timeServer);
  lastSendPacketTime = millis();
}


void loop()
{
  static int counter=0;
  byte DayOfWeek;
 
  long now = millis();
  if (now - lastSendPacketTime > 1000) { // 1秒経過
    lastSendPacketTime = now;
    counter++;
    if (counter > INTERVAL) {
      // NTPサーバへ時刻リクエストを送信
      sendNTPpacket(timeServer);
      counter=0;
    }
  }

  // NTPサーバからのパケット受信
  int cb = udp.parsePacket();
  if (cb) {
    Serial.print("packet received, length=");
    Serial.println(cb);
    // バッファに受信データを読み込む
    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    // 時刻情報はパケットの40バイト目からはじまる4バイトのデータ
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

    // NTPタイムスタンプは64ビットの符号無し固定小数点数(整数部32ビット、小数部32ビット)
    // 1900年1月1日0時との相対的な差を秒単位で表している
    // 小数部は切り捨てて、秒を求めている
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);

    // NTPタイムスタンプをUNIXタイムに変換する
    // UNITタイムは1970年1月1日0時からはじまる
    // 1900年から1970年の70年を秒で表すと2208988800秒になる
    const unsigned long seventyYears = 2208988800UL;
    // NTPタイムスタンプから70年分の秒を引くとUNIXタイムが得られる
    unsigned long epoch = secsSince1900 - seventyYears;
    Serial.print("Unix time = ");
    Serial.println(epoch);

    // グリニッジ標準時間
    uint8_t DayOfWeek = dow(epoch);
    showTime("The UTC time is ", epoch, dow_char_EN(DayOfWeek));
    // 日本標準時にあわせるために+9時間しておく
    DayOfWeek = dow(epoch + (9 * 60 * 60));
    showTime("The JST time is ", epoch + (9 * 60 * 60), dow_char_JP(DayOfWeek));

#if 0
    // Timeライブラリに時間を設定(UNIXタイム)
    // 日本標準時にあわせるために+9時間しておく
    setTime(epoch + (9 * 60 * 60));
    DayOfWeek = dow(epoch + (9 * 60 * 60));

    Serial.print("JST time = ");
    Serial.print(year());
    Serial.print('/');
    Serial.print(month());
    Serial.print('/');
    Serial.print(day());
    Serial.print(' ');
    Serial.print(hour());
    Serial.print(':');
    Serial.print(minute());
    Serial.print(':');
    Serial.print(second());
    Serial.print(" [");
    Serial.print(dow_char_EN(DayOfWeek));
    Serial.print(" ");
    Serial.print(dow_char_JP(DayOfWeek));
    Serial.println("]");
#endif
  }
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
  Serial.println("\nsending NTP packet...");
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  udp.beginPacket(address, 123); //NTP requests are to port 123
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  udp.endPacket();
}

このスケッチを実行すると、(大体)1分ごとにNTPサーバが応答した現在日時(日本時間)と曜日を表示します。
年月日から曜日を求めるロジックはこ ちらから拝借しました。
こんなロジックがあるなんて知りませんでした.....


続く....