/*
RTC User Memoryを使用するDeelSleepモードのテスト
1日1回、決まった時間にMQTTで通信する
rtcData.data[0]を以下のように使用する
=0:MQTTクライアントモード
=1:NTPクライアントモード
<1:単純Sleepモード(単純Sleepする回数)
例
10:00 電源ON NTPで時間を確認 rtcData.data[0]=0->2
Sleep=70分
11:08 復帰
単純Sleep
rtcData.data[0]=2->1 Sleep=70分
12:16 復帰 NTPで時間を確認
rtcData.data[0]=1->1 Sleep=64分
13:18 復帰 NTPで時間を確認
rtcData.data[0]=1->1 Sleep=2分
13:19 復帰 NTPで時間を確認
rtcData.data[0]=1->0 Sleep=60秒以下
13:20 復帰
MQTT送信
rtcData.data[0]=0->20 Sleep=70分
14:28 復帰
単純Sleep
rtcData.data[0]=20->19 Sleep=70分
15:36 復帰
単純Sleep
rtcData.data[0]=19->18 Sleep=70分
16:44 復帰
単純Sleep
rtcData.data[0]=18->17 Sleep=70分
17:52 復帰
単純Sleep
rtcData.data[0]=17->16 Sleep=70分
19:00 復帰
単純Sleep
rtcData.data[0]=16->15 Sleep=70分
20:08 復帰
単純Sleep
rtcData.data[0]=15->14 Sleep=70分
21:16 復帰
単純Sleep
rtcData.data[0]=14->13 Sleep=70分
22:24 復帰
単純Sleep
rtcData.data[0]=13->12 Sleep=70分
23:32 復帰
単純Sleep
rtcData.data[0]=12->11 Sleep=70分
0:40 復帰
単純Sleep
rtcData.data[0]=11->10 Sleep=70分
1:48 復帰
単純Sleep
rtcData.data[0]=10->9 Sleep=70分
2:56 復帰
単純Sleep
rtcData.data[0]=9->8 Sleep=70分
4:04 復帰
単純Sleep
rtcData.data[0]=8->7 Sleep=70分
5:12 復帰
単純Sleep
rtcData.data[0]=7->6 Sleep=70分
6:20 復帰
単純Sleep
rtcData.data[0]=6->5 Sleep=70分
7:28 復帰
単純Sleep
rtcData.data[0]=5->4 Sleep=70分
8:36 復帰
単純Sleep
rtcData.data[0]=4->3 Sleep=70分
9:44 復帰
単純Sleep
rtcData.data[0]=3->2 Sleep=70分
10:52 復帰
単純Sleep
rtcData.data[0]=2->1 Sleep=70分
12:00 復帰 NTPで時間を確認
rtcData.data[0]=1->1 Sleep=70分
13:17 復帰 NTPで時間を確認
rtcData.data[0]=1->1 Sleep=3分
13:19 復帰 NTPで時間を確認
rtcData.data[0]=1->0 Sleep=60秒以下
13:20 復帰
MQTT送信
rtcData.data[0]=0->20 Sleep=70分
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h> //
https://github.com/PaulStoffregen/Time
#define TARGET_HOR 13 // DeepSleep復帰時刻(時) 特に意味はない
#define TARGET_MIN 20 // DeepSleep復帰時刻(分) 特に意味はない
#define TARGET_SEC 0 // DeepSleep復帰時刻(秒)
// Update these with values suitable for your network.
const char* ssid = "アクセスポイントのSSID";
const char* password = "アクセスポイントのパスワード";
const char* mqtt_server = "MQTTブローカーのIPアドレス";
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];
// Udpクラス
WiFiUDP udp;
// Print RTC memory
void printMemory(int sz) {
char buf[3];
//for (int i = 0; i < sizeof(rtcData); i++) {
for (int i = 0; i < sz; i++) {
sprintf(buf, "%02X", rtcData.data[i]);
Serial.print(buf);
if ((i + 1) % 32 == 0) {
Serial.println();
}
else {
Serial.print(" ");
}
}
Serial.println();
}
// Connect AP
void setup_wifi() {
// We start by connecting to a WiFi network
Serial.println();
Serial.println();
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();
}
/*
* 指定日時のUnixTimeを求める
*/
time_t getTime(int hr,int min,int sec,int dy, int mnth,
int yr){
tmElements_t tm;
// year can be given as full four digit year or two
digts (2010 or 10 for 2010);
//it is converted to years since 1970
if( yr > 99)
yr = yr - 1970;
else
yr += 30;
tm.Year = yr;
tm.Month = mnth;
tm.Day = dy;
tm.Hour = hr;
tm.Minute = min;
tm.Second = sec;
return makeTime(tm);
}
/*
* UnixTimeをYY/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));
}
/*
* 現在日時をYY/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());
}
/*
* 指定時間(HH:MM:SS)までのSleep秒を求める
* jst: 現在日時(日本時間のUNIXTIME)
* hh : 指定時間(時)
* mm : 指定時間(分)
* ss : 指定時間(秒)
*/
long getSleepSecond(time_t jst, int hh, int mm, int ss) {
time_t time_tg;
time_t time_tn;
long diff;
// 現在日時を設定
//Serial.print("jst=");
//Serial.print(jst);
setTime(jst);
// 指定日時(当日)のUnixTime
time_tg = getTime(hh,mm,ss,day(),month(),year());
//Serial.print(" time_tg=");
//Serial.print(time_tg);
// 指定日時(翌日)のUnixTime
time_tn = time_tg + 60*60*24;
//Serial.print(" time_tn=");
//Serial.println(time_tn);
diff=time_tg-jst;
if (diff < 0) {
diff=time_tn-jst;
}
showTime("Now",jst);
showTime("Target",time_tg);
//Serial.print("diff=");
//Serial.println(diff);
showTime("Result",jst+diff);
return diff;
}
/*
* 次のMM:SSまでのSleep秒を求める
* jst: 現在日時(日本時間のUNIXTIME)
* mm : 指定時間(分)
* ss : 指定時間(秒)
*/
long getSleepSecond2(time_t jst, int mm, int ss) {
time_t time_tg;
time_t time_tn;
long diff;
// 現在日時を設定
//Serial.print("jst=");
//Serial.println(jst);
setTime(jst);
// 1時間後のUnixTime
time_tg =
getTime(hour()+1,mm,ss,day(),month(),year());
diff=time_tg-jst;
showTime("Now",jst);
showTime("Target",time_tg);
//Serial.print("diff=");
//Serial.println(diff);
showTime("Result",jst+diff);
return diff;
}
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");
printMemory(10);
if (prst->reason != 5) { // Not
REANSON_DEEP_SLEEP_AWAKE
for (int i = 0; i <
sizeof(rtcData.data); i++) {
rtcData.data[i]
= 0;
}
rtcData.data[0] = 1; //
最初はNTPで時間を確認
rtcData.data[1] =
TARGET_HOR;
rtcData.data[2] =
TARGET_MIN;
rtcData.data[3] = 0;
}
} else {
Serial.println("rtcUserMemoryRead
Fail");
}
// NTPクライントモード
if (rtcData.data[0] == 1) {
Serial.println("Starting NTP Client");
setup_wifi();
udp.begin(localPort);
// 時刻リクエストを送信
sendNTPpacket(timeServer);
// MQTTクライアントモード
} else if (rtcData.data[0] == 0) {
Serial.println("Starting MQTT Client");
setup_wifi();
client.setServer(mqtt_server, 1883);
server_connect();
//
// ここに何か処理を書く(例えばセンサーからのデータ読み込み)
//
char topic[50];
char msg[50];
sprintf(topic,"Daily/ESP8266-%06x",ESP.getChipId());
sprintf(msg,"%02d %02d:%02d %d",
rtcData.data[0],rtcData.data[1],rtcData.data[2],rtcData.data[3]);
char wk[10];
for(int i=0;i<rtcData.data[3];i++) {
memset(wk,0,sizeof(wk));
sprintf(wk,"
%02d:%02d",rtcData.data[i*2+4],rtcData.data[i*2+5]);
strcat(msg,wk);
}
Serial.print("MQTT topic=");
Serial.println(topic);
Serial.print("MQTT msg=");
Serial.println(msg);
client.publish(topic, msg);
client.disconnect();
WiFi.disconnect();
rtcData.data[0] = 20; //
次回起動は単純Sleepモード 単純Sleepする回数(20*70=1400分=23時間20分)
rtcData.data[3] = 0;
// RTC memoryにデータを書き込む
if (ESP.rtcUserMemoryWrite(0,
(uint32_t*) &rtcData, sizeof(rtcData))) {
Serial.println("rtcUserMemoryWrite Success");
printMemory(10);
} else {
Serial.println("rtcUserMemoryWrite Fail");
}
//
次回は70分後に起動する(DeepSleepの最大Sleep時間は約71分)
// DEEP SLEEPモード突入命令
Serial.println("DEEP SLEEP START!!");
uint32_t sleepUsec =70 * 60 * 1000 *
1000;
ESP.deepSleep(sleepUsec ,
WAKE_RF_DEFAULT);
delay(1000);
// 単純Sleepモード
} else {
Serial.println("Sleep Again");
rtcData.data[0]--;
// RTC memoryにデータを書き込む
if (ESP.rtcUserMemoryWrite(0,
(uint32_t*) &rtcData, sizeof(rtcData))) {
Serial.println("rtcUserMemoryWrite Success");
printMemory(10);
} else {
Serial.println("rtcUserMemoryWrite Fail");
}
// DEEP SLEEPモード突入命令
Serial.println("DEEP SLEEP AGAIN!!");
uint32_t sleepUsec =70 * 60 * 1000 *
1000; // 再び70分のSleep
ESP.deepSleep(sleepUsec ,
WAKE_RF_DEFAULT);
delay(1000);
}
}
void loop()
{
// 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
// Wifiはもう使わない
WiFi.disconnect();
// 時刻情報はパケットの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タイムが得られる
time_t epoch = secsSince1900 -
seventyYears;
Serial.print("Unix time = ");
Serial.println(epoch);
// 日本時間のUNITタイム
time_t jst = epoch + (9 * 60 * 60);
// 目的の時間までのSleep時間を求める
long sleepSec =
getSleepSecond(jst,TARGET_HOR,TARGET_MIN,TARGET_SEC);
Serial.print("sleepSec = ");
Serial.println(sleepSec);
// 目的の時間まで60秒以下
if (sleepSec <= 60) {
rtcData.data[0] = 0;
// 次回起動はMQTTモード
// 目的の時間まで10分以下
} else if (sleepSec <= 600) {
sleepSec = sleepSec - 60;
rtcData.data[0] = 1;
// 次回起動はNTPモード
// 目的の時間まで70分以下
} else if (sleepSec <= 4200) {
rtcData.data[0] = 1;
// 次回起動はNTPモード
// 目的の時間まで70分を超える
} else {
rtcData.data[0] = sleepSec
/ 4200;
sleepSec = (70 * 60);
// 70分のSleep
}
Serial.print("rtcData.data[0] = ");
Serial.println(rtcData.data[0]);
rtcData.data[3]++; // NTP確認回数
int i = rtcData.data[3];
rtcData.data[i*2+2] = hour(); //
NTP確認時間(時)
rtcData.data[i*2+3] = minute(); //
NTP確認時間(分)
// RTC memoryにデータを書き込む
if (ESP.rtcUserMemoryWrite(0,
(uint32_t*) &rtcData, sizeof(rtcData))) {
Serial.println("rtcUserMemoryWrite Success");
printMemory(10);
} else {
Serial.println("rtcUserMemoryWrite Fail");
}
Serial.print("sleepSec = ");
Serial.println(sleepSec);
showTime("Next Wake Up",jst+sleepSec);
// DEEP SLEEPモード突入命令
Serial.println("DEEP SLEEP START!!");
uint32_t sleepUsec =sleepSec * 60 *
1000 * 1000;
ESP.deepSleep(sleepUsec ,
WAKE_RF_DEFAULT);
delay(1000);
}
}
|