日常生活の物理|身近で体験できる科学のブログ

日常に関すること、何でもつぶやきます。物理的な視点を加えることが多いかも。

温湿度計作り(6):Raspberry Pi センサデータのグラフ表示

Webサーバーにデータを送信できるようになったので、あとはGUIをどう作るかですが、前回と同じく、インデペンデンスシステムズ横浜のページを参考にさせて頂きました。
http://independence-sys.net/main/?p=3973

こちらのページで紹介されているCanvasJSが実装も簡単で使いやすく、見た目も折れ線グラフで複数パラメータを同時に表示でき、日・週・月毎の表示も簡単に切り替えられるため、そのまま参考にしました。

まず、データベースにアクセスするためのホスト名やパスワードをまとめたファイルを、以下のように用意します。XXXXXXXやYYYYYYYに入れる文字列は、前回記事と同様です。

【Database.php

<?php
function GetDb(){
// mysql:host=ホスト名;dbname=データベース名;charset=文字エンコード
    $dsn = 'mysql:host=XXXXXX.php.xdomain.ne.jp;dbname=YYYYYYYY;charset=utf8';
// データベースのユーザー名
    $usr = 'ZZZZZZZZZ';
// データベースのパスワード
    $passwd = 'AAAAAAAAAA';
    $db = new PDO($dsn, $usr, $passwd);
    $db-> exec('SET NAMES utf8');
    return $db;
}
?>


続いて、MySQLのデータを取得するプログラムを以下のように記述します。

MySQL.php

<?php
require_once 'Database.php';
$target  = isset($_GET['target']) ? $_GET['target'] : 'temp';
$range  = isset($_GET['range']) ? $_GET['range'] : 'Day';
switch ($range){
case 'Average':
case 'Month':
     $dayRange = 30;
     break;
case 'Week':
     $dayRange = 7;
     break;
case 'Day':
     $dayRange = 1;
     break;
default:
     break;
}
$day = date("Y-m-d H:i:s",strtotime("-$dayRange day"));
$qryDate = strtotime("-{$dayRange} day");
$db = GetDb();
switch ($range){
case 'Average':
for ($day=0; $day <$dayRange ; $day++) {
$days = date("Y-m-d", $qryDate);
$sth = $db->prepare("SELECT datetime,avg($target) FROM raspi_sensorvalues where datetime like '$days%' ORDER BY id ASC");
$sth->execute();
        $row = $sth->fetch(PDO::FETCH_ASSOC);
                $jsonData[] = [
               'label'=>substr($row['datetime'],5,5),
               'y'=>(float)round($row['avg('.$target.')'],2)
               ];
        $qryDate = strtotime("+1 day", $qryDate);
     }   
     break;
case 'Month':
case 'Week':
case 'Day':
$sth = $db->prepare("SELECT datetime,$target FROM raspi_sensorvalues where datetime >= '$day' ORDER BY id ASC");
$sth->execute();
        while($row = $sth->fetch(PDO::FETCH_ASSOC)){
                $jsonData[]=[
                'label'=>substr($row['datetime'],5,11),  
                'y'=>(float)$row[$target]
                ];
        }
     break;
}
        header('Content-type: application/json');
        echo json_encode($jsonData); 
?>


