今度こそ管理するぞ(4)

今使用しているセンサーAM2320は温湿度が測定出来ます。ヒーターの近くに置くセンサーは湿度を測定する必要が無いので他のセンサーを使用することにしました。

今回追加のセンサー

測定するのは温度だけなので、ADT7410を使用する事にしました。

  • ADT7410
    • インターフェイスは、I2C。
    • 温度のみ測定出来るが測定範囲はAM2320より広い。( -55°C~+150°C)
    • 以前使用したログが ここ に有ります。参照下さい。

回路の変更

回路にADT7410を追加しています。回路図からは基板に追加される様に見えますが、インターフェイスが I2C なのでケーブルを作って基板のコネクターにつないで使用します。

センサーの配置と測定の準備

ヒーターの近くと缶の中では温度に差が生じると思われるので缶の中と外の温度を測定することにしました。

  • 缶を外して、ヒーターの上にADT7410を配置
  • 缶を保温器にセットして、缶の隅からセンサーのコードを出す
  • その後、隙間はアルミテープで塞ぐ
  • AM2320は缶の中に配置し、保温器の蓋を閉める。
  • 基板のI2C用コネクターに各センサーのコードを接続する。
  • その他は前回と同じ。

プログラムの変更

今回の変更は以下の通り。

  • Monitor_00.ino と menu.html の2つを変更
  • ADT7410制御用のプログラムを追加しています。

それ以外のプログラム は変更していません。

  • Monitor_00.ino 変更内容
    • ADT7410の温度データを元にヒーターのオンオフを行う。
    • WebのHPにAD7410の温度データ(ヒーターの温度)を表示。
    • 缶の内部の温湿度とヒーターの温度を保存。
    • ADT7410用プログラム、ADT7410.cppとADT7410.hを追加
Monitor_00.ino

#include "Arduino.h"
#include "SD.h"                          
#include <Wire.h>
#include <WebServer.h>
#include <ESPmDNS.h>

#include "AM2320.h"
#include "ADT7410.h"
#include "AQM1248A.h"

#include <DS3231.h>

// SD Card select PIN
#define sd_ss             5
/*
#define sd_sck            18
#define sd_mosi           23
#define sd_miso           19
*/

// AD Conversion R
#define ad_r1             100
#define ad_r2             22

// AD Conversion IO
#define ad_00             36
#define ad_01             39
#define ad_02             34

// LED Driver Ccontroll
#define LED_ctl           12

// External Interrupt
// Only use GPIO:0,2,4,12-15,25-27,32-39
#define RTC_INT           GPIO_NUM_4

// Wakeup caused 
#define by_ext_RTC_IO     2
#define by_ext_RTC_CNTL   3
#define by_timer          4
#define by_touchpad       5
#define by_ULP_program    6

// Power control 
#define Power_ctl         13

// DS3231 Flg 
#define every_s         0x0f    //Alarm once per second
#define match_s         0x0e    //Alarm when seconds match
#define match_ms        0x0c    //Alarm when min, sec match
#define match_hms       0x08    //Alarm when hour, min, sec match
#define match_dhms      0x00    //Alarm when date, h, m, s match
#define match_whms      0x00    //Alarm when DoW, h, m, s match
#define alarm1          1
#define alarm2          2

RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR int st_h = 0;
RTC_DATA_ATTR int st_m = 0;
RTC_DATA_ATTR int st_s = 0;
RTC_DATA_ATTR int target_tmp = 0;

#define ADT7410_addr     0x4b

WebServer server(80);
/*
const char ssid[] = "Enter your SSID";  // SSID
const char pass[] = "Enter your Password";   // password
*/

const char *SSID = "aterm-193b5c-g";
const char *PASSWORD = "9c182d72c9385";

DS3231 Clock;
byte tm_data[10];           //Year,Month,Date,DoW,Hour,Minute,Secon
bool Century,h12,PM;
int int_flg = 0;

void Handle_int()
{
    int_flg = 1;
}

