その他

無線通信規格920MHz帯「Wi-SUN」を活用した気象ステーションの製作【第4回 】

クラウド連携と自宅内サーバーへのデータ保存・グラフ表示

 

第1回:システムの概要と部品構成
第2回:ハードウエアについて
第3回:ソフトウェアと省電力手法

 

Wi-SUNを活用した気象ステーションの製作の連載第4回は、測定したデータを外部のクラウドサービスや自宅のLinuxサーバに保存し、グラフ表示などを行う手法について解説します。

 

目次

  1. データ処理・保存の全体構成
  2. Ambientを利用したデータ蓄積と可視化
  3. 宅内のLinuxサーバへのデータ送信・保存とグラフ表示
  4. LinuxサーバのWeb系ソフトウェア環境:XAMPPについて
  5. データベースの作成
  6. http/POSTとデータベースアクセス用PHPスクリプト
  7. Linuxサーバ向けpythonプログラムの改版
  8. Grafanaを使用したグラフ表示
  9. Grafanaプラグインを使用した風向表示

 

1.データ処理・保存の全体構成

初めに、図1にデータ処理・保存の全体構成を示します。

図1 データ処理の全体構成

左上の屋外の気象ステーションからWi-SUNで測定データが送信されてきます。前回の記事・第3回では、このWi-SUNの通信データをラズパイ上のpythonによるプログラムで受信し、データを抽出するところまでを解説しました。

 

今回はこの受信データを2つの方法で蓄積・可視化(グラフ表示など)をおこないます。右上は外部のクラウドサービス「Ambient」を利用する方法です。もう一つは右下の自宅Linuxサーバへの接続です。測定データはデータベースに保存・蓄積され、可視化ツールであるGrafanaを使ってグラフ表示他をおこないます。以下それぞれの方法について解説します。

 

2.Ambientを利用したデータ蓄積と可視化

Ambientは下島健彦氏が開発・運営しているIoTデータの可視化サービスで、限定した範囲では無料で利用可能です。

https://ambidata.io/

 

Arduino、ラズパイなどから使用できるライブラリが公開されており、それらから送られたデータを受信、蓄積し、グラフで見やすくデータを表示することが可能になります。

Ambientの使用方法の概略については、非常に分かりやすいチュートリアルが以下に公開されているのでご参照ください。

https://ambidata.io/docs/gettingstarted/

Ambientを利用するにはまずユーザ登録が必要です。このページの「1.ユーザー登録(無料)」に説明があるのでそれに従ってください。

 

それでは実際に本機のpython受信プログラムからambientにデータを送る方法について解説します。以下に第3回で使用したpythonのデータ受信プログラムにambientへのデータ送信を加えたリストを示します。

Code-Example
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Wi-SUN Rx program:Ambient
import serial
import datetime
import ambient
import requests
from time import sleep
 
