部室に温度センサーとかつけて監視する

こんにちは, KMC2回生のwass80です。

この記事はKMC Advent Calendar 2016 21日目の記事です.
昨日の記事はtronくんの「Neutron 買ってみたのは いいけれど……」でした。記事タイトルが575ですね。
明日の記事はbase64くんの「いい感じのメドレーを自動生成したい」です。自分がもやりたかったことやられたので, 後でいい感じコミットぜったいしたる。

今回はRaspberryPi3 ModelBを買ったので, 使って部室の監視をしたいと思います。

概略図

いい感じ図

Raspberryにつながった温度センサーの値をfluentdでinfluxDBに送りつける。
grafanaでグラフを表示。

用意するもの

RasberryPi3はArduinoと違って, オス-メスのジャンプワイヤーが必要になるので注意しましょう。買い忘れました。

RasberryPi3

センサーをつなげて値を読み取りましょう。

セットアップ

Raspberry Pi 3を買ってMacを使ってWiFi接続とSSHの接続するまで

SDカードにRaspbianを焼いてRasberryPi3に差し込みます。
USBで電源を供給すれば起動します。HDMIで画面を見ます。
初期パスワードはuser:pi/pass:raspberryです。速やかに変更しましょう。
sshがデフォルトで無効になっているので有効化する必要があります。
Wifiでつながると便利なのでその設定もします。

温度センサー

第39回「ラズベリーパイで温度・湿度・気圧をまとめて取得!AE-BME280でIC2通信」

このセンサーにははんだ付けが必要です。
I²C方式シリアル通信をします。

上の記事通りに接続したら, I²Cを有効化します。
Github: SWITCHSCIENCE/BME280のコードを借りて(少し改変して)データを表示してみます。

t, p, h = readData()
print("気温:%f\t大気圧:%f\t湿度:%f" % (t, p, h))
pi@raspberrypi:~ $ python bme280/bme280.py 
気温:19.729584     大気圧:1005.779496   湿度:56.260745

動いてそうです。

このデータを10秒おきに次のfluentdに送りつけましょう。

fluent/fluent-logger-python

from fluent import sender
import time

logger = sender.FluentSender('raspi', host='sharp')

if __name__ == '__main__':
        while True:
                t, p, h = readData()
                print("気温:%f\t大気圧:%f\t湿度:%f" % (t, p, h))
                logger.emit('climate', {'temperature': t, 'pressure': p, 'humidity': h})
                time.sleep(10)

systemd用のunitファイルを書きましょう。

#/etc/systemd/system/bme280.service
[Unit]
Description = bme280 climate sensor

[Service]
ExecStart = /home/pi/bme280/bme280.py
Restart = always
Type = simple

[Install]
WantedBy = multi-user.targe
$ sudo systemctl enable bme280
$ sudo systemctl start bme280

fluentd

ログの受け渡しをするサービス。
fluentdは Input → Filter → Output の経路でJSONのログ(event)を流します。

例えば, 以下のことができます。

  • あるログファイルの書き込みを感知して(Input)
  • それがErrorのログならば(Filter)
  • Slackへ通知する(Output)

今回は以下の構成になります。

  • TCPでログを受け取る(Input)
  • そのすべてを(Filterなし)
  • influxDBに送りつける(Output)

InputとFilterとOutputを結びつけるのは, ログに紐づくタグです。
Inputでログにタグを付け, 対応するFilter, Outputが動きます。

fluentdはすでに部室で動いていたので間借りします。

fluentdはデフォルトでTCPで受け取る以下のForward Inputが動いています。

#不要なコード
<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>

RaspberryPIからraspi.climateタグをつけてfluentdに送っています。

#前述抜粋
logger = sender.FluentSender('raspi', host='sharp') #sharpはfluentdのあるサーバ名
logger.emit('climate', {'temperature': t, 'pressure': p, 'humidity': h})

なので以下の設定を追加します。

<match raspi.climate> #このタグであれば
  @type copy #次のOutputそれぞれに受け渡す
  <store> #ファイルに保存
    @type file
    path /var/log/td-agent/raspi/climate.log
  </store>
  <store> #influxdbに送りつける
    @type influxdb
    host  192.168.220.31
    port  8086
    dbname climate
    user  root
    password  root
    use_ssl false
    time_precision s
  </store>