void setup() 
{
  File dataFile;

    Wire.begin();
    SPI.begin();
    SD.begin(sd_ss);

    Serial.begin(115200);

    pinMode(Power_ctl, OUTPUT);     
    digitalWrite(Power_ctl,LOW);
    pinMode(LED_ctl, OUTPUT);     
    digitalWrite(LED_ctl,LOW);

    init_DS3231();

    pinMode(RTC_INT, INPUT_PULLUP);

    Serial.println("Connecting to WiFi");
    WiFi.disconnect(true);
    WiFi.softAPdisconnect(true);
    delay(500);

    WiFi.mode(WIFI_STA);
    WiFi.begin(SSID, PASSWORD);
    delay(1000);

    // Try forever
    while (WiFi.status() != WL_CONNECTED) {
      Serial.println("...Connecting to WiFi");
      delay(1000);
    }
    Serial.println("Connected");
    Serial.println(SSID);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    if (MDNS.begin("esp32monitor")) {
        Serial.println("MDNS responder started");
    }

/*
      WiFi.softAP(ssid, pass); 
      delay(1000);

      Serial.print("AP IP address: ");            //    192.168.4.1
      Serial.println(WiFi.softAPIP());
*/

    server.on("/", handleRoot);
    server.onNotFound(handleWebRequests); 
    server.begin();
    Serial.println("HTTP server started");

    Init_LCD();
    LCD_CLS(0);
    LCD_Print_Str(0,0,"LCD OK",1,2);

}

void loop() 
{
  server.handleClient();

  if(int_flg) measure_data();

}

void handleRoot() {
  String buf,cmd;
  int a,b,fl,fl_sleep;
  File dataFile;
  float h_data[2];
  uint8_t para[7];

    fl=1; fl_sleep=0;
    cmd=server.argName(0);
    switch(cmd.toInt())
    {
      case 1:   // Update Data
                break;
                
      case 99:  // Back or Cansel
                break;      
      
      case 2:   //Set TIME (send url data)
                buf=server.arg("2");
                get_h_m_s_tmp(buf);
                
                dataFile = SD.open("/Set_Time.html", FILE_READ);
                server.streamFile(dataFile,"text/html");
                dataFile.close();
                fl=0;
                break;

      case 3:   // Stop Logger
                detachInterrupt(RTC_INT);
                Clock.turnOffAlarm(alarm1);
                
                digitalWrite(Power_ctl,LOW);
                digitalWrite(LED_ctl,LOW);

                bootCount = 0;
                int_flg = 0;
                Serial.println("Stop Monitoring");

                break;
                
      case 4:   // Start Logger
                buf=server.arg("4");
                get_h_m_s_tmp(buf);
                Serial.println("Start Monitoring");
                Serial.println("buf:" + buf);
                dataFile = SD.open("/data0.txt", FILE_WRITE);
                dataFile.close();
                dataFile = SD.open("/data1.txt", FILE_WRITE);
                dataFile.close();
                
                bootCount = 0;
                measure_data();
                attachInterrupt(RTC_INT, Handle_int, FALLING);
                
                break;
                
      case 80:  // Send parameter
                buf=get_current_time() + ",";
                AM2320(h_data);
                buf += ("Temp:" + String(h_data[1]) + "'c / Humi:" + String(h_data[0]) +"%,");
                h_data[0] = ADT7410_get_temp(ADT7410_addr);
                buf += (String(h_data[0]) +"'c,");
                buf += (String(bootCount) + ",");
                disp_temp(h_data);
                buf += (String(st_h) + "," + String(st_m) + "," + String(st_s) + "," + String(target_tmp) + ",");
                server.send(200, "text/plain", buf);
                fl=0;

                Serial.println("buf:" + buf);

                break;
              
      case 81:  // Send RTC parameter
                get_DS3231(tm_data);
                buf = "";
                for(a = 0; a < 7; a ++) buf += (String(tm_data[a]) + ",");
                server.send(200, "text/plain", buf);
                fl=0;
                break;
              
      case 100: // Set TIME
                buf=server.arg("100");
                b=0;
                for(a=0; a<7; a++)
                {
                    cmd="";
                    while( buf[b] != ',') 
                    {
                        cmd += buf[b];
                        b ++;
                    }
                    tm_data[a]=cmd.toInt(); b ++; 
                }

                set_DS3231(tm_data);
                break;
    }
    
    if(fl)
    {
      dataFile = SD.open("/menu.html", FILE_READ);
      server.streamFile(dataFile,"text/html");
      dataFile.close();
    }
}

