Raspberry Pi + 2JCIE-BU01 + Grafanaでデータ取得・可視化してみる
今回使用したもの
・Raspberry Pi4 ModelB 4GB
ラズパイ本体。
・OMRON 2JCIE-BU01
今回使用したセンサー。
取得できるのは、温度、湿度、照度、気圧、騒音、3軸加速度、eTVOC、不快指数、熱中症警戒度、振動情報、と多くのデータが取得可能です(少し値段が張りますが)。
ラズパイのセンサーは、ググれば、基盤に接続するものがよく見つかります。これはUSB接続で使用できるので、お手軽に作れるのが良いところです。
・Grafana
OSSの可視化ツールです。
Grafana自体は、Web UIでメトリックス情報を可視化するだけの機能になります(だけど、見た目は良さそう)。
・InfluxDB
時系列データの蓄積、分析に特化された、OSSのDBです。今回はラズパイ1台だけで、それほど大量のデータは想定してませんが、IoTからのデータの扱いには向くものだそうで、試しに使用してみます。
今回の環境と詳しいバージョンは、以下の通りです。
- Raspberry Pi4 ModelB 4GB (Raspberry Pi OS bullseye)
- OMRON 2JCIE-BU01(センサー)
- Grafana v10.0.1
- InfluxDB v2.7.1
- Python 3.9.2
本当はGrafana、InfluxDBは、クラウドなどの安定した環境のサーバーに立て、ラズパイ自体はエッジの機能として使用するのが良いですが、今回は機材の関係でラズパイに全部入れてしまいます。
構築
ラズパイのセットアップ(NW周りの設定とか)は完了している前提とします。
2JCIE-BU01接続
公式のGitHubに、Pythonのサンプルあり、その説明書を参考にします。
動作確認も同時にしてみます。
適当なディレクトリを作成し、git clone
、
$ mkdir omron_sensor
$ cd omron_sensor
$ git clone https://github.com/omron-devhub/2jciebu-usb-raspberrypi.git
これで、センサーが認識され、
$ sudo modprobe ftdi_sio
$ sudo chmod 777 /sys/bus/usb-serial/drivers/ftdi_sio/new_id
$ sudo echo 0590 00d4 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id
root権限で実行してみます(昇格が必要)。
$ sudo python3 sample_2jciebu.py
できました。
Time measured:2023/06/25 10:57:18
Temperature:26.15
(略)
なお、ラズパイ本体が熱を持つため、センサーとの接続にはUSB延長ケーブルも使いました。壁面に、テープで固定しています。
InfluxDB構築
公式ドキュメントがちょっとややこしく、以下のページに、セットアップスクリプトが掲載されています。「Get InfluxDB構築」をクリックし、
バージョンと適切なプラットフォームを選択すると、
スクリプトが表示されるので、後はその通りに実行。
自動起動させたいので、
sudo systemctl enable influxdb
sudo systemctl start influxdb
とし、サービスは立ち上げておきます。
ブラウザでポート8086にアクセスし、Web UIが表示されたらOK。
http://[ip address]:8086/
初回のみユーザー情報とUser Name、パスワード、Organization Name、Bucketの入力が促されますので、適切に入力しておきます。
BucketはInfluxDB独自の仕組みというか概念だと思いますが、おそらく、(Oracleで言えば)Databaseに近い概念だと思います。
Grafana構築
公式ドキュメントの通りに実行するだけです。
インストール。
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install -y grafana
InfluxDBと同じように、自動起動、サービス立ち上げ。
sudo systemctl enable grafana-server
sudo systemctl start grafana-server
デフォルトポートが3000で、以下にアクセスすると、ログイン画面がでてきます。
http://[ip address]:3000
admin/adminでログインし、初回のみパスワード変更が求められます。
GrafanaとInfluxDBの連携設定は、データを書き込むPythonコードができた後にするとします。
Pythonコード
get_dataディレクトリに以下のPythonコードを作成します。
- get_2jciebu_data.py(センサーからデータを取得する関数)
- write_sensor_data.py(本体:IndluxDBにデータを書込み)
前者のコードはOMRON公式サンプルを、今回用に、ライブラリ(のようなもの)として改変したものです。なお、公式リポジトリは、以下。
・get_2jciebu_data.py
センサーからデータを取得し、ただ返すだけの役割で、後述するwrite_sensor_data.pyから呼び出されます。
改変点としては、LEDを光らせるコードを削除し(必要ないし)、データを数値型にし、また、辞書型にして返すものにしています。
また、日時情報をretval["time_measured"] = datetime.utcnow()
に変えた理由は、InfluxDBにはUTCの日時情報を渡す必要があるためです。
後は、ほぼそのまま流用。
データ取得の間隔は、とりあえず、10秒毎にしています。
なお、事前にinfluxdb-clientを入れておきます(pip install influxdb-client
)。
import serial
import time
from datetime import datetime
"""
以下のOMRON公式コードを改変
https://github.com/omron-devhub/2jciebu-usb-raspberrypi
"""
# LED display rule. Normal Off.
DISPLAY_RULE_NORMALLY_OFF = 0
# LED display rule. Normal On.
DISPLAY_RULE_NORMALLY_ON = 1
def s16(value):
return -(value & 0x8000) | (value & 0x7fff)
def calc_crc(buf, length):
"""
CRC-16 calculation.
"""
crc = 0xFFFF
for i in range(length):
crc = crc ^ buf[i]
for i in range(8):
carrayFlag = crc & 1
crc = crc >> 1
if (carrayFlag == 1):
crc = crc ^ 0xA001
crcH = crc >> 8
crcL = crc & 0x00FF
return (bytearray([crcL, crcH]))
def get_data() -> dict:
"""
return measured latest value.
"""
ser = serial.Serial("/dev/ttyUSB0", 115200, serial.EIGHTBITS, serial.PARITY_NONE)
# Get Latest data Long.
command = bytearray([0x52, 0x42, 0x05, 0x00, 0x01, 0x21, 0x50])
command = command + calc_crc(command, len(command))
tmp = ser.write(command)
time.sleep(0.1)
data = ser.read(ser.inWaiting())
retval = {}
retval["time_measured"] = datetime.utcnow()
retval["temperature"] = ( s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
retval["relative_humidity"] = (int(hex(data[11]) + '{:02x}'.format(data[10], 'x'), 16) / 100)
retval["ambient_light"] = (int(hex(data[13]) + '{:02x}'.format(data[12], 'x'), 16))
retval["barometric_pressure"] = (int(hex(data[17]) + '{:02x}'.format(data[16], 'x')
+ '{:02x}'.format(data[15], 'x') + '{:02x}'.format(data[14], 'x'), 16) / 1000)
retval["sound_noise"] = (int(hex(data[19]) + '{:02x}'.format(data[18], 'x'), 16) / 100)
retval["eTVOC"] = (int(hex(data[21]) + '{:02x}'.format(data[20], 'x'), 16))
retval["eCO2"] = (int(hex(data[23]) + '{:02x}'.format(data[22], 'x'), 16))
retval["discomfort_index"] = (int(hex(data[25]) + '{:02x}'.format(data[24], 'x'), 16) / 100)
retval["heat_stroke"] = (s16(int(hex(data[27]) + '{:02x}'.format(data[26], 'x'), 16)) / 100)
retval["vibration_information"] = (int(hex(data[28]), 16))
retval["si_value"] = (int(hex(data[30]) + '{:02x}'.format(data[29], 'x'), 16) / 10)
retval["pga"] = (int(hex(data[32]) + '{:02x}'.format(data[31], 'x'), 16) / 10)
retval["seismic_intensity"] = (int(hex(data[34]) + '{:02x}'.format(data[33], 'x'), 16) / 1000)
return retval
・write_sensor_data.py
本体部分。ループさせて、強制的に終了されるまで実行し続けます。
トークンは、InfluxDBのWeb UI画面から取得可能です。
import get_2jciebu_data
import influxdb_client, time, sys
from influxdb_client import Point
from influxdb_client.client.write_api import SYNCHRONOUS
token = "[トークン]"
org = "[Organization Name]"
url = "http://localhost:8086"
bucket="[bucket name]"
if __name__ == '__main__':
write_client = influxdb_client.InfluxDBClient(url=url, token=token, org=org)
write_api = write_client.write_api(write_options=SYNCHRONOUS)
while True:
data = get_2jciebu_data.get_data()
point = (
Point("2jciebu")
.tag("location", "home")
.time(time=data["time_measured"])
.field("temperature", data["temperature"])
.field("relative_humidity", data["relative_humidity"])
.field("ambient_light", data["ambient_light"])
.field("barometric_pressure", data["barometric_pressure"])
.field("sound_noise", data["sound_noise"])
.field("eTVOC", data["eTVOC"])
.field("eCO2", data["eCO2"])
.field("discomfort_index", data["discomfort_index"])
.field("heat_stroke", data["heat_stroke"])
.field("vibration_information", data["vibration_information"])
.field("si_value", data["si_value"])
.field("pga", data["pga"])
.field("seismic_intensity", data["seismic_intensity"])
)
write_api.write(bucket=bucket, org="Home", record=point)
time.sleep(10) # separate points by 10 secon
バックグラウンドで実行しておきたいので、以下のコマンドで実行。
sudo nohup python write_sensor_data.py > nohup.log &
InfluxDBのWeb UI画面に入り、Data Explorerから、データを確認できます。
InfluxDBのWeb画面でもデータは確認できそうですが、現状、ログインしないと確認できないのと(Grafanaだとログインなしの閲覧が可能)、ダッシュボードの機能としてはGrafanaのほうが良さそうかもです。
Grafanaに連携
Grafanaにログインし、Add your first data sourceから、
InfluxDBを選択
まだ、ベータ機能ということですが、Query Languageで「Flux」を選択
あとは、必要な情報を入力し、「Save & Test」。
Grafanaで表示
ダッシュボードにグラフを追加する時のクエリは、
InfluxDBのData Explorerの、Script Editorから取得できます。
ダッシュボードに追加すると、こんな感じ。
とりあえず、基本的な部分は一通りはできたでしょうか。
さらに、tailscaleも使って、遠隔の場所をモニタリングすることもできそうです。
以上。
Discussion