</match>

# matchは上からマッチし, マッチしたものがあればそれ以上マッチしない。
<match raspi.**> #上にマッチしなければ, 別のファイルに保存。
  @type file
  path /var/log/td-agent/raspi/raspi.log
</match>

今回はinfluxDBに送るついでにファイルにも保存していますが, DBを真面目に運用するなら必要ないでしょう。

influxDB

field/tagキーに値を時系列で突っ込んで行くデータベース。

Key Concepts

「性別(∋男,女)」のように値の種類(=カーディナリティ)が少ないものはtagキー。
「気温=実数」のようにカーディナリティが高いものはfieldキーに指定します。

influxDBの準備にはdocker-composeを用いました。

参考: nicolargo/docker-influxdb-grafana

# docker-compose.yml
version: '2'
services:
  influxdb:
    image: influxdb:latest
    ports:
      - "8083:8083"
      - "8086:8086"
    env_file:
      - 'env.influxdb'
    volumes:
      - influxdb-storage:/var/lib/influxdb
  grafana:
    image: grafana/grafana:latest
    ports:
      - "13000:3000"
    links:
      - influxdb
    volumes:
      - grafana-storage:/var/lib/grafana
    environment:
      - GF_SERVER_ROOT_URL=%(protocol)s://example.jp/~wass80/app/grafana
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
volumes:
  influxdb-storage:
    driver: local
  grafana-storage:
    driver: local

起動する。

$ docker-compose up -d

InfluxDB と fluentd を組み合わせを試してみた

データベースを作ればデータを受け取る準備が完了します。
influxDBのWebインターフェースはdeprecatedのようなので, 今回はCLIを用いました。

$ docker ps
CONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS              PORTS                                                                 NAMES
791734f2cf3a        grafana/grafana:latest               "/run.sh"                8 hours ago         Up 8 hours          0.0.0.0:13000->3000/tcp                                               dockerinfluxdbgrafana_grafana_1
d39bac4136e8        influxdb:latest                      "/entrypoint.sh influ"   8 hours ago         Up 8 hours          0.0.0.0:8083->8083/tcp, 0.0.0.0:8086->8086/tcp                        dockerinfluxdbgrafana_influxdb_1

$ docker exec -it d39 influx
Visit https://enterprise.influxdata.com to register for updates, InfluxDB server management, and monitoring.
Connected to http://localhost:8086 version 1.1.1
InfluxDB shell version: 1.1.1
> CREATE DATABASE climate
> SHOW databases
name: databases
name
----
_internal
climate
#fluentdの設定 前述抜粋
  <store> #influxdbに送りつける
    @type influxdb
    host  192.168.220.31 # influxDBの動くサーバ
    port  8086
    dbname climate #データベース名
    user  root
    password  root
    use_ssl false
    time_precision s
  </store>

これで{'temperature': t, 'pressure': p, 'humidity': h}というfleid:値がinfluxdbに流れます。

Grafana

influxDBの内容をめっちゃかっこよく表示してくれるいい子。

先程のdocker-composeで一緒に起動していました。
今回はBasic認証がすでにかかっているところで動かすため, Grafanaの認証を無効化しています。
リバースプロキシ用の設定を環境変数に追加しています。

Grafana: Configuration

# docker-compose.yml (前述抜粋)
  grafana:
    image: grafana/grafana:latest
    ports:
      - "13000:3000"
    links:
      - influxdb
    volumes:
      - grafana-storage:/var/lib/grafana
    environment:
      - GF_SERVER_ROOT_URL=%(protocol)s://example.jp/~wass80/app/grafana
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin

influxDBを登録しましょう。
GUIで設定できます。

あとはめっちゃいい感じGUIでグラフの設定をします。

いいですね。

できました。

凡例の色付き横線を押すと色の変更と軸の左右の変更ができます。

他にも色々センサーを買いましたが, RaspberryPIがアナログ入出力が出来ないことを知りませんでした。A-D変換を買ってきます。
明日の記事をお楽しみに。