void disp_temp(float * th_data)
{
    char c_buf[50];
    String buf; 
    
    LCD_CLS(0);
    buf = "T:" + String(th_data[1]) + "'c ";
    buf.toCharArray(c_buf, 50);
    LCD_Print_Str(0,0,c_buf,1,2);
    
    buf = "t:" + String(th_data[0]) + "'c ";
    buf.toCharArray(c_buf, 50);
    LCD_Print_Str(0,16,c_buf,1,2);

    buf = "N:" + String(bootCount) ;
    buf.toCharArray(c_buf, 50);
    LCD_Print_Str(0,32,c_buf,1,2);

}

void set_next(byte cntl)
{
  int a,b,c;
  
    get_DS3231(tm_data);
    a = tm_data[6] + st_s;
    b = c = 0;
    if(a > 59) 
    {
        a -= 60;
        b ++;
    }
    b += (tm_data[5] + st_m);
    c = tm_data[4] + st_h;
    if(b > 59) 
    {
        b -= 60;
        c ++;
    }
    if(c > 23) c -= 24;
    
    Clock.setA1Time(0, c, b, a, cntl, 0, 0, 0);
}

void init_DS3231()
{
    Clock.turnOffAlarm(alarm1);               // Disables alarm 1 or 2 (default is 2 if Alarm != 1);
    Clock.turnOffAlarm(alarm2);               // Disables alarm 1 or 2 (default is 2 if Alarm != 1);
    Clock.setClockMode(0);
}

void get_h_m_s_tmp(String str)
{
  int a;
  String cmd;
              
    st_h = str.toInt();
    
    a = 0; 
    while(str[a] != ',') a ++;
    a ++;
    cmd = str.substring(a);
    st_m = cmd.toInt();
    
    while(str[a] != ',') a ++;
    a ++;
    cmd = str.substring(a);
    st_s = cmd.toInt();
    while(str[a] != ',') a ++;

    a ++;
    cmd = str.substring(a);
    target_tmp = cmd.toInt();
}

//#define CLOCK_ADDRESS 0x68
void set_DS3231(byte* tm_data)
{
    Clock.setYear(tm_data[0]);          //Set the year (Last two digits of the year)
    Clock.setMonth(tm_data[1]);                
    Clock.setDate(tm_data[2]);         
    Clock.setDoW(tm_data[3]);           //Set the day of the week  SUN=1 / SAT=7
    Clock.setHour(tm_data[4]);          
    Clock.setMinute(tm_data[5]);        
    Clock.setSecond(tm_data[6]);       
}

void get_DS3231(byte* tm_data)
{
  bool Century=false;
  bool h12;
  bool PM;
    
    tm_data[0] = Clock.getYear();
    tm_data[1] = Clock.getMonth(Century);
    tm_data[2] = Clock.getDate();
    tm_data[3] = Clock.getDoW();
    tm_data[4] = Clock.getHour(h12,PM);
    tm_data[5] = Clock.getMinute();
    tm_data[6] = Clock.getSecond();
}

void measure_data()
{
  String buf;
  File dataFile;
  float h_data[2],t_temp;
  
    buf= String(bootCount) + ",";
    buf += get_current_time() + ",";
    AM2320(h_data);
    buf += ("T/H," + String(h_data[1]) + "," + String(h_data[0]) +",");
    Serial.println("N:" + String(bootCount) + "/T:" + String(h_data[1]) + "/H:" + String(h_data[0]) );
    h_data[0] = ADT7410_get_temp(ADT7410_addr);
    Serial.println("t:" + String(h_data[0]));
    buf += ("t," + String(h_data[0]) + ",");

    disp_temp(h_data);

    Serial.println("N:" + String(bootCount) + "/T:" + String(h_data[1]) + "/t:" + String(h_data[0]) );
                
    dataFile = SD.open("/data0.txt", FILE_APPEND);
    dataFile.println(buf);
    dataFile.close();

    dataFile = SD.open("/data1.txt", FILE_APPEND);
    dataFile.println(buf);
    dataFile.close();

    t_temp = target_tmp * 1.01;
    if(h_data[0] > t_temp) 
    {
        digitalWrite(Power_ctl,LOW);
        digitalWrite(LED_ctl,LOW);
    }
    t_temp = target_tmp * 0.99;
    if(h_data[0] < t_temp)
    {
        digitalWrite(Power_ctl,HIGH);
        digitalWrite(LED_ctl,HIGH);
    }
    
    bootCount ++;

    set_next(match_hms);
    Clock.checkIfAlarm(alarm1);
    Clock.turnOnAlarm(alarm1);

    int_flg = 0;
}

