/* アナレンマをAmbient http://ambidata.io/ に作図。朝夕記録タイプ最終版 Ch:200にアナレンマを作図。Ch:202にパネル電圧を作図(30分に1ポイントに間引き) 2018/6/7 ラジオペンチ http://radiopench.blog96.fc2.com/ IPアドレス=28, fileName=_20180607_AnaLtest-IP28_final2.ino */ #include // ESP8266用ライブラリ #include // UDP通信を行うライブラリ #include "Ambient.h" // Ambient用のライブラリ #define PIN_LED 13 // IO 13(5番ピン)にLEDを接続する #define SSID "?????????????" // 無線LANアクセスポイントのSSID #define PASS "???????????????" // パスワード #define AmbientChannelId_200 200 // チャネルID=200 アナレンマ #define AmbientWriteKey_200 "????????????" // ライトキー(?桁の???) #define AmbientChannelId_202 202 // チャネルID=202 パネル電圧 #define AmbientWriteKey_202 "??????????" // ライトキー(?桁の???) char s[12]; float jaTime; unsigned int localPort = 2390; unsigned long unixTime; // unix時刻(1970年1月1日からの秒数) unsigned long jstTime; // JST時刻 unix +9Hr unsigned long xd; float daySince2016; // 2016年1月1日からの日数 IPAddress timeServerIP; // time.nist.gov NTP server address const char* ntpServerName = "ntp.nict.jp"; // NICTのNTPを使用 const int NTP_PACKET_SIZE = 48; // NTPパケット用バッファサイズ = 48 bytes byte packetBuffer[NTP_PACKET_SIZE]; // NTPパケットバッファ const float k1 = 0.024414; // 電圧変換係数 k1 * ADC = パネル電圧(V) const float k2 = 2.0; // 夜の開始判定電圧(これ以下なら夜の始まり) const float k3 = 6.0; // 昼の開始判定電圧(これ以上なら昼の始まり) const float t1Min = 0.15; // 夜明け時刻最小 const float t1Max = 0.3; // 夜明け時刻最大 const float t2Min = 0.64; // 日の入り時刻最小 const float t2Max = 0.84; // 最大 const int vLogInterval = 30; // Ambientへの電圧ログインターバル 30分 int vLogCount = 0; // 電圧ログカウンタ float panelVoltage; float t1 = 0.0; // 日の入り時刻 float t2 = 0.0; // 日没時刻 float tDayLong; // 昼間の時間 float tCulmination; // 南中時刻 boolean ItIsNoon = false; // 昼ならtrue boolean D2Nchanged = false; // 昼から夜に変わった場合のフラグ boolean N2Dchanged = false; // 夜から昼に変わった WiFiUDP udp; // A UDP instance to let us send and receive packets over UDP Ambient ambient; WiFiClient client; extern "C" { // ESP8266でADCを使うためのおまじない #include "user_interface.h" } void setup() { pinMode(12, OUTPUT); // t1表示LED pinMode(14, OUTPUT); // t2表示LED pinMode(PIN_LED, OUTPUT); digitalWrite(PIN_LED, HIGH); // 動作確認LED点灯 Serial.begin(115200); // 動作確認のためのシリアル出力開始 connectWiFi(); // 最初にネット接続確立 ItIsNoon = DayNightCheck(); // 開始時の昼夜判定 Serial.println(); Serial.print("Start DN = "); Serial.println(ItIsNoon); digitalWrite(PIN_LED, LOW); } void loop() { // メインループ D2Nchanged = false; N2Dchanged = false; // 10秒周期で6回昼夜の変化をチェック。(つまり1分間ここでループ) for (int i = 0; i < 6; i++) { digitalWrite(PIN_LED, HIGH); panelVoltage = k1 * system_adc_read(); // パネル電圧測定 Serial.print("Voltage = " ); Serial.println(panelVoltage); if ( ItIsNoon == true) { // これまでが昼で if (panelVoltage < k2) { // 測定結果が夜だったら、 ItIsNoon = false; // 昼夜フラグを夜に設定 D2Nchanged = true; // 夜に変わった直後フラグON D2Ntime(); // 昼から夜へ変わった時刻を記録 digitalWrite(14, HIGH); // 夜の開始表示表示 } } else { // これまでが夜で if (panelVoltage > k3) { // 測定結果が朝だったら、 ItIsNoon = true; // 昼夜フラグを昼に設定 N2Dchanged = true; // 昼に変わった直後フラグON N2Dtime(); // 夜から昼に変わった時刻を記録 digitalWrite(12, HIGH); // 昼の開始表示 } } digitalWrite(PIN_LED, LOW); delay(10000); // 10秒周期で実行 } // connectWiFi(); // ハングするのでコメントアウト //   チェックが終わったらまずは電圧を記録 if (vLogCount == 0) { // カウンタがゼロなら(30分間隔にするために追加 20180607) digitalWrite(PIN_LED, HIGH); // 動作中表示 // connectWiFi(); // WiFiに接続(30分間隔に変更したので削除) ambient.begin(AmbientChannelId_202, AmbientWriteKey_202, &client); // ID:202のAmbient開始 dtostrf(panelVoltage, 5, 2, s); // 少数点以下2桁 ambient.set(1, s); // パネル電圧をセット Serial.print("Ambient send ch:202, "); Serial.print(s); Serial.println("V"); ambient.send(); // Ambientへ電圧を送信 vLogCount++; // カウンタをインクリメント } else { // ゼロでなかった場合は vLogCount++; // カウンタをインクリメントし、 if (vLogCount >= vLogInterval) { // もし規定回数と一致していたら vLogCount = 0; // カウンタをリセット(これで次回は } } // (追加終了 20180607) //  もし昼夜が変わっていたらアナレンマ情報をAmbientに送信 if ((N2Dchanged == true) && (D2Nchanged == false)) { // もし朝が来ていたら if (t2 == 0) { // まだ昨日の夜のデータが無い,つまり起動直後なら if (t1Check() == true) { // t1が変な値でないなら、 delay(5000); // 送信間隔を空けて ambient.begin(AmbientChannelId_200, AmbientWriteKey_200, &client); // ID:200のAmbient開始 SetAnalemmaInft1(); // t1の情報だけを送信 ambient.send(); // Ambientへ送信 } } else { // 昨夜のデーター有りなのでアナレンマ情報を送信 if (t1t2Check() == true) { delay(5000); // 送信間隔を空けて ambient.begin(AmbientChannelId_200, AmbientWriteKey_200, &client); // ID:200のAmbient開始 SetAnalemmaInfN2D(); // 朝が来た時の情報を ambient.send(); // Ambientへ送信 } } Serial.println(); Serial.println("Analemma data sent completed. (N to D)"); Serial.println(); digitalWrite(14, LOW); // 夜表示(t2 get)を消す } if ((N2Dchanged == false) && (D2Nchanged == true)) { // もし夜になっていたら if (t1 == 0) { // まだ朝のデータが無い、つまり起動直後なら if (t2Check() == true) { // t2が変な値でないなら、 delay(5000); // 送信間隔を空けて ambient.begin(AmbientChannelId_200, AmbientWriteKey_200, &client); // ID:200のAmbient開始 SetAnalemmaInft2(); // t2の値だけを ambient.send(); // Ambientへ送信 } } else { // 朝のデーター有りななのでアナレンマ情報を送信 if (t1t2Check() == true) { delay(5000); // 送信間隔を空けて ambient.begin(AmbientChannelId_200, AmbientWriteKey_200, &client); // ID:200のAmbient開始 SetAnalemmaInfD2N(); // 夜が来た時の情報を ambient.send(); // Ambientへ送信 } } Serial.println(); Serial.println("Analemma data sent completed. (D to N)"); Serial.println(); digitalWrite(12, LOW); // 昼表示を消す } digitalWrite(PIN_LED, LOW); // 動作表示LED off } boolean DayNightCheck() { // 開始時の昼夜判定 boolean x; panelVoltage = k1 * system_adc_read(); if ( panelVoltage > (k2 + k3) / 2.0 ) { // 中央値で判定 x = true; // 昼なのでtrueを返す } else { x = false; // 夜なのでfalse } return x; } void D2Ntime() { // 昼から夜へ変化した時の処理 (Day to Night) // connectWiFi(); // WiFiに接続 (ハングするのでコメントアウト 2018/6/5) xd = readNTP(); // NTPから時刻情報取得 t2 = jaTime; // 日本時間 Serial.print("Night Started. t2= "); Serial.println(t2, 6); } void N2Dtime() { // 夜から昼へ変化した時の処理 (Night to Day) // connectWiFi(); // WiFiに接続 (ハングするのでコメントアウト 2018/6/5) xd = readNTP(); t1 = jaTime; // 日の出時刻を記録 Serial.print("Day Started. t1= "); Serial.println(t1, 6); } boolean t1Check() { // t1が正常範囲内かチェック boolean flag = false; if ((t1Min < t1) && (t1 < t1Max)) { flag = true; // OKならtrue } // flag = true; // デバッグ用 return flag; } boolean t2Check() { // t2が正常範囲内かチェック boolean flag = false; if ((t2Min < t2) && (t2 < t2Max)) { flag = true; // OKならtrue } // flag = true; // デバッグ用 return flag; } boolean t1t2Check() { // t1,t2が正常範囲内かチェック boolean flag = false; if ((t1Check() == true) && (t2Check() == true)) { flag = true; } // flag = true; // デバッグ用 return flag; } void SetAnalemmaInft1() { // 昨夜のデータ無しで朝の場合 ambient.clear(1); // 1列目は空欄 dtostrf(t1, 7, 5, s); // t1を ambient.set(2, s); // 2列に ambient.clear(3); // t2は空欄 ambient.clear(4); // 昼の長さは空欄 ambient.clear(5); // 南中時刻は空欄 SetDay(0.0); // 日付連番 } void SetAnalemmaInfN2D() { // 夜から朝になった場合 ambient.clear(1); // 1列目は空欄 dtostrf(t1, 7, 5, s); // t1を ambient.set(2, s); // 2列に ambient.clear(3); // t2は空欄 SetAnalemmaInfCom(); // 共通部を設定 SetDay(0.0); } void SetAnalemmaInft2() { // 朝のデータ無しで夜の場合 ambient.clear(1); // 1列目は空欄 ambient.clear(2); // t1は空欄 dtostrf(t2, 7, 5, s); // t2を ambient.set(3, s); // 3列に ambient.clear(4); // 昼の長さは空欄 ambient.clear(5); // 南中時刻は空欄 SetDay(0.5); // 日付連番 (+0.5日) } void SetAnalemmaInfD2N() { // 昼から夜になった場合 ambient.clear(1); // 1列目は空欄 ambient.clear(2); // t1は空欄 dtostrf(t2, 7, 5, s); // t2を ambient.set(3, s); // 3列に SetAnalemmaInfCom(); // 共通部を設定 SetDay(0.5); // 日付連番 (+0.5日) } void SetAnalemmaInfCom() { // アナレンマの情報をセット tDayLong = t2 - t1; // 昼間の時間 (t2-t1) dtostrf(tDayLong, 7, 5, s); ambient.set(4, s); tCulmination = (t1 + t2) * 0.5; // 南中時刻 (t1+t2)/2 dtostrf(tCulmination, 7, 5, s); ambient.set(5, s); } void SetDay(float ddd) { // 日付連番のセット daySince2016 = ddd + ((xd + 32400UL - (24 * 3600 * 16801UL)) / (24 * 3600)) + 1; // 日付連番 dtostrf(daySince2016, 7, 1, s); // 2016/1/1=1の日付連番 ambient.set(6, s); } void connectWiFi() { // WiFi接続 Serial.println(); Serial.print("Connecting to "); // WiFi接続中 Serial.println(SSID); // 固定IPアドレスで接続 IP Address, Gate way, Subnet mask の順に指定 WiFi.config(IPAddress(192, 168, 0, 28), IPAddress(192, 168, 0, 1), IPAddress(255, 255, 255, 0)); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("WiFi connected. IP address: "); Serial.println(WiFi.localIP()); Serial.print("Starting UDP "); udp.begin(localPort); Serial.print("Local port: "); Serial.println(udp.localPort()); } unsigned long readNTP() { // NTPから時刻を取得 // unsigned long epoch; unsigned long epoch = 0; // 取得失敗の場合の戻り値 unsigned long highWord; unsigned long lowWord; unsigned long secsSince1900; // Serial.println(); // 先頭で改行 WiFi.hostByName(ntpServerName, timeServerIP); // サーバー選択 // Serial.println(timeServerIP); // 時々ミスするので調査用にIPアドレス表示 sendNTPpacket(timeServerIP); // 時刻問い合わせパケット送信 delay(1000); // 返事が来るまでちょっと待つ int cb = udp.parsePacket(); // 受信文字数を取得 if (!cb) { // cbが空なら Serial.println("no packet yet"); } else { // Serial.print("packet received, length="); // Serial.println(cb); udp.read(packetBuffer, NTP_PACKET_SIZE); // データーをパケットバッファに読み取り highWord = word(packetBuffer[40], packetBuffer[41]); lowWord = word(packetBuffer[42], packetBuffer[43]); secsSince1900 = highWord << 16 | lowWord; // 32ビットに合成 // Serial.print("Seconds since Jan 1 1900 = " ); // Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.print("Unix time = "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: epoch = secsSince1900 - seventyYears; Serial.println(epoch); // print Unix time: jaTime = (float) ((epoch + (9 * 3600)) % (24 * 3600)) / (24 * 3600); // Serial.println(jaTime, 6); } return epoch; // UnixTimeを返す } void sendNTPpacket(IPAddress& address) { // NTPリクエストパケット送信 // Serial.println("sending NTP packet..."); memset(packetBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0 // Initialize values needed to form NTP request 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; udp.beginPacket(address, 123); //NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); }