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

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

地震発生時の津波速報について

先週末の地震は東北から関東を含めて、大きな揺れでしたね。

すぐにテレビをつけて、地震震源をみると東北沖とのことで、東日本大震災のことが頭をよぎった方も多かったのではないか、と思います。ところが、テレビをつけて数分後には「津波の心配はありません」とのメッセージがすぐに出されました。「よかった、津波の心配はないんだな」と思ったと同時に、「ん?なんで発生直後にも関わらず、自信を持って言えるんだろう?」と疑問に感じました。

それでニュース記事を調べていたのですが、判断理由は「震源が深いから」とのこと。どういうこっちゃということで、そもそも津波が発生するメカニズムを調べていると、気象庁のページに分かりやすく書かれてました。

地震による津波発生のメカニズム
気象庁|津波発生と伝播のしくみ

原理としては、
①まずは地震発生により海底が動く
②海底に動きにより、海水面が押し上げられる
③持ち上げられた海水面の高さの差により、津波が発生する
津波が海岸にまで伝播する
という流れのようです。
震源が深いと、①の海底の動きがそもそも変形しにくく、大きな津波にならないそうです。東日本大震災のときは、震源の深さが24 kmであったのに対して、今回の地震では約55 kmとのこと。そこまで震源が深いと、津波も発生しないと判断できた訳ですね。

…しかしいつもニュース速報で出てくるけれど、何で震源の深さがすぐに分かるんだろう?と思って調べてみると、世の中には海底地震計なんてものがあるようです。

自己浮上式海底地震計 東京測振
http://www.to-soku.co.jp/products/oceanbottom/pdf/tobs24n-tmc8200.pdf

水深6000 mの水圧に耐えて計測できるなんて、凄い技術ですね!地震発生直後に計測結果を出すためには、ケーブル式海底地震計なんてものもあるそうで、こちらはリアルタイムでデータ送信できるように、地震計と地上局の間をケーブルで結ばれているそうです。海底から配線してしまうとは、驚きです。

それにしても、津波が来るかどうかなんて、センサデータから予測するには計算時間がかかるんじゃないか、と思って調べてみると、気象庁のページに答えがありました。

地震発生時の津波予報データベースを用いた速報システム
気象庁|津波を予測する仕組み

事前にシミュレーションデータを蓄積しておき、その計算結果に照らし合わせることで、計算速度を大幅に早めているそうです。インプットとするセンサデータ候補(Sensor1,Sensor2,Sensor3,…)がいくつかあり、地震発生直後はこれらを使って津波シミュレーションをイチから計算するのではなく、データベースに格納された計算結果を参照するだけで、概算値を出力できるのだと思います。おそらく、最近流行りのシミュレーションとAIをコラボさせる取組みに近いものかと思います。シミュレーション計算で、AIの教師データを予め作っておくイメージですかね。

今回調べられたものも、恐らく国内の取組みの一部かと思います。
さすが日本、地震対策の技術はすばらしいです。

結婚式の動画作り

Twitterにも書いていたのですが、最近は結婚式準備に追われていて、センサ作りの手が止まってます。
*アカウントはこちら → りけお@コロナで結婚式延期中 (@butsuyaki) | Twitter

温湿度計作りが概ね完成した、というのもあるのですが、結婚式のムービー作りに時間をとられています。
私たちの場合は、
①オープニング (前半)
②オープニング (後半)
③プロフィール
④再入場
の4本の動画を流す予定です。
①と②は繋げて1本の動画にするのですが、それぞれ2-3分程度の別動画なので、手間は倍かかっていると思います。

先日、Youtubeに①の動画をアップロードしました。
動画作り初心者で、クオリティはいまいちですが、見ていただけると嬉しいです。(ムービー中に表示される個人名はXXXX、YYYYと変えてあります)

www.youtube.com

本番では、①の動画のあとに、②で前撮り写真のムービーを流しつつ入場する、という演出で考えています。

動画作りは以前にもやったことがあり、タイムラプスをいじっていけばいいんだろうな、と漠然とした作業量はイメージしていたのですが、その予想を超えて時間がかかってしまいました。最も大きな理由は、今回使用した動画編集ソフト(Adobe After Effects)の操作に慣れていなかった、、、という点です。

