前回設計した回路を元にADT7410の使い方、ESP8266で今回使用した機能を説明した後、Logger用プログラムを書いて行きます。
先ずはADT7410
この素子の使い方は”ADT7410を使う”で説明しています。
- ホストとの通信は、I2C
- ハードの関係からアドレスは、0x48, 0x49, 0x4A, 0x4B の4つが指定可能
- 13ビットモードならば電源投入のみで設定無しで温度データが測定可能
- 測定モードは連続を使用
- シャットダウン機能を使用し省電力を図る
先ずは読み込み用スケッチ。
#include <Wire.h>
void setup() {
float f_data;
delay(1000);
Serial.begin(115200);
Serial.println();
Wire.begin();
Serial.println("I2C started");
f_data = get_Temp(0x48);
Serial.println("tenp:" + String(f_data));
}
float get_Temp(int addr)
{
short int data;
float f_data;
Wire.requestFrom(addr, 2);
data = Wire.read() << 8;
data |= Wire.read();
data >>= 3;
f_data = (float)data / 16.0;
return f_data;
}
void loop() {
}
- 11行:I2Cの開始。今回はArduino仕様を使用したので、”Wire.begin();” のみでOK。 ESP8266仕様なら、”Wire.begin(2,14);” となる。
- 14行:読み込み用関数。引数はI2Cアドレス。今回は0x48。
- 17行:読み込み用関数本体。
- 22行:2バイト読み込みをI2Cに申請。
- 23行:最初の1バイトは測定値上位バイト
- 24行:次の1バイトは測定値下位バイト。
- 25行:有効は部分は上位13ビット(下位3ビットは無効)なので3ビット右にシフト。
- 26行:最後の変換(13ビット長の場合16で割る)して
- 27行:本体に戻す
これだけでシリアルモニタに温度の値が表示されます。
次は測定モード変更用スケッチ。
void Adt_ope_mode(int data)
{
data <<= 5;
Wire.beginTransmission(0x48);
Wire.write(0x03);
Wire.write(data);
Wire.endTransmission();
}
- 4行:通信の開始をI2Cに依頼
- 5行:レジスタ番号0x3のコンフィグレーションレジスタを指定
- 6行:ビット6,5に値を書き込にモードを指定します。
- 0:連続モード
- 1:ワンショット
- 2:インターバル
- 3:シャットダウン
- 7行:通信終了
今回使用するモードは、0と3です。
ちょっと分からなかったDeep Sleep
Deep Sleepに関する資料が少なく、良く分からなかったのですが、Webで調べると大体下記のよう
- このモードに入るとRTC関係以外の回路の電源が切られ、ESP8266の消費電力が非常に小さくなる。
- 関数は、deepSleep(uint32_t time_us, RFMode mode = RF_DEFAULT)
- 1番目の引数:uint32_t time_usはスリープしている時間。単位はマイクロ秒。計算すると約71分30秒位がスリープ出来るMAX時間
- 2番目の引数:引数としてRF_DEFAULT、RF_CAL、RF_NO_CAL、RF_DISABLEDが有るようだが効果が分からない。この引数を省力するとRF_DEFAULTになる模様
- 設定値の時間になると、IO16(17番ピン)がLowに落ちる。この端子をESP8266のRSTに繋ぐと設定時間にリセットがかかり、EPS8266はBOOTから起動する。
- RTC関係の回路としてRTC Memoryがある。これはDeep Sleepに入っても値を保持する。
- 読み込み用の関数:bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size);
- 書込用の関数:bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size);
- 引数は全て、uint32_t型。
取り合えすDeep Sleepに入るスケッチ
void setup() {
delay(100);
Serial.begin(115200);
Serial.println();
Serial.println("Enter Deep Sleep");
ESP.deepSleep(10 * 1000 * 1000);
delay(100);
}
void loop() {
}
- 8行:ここで、時間の設定を行っています。単位がマイクロ秒なので、10 * 1000 * 1000 で10秒
- 9行:8行でDeepSleepにはいるのであればこの行はいらない。Webに直ぐに移行しない場合を考慮してdelay()を入れる方が安全と有りました。
IO16(17番ピン)をRSTに繋いで実行して下さい。10秒毎にBootして、”Enter Deep Sleep”とモニタに表示されます。
ただこれでは無限ループです。無限ループから抜ける為にBOOT後、IO14端子を調べて分岐します。
RTC Memoryですが、今回は、
- 現在の測定回数。
- Sleepの時間。
の2つを保存しています。
用に使用しています。
データは何処に保管?
長時間の測定を想定しています。データの保管は、SIPFFSを使用します。ESP8266のSPIFFSのインストールは、”Arduino IDE に ESP8266 SPIFFS ファイルシステムアップローダーをインストールする方法”を参考にしました。
SPIFFSを使用して、データ保管ファイル、”data.txt” と ”data1.txt”の2つを用意しました。これらのファイルを追加書込モードで開き、測定の度に測定の回数と温度データを保管しています。2つのファイルの中身は同じです。もし仮にどちらかのファイルの保管中に電池が切れても、もう一方は残るだろうと2つ作っています。ちなみにファイルのオープンモードは
- “r”:読み込み
- “w”:書き込み
- “a”:追記
- “r+”:読み込み+書き込み
- “w+”:書き込み+読み込み
- “a+”:追記+読み込み
が有ります。
電池の本数
最終的にバッテリー駆動でこの回路を動かしたいと思っています。単3電池(1.5V)で、3.3Vを出そうとすると多分2本使う事になりますが、それでは直ぐに寿命が来ます。電池を何本が直列に繋ぎ、3端子レギュレータで3.3Vを作った方が時間は伸びると思われます。では何本使うか。
- 電池には終止電圧というものが有りその値は約1V。
- ESP8266が約3Vまで起動すると仮定すると電池を寿命まで使うとして最低3本
- 3本の新品の電池を直列に繋ぐと約4.5V。これは3端子レギュレータで3.3Vに減圧。
- この時レギュレータには電圧降下が有り、それを約0.5Vとすると、3本では終始電圧の時レギュレータが3Vを出力出来ない
- 4本の場合、終始電圧が4V。これは電圧降下を考慮しても3Vは十分に出力出来る。終始電圧以下でも若干使用可か
- そこで乾電池の数を4本としばらつきを考えてMAX6.5V(普通は6V)とすると、TOUTは約0.644V。EPS8266が3Vまで動いたとすると
これより、乾電池4本使用する事にしました。
ちょっと便利なバッテリーチェッカー
乾電池4本。1本が約1.5V。ばらつきを考えて、MAX 6.5Vと仮定。レギュレータの電圧降下を約0.5Vとすると、今回の電源の動作範囲として6.5から3.5Vを想定します。使用中バッテリーが切れるのは困るので電池残量を測定したい所です。ESP8266のTout(16番ピン)は0から1Vの電圧を10ビット分解能でAD変換する事が出来ます。ここの利用してバッテリー残量を測定します。
この回路で、入力が6.5から3.5Vまで触れた場合、TOUTの電圧は約0.644から0.347Vになります。上記の回路で、
- 1Vを10ビットの分解能で変換します。つまり1カウントは、1/1024(V)。
- つまりバッテーリーの電圧 B(v)は、NをAD変換後の値として
- B = N * ( 1/1024 ) x (100 / 11 )
- B = N * 9.09/1024 。
となります。先ずはこの値でスケッチを実行し、値の分かっている電圧を掛けてこの値を調整します。今回は、調整後の値は、9.82でした。
最後はインターフェイス
ESP8266でAPモードのサーバを上げサーバーにスマホでアクセスすることを前提にしています。サーバーにアクセスすると、この様な画面が表示されます。
スマホの画面縦型にちょうど良い大きさです。
- 温度の測定。
- 測定間隔時間の設定。
- 測定の開始。
- 測定データのダウンロード
が出来ます。
今回のスケッチ
今回のスケッチは以下の通り
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <Wire.h>
#include <FS.h>
#define Adt_On 0
#define Adt_Off 3
#define Mic_Min 60000000
/* Set these to your desired credentials. */
const char *ssid = "ESP8266";
const char *password = "12345678";
ESP8266WebServer server(80);
String index_Dt =
"<!DOCTYPE html>\n<html>\n<head>\n"
"<title>ESP8266 Logger</title>\n"
"<style type='text/css'>\n"
"button.bt1 {background:#90ee90;font-size:50px;padding:10px;margin:20px;border-radius:30px;box-shadow:4px 4px #555;}\n"
"</style>\n</head>\n"
"<body>\n<center>\n"
"<div style='width:550px; background-color:#d8a373; border-style:solid; border-radius:30px; border-color:#6b3f31'>\n"
"<p style='font-size:70px'><b><i><u>Temp Logger</u></i></b></p>\n"
"<div style='font-size:50px'>\n"
"<span>---Temp---</span><br>\n"
"<span id='t_48'>48: XX.X</span><br>\n"
"<span id='t_49'>49: XX.X</span><br>\n"
"<span id='t_4a'>4a: XX.X</span><br>\n"
"<span id='t_4b'>4b: XX.X</span><br>\n"
"<span id='p_volt'>Battery: X.XXV</span><br>\n"
"<form method='get'>\n"
"<button type='submit' name='10' class='bt1'>Measure</button><br><br>\n"
"<input type='number' max='71' id='d_time' style='font-size:50px;width:100px;'/>\n"
"<button type='submit' name='1' id='t1' onclick='onBtnStart()' class='bt1'>Start</button><br>\n"
"</form><br>\n"
"<a style='font-size:50px' href='./data.txt' download='data.txt'>Download</a><br><br>\n"
"<a style='font-size:50px' href='./data1.txt' download='data1.txt'>Backup</a><br><br>\n"
"</div></div>\n</center>\n<script>\nfunction onBtnStart() {\n"
"document.getElementById('t1').value =document.getElementById('d_time').value;\n}\n";
/* Just a little test message. Go to http://192.168.4.1 in a web browser
connected to this access point to see it.
*/
// Arduino I2C SCL:A5 SDA:A4
void handleRoot() {
String buf,cmd;
double inter_time;
float d_temp;
int a,b,fl;
File dataFile;
char temp[16];
uint32_t offset,m_data;
fl=0;
cmd=server.argName(0);
switch(cmd.toInt())
{
case 1: // Start Log
cmd=server.arg("1");
m_data=cmd.toInt();
offset=0; // interval time
ESP.rtcUserMemoryWrite(offset, &m_data, sizeof(m_data)) ;
offset=4; m_data=0; // measurement No.
ESP.rtcUserMemoryWrite(offset, &m_data, sizeof(m_data)) ;
fl=1;
break;
}
buf=index_Dt;
for(a=0x48; a<0x4C; a++)
{
dtostrf(get_Temp(a), 5, 1,temp);
buf += ("document.getElementById('t_" + String(a,HEX) + "').innerHTML=\"");
cmd=temp;
buf += (String(a,HEX) + ": " + cmd + " 'C\";\n");
}
dtostrf(Battery_chk(), 5, 2,temp);
cmd=temp;
buf += ("document.getElementById('p_volt').innerHTML=\"Battery: " + cmd + " V\";\n");
if(fl)
if(digitalRead(14))
{
buf += "alert('Switch is AP position');";
fl=0;
}
buf += "history.pushState(null,null,'/');\n</script>\n</body>\n</html>";
server.send(200, "text/html", buf);
if(fl)
{
Serial.println("Start");
SPIFFS.remove("/data.txt");
SPIFFS.remove("/data1.txt");
get_save_Temp();
Adt_ope_mode(Adt_Off);
offset=0; // interval time
ESP.rtcUserMemoryRead(offset, &m_data, sizeof(m_data)) ;
inter_time = m_data * Mic_Min;
ESP.deepSleep(inter_time);
delay(500);
}
}
void setup() {
String buf;
File dataFile;
uint32_t offset,m_data;
double inter_time;
delay(500);
Serial.begin(115200);
Serial.println();
Wire.begin();
Serial.println("I2C started");
SPIFFS.begin();
Serial.println("SPIFFS started");
Adt_ope_mode(Adt_On);
delay(500);
pinMode(14,INPUT);
if(digitalRead(14)){
Serial.print("Configuring access point...");
// You can remove the password parameter if you want the AP to be open.
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.on("/", handleRoot);
server.onNotFound(handleWebRequests); //Set setver all paths are not found so we can handle as per URI
server.begin();
Serial.println("HTTP server started");
pinMode(13,OUTPUT);
digitalWrite(13, HIGH);
}
else{
get_save_Temp();
Serial.println("Measured");
Adt_ope_mode(Adt_Off);
offset=0; // interval time
ESP.rtcUserMemoryRead(offset, &m_data, sizeof(m_data)) ;
inter_time = m_data * Mic_Min;
ESP.deepSleep(inter_time);
delay(500);
}
}
float Battery_chk()
{
float d_temp;
int a;
d_temp=0;
for(a=0; a<5; a++)
{
d_temp += (system_adc_read() * 9.822 / 1024);
delay(100);
}
return d_temp/5;
}
void Adt_ope_mode(int data)
{
int a;
data <<= 5;
for(a=0x48; a<0x4c; a++)
{
Wire.beginTransmission(a);
Wire.write(0x03);
Wire.write(data);
Wire.endTransmission();
}
}
void loop() {
server.handleClient();
}
void handleWebRequests()
{
String dataType = "text/plain";
String path;
File dataFile;
path = server.uri();
if(path.endsWith(".txt")) dataType = "text/plain";
else if(path.endsWith(".css")) dataType = "text/css";
else if(path.endsWith(".js")) dataType = "application/javascript";
else if(path.endsWith(".png")) dataType = "image/png";
else if(path.endsWith(".gif")) dataType = "image/gif";
else if(path.endsWith(".jpg")) dataType = "image/jpeg";
delay(5);
dataFile = SPIFFS.open(path.c_str(), "r");
server.streamFile(dataFile, dataType);
dataFile.close();
delay(5);
}
float get_Temp(int addr)
{
short int data;
float f_data;
Wire.requestFrom(addr, 2);
data = Wire.read() << 8;
data |= Wire.read();
data >>= 3;
f_data = (float)data / 16.0;
return f_data;
}
void get_save_Temp()
{
float f_data[4];
int a,b;
uint32_t offset,m_data;
for(b=0; b<4; b++)
{
f_data[b]=0;
for(a=0; a<5; a++)
{
f_data[b] += get_Temp(0x48 + b);
delay(300);
}
f_data[b] /= 5;
}
offset=4;
ESP.rtcUserMemoryRead(offset, &m_data, sizeof(m_data)) ;
save_data("/data.txt",f_data,m_data);
save_data("/data1.txt",f_data,m_data);
m_data += 1;
ESP.rtcUserMemoryWrite(offset, &m_data, sizeof(m_data)) ;
}
void save_data(String fn, float *data, uint32_t m_data)
{
File dataFile;
char temp[16];
String str_time;
int a;
dataFile = SPIFFS.open(fn, "a");
dataFile.print(String(m_data) + ",");
for(a=0; a<4; a++)
{
dtostrf(data[a], 5, 1,temp);
str_time=temp;
if(a != 3) dataFile.print(str_time + ",");
else dataFile.println(str_time);
}
dataFile.close();
}
簡単に使い方を説明すると
- 今回は、ADT7410gが4個付いているのが前提です。(各アドレスは0x48,0x49,0x4a,0x4b)
- 先ず、測定/APモード切替スイッチをAPモードに(IO14がHigh)にして電源をオン
- ESP8266がレディーになるとLEDが点灯します。
- スマホでWiFiを検索。検索対象は、”ESP8266″。
- 見つかったらパスワードを”12345678”と入力してESP8266と繋いで下さい。
- ESP8266をデフォルトAPモードで立ち上げると、アドレスが、”192.168.4.1”になる上です。
- ”192.168.4.1”にアクセスして下さい。下記のWeb画面が表示されます。
- 管理画面で、”Meaure”ボタンを押すと各センサーの値がTempの下に表示されます。
- ”Start”ボタンの横の欄に測定間隔時間を入力します。
- 入力は、”1分”単位で71分まで入力出来ます。
- モード切替スイッチを、測定モード(IO14がLow)にセットした後、”Start”ボタンを押して、測定を開始します。
- 先ず、現在の温度を測定し、ファイルの保存してDeep Sleepに入ります。
- Deep Sleepに入るとLEDが消灯します。
- 電源は切らないで下さい。入れたままです。
これで測定が開始します。このスケッチは、オペレータが測定を停止しなければ、無限ループ測定を行います。測定の停止は以下の様に行います。
- 先ず電源を切ります。
- モード切替スイッチを、APモードにセットします。
- 電源を入れます。
- スマホでWiFiに繋いで、”192.168.4.1”にアクセスすると、上記の管理画面が表示されます。
- 管理画面下にある、”Download”をクリックすると、スマホにデータがダウンロードされます。
- 念の為に、”Backup”もクリックしてバックアップのデータをダウンロードします。
- スマホのファイル管理ソフトで、ダウンロードしたファイルを開きます。こんな感じでデータがデーブされています。
0,14.5,14.7,14.8,14.9
1,14.4,14.5,14.8,14.4
2,14.4,14.4,14.5,14.6
- データの形式は:測定回数、0x48の値、0x49の値、0x4aの値、0x4bの値
- 測定回数は保存されますが、測定時間は保存していません。測定間隔と回数で後で自分で計算して求めます。
ここに、今回のスケッチを置きますー>”ESP8266_Logger“ これで実際に畑の温度を測定して見ます。