/*
ESP.reset()/ESP.resart()ではRTC User Memoryがクリアされない。
この性質を利用した時刻設定のサンプル。
初回起動時のみWiFiに接続してNTPに時刻問い合わせ。
RTC User Memoryに現在日時を設定し強制Reset。
Resetからの復帰時にRTC User Memoryから日時を取り出して設定。
ESPの内蔵タイマーの精度を調べる。
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h> //
https://github.com/PaulStoffregen/Time
// Update these with values suitable for your network.
const char* ssid = "SSID of your AP";
const char* password = "PASSWORD of your AP";
const char* mqtt_server = "broker.hivemq.com";
#define INTERVAL 600 // 10分
//#define INTERVAL 3600 // 60分
WiFiClient espClient;
PubSubClient client(espClient);
//RTC memory(512Byte)の定義
struct {
uint8_t data[512]; // User Data
} rtcData;
// UDPローカルポート番号
unsigned int localPort = 2390;
// NTPサーバー
char timeServer[] = "ntp.nict.jp";
// NTPパケットバッファサイズ
const int NTP_PACKET_SIZE= 48;
// NTP送受信用パケットバッファ
byte packetBuffer[NTP_PACKET_SIZE];
unsigned long lastMillis = 0;
// Udpクラス
WiFiUDP udp;
// Connect AP
void setup_wifi() {
// We start by connecting to a WiFi network
Serial.print("Wait for WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
// Connect MQTT server
void server_connect() {
char clientid[20];
sprintf(clientid,"ESP8266-%06x",ESP.getChipId());
Serial.print("clientid=");
Serial.println(clientid);
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT
connection...");
// Attempt to connect
if (client.connect(clientid)) {
Serial.println("connected");
} else {
Serial.print("failed,
rc=");
Serial.print(client.state());
Serial.println(" try again
in 5 seconds");
// Wait 5 seconds before
retrying
delay(5000);
}
}
}
// send an NTP request to the time server at the given
address
void sendNTPpacket(char * address)
{
Serial.println("sending 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();
}
// read an NTP request from the time server
unsigned long readNTPpacket(byte * packetBuffer, int
bufferLength) {
Serial.println("reading NTP packet...");
// バッファに受信データを読み込む
udp.read(packetBuffer, bufferLength); // 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);
return epoch;
}
// time_tをYYYY/MM/DD hh:mm:ssで表示する
void showTime(char * title, time_t timet) {
Serial.print(title);
Serial.print(":");
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.println(second(timet));
}
// 現在日時をYYYY/MM/DD hh:mm:ssで表示する
void showNow(char * title) {
Serial.print(title);
Serial.print(":");
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.println(second());
}
void setup()
{
delay(1000);
Serial.begin(115200);
Serial.println();
Serial.print("ESP.getResetReason()=");
Serial.println(ESP.getResetReason());
String resetReason = ESP.getResetReason();
/*
enum rst_reason {
REANSON_DEFAULT_RST = 0, // ノーマルスタート。電源オンなど。
REANSON_WDT_RST = 1, // ハードウェアウォッチドッグによるリセット
REANSON_EXCEPTION_RST = 2, //
例外によるリセット。GPIO状態は変化しない
REANSON_SOFT_WDT_RST = 3, //
ソフトウェアウォッチドッグによるリセット。GPIO状態は変化しない
REANSON_SOFT_RESTART = 4, //
ソフトウェアによるリセット。GPIO状態は変化しない
REANSON_DEEP_SLEEP_AWAKE= 5, // ディープスリープ復帰
REANSON_EXT_SYS_RST = 6, // 外部要因(RSTピン)によるリセット。
};
*/
rst_info *prst = ESP.getResetInfoPtr();
/*
struct rst_info{
uint32 reason;
uint32 exccause;
uint32 epc1;
uint32 epc2;
uint32 epc3;
uint32 excvaddr;
uint32 depc;
};
*/
Serial.print("reset reason=");
Serial.println(prst->reason);
// RTC memoryからデータを読み込む
if (ESP.rtcUserMemoryRead(0, (uint32_t*)
&rtcData, sizeof(rtcData))) {
Serial.println("rtcUserMemoryRead
Success");
if (prst->reason != 4) { // Not
Software/System restart
for (int i = 0; i <
sizeof(rtcData.data); i++) {
rtcData.data[i]
= 0;
}
}
} else {
Serial.println("rtcUserMemoryRead
Fail");
}
if (rtcData.data[0] == 0) { // NTPクライントモード
// Wifi接続
setup_wifi();
Serial.println("Starting UDP");
udp.begin(localPort);
// NTPサーバーに時刻リクエストを送信
sendNTPpacket(timeServer);
} else { // Resetからの復帰
Serial.print("rtcData=");
Serial.print(rtcData.data[0]);
Serial.print(" ");
Serial.print(rtcData.data[1]);
Serial.print(" ");
Serial.print(rtcData.data[2]);
Serial.print(" ");
Serial.print(rtcData.data[3]);
Serial.print(" ");
Serial.print(rtcData.data[4]);
Serial.print(" ");
Serial.print(rtcData.data[5]);
Serial.println();
showNow("Before");
setTime(rtcData.data[3],rtcData.data[4],rtcData.data[5],
rtcData.data[2],rtcData.data[1],rtcData.data[0]);
showNow("After");
// Wifi接続
setup_wifi();
Serial.println("Starting MQTT");
client.setServer(mqtt_server, 1883);
server_connect();
// MQTT送信
char topic[50];
char msg[50];
sprintf(topic,"Reset/ESP8266-%06x",ESP.getChipId());
sprintf(msg, "%02d/%02d/%02d
%02d:%02d:%02d",
year(),month(),day(),hour(),minute(),second());
Serial.print("Publish message: ");
Serial.println(msg);
client.publish(topic, msg);
}
lastMillis = millis();
}
void loop()
{
static int counter=0;
client.loop();
long now = millis();
// MQTT送信
if (now - lastMillis > 1000) { // 1秒経過
lastMillis = now;
counter++;
if (counter > INTERVAL) {
char topic[50];
char msg[50];
sprintf(topic,"Reset/ESP8266-%06x",ESP.getChipId());
sprintf(msg,
"%02d/%02d/%02d %02d:%02d:%02d",
year(),month(),day(),hour(),minute(),second());
Serial.print("Publish
message: ");
Serial.println(msg);
client.publish(topic, msg);
showNow("Loop");
counter=0;
}
}
// NTPサーバからのパケット受信(初回起動時のみ)
int cb = udp.parsePacket();
if (cb) {
Serial.print("packet received,
length=");
Serial.println(cb);
// NTPサーバーからUTC時刻を受信
unsigned long utc_epoch;
utc_epoch = readNTPpacket(packetBuffer,
NTP_PACKET_SIZE);
showTime("UTC",utc_epoch);
// 日本時間を求める
// 日本標準時にあわせるために+9時間する
unsigned long jst_epoch = utc_epoch +
(9 * 60 * 60);
showTime("JST",jst_epoch);
// 現在の時間を設定
rtcData.data[0] = year(jst_epoch);
rtcData.data[1] = month(jst_epoch);
rtcData.data[2] = day(jst_epoch);
rtcData.data[3] = hour(jst_epoch);
rtcData.data[4] = minute(jst_epoch);
rtcData.data[5] = second(jst_epoch);
// RTC memoryにデータを書き込む
if (ESP.rtcUserMemoryWrite(0,
(uint32_t*) &rtcData, sizeof(rtcData))) {
Serial.println("rtcUserMemoryWrite Success");
} else {
Serial.println("rtcUserMemoryWrite Fail");
}
// Reset
WiFi.disconnect();
Serial.println("Do ESP.restart");
ESP.reset();
}
}
|