ambi1 = ambient.Ambient(#####, "################")
ambi2 = ambient.Ambient(#####, "################")
con=serial.Serial('/dev/ttyUSB0',115200)
print(con.portstr)
 
send_data1 = [0.0]*7
send_data2 = [0.0]*10
 
while True:
    str_bf=con.readline()
    if str_bf !="":
        index = str_bf.find(b'Data1:')
        if index !=-1:
            dt=datetime.datetime.now()
            print(dt)
            index_end = str_bf.find(b'\x00')
            datastr = str_bf[index+6:index_end].decode('utf-8')
            rssi = str_bf[index-12:index-10].decode('utf-8')
            env_data = datastr.split(',')
             
            for i in range(7):
                send_data1[i-1] = float(env_data[i-1])
            print('Send Data1 =',send_data1)
             
            rssi_int = int(rssi,16)
            print('RSSI_int =',rssi_int)
             
            try:     
                r = ambi1.send({'created': "{:%Y-%m-%d %H:%M:%S}".format(dt) ,"d1": send_data1[0], "d2": send_data1[1], "d3": send_data1[2], "d4": send_data1[4], "d5": send_data1[5], "d6": send_data1[6], "d7": rssi_int})
                print('Ambient status:', r.status_code)
            except requests.exceptions.RequestException as e:
                print('request failed: ', e)
        else:
            index = str_bf.find(b'Data2:')
            if index !=-1:
                dt=datetime.datetime.now()
                print(dt)
                index_end = str_bf.find(b'\x00')
                datastr = str_bf[index+6:index_end].decode('utf-8')
                rssi = str_bf[index-12:index-10].decode('utf-8')
                env_data2 = datastr.split(',')
                print('env_data2= ',env_data2)
                 
                for i in range(10):
                    send_data2[i-1] = float(env_data2[i-1])
                print('Send Data2 =',send_data2)
                 
                rssi_int = int(rssi,16)
                print('RSSI_int =',rssi_int)
                 
                try:  
                    r = ambi2.send({'created': "{:%Y-%m-%d %H:%M:%S}".format(dt) , "d1": send_data2[2], "d2": send_data2[3], "d3": send_data2[4], "d4": send_data2[5], "d5": send_data2[6], "d6": send_data2[7], "d7": send_data2[8], "d8": send_data2[9]})
                    print('Ambient status:', r.status_code)
                except requests.exceptions.RequestException as e:
                    print('request failed: ', e)
                 
con.close()

まず、python用のambientライブラリをインストールする必要があります。以下にインストール方法と使用方法についての解説があります。

https://ambidata.io/refs/python/

このページの「インストール」のセクションに記載された手順でライブラリをインストールしてください。

 

プログラムリストの6行目「import ambient」がライブラリの読み込みです。次に10、11行目でチャネルIdとライトキーを指定してAmbientのインスタンスを作成。チャネルIdとライトキーは上記のチュートリアル「2.チャネル生成」に解説があります。ひとつのチャネルあたりの最大データ数が8なので、「ambi1」「ambi2」の2つのチャネルを作成し、ambi1を気温、湿度、気圧など、ambi2を風速・風向・雨量に割り当てています。

37行から41行がambientへのデータ送信部分(ambi1)です。ライブラリにsend()メソッドが定義されており、これを使用してデータ送信します。

Code-Example
1
2
3
r = ambi1.send({'created': "{:%Y-%m-%d %H:%M:%S}".format(dt) ,"d1":
send_data1[0], "d2": send_data1[1], "d3": send_data1[2], "d4": send_data1[4], "d5":
send_data1[5], "d6": send_data1[6], "d7": rssi_int})

 

‘created’でデータの生成日時を指定することも可能です。ここではWi-SUNでデータを受信した時刻dt(23行で取得)をフォーマット指定して代入しています。

 

測定データは配列(send_data1[])に入っています。値はd1:外気温、d2:湿度、d3:気圧、d4:機器内部温度、d5:照度、d6:バッテリ電圧を送信。あとWi-SUNの受信レベルRSSIをd7に入れています。ambi2の送信プログラム部分に関してもambi1と全く同じ構造になっています。

送信時にはpythonの例外処理:try〜exceptを使用して、通信エラーを検知・処理します。Ambientライブラリは内部的に「requests」http通信モジュールを使用。except requests.exceptions.RequestException as e:でHTTPErrorなどの通信エラーをまとめて例外処理できます。これによって通信エラーが起こっても、プログラムを停止することなく継続可能です。次章で説明するサーバとhttp/POSTで通信する際も、この「requests」モジュールを使用しています。

 

次に送信したデータの可視化(グラフ化)ですが、Ambientではボードを作成して自由にグラフを配置・設定できるようになっています。下図2にボードの表示例を示します。

図2 ボードの表示例

上部中央にあるグラフのアイコンをクリックして、ボード上に表示するグラフを追加することができます。「チャートの作成中」という名前のウインドウが出るので、マウスでドラッグして表示位置や大きさを決め、「チャネル/データ設定」ボタンを押して表示データ他を設定します。(図3)

図3 チャート設定画面

ここでは主に、チャート名、表示するチャネル、チャート・グラフの種類、データの選択をおこないます。このグラフで表示するチャネルの選択ができるので、ひとつのボードには異なるチャネルのグラフ表示が可能。チャートのタイプは折れ線、棒グラフ、散布図など種々のフォーマットが選択できます。気温、湿度、気圧などは折れ線を、雨量は棒グラフを使用。表示する数値データはD1〜D8の中から選びます。

以上のようにAmbientはプログラムからのデータ送信、グラフ・チャート表示が非常にシンプルかつスムーズにおこなえる優れたクラウド環境です。細かいグラフ表示の変更などについてはわかりやすいチュートリアルがありますので是非ご参照ください。
https://ambidata.io/docs/

 

3.宅内のLinuxサーバへのデータ送信・保存とグラフ表示

次に自宅内のLinuxサーバへの測定データの送信蓄積、グラフ表示などの可視化手法について解説します。前項ではクラウドサービスの利用について説明しましたが、データを永続的に保存し、柔軟に表示・解析するには、自前のサーバでそれらを管理する方法にメリットが出てきます。

今回の用途で使用するような自宅のサーバですが、最新のOS環境では使用不可になった前の世代の低スペックPCにLinuxを導入し、それらをサーバとして再利用することが可能です。当方の場合はEpson社のNP-11というファンレスの小型PCと、Apple社のMac mini 2012にubuntu系のLinuxをインストールしてサーバとして活用。前者のNP-11はCPUがATOM230/1.6G、メモリ2GBと現代の最新OSでは使用不可のスペックですが、こちらには軽量LinuxのPeppermintを入れて問題なく動作しています。

 

またMac mini 2012はMac OSとUbuntuのデュアルブート構成にして利用しています。NP-11はファンレスで動作音が静かな点をメリットとしますが、内部温度が上昇すると稀に動作停止することがありました。そのためMac miniとの2台・冗長構成にしてデータの欠損を防止。これらに加えてクラウドのambientにも同じデータが送信・保存されて、3重の冗長構成になっております。

図1の構成で、Wi-SUNの受信用にはラズパイを使用していますが、ラズパイ自身もLinuxサーバなので、それをそのままデータ保存・表示用に利用することも可能ではあります。しかしラズパイはOSのシステムストレージにSDカードを使用しており、長期使用時の書き込み寿命・耐久性には不安かもしれません。また、保存容量もHD/SSDほど大きくはできません。そのためラズパイはデータ受信とその配信の機能にとどめ、保存・表示は別のLinuxサーバを利用する構成にしています。

 

4.LinuxサーバのWeb系ソフトウェア環境:XAMPPについて

ラズパイからLinuxサーバへのデータ送信ですが、受け渡しは、httpのPOSTメソッドを使用し、保存・管理にはデータベースを使用しています。その機能を使用するためLinuxサーバには以下の機能を実装しています。

  • Apache(Webサーバ)
  • MariaDB(データベース)
  • PHP(webプログラミング言語)

これらを個別にインストール・環境設定しても良いのですが、管理ソフトウェアも一緒にまとめてインストールできる便利なツール:XAMPPを利用するといいでしょう。XAMPPについての簡単な説明は以下にあります。

https://ja.wikipedia.org/wiki/XAMPP

 

XAMPPをインストールすると、上記3つに加えて、phpMyAdminという便利なデータベース管理ソフトもインストールされます。データベースに関しても、現在はMySQLの互換派生版であるMariaDBがインストールされます。こちらはMySQLと同様に使用可能です。

はじめにXAMPPのソフトウェアを以下のリンクからダウンロードします。

https://www.apachefriends.org/jp/download.html

 

XAMPPのインストール方法については以下のリンクの「XAMPP のインストール方法」に説明がありますのでそちらに従ってください。

https://www.apachefriends.org/jp/faq_linux.html

 

XAMPPは、/opt/lamppにインストールされます。この下に/htdocsというディレクトリがあり、ここがWebサービスの参照ディレクトリです。index.htmlや以降で作成するPHPのスクリプトファイルも基本的にこのディレクトリに置きます。

まず、最初にXAMPPを起動しましょう。同様にFAQサイト「XAMPP の起動方法」に解説があります。

https://www.apachefriends.org/jp/faq_linux.html
(このリンクの「XAMPP の起動方法」をクリック)

 

コマンドラインからは、

 

sudo /opt/lampp/lampp start

 

で起動します。またGUIツールも用意されていますおり、以下でウインドウが現れます。

 

cd /opt/lampp

sudo ./manager-linux.run (or manager-linux-x64.run)

 

モジュールごとの起動・停止・状態表示などがおこなえます。XAMPPが正常に動作していれば、自分の端末からはブラウザでhttp://localhost、別のPCなどではhttp://[LinuxのIPアドレス]で以下の画面が現れます。

上部のメニューからPHPinfoやphpMyAdminに直接アクセスできます。

 

5.データベースの作成

MySQL: MariaDBに気象ステーションデータを保存します。はじめにデータベースに記録するデータの構造を決めていきましょう。今回の気象ステーションで測定するパラメータとデータベースの構成を以下の表に示します。

データベース名: wisun1 ユーザー名: wisun1editor
テーブル名: wisun1_table パスワード: *****
フィールド名 データ型 内容 単位
id INT UNSIGNED ID PRIMARY KEY AUTO_INCREMENT
temp_out FLOAT 外気温
humi_out FLOAT 湿度
press FLOAT 大気圧 hPa
temp_unit FLOAT ユニット内温度
illumi FLOAT 照度 lux
battery FLOAT バッテリ電圧 V
rssi FLOAT 受信レベル 0〜255
me_time DATETIME 測定時刻 日時

 

データベース名: wisun2 ユーザー名: wisun2editor
テーブル名: wisun2_table パスワード: *****
フィールド名 データ型 内容 単位
id INT UNSIGNED ID PRIMARY KEY AUTO_INCREMENT
wdir FLOAT 風向 deg
wspeed FLOAT 風速 m/s
wgust FLOAT 突風 m/s
wgustdir FLOAT 突風風向 deg
wsp_avr2m FLOAT 風速2分平均 m/s
wdir_avr2m FLOAT 風向2分平均 deg
wgust_10m FLOAT 突風10分平均 m/s
wgdir_10m FLOAT 突風風向10分平均 deg
rain_1h FLOAT 雨量1時間 mm
rain_day FLOAT 雨量1日 mm
rssi FLOAT 受信レベル 0〜255
me_time DATETIME 測定時刻 日時

 

測定データは、wisun1とwisun2の2つのデータベースに保存。

wisun1が2分に1回定期測定する気温、湿度などのデータで、wisun2が割り込み処理でカウントしている風速・風向、雨量のデータになります。

このデータベースに、wisun1_table、wisun2_tableという名前でテーブルを作成してください。テーブルに表でまとめたデータフィールドを定義します。あとはデータベースにアクセスするユーザー名とパスワードも決めてください。これらをSQLスクリプトやphpMyAdminのGUIを使用してデータベースに設定していきます。

 

(1)データベース作成

phpMyAdminの「データベースを作成する」に名前を入れ、作成ボタンを押します。下の例はwisun1という名前で作成しました。完了すると左のツリーと右のリストに作成されたデータベース名が現れます。

(2)テーブルの作成

次に作成したデータベースにテーブルを登録します。

 

作成したwisun1を左のツリーで選択します。右上のメニュータブで「SQL」をクリック。すると選択したデータベース上でSQLスクリプトを実行することができます。

wisun1に以下のSQLスクリプトでwisun1_tableを作成してください。

 

Code-Example
1
2
3
CREATE TABLE wisun1_table(id INT UNSIGNED AUTO_INCREMENT PRIMARY
KEY, temp_out FLOAT, humi_out FLOAT, press FLOAT, temp_unit FLOAT, illumi
FLOAT, battery FLOAT, rssi FLOAT, me_time DATETIME );

 

正常に実行されれば作成したテーブル名が左のツリーに表示され、それをクリックすればテーブルの内容が確認できます。

 

この例ではテスト用のダミーデータが1つ入っています。データ作成は以下のスクリプトで実行しています。

Code-Example
1
2
3
INSERT INTO
wisun1_table(temp_out,humi_out,press,temp_unit,illumi,battery,rssi,me_time)
VALUES(5.48,69.34,1015.94,5.31,4104.17,6.17,133,'2022/3/18 10:01:37');

「SQL」タブで実行するとVALUESで書いたデータがテーブルに表示されます。

 

(3)ユーザの登録

データベースwisun1に対してオペレーションできるユーザーを登録し、パスワードを設定。ツリーでwisun1を選択し、権限のタブをクリックします。

 

左下の新規作成:「ユーザアカウントを追加する」をクリックします。

 

ユーザ名を入力し、ホスト名(ローカルを選択)、パスワードを設定してください。

wisun1の権限タブに作成したユーザーが表示されることを確認しましょう。同様にwisun2もデータベースとテーブル、ユーザーを作成します。wisun2のテーブル作成SQLスクリプトを参考まで以下に示します。

Code-Example
1
2
3
4
CREATE TABLE wisun2_table(id INT UNSIGNED AUTO_INCREMENT PRIMARY
KEY, wdir FLOAT, wspeed FLOAT, wgust FLOAT, wgustdir FLOAT, wsp_avr2m
FLOAT, wdir_avr2m FLOAT, wgust_10m FLOAT, wgdir_10m FLOAT, rain_1h
FLOAT, rain_day FLOAT, rssi FLOAT, me_time DATETIME );

 

以上でデータベースの作成が完了しました。

 

6.http/POSTとデータベースアクセス用PHPスクリプト

次にラズパイから送信されるhttp/POSTリクエストを受け取って、データベースにそのデータを保存するPHPのスクリプトについて解説します。

以下が前章で作成したデータベース:wisun1にデータを保存するPHPスクリプトです。

 

Code-Example
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<?php
  $rvmsg = $_POST["message1"];
  if (strlen($rvmsg) < 150){
    // Data読み込み
    $rsvdata = htmlspecialchars($rvmsg,ENT_QUOTES);
    // Mysql書き込み
    $pdo=new PDO('mysql:host=localhost;dbname=wisun1;charset=utf8','wisun1editor', 'password');
    $arrList = explode (",",$rsvdata);
    $sql1 = "insert into wisun1_table(temp_out,humi_out,press,temp_unit,illumi,battery,rssi,me_time)values(";
    $sql2 = $arrList[0].",".$arrList[1].",".$arrList[2].",".$arrList[4].",".$arrList[5].",".$arrList[6].",".$arrList[7].",'".$arrList[8]."')";
    $sql = $sql1.=$sql2;
 
    if ($pdo->query($sql)!== FALSE) {
      echo 'wisun1 PHP Post Success!!';
    } else {
      echo 'wisun1 PHP Post Fail';
    }
  }
?>

 

2行目の、$_POST[“message1”];はPHPのスーパーグローバル変数で、message1でリクエストされたデータが入ります。これを$rvmsgに代入しておきます。

今回使用しているLinuxサーバは宅内のプライベート・ネットワーク内に置かれ、外部からの接続は考えていません。そのためPHPスクリプトのセキュリティ対策は最小限にとどめています。対策の1つ目はリクエストメッセージの長さ制限です。3行目のif文で150以上の長さのメッセージは無視されます。もう1つは5行目の「htmlspecialchars()」で、こちらはクロスサイトスクリプティング(XSS)防止用になります。

 

PHPからのデータベースアクセスには、PDO(PHP Database Object)を使用。7行目でPDOのインスタンスを作成して、データベースwisun1に接続してください。この時前項で作ったユーザーアカウント(wisun1editor)とパスワードを指定します。

8行目でカンマ区切りになっている受信データ($rvmsg)をexplode関数で分離して配列($arrList)に代入しています。9〜11行目でこのデータをwisun1_tableに書き込むSQL文:insertに変換。13行で書き込みを行い、可否のステータスをリクエスト先に返送しています。wisun2のPHPプログラムも同様の構成ですが、リストを以下に掲載しておきます。

 

Code-Example
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<?php
  $rvmsg = $_POST["message2"];
  if (strlen($rvmsg) < 150){
    // Data読み込み
    $rsvdata = htmlspecialchars($rvmsg,ENT_QUOTES);
 
    // Mysql書き込み
    $pdo=new PDO('mysql:host=localhost;dbname=wisun2;charset=utf8','wisun2editor', 'password');
    $arrList = explode (",",$rsvdata);
    $sql1 = "insert into wisun2_table(wdir,wspeed,wgust,wgustdir,wsp_avr2m,wdir_avr2m,wgust_10m,wgdir_10m,rain_1h,rain_day,rssi,me_time)values(";
    $sql2 = $arrList[0].",".$arrList[1].",".$arrList[2].",".$arrList[3].",".$arrList[4].",".$arrList[5].",".$arrList[6].",".$arrList[7].",".$arrList[8].",".$arrList[9].",".$arrList[10].",'".$arrList[11]."')";
    $sql = $sql1.=$sql2; 
 
    if ($pdo->query($sql)!== FALSE) {
      echo 'wisun2 PHP Post Success!!';
    } else {
      echo 'wisun2 PHP Post Fail';
    }
  }
?>

 

これらのPHPスクリプトは、/opt/lampp/htdocsの下に置きます。

 

7.Linuxサーバ向けpythonプログラムの改版

前項で作成したPHPスクリプトにhttp/POSTで測定データを送るように、ラズパイ上のpythonプログラムに追加します。プログラムリストを以下に示します。

Code-Example
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Wi-SUN Rx program:Ambient/http POST
import serial
import datetime
import ambient
import requests
from time import sleep
 
ambi1 = ambient.Ambient(#####, "################")
ambi2 = ambient.Ambient(#####, "################")
con=serial.Serial('/dev/ttyUSB0',115200)
print(con.portstr)
 
send_data1 = [0.0]*7
send_data2 = [0.0]*10
 
while True:
    str_bf=con.readline()
    if str_bf !="":
        index = str_bf.find(b'Data1:')
        if index !=-1:
            dt=datetime.datetime.now()
            print(dt)
            index_end = str_bf.find(b'\x00')
            datastr = str_bf[index+6:index_end].decode('utf-8')
            rssi = str_bf[index-12:index-10].decode('utf-8')
            env_data = datastr.split(',')
             
            for i in range(7):
                send_data1[i-1] = float(env_data[i-1])
            print('Send Data1 =',send_data1)
             
            rssi_int = int(rssi,16)
            print('RSSI_int =',rssi_int)
             
            try:     
                r = ambi1.send({'created': "{:%Y-%m-%d %H:%M:%S}".format(dt) ,"d1": send_data1[0], "d2": send_data1[1], "d3": send_data1[2], "d4": send_data1[4], "d5": send_data1[5], "d6": send_data1[6], "d7": rssi_int})
                print('Ambient status:', r.status_code)
            except requests.exceptions.RequestException as e:
                print('request failed: ', e)
             
            try:
                Testmessage = datastr + "," + str(rssi_int) + "," + "{:%Y-%m-%d %H:%M:%S}".format(dt)
                response = requests.post('http://192.168.##.##/wisun1_post_mysql.php', data={'message1': Testmessage})
                print('Linux status:',response.status_code)    # HTTPのステータスコード取得
                print(response.text)    # レスポンスのHTMLを文字列で取得
            except requests.exceptions.RequestException as e:
                print('request failed: ', e)
        else:
            index = str_bf.find(b'Data2:')
            if index !=-1:
                dt=datetime.datetime.now()
                print(dt)
                index_end = str_bf.find(b'\x00')
                datastr = str_bf[index+6:index_end].decode('utf-8')
                rssi = str_bf[index-12:index-10].decode('utf-8')
                env_data2 = datastr.split(',')
                print('env_data2= ',env_data2)
                 
                for i in range(10):
                    send_data2[i-1] = float(env_data2[i-1])
                print('Send Data2 =',send_data2)
                 
                rssi_int = int(rssi,16)
                print('RSSI_int =',rssi_int)
                try:  
                    r = ambi2.send({'created': "{:%Y-%m-%d %H:%M:%S}".format(dt) , "d1": send_data2[2], "d2": send_data2[3], "d3": send_data2[4], "d4": send_data2[5], "d5": send_data2[6], "d6": send_data2[7], "d7": send_data2[8], "d8": send_data2[9]})
                    print('Ambient status:', r.status_code)
                except requests.exceptions.RequestException as e:
                    print('request failed: ', e)
                     
                try:
                    msg2 = datastr + "," + str(rssi_int) + "," + "{:%Y-%m-%d %H:%M:%S}".format(dt)
                    print('msg2= ',msg2)
                    response = requests.post('http://192.168.##.##/wisun2_post_mysql.php', data={'message2': msg2})
                    print('Linux status:',response.status_code)    # HTTPのステータスコード取得
                    print(response.text)    # レスポンスのHTMLを文字列で取得
                except requests.exceptions.RequestException as e:
                    print('request failed: ', e)
                 
con.close()

 

追加したwisun1向けの部分が43から49行目、wisun2向けが73から80行目になります。http/POSTの機能には「requests」モジュールを使用。Ambientへの送信時と同様に、例外処理:try〜exceptを使用して、通信エラーを検知・処理しましょう。

 

45行目の、requests.postで受信するLinuxサーバのIPアドレス、対象のPHPスクリプトを指定してください。data={‘message1’: msg1}で、PHPスクリプト中のスーパーグローバル変数 $_POSTに記載したname属性’message1’を付与します。responceにはhttpステータスコード(正常終了なら200)が返ってきます。

Linuxサーバでデータベースへ正常にデータが書き込まれているかどうかを、phpMyAdminの以下のテーブル画面で確認することができます。

この例ではwisun1のテーブルwisun1_tableに保存された時系列データを一覧表形式で見ることが可能です。

 

8.Grafanaを使用したグラフ表示

前章までで、自宅Linuxサーバーのデータベースに測定データが保存されるようになりました。このデータを、グラフ表示が可能なツールであるGrafanaを使って可視化する方法について解説します。

まずGrafanaのインストール・起動方法ですが、手順は以下のページに説明があります。

https://grafana.com/docs/grafana/next/setup-grafana/installation/debian/

 

こちらは、Debian、Ubuntuへのインストール手順です。GrafanaにはGrafana EnterpriseとGrafana OSSの2つのバージョンがあり、OSS版は無料で利用できます。

インストールと起動が完了したら、ブラウザでhttp://localhost:3000にアクセスすればGrafanaのログイン画面が現れます。(外部PCなどの場合はLinuxサーバのIPアドレス:3000)デフォルトのログインユーザ、パスワードはadminです。

まず、Grafanaのグラフ画面の例を以下に示します。

 

グラフを配置する画面をdashboardと言い、ここに複数のpanelを配置することができます。dashboardには名前を付けて複数管理可能です。グラフのタイプなどは非常に細かく設定、カスタマイズすることも可能。標準のグラフ表示以外もプラグインによって様々な表示形式がサポートされています。本機でも360度の風向表示はプラグインを使用して実現しています。以下、Grafanaの使用、設定方法について解説します。

 

(1)Data sourceの設定

まず初めにグラフ表示をおこなうデータの選択・設定を行います。左の歯車のアイコン「Configuration」からData sourceを選択してください。

 

新規に作成する場合は、右のAdd data sourceを押します。すると様々なデータソースのアイコンが出てくるので、SQLからMySQLを選択します。

 

ここで、使用するデータベースの情報を入力。Name:はこのData sourceの管理名です。次に「MySQL Connection」で作成したデータベースの情報を入力します。

Host:はlocalhost:3306です。MySQLのポート番号が3306と異なる場合は変更してください。データベース名と作成したユーザー名、パスワードも設定しましょう。本機の例ではwisun1,wisun1editor,(設定したパスワード)になります。

入力が終わったら画面の一番下に出る青いボタン[Save & test]を押して、問題なくデータベースに接続されればグリーンのDatabase Connection OKというメッセージが出ます。これでData sourceの設定は完了です。

 

(2)DashboardとPanelの登録

新規Dashboardを作成すると、Add a new panelというアイコンが出てきます。これを押すとpanelの設定画面に切り替わります。

 

画面左・中央の[Data source]のプルダウンメニューで、(1)で作成したデータソースの一覧が出てきます。ここではwisun1を選択しました。

以降の設定ですが、GUIから行う方法と、鉛筆マークのアイコンをクリックしてテキストエディターでSQL文を直接入力する方法があります。後者の方がわかりやすいので入力文の例を以下に示します。

Code-Example
1
2
3
4
5
6
7
SELECT
  UNIX_TIMESTAMP(me_time) AS "time",
  temp_out
FROM wisun1_table
WHERE
  $__timeFilter(me_time)
ORDER BY me_time

「データベースの作成」で示した、wisun1のデータベースで、グラフに表示する項目をSELECT文で指定します。X-軸が時間に関するパラメータで、Y-軸には外気温のデータが保存されているwisun1_tableのtemp_outを指定してください。

データベースに時間情報はme_timeという名前で保存しています。この時UNIX_TIMESTAMPを指定することによってJST(日本標準時)で正しく表示されますよ。正しく入力されていれば、グラフ面をクリックするとデータが表示されます。

後は右側に表示されているオプション設定パネルで、グラフ表示をモディファイできます。かなり細かい項目まで設定できるのですが、一例として単位の設定画面を紹介します。

 

右側画面をスクロールしていくと、「Standard options」というブロックがあります。ここの1行目に「Unit」というY-軸の単位を設定するメニューがあり、クリックすると画面のように様々なカテゴリの選択肢が現れます。例えば温度はTemperatureで、クリックすると℃や℉などを選ぶことが可能。これが非常に多義にわたって網羅されており、今回の気象ステーションで使用している単位は全てサポートされていました。例えば風速のm/sや気圧のhPaも入っています。

 

以上のようにGrafanaではdashboard上にpanelを複数登録して見やすい画面を作成できるのです。Panelのグラフの大きさや位置はマウスで自由に変更できます。作成し終わったら必ずsave dashboardで保存を忘れないようにしてください。

 

9.Grafanaプラグインを使用した風向表示

気象ステーションのデータ表示形式のうち、標準のグラフ機能で実現できないものとして風向表示があります。これは現在の風向きが東西南北どちらから来ているかを示すもので、例えば360°の表示で方角を指針で表示します。この表示をGrafanaのプラグイン「D3 Gauge」を使用して実現できるので、設定方法を説明しましょう。まず実際に作成した風向表示を以下に示します。

 

これはメーターやゲージと言われる表示形式になりますが、360°表示で一番上が0°(北)になり、ラベルが風向のテキスト(例えばNorthなど)になっています。

 

次に実際の設定方法を順に解説します。まずプラグイン「D3 Gauge」をインストールしてください。Configuration→Pluginsメニューで、“d3”等入れて検索すると下図のようにD3 Gaugeが現れます。

 

これをクリックして次画面でプラグインをインストールします。

そしてdashboardで新しいpanelを作ります。表示データの設定は前章で解説したものと同様です。

 

右・上部のvisualizationのメニューをクリックするとグラフモジュールの一覧が出てきます。そこから「D3 Gauge」を選択してください。するとパネルにデフォルトのゲージ表示が現れるので、後は以下のオプション設定をおこなっていきます。

 

左上から、Data Optionsではcurrentの現在値を選びます。その数値がゲージ内に表示されますが、小数点以下の桁数と表示のオフセット値を設定してください。360°表示にした時に目盛りとこの数値表示がぶつかるので、-40という値を入れて中央付近に移動させます。

 

左中央は最小、最大値の角度の値を入れます。これは0〜360°表示にした際に最小値0が北(North)で円の一番上になるので、180°を入力してください。最大値は360ですが、ゼロが180なので180+360=540をMax tick/needleに入力します。

左下がlimitsの設定ですが、これをONにしてMaximumに360を入れると、表示が0〜360°に変更されます。これを入れないとデフォルトの0〜100になってしまうので注意です。

 

最後は目盛り関係の設定になります。右側上は目盛り線の表示間隔です。テキストのラベルと大きな目盛り線(水色の太線)が一致するように45°に設定します。

右下が目盛りの数値をテキストに対応させるテーブルです。[+Add tick mapping]というボタンを押すと、数値とテキストの変換テーブルが現れるでしょう。ここに風向表示に必要な方角のテキストとその表示数値を入力していきます。例えばNorth(北側)の場合、対応する値は0になります。これを同様に表示したい方角の分だけテーブルを作成してください。

 

風向表示向けのオプション設定は以上ですが、このようにプラグインを利用することで、柔軟な表示形式に対応できるのがGrafanaの非常に優れたポイントです。Grafanaはこのような気象・環境系のデータ表示以外にもさまざまな用途(例えば通信、ネットワーク監視など)に使用できますのでぜひお試しいただければと思います。

 

次回、第5回は最終回ということで、Wi-SUNの伝送距離評価とシステム全体のまとめについて触れたいと思います。お楽しみに!

 

 

今回の連載の流れ

第1回:システムの概要と部品構成
第2回:ハードウエアについて
第3回:ソフトウェアと省電力手法
第4回:クラウド連携と自宅内サーバへのデータ保存・グラフ表示(今回)
第5回:Wi-SUNの伝搬距離評価とシステム全体のまとめ

 

アバター画像

測定器会社、ネットワーク機器ベンダーでシステム・エンジニアに従事。現在自作派向けの電子工作記事を各誌に掲載中。趣味は古楽器・リュートの演奏。日本リュート協会・理事

http://lute.penne.jp/thumbunder/

スマホでコントロールできるロボットを作ろう