これではうまくいかないことがわかりました。通信が安定せず、時刻のデータを更新しなくなるようです。 現在、対策を検討中。 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; }