これではうまくいかないことがわかりました。通信が安定せず、時刻のデータを更新しなくなるようです。 現在、対策を検討中。 2019-06-18 (火) 09:36:15
ezTimeを参照のこと。
#include <WiFi.h>
#include <time.h>
#include <WiFiUdp.h>
#include "NTPClient.h"
const char* ssid = "xxxxxxxxxxxxx";
const char* password = "xxxxxxxxxxxxx";
const char* ntpServer = "ntp.jst.mfeed.ad.jp";
WiFiUDP ntpUDP;
NTPClient ntpc(ntpUDP, ntpServer, long(3600*9), 30000); // 30秒で更新
void printLocalTime()
{
struct tm timeinfo;
if(!getLocalTime(&timeinfo)) return;
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
void setup()
{
// --------------------------------------- シリアル通信
Serial.begin(115200);
// --------------------------------------- WiFi
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" CONNECTED");
// --------------------------------------- NTP : タイムゾーンを環境変数で設定する。
setenv("TZ","JST-9",1);
}
void loop()
{
delay(100);
// NTPClient : update() の頻度を高める。ただし、冒頭で設定した時間間隔以下では、実際には実行されない。
ntpc.update();
printLocalTime();
}
NTPClient ntpc(ntpUDP, ntpServer, long(3600*9), 30000); // 30秒で更新3600*9 は日本時間(+9時間)に対応している。更新時間間隔はミリ秒で与える。30000[ms] = 30[s]。
setenv("TZ","JST-9",1);
関数get Local Time()で日本時間が反映されるようになる。
ntpc.update();頻繁にこれを実行する。実際には更新時間間隔内だと更新は実行されない。
#pragma once
#include "Arduino.h"
#include <Udp.h>
#define SEVENZYYEARS 2208988800UL // 70年間の秒数
#define NTP_PACKET_SIZE 48
#define NTP_DEFAULT_LOCAL_PORT 1337
class NTPClient {
private:
UDP* _udp;
bool _udpSetup = false;
const char* _poolServerName = "pool.ntp.org"; // Default time server
int _port = NTP_DEFAULT_LOCAL_PORT;
long _timeOffset = 0;
unsigned long _updateInterval = 60000; // In ms : 60000ms means 1min.
unsigned long _currentEpoc = 0; // In s
unsigned long _currentEpoc_us = 0; // In us
unsigned long _lastUpdate = 0; // In ms
byte _packetBuffer[NTP_PACKET_SIZE];
void sendNTPPacket();
public:
/* ----- コンストラクタ ---------------------------------------- */
/* UDP オブジェクト、サーバー名、オフセット、更新時間間隔 */
NTPClient(UDP& udp);
NTPClient(UDP& udp, long timeOffset);
NTPClient(UDP& udp, const char* poolServerName);
NTPClient(UDP& udp, const char* poolServerName, long timeOffset);
NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval);
/* -- 開始 ---------------------------------------------------- */
void begin();
void begin(int port);
/* -- 更新 ---------------------------------------------------- */
/* 成否を返す。デフォルトで60秒で更新 */
bool update();
/* -- 強制更新 ------------------------------------------------ */
bool forceUpdate();
/* -- 時刻抽出 ------------------------------------------------ */
int getDay() /* const */;
int getHours() /* const */;
int getMinutes() /* const */;
int getSeconds() /* const */;
/* -- UNIX Epoch 表示 ----------------------------------------- */
unsigned long getEpochTime() /*const*/;
/* -- 時刻表示 ------------------------------------------------ */
String getFormattedTime() /* const */;
/* -- オフセットの表示 ---------------------------------------- */
void setTimeOffset(int timeOffset);
/* -- NTP サーバの指定 ---------------------------------------- */
void setPoolServerName(const char* poolServerName);
/* -- 更新間隔再設定 ------------------------------------------ */
void setUpdateInterval(unsigned long updateInterval);
/* -- 終了 ---------------------------------------------------- */
void end();
};
/**
* The MIT License (MIT)
* Copyright (c) 2015 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* #define DEBUG_NTPClient */
/* ---- システムが持っている時間をセットする。 ---- */
/* ESP32 配布ファイルの中の lwipopts.h を参照した。また、UNIX の settimeofday() も参照した */
#include <time.h> // 多分必要
#include <unistd.h> // settimeofday() に必要
#include <sys/time.h> // struct timeval に必要
#include "NTPClient.h"
/* ----- private 関数 ------------------------------------------ */
void NTPClient::sendNTPPacket() {
// NTP は、48バイトのデータをやり取りする。初期にそれらをゼロにする。
memset(this->_packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// 参照URL : https://milestone-of-se.nesuke.com/I7protocol/ntp/ntp-format/ )
this->_packetBuffer[0] = 0b11100011; // LI(Leap Indicator):2bit:11(不明),
// Version:3bit:100:4(現行バージョン4), Mode:3bit:3(クライアント)
this->_packetBuffer[1] = 0; // Stratum, 0 としても問題ない。
this->_packetBuffer[2] = 6; // Polling Interval 6-> 2^6 = 64秒 次のNTPパケット送出までの最大時間間隔
this->_packetBuffer[3] = 0xEC; // Peer Clock Precision : 2^(-20?) マイクロ秒まで?
// 8 bytes of zero for Root Delay & Root Dispersion
this->_packetBuffer[12] = 49; // この辺は、サーバーとの前回のやり取りにかかった時間などの情報。
this->_packetBuffer[13] = 0x4E;
this->_packetBuffer[14] = 49;
this->_packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
this->_udp->beginPacket(this->_poolServerName, 123); //NTP requests are to port 123
this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE);
this->_udp->endPacket();
}
/* ----- コンストラクタ ---------------------------------------- */
/* UDP オブジェクト、サーバー名、オフセット、更新時間間隔 */
NTPClient::NTPClient(UDP& udp) {
this->_udp = &udp;
}
NTPClient::NTPClient(UDP& udp, long timeOffset) {
this->_udp = &udp;
this->_timeOffset = timeOffset;
}
NTPClient::NTPClient(UDP& udp, const char* poolServerName) {
this->_udp = &udp;
this->_poolServerName = poolServerName;
}
NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset) {
this->_udp = &udp;
this->_timeOffset = timeOffset;
this->_poolServerName = poolServerName;
}
NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval) {
this->_udp = &udp;
this->_timeOffset = timeOffset;
this->_poolServerName = poolServerName;
this->_updateInterval = updateInterval;
}
/* -- 開始 ---------------------------------------------------- */
void NTPClient::begin() {
// 引数無しなら、次の bigin()
this->begin(NTP_DEFAULT_LOCAL_PORT);
}
void NTPClient::begin(int port) {
this->_port = port;
this->_udp->begin(this->_port);
// UDP のセットアップができていることの識別子
this->_udpSetup = true;
}
/* -- 更新 ---------------------------------------------------- */
bool NTPClient::update() {
// 実際には、どのような状況で forceUpdate() を呼ぶか、を判定し実行する関数。
if (
// 1. 前回更新後、updateInterval 以上の時間が経過している場合
(millis() - this->_lastUpdate >= this->_updateInterval) ||
// 2. millis() があふれて戻ってしまった場合
(millis() < this->_lastUpdate) ||
// 3. 初回
(this->_lastUpdate == 0)) {
if (!this->_udpSetup) this->begin(); // 初回でUDPのセットアップができていないとき
return this->forceUpdate();
}
return true;
}
/* -- 強制更新 ------------------------------------------------ */
bool NTPClient::forceUpdate() {
#ifdef DEBUG_NTPClient
Serial.print("Update from NTP Server");
#endif
// --------------------- データ送信 ---------------------
this->sendNTPPacket();
// --------------------- データ受信 ---------------------
// タイムアウト(10ms×100回=1秒)まで受信を待つ
// 受信したパケットのサイズ?が値をもったら受信したことになる
byte timeout = 0;
int cb = 0;
do {
delay ( 10 );
cb = this->_udp->parsePacket();
if (timeout > 100) return false; // タイムアウト時は false で終わり。
timeout++;
} while (cb == 0);
// -------------- 更新時刻はパケット送信時 --------------
// タイムアウトしたらここまで来ない。
this->_lastUpdate = millis() - (10 * (timeout + 1));
// -------------------- パケット解析 --------------------
// cf. http://www.venus.dti.ne.jp/~yoshi-o/NTP/NTP-SNTP_Format.html
// Transmit Timestamp まで読み込む(48バイト)
// 40-43 の 4tyte, 32bit が UNIX Epoch
// 44-47 の 4byte, 32bit が マイクロ秒
this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE);
// 上位32bit
unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); // 2 byteで16bit。
unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]);
// combine the four bytes (two words) into a long integer : 上記の word は 2バイト=16bit。
// this is NTP time (seconds since Jan 1 1900) : 上記の 32bit が 1900年からの秒数。
unsigned long secsSince1900 = highWord << 16 | lowWord;
// NTP は、1900年からの秒数を数える。一方、UNIX Epoch は、1970年からの秒数を数える。換算する。
// _currentEpoc がこのライブラリが保持する時刻。
this->_currentEpoc = secsSince1900 - SEVENZYYEARS;
// 下位32bit : 固定小数点なので、2^32 = 4294967296 で割る。
highWord = word(this->_packetBuffer[44], this->_packetBuffer[45]);
lowWord = word(this->_packetBuffer[46], this->_packetBuffer[47]);
this->_currentEpoc_us = int(double( highWord << 16 | lowWord)/ 4294967296 * 1000 * 1000);
struct timeval tv = { .tv_sec = this->_currentEpoc, .tv_usec = this->_currentEpoc_us };
// struct timezone tz = { .tz_minuteswest = int(this->_timeOffset/60), .tz_dsttime=0};
// settimeofday(&tv, &tz); // 設定しても効かなかった。
settimeofday(&tv, NULL);
#ifdef DEBUG_NTPClient
Serial.print(" ");
Serial.print(timeout);
Serial.print(" ");
Serial.println(this->_currentEpoc_us);
#endif
return true;
}
/* -- 時刻抽出 ------------------------------------------------ */
int NTPClient::getDay() /* const */ {
return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday
}
int NTPClient::getHours() /* const */ {
return ((this->getEpochTime() % 86400L) / 3600);
}
int NTPClient::getMinutes() /* const */ {
return ((this->getEpochTime() % 3600) / 60);
}
int NTPClient::getSeconds() /* const */ {
return (this->getEpochTime() % 60);
}
/* -- UNIX Epoch 計算 ----------------------------------------- */
unsigned long NTPClient::getEpochTime() /*const*/ {
// millis() は50日で溢れる。リセットされたときは、updateする。これを入れたので 全部 const を外す。
if( millis() < this->_lastUpdate ){
this->forceUpdate();
}
return this->_timeOffset + // タイムゾーンの offset
this->_currentEpoc + // 前回の結果
((millis() - this->_lastUpdate) / 1000); // 前回からの経過時間
}
/* -- 時刻表示 ------------------------------------------------ */
String NTPClient::getFormattedTime() /* const */ {
unsigned long rawTime = this->getEpochTime();
unsigned long hours = (rawTime % 86400L) / 3600;
String hoursStr = hours < 10 ? "0" + String(hours) : String(hours);
unsigned long minutes = (rawTime % 3600) / 60;
String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes);
unsigned long seconds = rawTime % 60;
String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds);
return hoursStr + ":" + minuteStr + ":" + secondStr;
}
/* -- オフセットの表示 ---------------------------------------- */
void NTPClient::setTimeOffset(int timeOffset) {
this->_timeOffset = timeOffset;
}
/* -- NTP サーバの指定 ---------------------------------------- */
void NTPClient::setPoolServerName(const char* poolServerName) {
this->_poolServerName = poolServerName;
}
/* -- 更新間隔再設定 ------------------------------------------ */
void NTPClient::setUpdateInterval(unsigned long updateInterval) {
this->_updateInterval = updateInterval;
}
/* -- 終了 ---------------------------------------------------- */
void NTPClient::end() {
this->_udp->stop();
this->_udpSetup = false;
}