String digit_2(byte num)
{
  String str;

    if(num < 10) str = ("0" + String(num));
    else str = String(num);

    return (str);
}

String get_current_time()
{
  uint8_t a;
  String buf;
  String w_data[8]={"Sun","Mon","Tue","Wen","Thu","Fri","Sat"};

    get_DS3231(tm_data);
    buf=String(tm_data[0] + 2000);
    buf += ("/" + digit_2(tm_data[1]));
    buf += ("/" + digit_2(tm_data[2]));
    buf += ("/" + w_data[tm_data[3]] + "/");

    for(a = 4; a < 7; a ++)
    {
        buf += digit_2(tm_data[a]);
        if(a != 6) buf += ":";
    }

    return(buf);    
}

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(".html")) dataType = "text/html";
    else if(path.endsWith(".jpg")) dataType = "image/jpeg";
    delay(5);

    dataFile = SD.open(path.c_str(), "r");
    server.streamFile(dataFile, dataType);
    dataFile.close();
    delay(5);
}
ADT7410.cpp

#include "ADT7410.h"

float ADT7410_get_temp(uint8_t addr)
{  
  short int data;
  float f_data;
  
    Wire.requestFrom((int)addr, 2);
    data = Wire.read() << 8;  
    data |= Wire.read();      

    data >>= 3;                          
    f_data = (float)data  / 16.0;          
  
    return f_data;
}

void ADT7410_ope_mode(uint8_t addr, uint8_t o_data)
{
  o_data <<= 5;
  Wire.beginTransmission((int)addr);
  Wire.write(0x03);         
  Wire.endTransmission();
  Wire.requestFrom((int)addr,1); 
  o_data |= (Wire.read() & 0x9f);  

  Wire.beginTransmission((int)addr);
  Wire.write(0x03);
  Wire.write(o_data);
  Wire.endTransmission();
}
ADT7410.h

#include <Wire.h>

// Operation Mode
#define ADT7410_CONT    0   // Continuous (default)
#define ADT7410_ONE     1   // One shot
#define ADT7410_SPS     2   // SPS mode
#define ADT7410_OFF     3   // Shutdown

//------------------------------------------------
// Get temperature
// uint8_t addr:    I2C Address (0x48,49,4A,4B)
//
// Return:          Temperature data (float)
//------------------------------------------------
float ADT7410_get_temp(uint8_t addr);

//------------------------------------------------
// Set Operation Mode 
// uint8_t addr:    I2C Address (0x48,49,4A,4B)
// uint8_t o_data:  Mode data (0,1,2,3)
//------------------------------------------------
void ADT7410_ope_mode(uint8_t addr, uint8_t o_data);
  • menu.html の変更箇所
    • HPにヒーターの温度を表示する部分を追加
