ESPr Developerを使って、温度を測定し、サーバー上のSQLiteファイルに記録してみる

 

概  要

ESPr Developerから、I2C接続で温度センサー(ADT7410)を使って温度測定し、レンタルサーバー上のPHPプログラムに一定時間間隔で測定結果を送信します。PHPプログラムはデータを受信した都度、SQLiteファイルに書き込みます。(SQLiteデータベースファイル処理については先回投稿していますので、参考にしてみて下さい。) 今回はこの様にして書き込まれた温度データをインターネット経由でブラウザー上で見るところまで確認してみます。
尚、SQLite処理については、下記も参考にしてみて下さい。
[https://kats-eye.net/info/2018/11/13/php-sqlite/]

 

配線接続

今回の確認は、これまで投稿したインターネットダイスとPHPによるSQLite書き込みの組み合わせです。全体構成イメージはそちらを参考にして下さい。配線接続もインターネットダイスで使用した基板配線に下の写真の通り、⑤ I2C接続温度センサー(ADT7410)を追加配線しています。

実際に配線した写真は下の通りです。写真上の中央(やや右)に温度センサーを接続しています。狭い領域ですが、ピンソケットをはんだ付けできたので、ジャンパー線で接続しています。

プログラム

(1)ESPr Developerプログラム
基本的には、インターネットダイス(サイコロ)用プログラムに温度センサーの値を測定するプログラムを追加しています。インターネットダイスでは、加速度センサーの測定値をサイコロの目に変換してサーバーに送信していましたが、今回はサイコロの目の代わりに温度測定データを送信します。加速度センサー測定関連プログラムは、今回関係ありませんが、また違う目的で使うことがあるかもしれませんので、基本的には残しています。
行番24~66が主な追加した温度センサー関連のコードです。Arduinoで同じセンサー接続を行っており、プログラムも同じです。行番151で初期化の為の関数を呼び出しています。
行番172~229が繰り返しループになっており、温度測定を行い、サーバーにデータ送信する処理を繰り返します。
行番173で温度測定を行い、その結果を行番177で変数bodyに格納し、インターネットダイスと同じように一連の送信処理を行います。行番228では、約5分(=5*60*1000)間処理を遅延させるので、およそ5分に1回データ送信するプログラムになっています。

#include <ESP8266WiFi.h>
#include <Wire.h>

// WIFI関連
const char* ssid = "アクセスポイントSSID";               // 接続アクセスポイントのSSID         
const char* password = "アクセスポイントパスワード";         // 接続アクセスポイントのパスワード   
const int httpPort = 80 ;                       // ポート番号指定
const char* host = "接続サーバードメイン";              // 接続サーバードメイン名
const char* path = "サーバー側プログラムPath";          // 呼び出すプログラム名
const int s_p = 4;                             
int loopCnt=0;

String body = "";
String rtn ="";

// I2C-加速度センサー(MMA8451)
#define DVC_ADRS 0x1D                           // デバイスアドレス
#define GRAV_E       (9.80665F)                 // 標準重力加速度

uint16_t x_r , y_r , z_r ;                      // XYZ 14bitデータ
long int x_v , y_v , z_v ;                      // XYZ 数字
float x_g , y_g , z_g ;                         // XYZ 角加速度

// ☆ ADT7410 定数・変数
#define DVC_ADT7410 0x48                        // 温度センサ(ADT7410)アドレス(外部設定)
uint16_t g_md = 0x40 ; 
uint16_t g_rs = 0x00 ;
float r_t ;
 
// ☆ 温度センサ-(ADT7410)初期化
void ADT7410_INIT(){
  Wire.beginTransmission(DVC_ADT7410);         // 接続開始
  Wire.write(0x03);                            // Configurationアドレス指定
  Wire.write(g_md | g_rs);                     // 操作モードと分解能指定
  Wire.endTransmission();                      // 接続終了
}
 
// ☆ 温度センサ-(ADT7410)データ読み取り
float rd_dat_ADT7410(void) {
    uint16_t ui_val ; long int in_val ; float fl_tmp ;
 
    Wire.beginTransmission(DVC_ADT7410);       // 接続開始
    Wire.write(0x00);                          // 読込開始アドレス指定
    Wire.endTransmission(false);               // 送信完了(接続維持)
    
    Wire.requestFrom(DVC_ADT7410 , 2);         // 2バイト要求
    ui_val = (uint16_t)Wire.read() << 8 ;      // データの読み出し(上位)
    ui_val |= Wire.read();                     // データの読み出し(下位)
 
    if (g_rs == 0x80){                         // 測定レンジによる処理選択
        in_val = (long int)ui_val;
        if(ui_val & 0x8000) {                  // 符号判定
            in_val = in_val - 65536 ;
        }
        fl_tmp = (float)in_val / 128.0 ;
        
    }else{
        ui_val >>= 3 ;
        in_val = (long int)ui_val;
        if(ui_val & 0x1000) {                  // 符号判定
            in_val = in_val - 8192 ;
        }
        fl_tmp = (float)in_val / 16.0 ;
    }
     return fl_tmp ;
}

 
// ①通信開始、デバイス初期化
bool DVC_INIT(uint8_t i2caddr){
    Wire.begin(12,14);                          // 接続開始
  
    wrt_reg(0x2B, 0x40);                        // DEVICE RESET ENABLED
    while (read_reg(0x2B) & 0x40);              // デバイスリセット待ち
    wrt_reg(0x0E , 0x01);                       // RANGE 設定
  
    wrt_reg(0x2B, 0x02);                        // PowerMode( 0x00:normal,0x01:Low Noise Low Power,0x02:High Resolution, 0x03:Low Power)
    wrt_reg(0x2D, 0x01);                        // INT_EN_DRDY
    wrt_reg(0x2E, 0x01);                        // INT_CFG_DRDY

    wrt_reg(0x11, 0x40);                        // PL_EN
    wrt_reg(0x2A, 0x01 | 0x04);                 // ACTIVE , LNOISE
    return true;   
}

// ②指定レジスタにデータ書き込み
void wrt_reg(uint8_t reg, uint8_t value) {
    Wire.beginTransmission(DVC_ADRS);           // デバイス指定、通信開始
    Wire.write((uint8_t)reg);                   // レジスタ指定
    Wire.write((uint8_t)(value));               // データ書込
    Wire.endTransmission();                     // 送信完了
}

// ③指定レジスタからデータを読み出し
uint8_t read_reg(uint8_t reg) {
    Wire.beginTransmission(DVC_ADRS);           // 送信処理開始
    Wire.write(reg);                            // レジスタ指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_ADRS, 1);              // 1byteデータに要求

    if (! Wire.available()) return -1;          // データ有無判定
    return (Wire.read());                       // 1byteデータ  
}

// ④データ取得、変換
void read_data(void) {
    Wire.beginTransmission(DVC_ADRS);           // 送信処理開始
    Wire.write(0x01);                           // アドレス指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_ADRS, 6);              // 6byteデータ要求
                                                //
    // データ取得、変換
    x_r = Wire.read() ; x_r <<= 8 ; x_r |= Wire.read() ; x_r >>= 2;
    y_r = Wire.read() ; y_r <<= 8 ; y_r |= Wire.read() ; y_r >>= 2;
    z_r = Wire.read() ; z_r <<= 8 ; z_r |= Wire.read() ; z_r >>= 2;

    x_v = (long int)x_r ; y_v = (long int)y_r ; z_v = (long int)z_r ;    

    // マイナス値変換
    if(x_r & 0x2000) x_v = x_v - 16384 ;        
    if(y_r & 0x2000) y_v = y_v - 16384 ;
    if(z_r & 0x2000) z_v = z_v - 16384 ;
 
    // スケールレンジを取得
    uint8_t range = read_reg(0x0E) & 0x03;
    uint16_t div_p = 1;

    // 加速度変換係数(測定レンジにより異なる)
    if (range == 0x02) div_p = 1024 ; 
    if (range == 0x01) div_p = 2048 ;
    if (range == 0x00) div_p = 4096 ;
    x_g = (float)x_v * GRAV_E / div_p ; 
    y_g = (float)y_v * GRAV_E / div_p ; 
    z_g = (float)z_v * GRAV_E / div_p ;
}

// ⑤最初に1回だけ行う処理
void setup() {
    Serial.begin(115200);
    // デバイスアドレス指定し、初期設定
    if(!DVC_INIT(DVC_ADRS))  {
        Serial.println("-----");
        while (1);
    }
                                            
    uint8_t reg1 = read_reg(0x2A);              // 元設定確認
    wrt_reg(0x2A, 0x00);                        // StandByMode
    wrt_reg(0x0E, 0x00 & 0x3);                  // Set Range( 0x00:2g , 0x01:4g , 0x02:8g , 0x03:reserved )
    wrt_reg(0x2A, reg1 | 0x01);                 // ActiveMode

    ADT7410_INIT();                             // ☆ 温度センサ-(ADT7410)初期化

    // ◆ WIFI-初期設定 ◆
    Serial.println() ; Serial.println() ;
    Serial.print("Connecting to ") ; Serial.println(ssid);

    WiFi.mode(WIFI_STA);                        // STAモード設定
    WiFi.begin(ssid, password);                 // SSID,パスワードを指定してアクセスポイント接続   

    // Wi-Fi接続確認
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFiに接続しました。"); 
    Serial.println("");
}

// ⑥繰り返し処理(センサーを読んで、シリアル通信出力)
void loop() {
        r_t = rd_dat_ADT7410();                   // ☆ 温度センサ-(ADT7410)データ取得
        read_data();                              // XYZ軸データ取得
        loopCnt = loopCnt + 1 ;

        body = r_t ;                              // 温度データを送信ボディに設定
        
        WiFiClient client;                        // TCP接続用にWiFiClient classを使用する

        // TCP接続確認
        if (!client.connect(host, httpPort)) {    // 外部サーバーに接続
            Serial.println("接続に失敗しました。");
            return;
        }

        // サーバにリクエストを送信(POST)
        client.print(String("POST ") + path + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Content-Length:" + String(body.length())+ "\r\n" + 
               "Connection: close\r\n\r\n" +
               body);
   
        // リクエスト結果受信待ち            
        unsigned long timeout = millis();
        while (client.available() == 0) {
            if (millis() - timeout > 12000) {
                Serial.println(">>> タイムアウトしました。");
                client.stop();
                return;
            }
        }

        // HTTPヘッダを含むデータの読み取り(1文字毎) 
        String raw = "";
        while (client.available()) {
            char c = client.read();
            raw = raw +c;
        }  

        // HTTPヘッダの削除、
        int index = raw.indexOf("\r\n\r\n");      // 改行が2回続く箇所検索(\r\n(='CR+LF'))
        
        // ( index + s_p + 2 )から1文字切り出す    
        rtn = raw.substring(index+s_p , index+s_p + 32 ) ;

        Serial.println("--------------------------------------------------------------");                   
        //Serial.print( loopCnt ) ;               // 送信回数
        Serial.print("送信:");                   
        Serial.print(r_t);                      // 送信データ
出力
        Serial.print(", 受信:");                   
        Serial.println(rtn);                    // 受信データ出力
        Serial.println("");                     // 改行
        // WiFi.disconnect();                    // Wifiの切断

        // 1000=1秒    
        delay(5 * 60 * 1000);                                
}

以下の図はプログラム実行時のシリアルモニター出力内容です。約5分おきにサーバーに送った「送信データ」とサーバから返信された「受信データ」を出力しています。

(2)レンタルサーバー側プログラム①(SQLite書き込み)
レンタルサーバー側のプログラムを示します。行番8でESPr® Developerから送信されたデータを受け取ります。行番10~17で受け取ったデータを書き込むSQLiteデータベースファイルが存在しているか確認します。存在しない場合、行番18~22で新たにデータベースファイルを作成します。行番28では受け取った値が、1度~45度以内の範囲であることを確認し、行番29~32でデータベースファイルに書き込みを行います。
行番34,36では、書き込みを行った場合、行わなかった場合の結果をESPr® Developer側に送信しています。

<?php

	// 日時取得
	date_default_timezone_set('Asia/Tokyo');
	$timestamp = date('Y-m-d H:i:s');

	// post送信のBodyすべて取得
	$entireBody = file_get_contents('php://input');	

	// 温度記録用テーブルを作成
	$db = null ; $sql = null ; $res = null;
	$db = new SQLite3("./sq/temp_record.sqlite3");	
        // テーブル有無確認用のSQL
	$sql = 'SELECT count(*) FROM sqlite_master WHERE type="table" AND name="temp_reco"';
        
	// テーブルがない場合は新たに作成
	if(!$db->querySingle($sql)){
		$sql = "CREATE TABLE temp_reco(
			id INTEGER PRIMARY KEY,
			g_temp TEXT NOT NULL,
			created_datetime TIMESTAMP DEFAULT(datetime(CURRENT_TIMESTAMP,'localtime')))";
		$res = $db->exec($sql);
		
	        //echo "<br>";
		//echo "商品リストテーブルを新たに作成しました。";
 	}

	if($entireBody >= 1 && $entireBody <= 45){
		$db = null ; $sql = null ; $res = null;
	    	$db = new SQLite3("./sq/temp_record.sqlite3");
	    	$sql = 'INSERT INTO temp_reco( g_temp ) VALUES ("'.$entireBody.'")';
	    	$res = $db->exec($sql);

		echo "OK , ".$entireBody." , ".$timestamp ;
	}else{
		echo "NG , ".$entireBody." , ".$timestamp ;
	}

