はじめに
この記事では、M5StackMP135でLivebookを動作させてGPIOを使ってみました。
M5Stackには、コネクターで接続するだけで使える豊富なデバイスが魅力です。加速度、3軸地磁気センサ、気圧・温度センサを一つのパッケージにしたM5STACK-U171(以下、U171と呼ぶことにします。)をコアジェニックさんにいただいたので、Livebookで遊んでみました。
i2cを使ってみる
U171には、3つのセンサーが搭載されています。いずれも一つのi2cバスに接続されていて、M5Stack本体からはi2cバスを介して制御します。
スイッチサイエンスの説明ページによると搭載されているセンサーとアドレスは以下の通り
センサーの種類 | チップ名 | アドレス |
---|---|---|
IMU | BMI270 | 0x68 |
地磁気センサ | BMM150 | 0x10 |
気圧・温度センサ | BMP280 | 0x76 |
まずは、i2cバスの状態を確認
3つのバス認識されていました。
ドキュメントによると、Port Aのi2cバスは、/dev/i2c-1
となっています。
detect_device/1を使って、接続されているデバイスを調べてみます。
0x68と0x76のデバイスを検知しました。
0x10の磁気センサーが見つかりません(謎)。
温度を測定してみる
温度センサーのBMP280はhexdocsにモジュールがありました。
これを使って値を読み取ってみます。
Mix.install([
{:circuits_i2c, "~> 2.0"},
{:bmp280, "~> 0.2.13"}
])
{:ok, bmp} = BMP280.start_link(bus_name: "i2c-1", bus_address: 0x76)
BMP280.measure(bmp)
{:ok,
%BMP280.Measurement{
temperature_c: 34.753966059455706,
pressure_pa: 100672.16892826237,
altitude_m: -56.548979627198875,
humidity_rh: :unknown,
dew_point_c: :unknown,
gas_resistance_ohms: :unknown,
timestamp_ms: 132942
}}
室温34℃!!!
取得できました。
グラフ表示してみる
動作を確認できたので、温度を定期的に測定してグラフ表示してみます。
グラフ表示まで簡単にできるところがLivebookのありがたい所ですね。
温度変化がないとつまらないので、エアコンのON/OFFをして、エアコンの働を可視化してみます。
15:27分エアコンON
15:47分エアコンOFF
です。
ちなみに、エアコンの設定温度は30℃です。
ちゃんと測定できてそう。
グラフ表示のプログラム
defmodule TemperatureServer do
use GenServer
# サーバーの開始
def start_link() do
{:ok, bmp} = BMP280.start_link(bus_name: "i2c-1", bus_address: 0x76)
GenServer.start_link(__MODULE__, %{bmp: bmp, measurements: []}, name: __MODULE__)
end
# 初期化
def init(state) do
# 定期的に :measure_temperature メッセージを自身に送信する
schedule_measurement()
{:ok, state}
end
# 定期的に測定するためのスケジューリング関数
defp schedule_measurement do
# 5000ミリ秒 (5秒後) に :measure_temperature メッセージを送る
Process.send_after(self(), :measure_temperature, 5000)
end
# 温度を測定して保存するハンドル
def handle_info(:measure_temperature, %{bmp: bmp} = state) do
# BMP280から温度を測定
{:ok, measurement} = BMP280.measure(bmp)
# 時刻を取得
timestamp = DateTime.utc_now()
# 結果をリストに追加
new_measurement = {timestamp, measurement.temperature_c}
new_state = %{state | measurements: [new_measurement | state.measurements]}
# 次の測定をスケジューリング
schedule_measurement()
{:noreply, new_state}
end
# 測定結果を取得するための関数
def handle_call(:get_measurements, _from, state) do
{:reply, Enum.reverse(state.measurements), state}
end
# 外部API:測定結果を取得する
def get_measurements do
GenServer.call(__MODULE__, :get_measurements)
end
end
defmodule TemperatureGraph do
@moduledoc """
Temperature measurements visualization using VegaLite.
"""
alias VegaLite, as: Vl
# 温度データをグラフ表示
def plot_measurements(measurements) do
# VegaLiteで必要なデータを生成
data =
measurements
|> Enum.map(fn {timestamp, temperature} ->
%{
"timestamp" => DateTime.to_string(timestamp),
"temperature" => temperature
}
end)
Vl.new(width: 600, height: 400)
|> Vl.data_from_values(data)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "timestamp", type: :temporal)
|> Vl.encode_field(:y, "temperature", type: :quantitative, scale: [domain: [30, 36]])
|> Kino.VegaLite.new()
end
end
TemperatureServer.get_measurements()
|> TemperatureGraph.plot_measurements()
まとめ
- BMP280での温度計測がLivebookから行えた
- エアコンの働を可視化できた
- 暑いので熱中症には気をつけましょう