ネットにトイストーリー風の結婚式ムービーを作った記事が出ており、そこまでハードルは高くないのかな、と勝手に思い込んでいたのですが、ちょっと見誤りました。。まず操作パネルの使い方に慣れるのに時間を要した、という点と、途中でトイストーリーのキャラ達が自作ボードの前を通るシーンの編集で、カメラトラックやロトブラシといった機能を使うのですが、途中何度かこの作業でミスをし、やり直しとなったことで、時間がかかってしまいました。

問題のシーンはこちらです↓

トイストーリー風 結婚式オープニングムービー Adobe After Effectsの課題シーン
トイストーリー風 結婚式オープニングムービー:スリンキー・ドッグ達がボード前を通過するシーン

Adobe After Effectsの無料使用期間が1週間なので、できれば1週間以内に作業を終えたかったのですが、結果的には3週間くらいかかりました。今なら操作に慣れてきたので、集中して取り組めば2-3日で作れると思いますが、初めて動画編集をされる人には1週間では厳しいかと思います。

そのうち、トイストーリー風結婚式オープニングムービーの作り方も、記事にまとめるかもしれません。
動画作りって沼要素ありますね。。

温湿度計作り(8) : Raspberry Piを使った絶対湿度計測(&風邪・コロナ対策)

さて、作りたかったことの大枠は完成しました。
今回はちょっとした作りこみです。

これは個人的なこだわりですが、”湿度”では健康状態(特に喉のうるおい状態)をコントロールするには不十分と考えています。”湿度”は正確には”相対湿度(%)”のことを意味し、空気中に存在可能な水蒸気の最大量(g/㎥)と、実際に空気中に存在する水蒸気量(g/㎥)の比とされています。専門用語では、空気中に存在可能な水蒸気の最大量を”飽和水蒸気量(g/㎥)”、実際の水蒸気量を”絶対湿度(g/㎥)”といい、次式のように表せます。

(式)相対湿度(%) = 絶対湿度(g/㎥) ÷飽和水蒸気量(g/㎥)

“相対湿度”では何が問題なのか、というと、”飽和水蒸気量”は温度によって変化するパラメータである、という点です。以下のグラフのように変化するとされています。

相対湿度の温度特性グラフ

環境省が推奨している冬場と夏場の室温は、夏は28℃、冬は20℃とされています。上のグラフで”飽和水蒸気量”を調べてみると、夏場は約30 g/㎥であるのに対して、冬場は約20 g/㎥ですね。

ここで、仮に部屋に存在する水分量(=”絶対湿度”)が10 g/㎥とすると、夏場は”飽和水蒸気量”が30 g/㎥なので、”相対湿度”は33.3%。一方、冬場は”飽和水蒸気量”が20 g/㎥なので、”相対湿度”は50%になります。このように”飽和水蒸気量”によって、”相対湿度”が変化するのですね。

”飽和水蒸気量”の直感的なイメージは、コップの容器の大きさです。コップの中に入っている水の量が”絶対湿度”で、コップのうちに占める水の比率(全容量のうち、何パーセントを占めるか)が”相対湿度”です。空気を吸ったときに、その中にどれだけの水分が含まれているかを示すのは、”相対湿度”ではなく、”絶対湿度”なのですね。

湿度、相対湿度、絶対湿度、飽和水蒸気量のイメージ

さて、”相対湿度”では健康状態をコントロールするには不十分と最初に述べましたが、じゃあどうやって評価したら良いかというと、”絶対湿度”です。”絶対湿度”は、喉のうるおいに関係するパラメータで、あるページで紹介されていた宮城県医師会のデータによると、”絶対湿度”とインフルエンザの流行には関係があり、以下の指標だそうです。
・〜7g/㎥ :より起こりやすい
・7〜11g/㎥:流行しやすい
・11〜g/㎥ :流行しにくい

最近は新型コロナウイルスの事情もありますし、”絶対湿度”を11 g/㎥以上に保つことは、ウイルス感染のリスクを減らす有効な手段と考えています。

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

そこで、これまでのRaspberry Piのシステムに、絶対湿度計測を追加することにしました。
過去記事はこちら↓
dailyphysics.hatenablog.com

”絶対湿度”は次式で計算できます。

(式)絶対湿度 (g/㎥) = 217*(6.1078*10^(7.5*温度(℃)/(温度(℃)+237.3)))/(温度(℃)+273.15)*相対湿度(%)/100

ソースコードは以下のとおりです。
表示部分のみを更新し、MySQL等のソースコードは、前回記事のままとしています。
絶対湿度は、これまで取得してきた温度と相対湿度だけで算出できるので楽ですね。

