Raspberry pi 3+ を買ったので、折角なのでセンサーも買ってみました。
BME280(気圧/湿度/温度センサ)です。
コマンドでデータ取得して、更にWebSocket経由でブラウザに表示してみます。
接続
BME280モジュール | RasPIピン番号 | RasPIピン機能 |
---|---|---|
VCC | 1 | 3V3 Power |
GND | 9 | Ground |
SCL | 5 | GPIO4(SCL) |
SDA | 3 | GPIO3(SDA) |
CSB | 17 | 3V3 Power |
SDD | 13 | Ground |
はんだは気合で付ける。
メスメスのジャンパピンは予め用意しときましょう。
I2Cを有効にする
raspi-config をインストールします。
Ubuntu Mate でしたが、apt ですんなり入りました。
$ sudo apt install -y raspi-config
設定を開きます。
$ sudo raspi-config
デバイス確認
再起動後に /dev/i2c-nn でデバイスが認識されています。
$ ls /dev/i2c-*
/dev/i2c-1 /dev/i2c-2
i2cdetect コマンドでスキャンしてみます。
$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --
0x76 でデバイスが見つかりました。適当なはんだでも認識するもんですね。
データ取得
データを取得してみます。巷では python が大半ですが、コマンドにしたいので Rust で書きます。
BME280のデータ取得できる Crates があるのでお世話になってみます。
ほぼ example のまま使えます。
use linux_embedded_hal as hal;
use linux_embedded_hal::{Delay, I2cdev};
use bme280::i2c::BME280;
fn main() {
let i2c_bus = I2cdev::new("/dev/i2c-1").unwrap();
let mut delay = Delay;
let mut bme280 = BME280::new_primary(i2c_bus);
bme280.init(&mut delay).unwrap();
let measurements = bme280.measure(&mut delay).unwrap();
println!("Relative Humidity = {} %", measurements.humidity);
println!("Temperature = {} C", measurements.temperature);
println!("Pressure = {} hPa", measurements.pressure/100.0);
}
ビルドしてみます。
$ cargo run
Relative Humidity = 52.11411 %
Temperature = 25.01252 C
Pressure = 1015.226 hPa
データが取れました。温度が5℃くらい高い気がしますが、こんなもんなんでしょうきっと。
リリースビルドします。
$ cargo build --release
target/release/ 以下にバイナリができます。実行するとデータが表示されます。
i2c-view というコマンドにしました。
$ ./i2c-view
Relative Humidity = 52.08504 %
Temperature = 25.02518 C
Pressure = 1015.21954 hPa
良いですね。/usr/sbin/ 以下に移動して実行権限をつけておきます。
ローカルのWebサービスからデータ参照する
ラズパイ上のWebブラウザから、ローカルのデータを拾います。
今回は WebSocket サーバを起動して、Webブラウザから投げたメッセージでコマンド結果を返すサービスを作りました。
localhost でphpなりpythonなりを動かせばいいんですが、諸事情でそうはなってないので少し遠回りします。
WebSocket サーバを作る
メッセージを受け取ってコマンドの結果を返すだけなので、セキュリティなんかは無視します。
どうせLAN内からでしか見ません。
use tokio::net::TcpListener;
use tokio_tungstenite::accept_async;
use tokio_tungstenite::tungstenite::protocol::Message;
use anyhow::Result;
use futures_util::{SinkExt, StreamExt};
use tokio::process::Command;
#[tokio::main]
async fn main() -> Result<()> {
let addr = "127.0.0.1:8888".to_string();
let listener = TcpListener::bind(&addr).await?;
println!("WebSocket server started on ws://{}", addr);
while let Ok((stream, _)) = listener.accept().await {
tokio::spawn(handle_connection(stream));
}
Ok(())
}
async fn handle_connection(stream: tokio::net::TcpStream) -> Result<()> {
let mut ws_stream = accept_async(stream).await?;
println!("WebSocket connection established");
while let Some(msg) = ws_stream.next().await {
let msg = msg?;
if msg.is_text() {
let received_text = msg.to_text()?;
println!("Received message: {}", &received_text);
let mut res = "".to_string();
if received_text == "i2c-view" {
let output = Command::new("i2c-view").output().await.unwrap();
res = String::from_utf8_lossy(&output.stdout).to_string();
println!("{}", &res);
}
ws_stream.send(Message::Text(res)).await?;
}
}
Ok(())
}
println!は無くてもいいです。分りやすいように書いただけなので。
HTML側は以下にしました。
サービス自体は別サーバに置いていても良いので、WebSocket は楽で良いですね。
※当然ラズパイ以外からはデータ取得できませんが。
var ws = new WebSocket('ws://127.0.0.1:8888');
ws.addEventListener("message", function(event) {
var i2cs = String(event.data).split(/\r?\n/g);
var hum = Number(i2cs[0].match(/([\d\.]+)/)[0]);
var temp = Number(i2cs[1].match(/([\d\.]+)/)[0]);
var pres = Number(i2cs[2].match(/([\d\.]+)/)[0]);
document.querySelector('.humi').textContent = '湿度: ' + Math.round(hum) + '%';
document.querySelector('.temp').textContent = '温度: ' + Math.round(temp)+ '℃';
document.querySelector('.pres').textContent = '気圧: ' + Math.round(pres)+ 'hPa';
});
setInterval(function() {
ws.send('i2c-view');
}, 10000);
エラーハンドリングがあまあまですが、必要に応じて。
BME280をラズパイに付けてデータ取得、更にWebSocketを通してWebブラウザ上に表示することができました。
所感
ラズパイ面白いですが、3+は流石に重いですね。
サイネージとかのGUI使うサービスを考えるならラズパイ4以上の方が良さそう…と買ってから思いました。
いかんせん、tauri のビルドに一晩どころか丸一日かかるとかしんどすぎます。
今回の Rust のコードのビルドは10分くらいで終わるので、スペックに合ったものを作るならいいデバイスですね。