?>

(3)レンタルサーバー側プログラム②(SQLite読み出し)(2)で書き込んだレンタルサーバー上のSQLiteファイルからデータを読み出すプログラムを示します。行番3~7で登録されている全てのデータセットを読み出し、行番9~18でブラウザー上に出力します。

<?php

	$db = null;$sql = null;$res = null;
	$db = new SQLite3("./sq/temp_record.sqlite3");
	$sql = 'SELECT * FROM temp_reco';
	
	$res = $db->query($sql);
 
	while($row = $res->fetchArray(SQLITE3_NUM)) {
	    for($i = 0 ; $i < count($row); $i++){
		echo $row [$i];
    			
		if($i < count($row) - 1){
		    echo " , " ;
		}
	    }
	    echo "<br>";
	}

?>

ブラウザーへの出力は以下の様になります。左からカンマで区切って、連番,温度,日時の順に登録時間単位で表示しています。
(件数が蓄積されると、出力時間が掛かりますので、最新20件表示に一部改良)

まとめ

今回はESPr Developerを使った測定値をサーバー側に保存する為にSQLiteを使ってみました。今回、サーバー側に登録したデータの呼び出しも行ってみましたが、単なるデータの羅列ですので、もう少し見る人にやさしい表示方法を検討する予定です。
IoTやCloudなどの意味は知らいないのですが、なんとなく少し近づいてきた様な気分になっています。