【Graph02.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", 
    type: "GET", 
    dataType: '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 abs_hum_data = [];
    var abs_hum_data_temp;
    var t;
    var RH;
    var $content = $('#content');
    $content.append("<li>" + "データ一覧" + "</li>");
    $content.append("<li>" + hum_data.length + "</li>");
    for (var i =0; i<hum_data.length; i++) {
	$content.append("<li>" + hum_data[i]["y"] + ":" + temp_data[i]["y"] + "</li>");
	t = temp_data[i]["y"];
	RH = hum_data[i]["y"];
	abs_hum_data_temp = 217*(6.1078* Math.pow(10.0, 7.50*t/(t+237.3))) / (t+273.15)*RH/100;
	abs_hum_data.push( {"label": hum_data[i]["label"], "y": abs_hum_data_temp} );
    }
    // ======================================
                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: "%"
                        },
                        {
                            title: "Abs. Humidity",
                            lineColor: "#360EAD",
                            tickColor: "#360EAD",
                            labelFontColor: "#360EAD",
                            titleFontColor: "#360EAD",
                            includeZero: false,
                            suffix: "[g/m3]"
                        }],
                    axisY2: {
                            title: "Illuminance",
                            lineColor: "#FF8C00",
                            tickColor: "#FF8C00",
                            labelFontColor: "#FF8C00",
                            titleFontColor: "#FF8C00",
                            includeZero: false,
                            suffix: "lx"
                        },
               toolTip: {shared: true},
               legend: {cursor: "pointer",itemclick: toggleDataSeries},
               
                    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: "abs_humidity",
                        color: "#360EAD",
                        axisYIndex: 2,
                        showInLegend: true,
                        dataPoints: abs_hum_data //表示するデータ
                    },
                    {
                        type: 'line',  //照度グラフ
                        name: "illuminance",
                        color: "#FF8C00",
                        axisYType: "secondary",
                        showInLegend: true,
                        dataPoints: lux_data //表示するデータ
                    }]
                });
    chart.render();

    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>

これで、絶対湿度のグラフ(青字)が追加されました。

Raspberry Pi Graph (Temperature, Humidity Absolute Humidity, Illuminance)、ラズパイの温湿度グラフ

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

さてせっかくなので、絶対湿度を使って、とある日中に換気したときの様子を分析してみました。

テスト日の行動は、①朝から昼まで暖房をつけ、②昼過ぎに一度暖房を消して20分程窓を開けて換気し、③夕方頃まで窓を閉めた状態にし、④気温が下がってきた夕方過ぎに暖房を再度付けました。①~④の間、加湿器はずっとオンのままにしていました。
※ 補足ですが、この実験は若干環境ストレスがかかるので、妻がいない日に実施しています

実験結果がこちらです↓

ラズパイを使った湿度、絶対湿度、相対湿度の変化実験。換気や暖房や加湿器のRaspberry Piセンサ影響。

こうしてデータを見比べてみると、面白いですね。

まず②で換気したタイミングで、温度(Temperature)・相対湿度(Humidity)・絶対湿度(Abs. Humidity)がガクンッと落ちています。この日は冬場(1月)でしたので、いずれの数値も下がりました。

③で窓が閉まっている間は、暖房をつけていなかったので、夕方にかけて徐々に温度は低下しました。13:15頃に微増したのは、私という約36 ℃の熱源があるために室温が僅かに上昇したのだと考えられます。
相対湿度と絶対湿度については、加湿器を動かしたので大きく上昇しました。しかしながら、相対湿度が60%に達しない段階で、頭打ちになりました。調べてみると、加湿器で上げられる湿度は、最大でも60~70%だそうで、窓での結露等があるために頭打ちになるそうです。

④で暖房をつけた直後(16:30頃)、室温が大きく上昇します。室温が上昇することで飽和水蒸気量が増加しますので、相対湿度は減少します。(絶対湿度は、短時間の変化ではほぼ一定) 私が相対湿度のグラフを嫌がったのはこうした挙動があるからで、相対湿度だけを見ていると、あたかも部屋の空気が乾燥してしまったかのように勘違いしてしまいます。実際には、暖房をつけた直後の絶対湿度は一定です。
また興味深いのは17:30以降の絶対湿度の挙動で、僅かではありますが増加しています。これは、温度が増加して、飽和水蒸気量が増加しているのにも関わらず、加湿器が相対湿度約48%を維持してくれたためです。コップに例えるなら、コップの容積(=飽和水蒸気量)が大きくなっていくのにも関わらず、コップの中に占める水の比率(=相対湿度:48%)が変わらなかった、という状態ですね。これは、コップの容積の増加に合わせて、水の量が増えたことに他なりません。

