Armoris日記 ラズパイと BME280 で温度・湿度・気圧を測って グラフ化して表示する編 [その 2]

このブログは、N 高等学校と VRChat の世界からやってきた株式会社 Armoris アルバイトの Shadero が書いています。
あるもりすぶろぐの内容は個人の意見です。

こんなことがやりたい!

  1. 温度・湿度・気圧を計測する
  2. 計測した値をデータベースに保存する
  3. グラフ化して表示する

本記事は[その 1]の続きです。
[その 2]ではグラフ化する前準備として、計測した値をデータベースに保存するところを行います。

環境

Type Name
Machine Raspberry Pi 3 Model B+
OS Raspbian 10 buster
Temperature, humidity & pressure sensor BME280

使用ソフトウェア

Name Version
Golang 1.15.7
InfluxDB 1.8.3

今回はデータベースに、時系列データに特化した InfluxDB を使用しました。
一般に、時間に紐付いた値を入れたい場合は InfluxDB や GridDB などの時系列データベースを使用すると高速化が見込めると思います。

制作手順

データベースを構築する

ラズパイのストレージに使用される SD カードは、書き込み耐性が低いため、長期運用する場合データベースの構築先はラズパイでない方が良いのですが、今回は構築を優先してラズパイ上に構築します。

InfluxDB をインストールする

InfluxDB は標準では登録されていないのでリポジトリを追加してインストールします。

  1. $ wget -qO- https://repos.influxdata.com/influxdb.key | sudo apt-key add -
  2. $ echo "deb https://repos.influxdata.com/debian buster stable" | sudo tee /etc/apt/sources.list.d/influxdb.list
  3. $ sudo apt-get update && sudo apt-get install influxdb

InfluxDB を起動及び自動起動させる

インストールしただけでは起動していないので起動させます。
また、このままだとラズパイを再起動した際に自動で起動してくれないので自動起動をさせる設定をします。

  1. $ sudo systemctl unmask influxdb.service
  2. $ sudo systemctl start influxdb.service
  3. $ sudo systemctl enable influxdb.service

データベースを作成する

値を保存する用のデータベースを作成します。

  1. Influx へ接続します。
    $ influx

  2. 今回は iot01 という名前のデータベースを作ります。
    CREATE DATABASE iot01

  3. データーベース一覧を見ます。
    iot01 という名前の名前のデータベースがあったら成功です。
    SHOW DATABASES

f:id:Armoris:20210226160452p:plain

ユーザーを作成する

後述するユーザー認証を有効にすると、他ユーザーの権限を変更したり、新しくデーターベースを作る際に管理者権限を持ったユーザーでないと行えないため、管理者ユーザーを作成します。
また、データベースへのデータ投入のためのアプリケーション用にユーザーを作成します。

  1. Influx へ接続します。
    $ influx

  2. 管理者権限を持った root という名前のユーザーを作成します。
    CREATE USER root WITH PASSWORD 'ここにパスワードを書く' WITH ALL PRIVILEGES

  3. データーベースに値を書き込むソフトが使う logger という名前のユーザーを作成します。
    CREATE USER logger WITH PASSWORD 'ここにパスワードを書く'

  4. このままだと何も出来ないので logger ユーザーに書き込み権限を与えます。
    GRANT WRITE ON iot01 TO logger

  5. ユーザー一覧を見ます。
    root と logger という名前のユーザーがあったら成功です。
    SHOW USERS

f:id:Armoris:20210226160512p:plain

ユーザー認証を有効にする

デフォルトではユーザー認証が無効になっており、このままだとどのクライアントからでもユーザー権限の編集やデーターベースの削除などを行えてしまうためユーザー認証を有効にします。

  1. InfluxDB のコンフィグファイルを編集します。
    $ sudo vim /etc/influxdb/influxdb.conf

  2. 260 行付近に下記の画像のようにauth-enabled = trueを挿入します。
    f:id:Armoris:20210226160542p:plain

