enebular InfoMotion でオムロン環境センサー (2JCIE-BL01) を可視化してみた。

## はじめに
<A HREF="https://enebular.connpass.com/event/97263/">enebular developer Meetuup Vol.3</A> で「enebular InfoMotion を使ってオムロン環境センサー (2JCIE-BL01) を可視化してみた。(仮)」というタイトルで LT する内容の記事です。

![スクリーンショット 2018-09-23 20.26.56.png](https://qiita-image-store.s3.amazonaws.com/0/72479/a3edbdaf-8843-0dbd-b545-51dd5d8e980b.png)

Raspberry Piとオムロン環境センサーを使用して測定したデータを可視化する事例やハンズオンはいくつかあるが、データを Firebase へアップロードして、Firebase のデータを enebular InfoMotion で可視化してみた。

- Ambient で可視化 (クラウド)
 - <A HREF="https://ambidata.io/samples/temphumid/ble-gw-omron/">オムロン環境センサーからBLE経由でデーターをクラウドに送る</A>
- Grafana で可視化 (ローカル)
 - <A HREF="https://qiita.com/t-funaki/items/61ff1487088f3f01d9a7">WxBeacon2をRaspberry Pi 3につないで部屋の状態を可視化</A>
 - <A HREF="https://poohkids.party/raspberrypi/raspi_wxbeaconsetting/">ラズパイでウェザービーコンをハックしてみる～環境作成～</A>
 - <A HREF="https://camp.isaax.io/hc/ja/articles/360001288647">Raspberry Pi でオムロン環境センサのログをGrafanaで可視化してみる</A>

## 事前準備
- Raspberry Pi
 - Raspberry Pi 3 Model B
 - Raspberry Pi 3 Model B+
 - Raspberry Pi Zero W
 - Raspberry Pi 2 Model B + Bluetooth 4.0 USBアダプタ(CSR V4.0、BT-Micro4 など)

　OS は <A HREF="https://www.raspberrypi.org/downloads/raspbian/">"RASPBIAN STRETCH" Version:June 2018 (Release date:2018-06-27)</A> を使用。

- <A HREF="https://www.omron.co.jp/ecb/product-info/sensor/iot-sensor/environmental-sensor">オムロン環境センサー (2JCIE-BL01)</A>
センサーの設定をビーコンモードに変更する。<A HREF="https://qiita.com/komde/items/7209b36159da69ae79d2">(参考)</A>

- <A HREF="https://docs.enebular.com/ja/GetStarted/Introduction.html">enebular 新規登録</A>
"enebular へのサインアップ" 〜 "Project の作成" を実施。

- <A HREF="https://docs.enebular.com/ja/infomotion/datasource/firebase/Setup.html">Firebase のセットアップ</A>

- <A HREF="https://docs.enebular.com/ja/InfoMotion/DataSource/Firebase/CreateDataSource.html">Firebase DataSource の作成</A>
"Firebase DataSource の作成" 〜 "DataBase のセットアップ" を実施。

## 構成
![スクリーンショット 2018-09-22 22.38.49.png](https://qiita-image-store.s3.amazonaws.com/0/72479/871e2ad8-a308-bc29-7a8e-0e1e7e312a70.png)

## 手順
### Raspberry Pi
#### 5分毎にデータをFirebaseへアップロードする場合 (<A HREF="https://github.com/AmbientDataInc/EnvSensorBleGw/blob/master/src/gw_RPi/env2ambientBS.py">Ambient のツール</A>を流用しています)

```bash:
# ライブラリをインストール
$ sudo apt-get install libglib2.0-dev
$ sudo pip3 install bluepy
$ sudo pip3 install pyrebase
# プログラムを実行
$ sudo nohup python3 omron_firebase.py < /dev/null &
```

omron_firebase.py プログラムは <A HREF="https://github.com/kitazaki/omron_firebase/blob/master/omron_firebase.py">こちら</A> 。

| 項目      |                 | 設定値 |
|----------|-----------------|--------|
| config   | apiKey          | Firebase の apiKey を設定 (必須) |
|          | authDomain      | Firebase の authDomain を設定 (必須) |
|          | databaseURL     | Firebase の databaseURL を設定 (必須) |
|          | storageBucket   | Firebase の storageBucket を設定 (必須) |
| email    |                 | Firebase の認証用 ID (必須) |
| password |                 | Firebase の認証用 Password (必須) |
| address  |                 | オムロン環境センサーの BT アドレス (任意) |

```python:omron_firebase.py
# -*- coding: utf-8 -*-
#

from bluepy.btle import Peripheral, DefaultDelegate, Scanner, BTLEException, UUID
import bluepy.btle
import sys
import struct
from datetime import datetime
import argparse
import pyrebase

config = {
    "apiKey": "",
    "authDomain": "",
    "databaseURL": "",
    "storageBucket": ""
}
email = ''
password = ''
companyID = 'd502'  # OMRON company ID (Bluetooth SIG.)
address = 'FFA511nnnnnn'  # BT ADDRESS (replace nnnnnn to own device)

Debugging = False
def DBG(*args):
    if Debugging:
        msg = " ".join([str(a) for a in args])
        print(msg)
        sys.stdout.flush()

Verbose = True
def MSG(*args):
    if Verbose:
        msg = " ".join([str(a) for a in args])
        print(msg)
        sys.stdout.flush()

def send2firebase(dataRow):
    (temp, humid, light, uv, press, noise, accelX, accelY, accelZ, batt) = struct.unpack('<hhhhhhhhhB', bytes.fromhex(dataRow))
    MSG(temp/100, humid/100, light, uv/100, press/10, noise/100, (batt+100)/100)

    firebase = pyrebase.initialize_app(config)
    db = firebase.database()
    auth = firebase.auth()
    user = auth.sign_in_with_email_and_password(email, password)

    now = datetime.now()
    dt_s = now.strftime("%s") + str(int(now.microsecond/1000))
    data = {
        "timestamp": int(dt_s),
        "value": {
          "created": int(dt_s),
          "address": address,
          "battery": float((batt+100)/100),
          "temperature": float(temp/100),
          "humidity": float(humid/100),
          "light": float(light),
          "uv": float(uv/100),
          "pressure": float(press/10),
          "noise": float(noise/100)
        }
    }
    MSG(data)
    results = db.child("omron").child(address).push(data, user['idToken'])

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)
        self.lastseq = None
        self.lasttime = datetime.fromtimestamp(0)

    def handleDiscovery(self, dev, isNewDev, isNewData):
            if isNewDev or isNewData:
                for (adtype, desc, value) in dev.getScanData():
                    if desc == 'Manufacturer' and value[0:4] == companyID:
                        delta = datetime.now() - self.lasttime
                        if value[4:6] != self.lastseq and delta.total_seconds() > 11:
                            self.lastseq = value[4:6]
                            self.lasttime = datetime.now()
                            send2firebase(value[6:])

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-d',action='store_true', help='debug msg on')

    args = parser.parse_args(sys.argv[1:])

    global Debugging
    Debugging = args.d
    bluepy.btle.Debugging = args.d

    scanner = Scanner().withDelegate(ScanDelegate())
    while True:
        try:
            scanner.scan(5.0)
        except BTLEException:
            MSG('BTLE Exception while scannning.')

if __name__ == "__main__":
    main()
```

#### 一括でCSVファイルのデータをFirebaseへアップロードする場合 (<A HREF="https://github.com/OmronMicroDevices/envsensor-observer-py">オムロンのサンプルプログラム</A>が出力するCSVファイルを使用)

```bash:
# ライブラリをインストール
$ sudo apt-get install python-bluez
$ sudo pip3 install pyrebase
# プログラムを実行
$ python3 omron_csv_firebase.py ./log/env_sensor_log.csv
```

omron_csv_firebase.py プログラムは <A HREF="https://github.com/kitazaki/omron_firebase/blob/master/omron_csv_firebase.py">こちら</A> 。

| 項目      |                 | 設定値 |
|----------|-----------------|--------|
| config   | apiKey          | Firebase の apiKey を設定 (必須) |
|          | authDomain      | Firebase の authDomain を設定 (必須) |
|          | databaseURL     | Firebase の databaseURL を設定 (必須) |
|          | storageBucket   | Firebase の storageBucket を設定 (必須) |
| email    |                 | Firebase の認証用 ID (必須) |
| password |                 | Firebase の認証用 Password (必須) |

```python:omron_csv_firebase.py
import sys
import csv
from datetime import datetime
import pyrebase

config = {
    "apiKey": "",
    "authDomain": "",
    "databaseURL": "",
    "storageBucket": ""
}
email = ''
password = ''

arguments = sys.argv

if len(arguments) == 1:
  print ("Usage: python omron_csv_firebase.py CSV_FILE")
  sys.exit()

csvfile = arguments[1]

def main():
    firebase = pyrebase.initialize_app(config)
    db = firebase.database()
    auth = firebase.auth()
    user = auth.sign_in_with_email_and_password(email, password)

    ##  CSV_FILE format
    ##  Time,Gateway,Address,Type,RSSI (dBm),Distance (m),Sequence No.,Battery (mV),Temperature (degC),Humidity (%%RH),Light (lx),UV Index,Pressure (hPa),Noise (dB),Discomfort Index,Heat Stroke Risk,Accel.X (mg),Accel.Y (mg),Accel.Z (mg)
    ##  2018-09-17 00:04:53.831591,raspberrypi,FFA511nnnnnn,EP,-66,1.77188476275,69,2830.0,22.68,82.6,0,0.01,996.3,34.64,71.4,23.37,0.0,0.0,0.0

    f = open(csvfile, 'r')
    reader = csv.reader(f)

    for row in reader:
      if row[0] == 'Time':
        continue
      dt = datetime.strptime(row[0][0:19], '%Y-%m-%d %H:%M:%S')
      dt_s = dt.strftime('%s') + row[0][20:23]

      data = {
        "timestamp": int(dt_s),
        "value": {
          "created": int(dt_s),
          "address": row[2],
          "rssi": float(row[4]),
          "distance": float(row[5]),
          "battery": float(row[7]),
          "temperature": float(row[8]),
          "humidity": float(row[9]),
          "light": float(row[10]),
          "uv": float(row[11]),
          "pressure": float(row[12]),
          "noise": float(row[13]),
          "di": float(row[14]),
          "heat": float(row[15])
        }
      }
##      print (data)
      results = db.child("omron").child(row[2]).push(data, user['idToken'])

    f.close()
    return
 
if __name__ == "__main__":
    main()
```


<B><FONT COLOR=#FF0000>※ pyrebase をインストールした後、プログラムを実行すると「ImportError: cannot import name 'opentype' 」エラーが出力されるので、google-auth-oauthlib をアップグレードする。</FONT></B>

```bash:
$ sudo pip3 install --upgrade google-auth-oauthlib
```

### Firebase
omron → BT アドレス (FFA511nnnnnn) 配下にデータが格納される。データ構造は以下。

| 第一階層  | 第二階層        | 値 |
|-----------|--------------|--------|
| timestamp |              | タイムスタンプ Unixtime (ミリ秒) |
| value     | address      | オムロン環境センサーの BT アドレス |
|           | battery      | バッテリー電圧 (mV) |
|           | created      | タイムスタンプ Unixtime (ミリ秒) |
|           | di           | 不快指数 |
|           | distance     | 距離 (m) |
|           | heat         | 熱中症警戒度 |
|           | humidity     | 湿度 (%) |
|           | light        | 照度 (Lm) |
|           | noise        | 騒音 (dBm) |
|           | pressure     | 気圧 (hPa) |
|           | rssi         | 電波強度 (dBm) |
|           | temperature  | 気温 (℃) |
|           | uv           | UV Index |

![Firebase_2.png](https://qiita-image-store.s3.amazonaws.com/0/72479/e49866df-2eda-d566-9ab4-bc14571660db.png)

### InfoType
InfoMotion 作成の前に InfoType 作成を実施する必要があるが、InfoType 作成には InfoMotion Tool を導入する必要があるため、今回はサンプルで用意されている <A HREF="https://docs.enebular.com/ja/_asset/sample/linechart.zip">linechart</A> をダウンロードして使用することにした。
(参考) https://docs.enebular.com/ja/infomotion/infomotiontool
(参考) https://docs.enebular.com/ja/infomotion/SampleInfoTypes.html

![enebular_linechart.png](https://qiita-image-store.s3.amazonaws.com/0/72479/d1874d38-79ab-d18d-dc42-6507f245beb7.png)

### InfoMotion
InfoType と DataSource を指定して InfoMotion ダッシュボードを作成する。(<A HREF="https://docs.enebular.com/ja/infomotion/createinfomotion">参考</A>)

TYPE に linechart を指定する。
DATASOURCE に Firebase DataSource を指定する。
Label に address データキーを指定する。
Value に 上記の第二階層のデータキーを指定する。(気温であれば temperature を指定する。)

![enebular_add_graph_3.png](https://qiita-image-store.s3.amazonaws.com/0/72479/f2e8882c-299e-eae2-b989-e913af6121d3.png)

![enebular_add_graph_2.png](https://qiita-image-store.s3.amazonaws.com/0/72479/ff78e9a6-3c91-07fa-7683-5da7f2bf44a8.png)

## 結果
![enebular.png](https://qiita-image-store.s3.amazonaws.com/0/72479/33a315f3-930d-5972-0122-cb85a6c9555f.png)