◯やりたいこと
(4)まででハードウエアの構成がほぼ決まったので動作仕様を決めてスケッチの作成をします。動作仕様は以下の通り。
1. 測定結果をWiFiで定期的(正10分)に送信する。
2. 時刻はNTP(Network Time Protocol)を利用する。
3. 電池動作とするためデータ送信時以外はスリープして省電力化を図る。
4. 1回の起動で1秒ごとに10回測定しその平均値を測定値とする。
◯やったこと
・ハードウエアの追加
WROOM-02(ESP8266)は省電力スタンバイモードとしていくつかのスリープモードを持ちますが、1番電力を消費しないのがディープスリープモード。今回はこのディープスリープモードを利用して必要時以外は極力電池を消費しないエコな水分計を作成します。
ディープスリープからWROOM-02を起動させるにはIO16に出力される起動用のリセットパルスでリセットをかける必要がありますので、あらかじめIO16とリセット端子を接続しておきます。
1KΩの抵抗をはさんであるのはIO16が出力設定でH出力時にリセットスイッチでショートするのを防ぐための電流制限抵抗です。
・スケッチの作成
動作仕様に基づきスケッチを作成しました。
/** * * WiFi 容量式水分計 WR02_CSMS v0.0.9b * * Module : ESP-WROOM-02(ESP8266) * Pin Function * IO0 : SCL(pull_up) * IO1 : TX * IO2 : SDA(pull_up) * IO3 : RX * IO4 : - * IO5 : - * IO12 : - * IO13 : - * IO14 : - * IO15 : - * IO16 : Reset出力(OnBord) * TOUT : - * **/ // Standerd Library #include <Arduino.h> #include <time.h> #include <Wire.h> // Include Library /* WiFi (ESP8266) */ #include <ESP8266WiFi.h> #include <WiFiClient.h> // Global Value /* WiFi */ #define CONN_SSID "SSID" #define CONN_PASS "PASSWORD" /* My ID,IP,MAC */ #define MY_ID 63 IPAddress ip (192,168, 1,MY_ID); // センサーIPアドレス IPAddress gateway (192,168, 1, 1); // ルーターIPアドレス IPAddress dnServer (192,168, 1, 1); IPAddress netmask (255,255,255, 0); /* UART */ #define TX 1 // D1 #define RX 3 // D3 /* I2C */ #define I2C_SDA 2 // D2 #define I2C_SCK 0 // D0 #define I2C_CLK 50000 // fSCL=50KHz /* ADC */ #define ADC_ADDR 0x68 /* NTP DATA */ #define JST (3600 * 9) #define DAYLIGHTOFFSET_JST (0) #define NTP_SERVER1 "ntp.nict.jp" // NTP1 #define NTP_SERVER2 "ntp.jst.mfeed.ad.jp" // NTP2 void setup() { // シリアル 開始 Serial.begin(9600); // I2C 開始 Wire.begin (I2C_SDA, I2C_SCK); // set I2C pins, default clock is 100kHz Wire.setClock(I2C_CLK); // set I2C clock(fSCL) // WiFi スタティック接続 (IP,GateWay,SubnetMask,DNS) WiFi.config(ip, gateway, netmask, gateway); if (!wifi_connect()) wifi_end(); // NPT 接続 configTzTime("JST-9", NTP_SERVER1, NTP_SERVER2); if (!ntp_connect()) wifi_end(); } void loop() { // 現在時刻取得 time_t tmUnix = time(NULL); // unix struct tm* tmNow = localtime(&tmUnix); // unix // 正10分(or11分)でなければsleep if ((tmNow->tm_min % 10)>2) wifi_end(); // 変数設定 float adcd = 0; float ratio= 0; uint8_t cunt = 0; byte conf = 0; // 測定(10回平均値) for (uint8_t i=0; i<10; i++) { // ADC コマンド送信 (ワンショット測定,16Bit,ゲイン1倍) Wire.beginTransmission(ADC_ADDR); Wire.write(0b10001000); Wire.endTransmission(); delay(10); // ADC data,config 受信 unsigned long timeout = millis(); while(millis() - timeout < 1000) { // データ(3Byte)要求 Wire.requestFrom(ADC_ADDR, 3); // 3Byte未確定は3回まで待つ for (uint8_t j=0; j<3; j++) { if(Wire.available() >= 3) { // ADC i2cデータ -> 数値変換 adcd +=(Wire.read()<< 8)+ Wire.read(); conf = Wire.read(); cunt += 1; break; } else delay(100); } // 確定データか確認 if (conf>=0x80) break; delay(250); } delay(1000); } // 平均値計算 if (cunt!=0) adcd = (adcd/cunt)*2.048/32768; // プローブ乾燥時:2.048V 水中時:0.800V で比率計算 ratio = 0; if (adcd >= 0.800) ratio = ((adcd-2.048)/(2.048-0.800))*100; if (ratio<0) ratio *= -1; // 結果表示 data = String(MY_ID)+ "," + String(adcd,3)+ "," + String(ratio,0); Serial.print("-> "+ data+ "%"); /* * 〜 ここにWiFiでデータを転送するスケッチを作成 〜 */ // 計測終了 sleep wifi_end(); } ///// WiFi Control ///// /* WiFi接続開始処理 -> True:WiFi接続完了, False:WiFi接続失敗 */ boolean wifi_connect() { // WiFi 接続サイクルスタート(接続->確認->再接続) 3トライ for(int i = 0; (i < 2); i++) { // WiFi接続 接続が記憶されていない場合接続情報を記憶させ次回自動接続にする。 if (WiFi.SSID() != CONN_SSID) { WiFi.persistent(true); WiFi.mode(WIFI_STA); WiFi.setAutoConnect(true); WiFi.begin(CONN_SSID, CONN_PASS); } // 5秒間 0.5秒毎に接続確認 unsigned long timeout = millis(); while(millis() - timeout < 10000) { // 接続できた場合 Trueリターン if(WiFi.status() == WL_CONNECTED) return true; // 未接続は 500ms待ちしてループ delay(500); } } // WiFi 接続不可 エラーリターン return false; } /* WiFi 終了・切断処理 */ void wifi_end() { // 現在時刻取得 time_t tmUnix = time(NULL); // unix struct tm* tmNow = localtime(&tmUnix); // unix // WiFi切断 if (WiFi.status() == WL_CONNECTED) { WiFi.persistent(false); WiFi.disconnect(); // ステーションモード維持,ssid・passwordをクリアしない delay(10); } // 現在から正10分までの時間を計算 int minNow = tmNow->tm_min % 10; int minSlp = 10 - minNow; int secSlp = 0; // 30秒過ぎは起動を30秒早める if (tmNow->tm_sec>30) { minSlp = minSlp-1; secSlp = 30; } ESP.deepSleep((minSlp*63+ secSlp)*1000*1000); delay(500); } ///// NTP Control ///// /* NTP 同期処理 */ boolean ntp_connect() { // NTP サーバー同期確認 (max 5sec) unsigned long timeout = millis(); while (millis() - timeout < 10000) { // 現在時刻読込 time_t timeNow = time(NULL); struct tm* tmNow = localtime(&timeNow); // NPT同期完了は正常リターン if(tmNow->tm_year>=100) return true; // 同期未了は0.25秒おきに再確認 delay(250); } // 接続できなかったので エラーリターン return false; }
大まかな流れとしては以下の通りとなっています。
1. 起動・初期設定
2. (setup)
UARTスタート
I2Cスタート
WiFi接続
NTP確認
3. (loop)
現在時刻読込
正10分でなければスリープ(11分もok)
10回測定し平均値を求める
測定データを出力(今回は単純にUART出力しています)
スリープ
起動して測定して終了するだけの簡単な一本道スケッチですが、ポイントはその終わり方(wifi_end()のdeepSleep処理)です。終了時の時刻から次の正10分までの時間を求めその時間分スリープさせる方式をとっています。
ESP8266の内部時計はかなりラフで、10分単位になると分単位で簡単にずれてしまいます。私の場合は1分あたり63秒で計算すると実時間に近づきましたがさすがにこれを計測時間のあてにはできません。そこでNTPとの連携で正10分の記録ができる様いろいろといじくってみました。
一回の起動で3回NTPにアクセスしますので1時間あたり18回、1時間平均20回以下のアクセスというNTPの要求事項はクリアしています。