BME280 のセンサーからの値をデーターベースに書き込むソフトを作り実行する

BME280 のセンサーからの値をデーターベースに書き込むソフトのコードを書きます。
$ vi iot-sensor-logger.go

package main

import (
    "context"
    "fmt"
    "time"

    influxdb2 "github.com/influxdata/influxdb-client-go/v2"
    "github.com/influxdata/influxdb-client-go/v2/api"
    "github.com/quhar/bme280"
    "golang.org/x/exp/io/i2c"
)

func initBme280() (*bme280.BME280, error) {
    d, err := i2c.Open(&i2c.Devfs{Dev: "/dev/i2c-1"}, 0x76) // 0x76は今回使用するBME280のI2Cアドレス
    if err != nil {
        return nil, err
    }

    b := bme280.New(d)
    err = b.Init()
    return b, err
}

func recordSensorValues(b *bme280.BME280, w api.WriteAPIBlocking, measurement string) (err error) {
    t, p, h, err := b.EnvData()
    if err != nil {
        return
    }
    point := influxdb2.NewPoint(
        measurement,
        map[string]string{},
        map[string]interface{}{
            "temperature": t,
            "humidity":    h,
            "pressure":    p,
        },
        time.Now(),
    )
    err = w.WritePoint(context.Background(), point)
    if err != nil {
        return
    }
    fmt.Printf("Temp: %.2fC, Hum: %.2f%%, Press: %.2fhpa\n", t, h, p)
    return
}

func main() {
    const influxURL = "http://localhost:8086"      // InfluxDB APIのURL
    const userName = "logger"                      // influxDBのUser名
    const password = "(loggerユーザーのパスワード)"  // influxDBのUserのPassword
    const db = "iot01"                             // influxDBのDatabase
    const measurement = "sensors"                  // influxDBのMeasurement(存在しない場合は実行時に生成される)
    const recordIntervalSec = 5                      // 値を書き込む間隔(秒)

    b, err := initBme280()
    if err != nil {
        panic(err)
    }
    c := influxdb2.NewClient(influxURL, fmt.Sprintf("%s:%s", userName, password))
    w := c.WriteAPIBlocking("", db+"/autogen")

    ticker := time.NewTicker(recordIntervalSec * time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            err = recordSensorValues(b, w, measurement)
            if err != nil {
                panic(err)
            }
        }
    }
}
  1. 先程のコードをラズパイ向けにビルドするように設定してビルドします。
    $ GOOS=linux && GOARCH=arm && GOARM=6
    $ go build ./iot-sensor-logger.go

  2. このままだと実行できないため実行ファイルに実行権限を付与して実行します。
    $ chmod 744 ./iot-sensor-logger
    $ ./iot-sensor-logger

動作確認をする

上記のコードをビルドし、ラズパイ上で実行すると 5 秒ごとに BME280 が計測した温度と湿度と気圧がデーターベースに書き込まれます。
正常に書き込まれているか確認するために、直接データベースを見に行って確認します。

  1. 先程ユーザー認証を有効にした影響で、管理者権限を持ったユーザーでないと値が見れないため root ユーザーで接続します。
    $ influx --username root --password (rootユーザーのパスワード)

  2. 使用するデータベースを選択します。
    USE iot01

  3. 登録されている値を全て確認します。
    下記の画像のように値が登録されていたら成功です。
    SELECT * FROM sensors

f:id:Armoris:20210226160601p:plain

補足

今回はすぐに値が書き込まれてほしかったので 5 秒毎にデーターベースに値を書き込むようにしていますが、本格的に運用するとなると確実に肥大化を招くので、1 分~ 5 分毎くらいに書き込むようにすると良いかもしれません…。

感想

InfluxDB、操作感が MySQL とさほど変わらなくてとても扱いやすかったです…。

次回は今回作成したデーターベースを元に Grafana を使ってグラフ化して表示するところを行います…!