menu.html

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel='stylesheet' type='text/css' href='./menu.css' >
        <title>ESP32 Monitor</title>
    </head>
    <body>
	<center>
       	<div style='font-size:35px'><b><i><u>ESP32 Monitor</u></i></b><br><br></div>
       	<div class="menu">
       	    <div class="input-group"> 
            	<label>Time:</label>
        	    <div id="0" ></div>
            </div>
            <div class="input-group"> 
                <label>Temp & Humi:</label>
        	    <div id="1" ></div>
            </div> 
            <div class="input-group"> 
                <label>Heater_Temp:</label>
        	    <div id="2" ></div>
            </div> 
            <div class="input-group"> 
                <label>Count:</label>
        	    <div id="3" ></div>
            </div> 
            <form method='get'>
                <button type='submit' name='2' onclick='onBtn_back(2)'>Set Time</button><br>
                <button type='submit' name='1' onclick='onBtn_back(1)'>Update</button>
                <div>
            		Traget Temp: <input type='number' max='150' min='0' id='7' class='inp_box' />('C)
            		<br><br>
            		<input type='number' max='23' min='0' id='4' class='inp_box' />(H)
            		<input type='number' max='59' min='0' id='5' class='inp_box' />(M)
            		<input type='number' max='59' min='0' id='6' class='inp_box' />(S)
        	    </div>
                <button type='submit' name='4' onclick='onBtn_back(4)'>Start</button>
                <button type='submit' name='3' onclick='onBtn_back(3)'>Stop</button><br>
    	    	<a style="color:#00ffff;cursor: pointer;" href='./data0.txt' download='data00.txt'>Download00</a>
    	    	<a style="color:#ffff00;cursor: pointer;" href='./data1.txt' download='data01.txt'> Download01</a><br><br>
            </form>
       	</div>
	</center>
    <script>

	    var para=['0','0','0','0','0','0','0','0','0'];
	    var url = "http://esp32monitor.local/?80=";
	    var xhr = new XMLHttpRequest();	
	    var a,b,str;
      
	    xhr.open('GET', url);
	    xhr.send();
     
	    xhr.onreadystatechange = function() 
	    {
	      if(xhr.readyState === 4 && xhr.status === 200) 
	      {
	        console.log( xhr.responseText );

	        b=0;
	        for(a = 0; a < 8; a ++)
       	    {
	            para[a]='';
       	        while( xhr.responseText[b] != ',') 
	            {
	                para[a] += xhr.responseText[b];
       	            b ++;
	            }
	            b ++; 
	        }

		    for(a = 0; a < 4; a ++) document.getElementById(String(a)).innerHTML = para[a];
		    for(a = 4; a < 8; a ++) document.getElementById(String(a)).value=para[a];	        
   	      }
	    }

        function onBtn_back(btno){
            var a;
            var str = "";
		    for(a = 4; a < 8; a ++) str += (document.getElementById(String(a)).value + ",");
		    document.getElementsByName(btno)[0].value=str;
	    }
	    
        history.pushState(null,null,'/');        
    </script>
    </body>
</html>

いざ測定

ヒーターのオンオフを前回は缶内の温度を基準に行いましたが、今回はヒーターの温度(ADT7410の温度)にしています。設定温度は前回と同じ50℃。サンプリングは5秒で先ず測定して見ました。

各線は、下記の通り。

  • 50℃外: ADT7410(ヒーター)の温度
  • 50℃内: AM2320(缶の中)の温度。

測定結果は、中々興味深い結果となりました。

  • ヒーターの温度は前回と同じ様なバウンド曲線となった。
    • ヒーター近くにセンサを置いてもやっぱり温度曲線はバウンドした曲線となりました。
    • そもそもこのセンサーだけでヒーターの温度を一定に管理するのは無理なのかもしれません。
  • 缶の中の温度は定常状態を保つ事が出来そう。かつオーバーシュートが無い。
    • ヒーターの温度は管理出来そうも無いですが、缶の中は上手い具合に一定の温度になる様です。
    • 波打ってるヒーターの温度が、缶を通って滑らかになるのでしょうか。
    • オーバーシュートが無い分一定温度になるのに若干時間がかかる様に見える
    • 何はともあれ、結果オーライでした。
  • 缶内の温度はヒーターの温度より低く、その差は約10℃。
    • 予想通り設定温度と缶内温度に差が生じました。
    • 管理したいのは缶内の温度です。ヒーター温との関係を調べる必要が有ります。

ヒーターと缶内部の温度関係を調べる

ヒーターの温度をある値に設定し缶内の温度が一定になったら温度を測定して両者の関係を求める事にします。方法は

  • ヒーターの設定温度を30℃から10℃毎に100℃まで測定
  • 各温度3000秒(50分)放置後、缶の中の温度測定
  • ヒーターの設定温度と上記缶温度の対応表を作成。
  • それをグラフにプロットし、直線近似を行い関係を求める。

測定結果は下記の通り。

この結果から 缶内温度とヒーターの温度関係は、以下となる事が分かりました。

     缶内温度 = 0.523 x ヒーター設定温度 + 12.624 

次は

ヒーターの近くにセンサーを置いてヒーターを管理すれば、缶内の温度を管理出来そうな手応えを得ました。引き続き次回は下記を行う予定です。

  • 時間が経つと若干缶内の温度が上がる様に見える。
    • 今回の方法では開始後、約30分で定常温度になるように見えます。
    • それ以降、見方によってはじわじわと温度が上がっているように見えます。
    • 納豆製作には約20時間温度を保つ必要が有ります。その間の温度上昇を確認
  • 缶内の湿度の管理
    • 湿度の管理機能は有りません。缶内は高温になると低湿に成ってしまいます。
    • 出来る事は水、又は湿った布をいれて高湿にすることですが。湿度の指定は出来ないでしょう。