部屋の絶対湿度を高めるには、加湿器と暖房の双方をつけることが大事、ということを裏付ける結果ですね。こうしたデータをふまえ、我が家では、11 g/㎥を下がらないよう、加湿器と暖房を入れることを心掛けています。

環境や部屋の大きさ、加湿器の種類によって、それぞれのお家に最適なカスタマイズもあると思います。加湿器をつけるだけで十分なお部屋、今の加湿器は小さく11 g/㎥には達しないので交換しなくてはいけない、などなど。もしこの記事を読まれた方で、絶対湿度計測に興味を持って頂ける方がいたら、是非ラズパイを使った計測に挑戦してもらいたいです!

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

以下、余談です。
温度・湿度の上昇により不快感を感じるかどうか、日中の体感でテストしていたのですが、私は18:00頃を境に不快感を感じるようになりました。

この感覚を検証するために、温度と湿度から不快指数も計算してみました。不快指数は次式で計算できます。

(式)不快指数 = 0.81 × 気温(℃) + 0.01 × 湿度(%) × (0.99 × 気温(℃) − 14.3) + 46.3

得られたデータを見てみると、暖房を入れる前の16:15に21.8℃で57.5%(不快指数:68.1)であったのに対して、不快感を感じた18:00頃は26.2℃で48.8%(不快指数:73.2)と、確かに不快指数が上昇していました。

不快指数の指標は、個人差があるそうですが、指数70以上で不快と感じる人が出始め、75以上で半数以上が、80以上で全員が不快と感じ、86を超えると我慢できなくなるそうです。センサを置いている場所は、暖房が当たらない部屋の隅に置いているのに対して、私が日中いた場所は暖房の風が当たる場所でした。私の周りの空気の温度が、センサ値よりも若干高かったとすると、75を超えていた可能性もあるので、18:00を境に不快感を感じ始めたのは、体感としても妥当な頃合いだったと考えられます。

不快指数をラズパイで表示してみるのも、楽しそうですね。

温湿度計作り(7):Raspberry Pi 照度センサ+Cron設定

最近はセンサのグラフ表示を進めてきたのですが、これまでの説明で漏れていた部分をまとめたいと思います。


まず、前々回から照度センサを追加したのですが、センサにはBH1750FVIを使いました。BH1750FVIの実装方法は下記サイトを参考にしました。こちらのセンサを使うには、Raspberriy PiでI2C通信ができるように初期設定をする必要があります。I2C通信の設定手順については、本ブログでは割愛します。
https://tech.recruit-mp.co.jp/iot/farm-monitoring-with-iot/

Raspberry Pi Zeroの接続ピンは、下記サイトを参考にしました。電源は3.3Vピン、GNDはGNDピン、SDAはSDAピン(pin3)、SCLはSCLピン(pin5)にそれぞれ接続します。
http://hara.jpn.com/_default/ja/Topics/RaspPiZero.html

Raspberry Piのサンプルコードも載せておきます。

import smbus
import time
bus = smbus.SMBus(1)
addr = 0x23
for i in range(0,100):
        lux = bus.read_i2c_block_data(addr,0x10)
        lux_value = (lux[0]*256+lux[1])/1.2
        print "Lux:" + str((lux[0]*256+lux[1])/1.2)
        print lux_value
        time.sleep(1)



続いて、もう1つ説明で漏れていたのは、cronの設定です。
センサデータを定期的にWebサーバに送信するために、一定時間間隔毎にプログラムを起動する必要があります。手順は以下のとおりです。

1. インストール
$ sudo apt-get install chkconfig

2. cron動作確認
$ chkconfig cron

3. Cronの記述 (sudoをつけないとNG)
$ sudo crontab –e
で画面を表示したら、例えば5分毎にtest.pyを実行するならば、
crontab内に「*/5 * * * * sudo python3 /home/user/Desktop/test.py」等と記述します。

これで定期送信をすれば、前回説明したグラフ表示ができるようになります。

温湿度計作り(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の接続が何故か切れたりすることがあり、設定しなおしが面倒で、ここで手を止めることになりました。(良い実装方法があればアイデア募集です)