最後に表示部分を以下のように記述します。
【Graph01.php

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>センサーデータ</title>
    <script type="text/javascript" src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script type="text/javascript">
    function drawChart(selectspan) {
    var temp_data;
    $.ajax({
    url: "MySQL.php", //データ取得のURL
    type: "GET", //GETメソッドで取得
    dataType: 'json', //処理結果はjson形式で受信
    async: false, //非同期通信
    data: {
           target: 'temp',
           range: selectspan
          },
    success: function(json1) { temp_data = json1;} 
    });
    var hum_data;
    $.ajax({
    url: "MySQL.php",
    dataType: 'json',
    async: false,
    data: {
           target: 'hum',
           range: selectspan
          },
    success: function(json1) { hum_data = json1;}
    });
    var lux_data;
    $.ajax({
    url: "MySQL.php",
    dataType: 'json',
    async: false,
    data: {
           target: 'lux',
           range: selectspan
          },
    success: function(json1) { lux_data = json1;}
    });
    
                var chart = new CanvasJS.Chart(chartContainer01, {
                    title: {text: "温度・湿度・照度データ"},
                    axisX: { labelAngle: -90, labelFontSize: 14, labelFontColor: '#222'},
                    axisY:[{
                            title: "Tempreture",
                            lineColor: "#C24642",
                            tickColor: "#C24642",
                            labelFontColor: "#C24642",
                            titleFontColor: "#C24642",
                            //includeZero: false,
                            suffix: "℃"
                        },
                        {
                            title: "Humidity",
                            lineColor: "#369EAD",
                            tickColor: "#369EAD",
                            labelFontColor: "#369EAD",
                            titleFontColor: "#369EAD",
                            includeZero: false,
                            suffix: "%"
                        }],
                    axisY2: {
                            title: "Illuminance",
                            lineColor: "#FF8C00",
                            tickColor: "#FF8C00",
                            labelFontColor: "#FF8C00",
                            titleFontColor: "#FF8C00",
                            includeZero: false,
                            suffix: "lx"
                        },
               toolTip: {shared: true},
               legend: {cursor: "pointer",itemclick: toggleDataSeries},
               
                    //axisY: { title: "temperature", suffix: '℃',axisYIndex: 0, labelFontSize: 14, labelFontColor: '#222' },
                    theme: "light1",  //デフォルトテーマに設定
                    data: [{
                        type: 'line',  //温度グラフの種類
                        name: "temperature",
                        color: "#C24642",
                        showInLegend: true,
                        axisYIndex: 0,
                        dataPoints: temp_data //表示するデータ
                    },
                    {
                        type: 'line',  //湿度グラフの種類
                        name: "humidity",
                        color: "#369EAD",
                        axisYIndex: 1,
                        showInLegend: true,
                        dataPoints: hum_data //表示するデータ
                    },
                    {
                        type: 'line',  //照度グラフの種類
                        name: "illuminance",
                        color: "#FF8C00",
                        axisYType: "secondary",
                        showInLegend: true,
                        dataPoints: lux_data //表示するデータ
                    }]
                });
    chart.render();
    //マウスカーソル一つに3つの値を表示する設定
    function toggleDataSeries(e) {
    if (typeof (e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
        e.dataSeries.visible = false;
    } else {
        e.dataSeries.visible = true;
    }
    e.chart.render();
    }
    }
    </script>
  </head>
  <body onload= drawChart('Day')>
      <form>
      <select name="selectspan" onchange="drawChart(this.value)">
        <option value="Day" selected="">1日</option>
        <option value="Week">1週間</option>
        <option value="Month">1ヵ月</option>
        <option value="Average">1ヵ月平均</option>
      </select>
    </form>
    <div id="chartContainer01" style="height: 450px; width: 100%;"></div><br>
  </body>
</html>

この3つのファイルをWebサーバーにアップロードします。
そして、Graph01.phpのページを開くと、以下のようなページがされました!

f:id:dailyphysics:20210117173002p:plain

見た目も綺麗ですし、カーソルを画面に触れるだけで、上図のように各時間帯の数値も確認できます。
これならスマホのブラウザからアクセスするだけで、いつでも家のセンサ情報を確認できますね。スマホのブラウザから開くと、若干見た目が崩れるので、私はPC版サイトから開くようにして、PCと同じビジュアルになるように設定しています。

温湿度計作り (5) : Raspberry PiのWebサーバ送信

前回までで、Raspberry Piのデータを取得し、Bluetoothssh・scpコマンドでパソコンにデータを送信するところまでは出来たのですが、これで毎日の温湿度を計測するには、まだ使いづらい。。

そこで、Webサーバを立てて、Raspberry Piのデータを送信し、スマホ等からいつでも見られるようにしよう、と考えました。下記サイトにRaspberry Piのデータ送信方法について詳しく纏められていたので、参考にさせて頂きました.。http://independence-sys.net/main/?p=3973

 これを進めるにあたっては、Webサーバーを借りたり、PHPやhtml等のコードは、最低限の知識が必要です。サーバーは無料のものでも十分で、Xserver(xfree)を使うことにしました。 https://www.xdomain.ne.jp/login/member.php


Xserverをレンタルしたら、PHPMyAdminにログインします。
f:id:dailyphysics:20210109154547p:plain

このような画面が出てくるので、まずは、

CREATE TABLE `sensorvalues` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `temp` float NOT NULL,
  `hum` float NOT NULL,
  `lux` float NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=316 ;

等と入力。データベース内にテーブルが作成されることを確認します。
f:id:dailyphysics:20210109154634p:plain


次に、Raspberry Piからの通信を受けるためのPHPファイルを、サーバー側に用意しましょう。下記ファイルをXserver側にアップロードしておきます。PHPファイルに記載する、データベース名、ユーザー名、ホスト名は、XFREEのPHPMySQLサーバーのサーバー管理パネルのページに記載されています(下図参照)。
f:id:dailyphysics:20210109154645p:plain


sql.php

<?php
// mysql:host=ホスト名;dbname=データベース名;charset=文字エンコード
$dsn = 'mysql:host=XXXXXX.php.xdomain.ne.jp;dbname=YYYYYYYYYY;charset=utf8';

// データベースのユーザー名
$user = 'ZZZZZZZZZZ';

// データベースのパスワード
$password = 'AAAAAAAAAAA';
$pdo = new PDO($dsn, $user, $password);

 switch ($_SERVER['REQUEST_METHOD']) {
    case 'GET':
        echo json_encode("GET start");
        $st = $pdo->query("SELECT * FROM sensorvalues");
        echo json_encode($st->fetchAll(PDO::FETCH_ASSOC));
        break;

    case 'POST':
        echo json_encode("POST start");
        $in = json_decode(file_get_contents('php://input'), true);
        if (!isset($in['id']))
        {
                $st = $pdo->prepare("INSERT INTO sensorvalues(datetime,temp,hum,lux) VALUES(:datetime,:temp,:hum,:lux)");
        }
        $st->execute($in);        
        break;
}
?>

これで通信の受け手側のプログラムが完成しました。
続いて、Raspberry Pi側の送信プログラムを記述します。

【Web_Upload.py】

import requests
import json
from datetime import datetime
def uploadSensorValues(temp, hum, lux):
url = 'http://XXXXXXXXXXX.php.xdomain.jp/sql.php'
sensorsdata = {'datetime':datetime.now().strftime("%Y/%m/%d %H:%M:%S"),'temp':temp,'hum':hum,'lux':lux}
print json.dumps(sensorsdata)
headers = {'content-type': 'application/json'}
res = requests.post(url, data=json.dumps(sensorsdata), headers=headers, verify=False)
print res.text
print res.status_code
pass

def main():
uploadSensorValues(11.8, 11.1, 51.1)
if __name__ == '__main__':
main()

このファイルをRaspberry Pi側で実行します。
すると、Webサーバーと通信して、PHPMyAdminに以下のようにデータを追加することができるようになります。
f:id:dailyphysics:20210109154846p:plain

あとはデータを表示するViewerのページを作るだけですね!
スマホ等からでも簡単に、家の温度・湿度・照度等が見れそうです。

温湿度計作り (4) : Raspberry PiでのBluetooth接続

Raspberry Piに無線で入れるようになったし、センサデータをいい感じに無線で出力できるようなものを作りたいなと思い、Bluetooth接続にトライしてみました。

 

参考にしたのは下記ページです。
https://qiita.com/oko1977/items/9f53f3b11a1b033219ea

 

こちらを参考にBluetoothの初期設定を行い、センサデータをBluetoothで出力しました。

*****************

[Raspberry Pi]

import Adafruit_DHT as DHT

import subprocess

import time

 

SENSOR_TYPE = DHT.DHT22

DHT_GPIO = 22

h,t = DHT.read_retry(SENSOR_TYPE, DHT_GPIO)

print("Temp= {0:0.1f} deg C" . format(t))

print("Humidity= {0:0.1f} %" . format(h))

num = 100

for i in range (num):

        h,t = DHT.read_retry(SENSOR_TYPE, DHT_GPIO)

        print("Count",i+1,sep=":")

        cmd = "sudo echo " + str(t) + "> /dev/rfcomm0"

        subprocess.run(cmd,shell=True)

        cmd = "sudo echo " + str(h) + "> /dev/rfcomm0"

        subprocess.run(cmd,shell=True)

*****************

 

こちらでシリアル通信をして、Teratermで受信した信号を出力したところ、想定どおりのデータが得られました。

Teratermでは見るのもいまいちなので、受信するPC側のPythonコードも用意しました。

 

*****************

[PC]

import serial

import platform

ser = serial.Serial('COM4', 9600)

print(ser.name)

   

for i in range(20):

    line = ser.readline()

    temp = float(line.strip().decode('utf-8'))

    line = ser.readline()

    humidity = float(line.strip().decode('utf-8'))

    print(temp,humidity,sep=" --- ")

ser.close()

*****************

 

出力結果も以下のとおりに得られました。

f:id:dailyphysics:20201130234204p:plain

 

…っと、ここまで着手したのですが、いまいち使い勝手のよいシステムがイメージできず。ここでBluetoothの実装を止めてしまいました。

携帯やPCから簡単にセンサデータを確認できるようなものを作りたかったのですが、Bluetooth接続の設定自体が簡単には扱いづらく。。パソコンとRaspberry Piの接続が何故か切れたりすることがあり、設定しなおしが面倒で、ここで手を止めることになりました。(良い実装方法があればアイデア募集です)

温湿度計作り (3) : Raspberry Piへのリモート接続

前回記事を書いてから、どうしてここまで更新があいてしまったのか。。
Raspberry Piの開発自体は楽しくて、裏で着々と進め、前回の状態から大分更新されています。ただブログ更新を完全に止めてました。。備忘録も兼ねて、更新内容を順に書きたいと思います。

 

まずリモート接続について、最初は普通にSSHでログインするところから。raspberry piIPアドレスは、wifiの文字の上にカーソルを合わせることで確認できます。IPアドレスに続く「/24」は無視して構わないようで、TCPポートは、raspberry piではデフォルトで「22」になっています。
ポート番号を変更するには、
>> sudo nano /etc/ssh/sshd_config
として、設定ファイルを開き、「# Port 22」とある部分の付近に「Port XXXXX」を追記します。sudo rebootで再起動して、設定を反映します。あとはTeratermを使ってログインできます。
コマンドプロンプトsshログインするなら、
>> ssh [username]@[ip] -p [port]
でログインできます。
参考URL:https://lotti.info/raspi-ssh/

 

…っと、ここまでパスワード認証方式でリモート接続したのですが、SSHで誰でもログインできる状態は不用心すぎやしないかと思い、下記サイトを参考に公開鍵認証方式に変えることにしました。

参考URL:https://monoist.atmarkit.co.jp/mn/articles/1912/11/news022_3.html

 

>>> ssh-keygen -t rsa
として、秘密鍵と公開鍵のキーペアを作成します。保存先のフォルダはデフォルトでよいです。途中で「Enter passphrase (empty for no passphrase):」が表示されるため、ここでは、秘密鍵を解くための新規パスフレーズを入力します。秘密鍵(id_rsa)と公開鍵(id_rsa.pub)の2つのファイルが「/home/newpiuser/.ssh」に作成されます。
Raspbianの初期状態では、公開鍵は「~/.ssh/authorized_keys」に配置変更し、authorized_keysファイルのパーミッションも下記のように設定するとのこと。
>>> cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
>>> chmod 600 ~/.ssh/authorized_keys
さらに、authorized_keysに書き込みしたので、不要となったid_rsa.pubを削除。
>>> rm ~/.ssh/id_rsa.pub
続いて、teratermの「ファイル」→「SSH SCP」を選択して、下段の「From:」へ「.ssh/id_rsa」と入力し、保存先をデスクトップに指定。「Receive」ボタンを押し、秘密鍵(id_rsa)ファイルをPCにダウンロードします。※「From:」には"~/"とか入れなくても".ssh/id_rsa"でOKです。
一度、Tera Termの接続を切断し、再度接続します。この時「RSA/DSA/ECDSA/ED25519鍵を使う」をチェックし、「秘密鍵」ボタンを押して、先ほどダウンロードした秘密鍵(id_rsa)ファイルを指定します。「ユーザー名」を入力し、「パスフレーズ」には先ほどキーペアを作成する時に指定したパスフレーズを入力します。秘密鍵のPASSにはデスクトップ上の「id_rsa」を選択。これで公開鍵認証方式で接続できることを確認します。
最後に、パスワード認証方式でのSSH接続を無効化します。「/etc/ssh/sshd_config」設定ファイルをテキストエディタで編集します。
>>> sudo nano /etc/ssh/sshd_config
ターミナルで設定ファイルの編集を行う「PasswordAuthentication no」を1行追加します。
>> sudo reboot
で再起動すれば、設定は反映されます。teratermでパスワード認証ではログインできないことを確認できればOKです。パスワード認証で、ユーザー名、パスフレーズを入れると、認証に失敗しました、となるはず。

 

ご参考までに、Teratermではなく、コマンドプロンプトから公開鍵認証方式でログインするときには、下記コマンドでログインできます。
>>> ssh -i C:\Users\User\Desktop\id_rsa [user]@[ip] -p [port]
パスフレーズを聞かれるので、入力します。

raspberry pi側のデスクトップを、丸ごとPC側にコピーしたいときは、scpコマンドを使います。開発途中でよく使いましたので、下記します。
>>> scp -i C:\Users\User\Desktop\id_rsa -p [port] -r [user]@[ip]:\home\username\Desktop\ C:\Users\User\Desktop\

 

リモートで開発できるようになると、USBハブやモニターも不要になります。前回までと比較すると、大分スッキリとした開発環境になりました。次回以降、センサデータを外部出力する方法をまとめていきます。

f:id:dailyphysics:20201129225252j:plain

温湿度計作り (2) : Raspberry Piの開発環境

昨日のRaspberry Pi記事の開発環境を、もう少し詳細に書きたいと思います。開発環境は、ざっと以下のような状態です。

f:id:dailyphysics:20200913180046p:plain

 

今回は少し気合を入れて、モニターまで購入しました。全てPCからリモートで実装することもできるのですが、今回触るのが初めてだったことと、長らくマイコンlinux等に触れていなかったので、開発環境を整えたいと思い、モニターまで揃えました。USBハブもあると、キーボードやマウスもすぐに繋げられるので便利です。この他に、HDMIケーブルやアダプターやSDカード等もあり、ゼロから立ち上げるとそれなりに物を揃える必要がありました。

 

上の写真から分かるとおり、周辺機器を全て繋げるとゴチャゴチャします。全てリモート接続にすれば、Raspberry pi+温湿度センサ+アダプター1個だけになるので、大分コンパクトになります。次回の記事で、リモート接続関連について書きたいと思います。

温湿度計作り (1) : Raspberry Pi Zeroを使ったPythonでのセンサデータ出力

家庭で使える実用的なものを、何か1つ作ってみたいな~という思いが以前からあり、温湿度計作りに挑戦してみました。

 

温湿度計とは言っても、センサや制御は購入品です。使った部品は、以下のとおりです。

シングルボードコンピュータRaspberry Pi Zero

・温湿度センサ:DHT22

 

簡単ですが、コードも記載しておきます↓

*****************

import Adafruit_DHT as DHT

SENSOR_TYPE = DHT.DHT22

DHT_GPIO = 22

h,t = DHT.read_retry(SENSOR_TYPE, DHT_GPIO)

print("Temp= {0:0.1f} deg C" . format(t))
print("Humidity= {0:0.1f} %" . format(h))

*****************

 

【出力結果】

Temp= 27.1 deg C
Humidity= 67.8 %

 

自宅の温湿度計を確認したところ、温度27.3℃、湿度69%だったので、概ね正しく計測できていそうですね。

Raspberry Piを使い始めたのはつい最近なのですが、センサの動作テストが本当に簡単で、あっという間にデータ出力まで辿り着けました。今後はパソコン等と通信してデータ出力させていきたいと思います。温湿度計作りはまた記事にしたいと思います。

マッチ箱

先日、妻が外食した際に、なぜか粗品でマッチ箱を貰ってきました。

 

f:id:dailyphysics:20200912161634j:plain

粗品のマッチ箱

え、なんで??と言ったのですが、妻も理由は分からないとか。

最近マッチって全然使わなくなったので、用途が思いつかないです。

 

粗品という割には、結構な本数が入っているし。。

そのうち理科の実験でも始めようかな。。