概要
センサーとArduinoを使って、温度・湿度などをモニタリングしてみました
使ったもの
- 計測
- Arduino Yun Mini https://docs.arduino.cc/retired/boards/arduino-yun-mini
- AE-BME680 https://akizukidenshi.com/catalog/g/gK-14469/
- データ保存、表示
- Google Cloud Functions https://cloud.google.com/functions
- Python 3.10
- Google Cloud BigQuery https://cloud.google.com/bigquery
- Google データポータル https://support.google.com/datastudio/answer/6283323
- Google Cloud Functions https://cloud.google.com/functions
準備編
Arduino Yun MiniをWiFiに接続する
こちらの記事を参考に接続しました。
Arduino Yun MiniとI2C接続する
Arduino Yun MiniでのSDA・SCLのアドレスを確認します。
公式ドキュメントの「SCHEMATICS IN .PDF」が回路図で、3Pを見ると次のようになります。
- SDA: D2
- SCL: D3
Arduino IDEでスケッチ例「i2c_scanner」を使うことで、接続を確認できます。
参考
https://www.denshi.club/cookbook/stem/maker-uno-stem-7-accuracy.html
BME680とArduino Yun Miniを接続する
秋月電子の資料に書いてある手順を参考に、ArduinoIDEにBME680用ライブラリを導入します。
Adafruit 社の"AdafruitBME680Library"のv2.0.2をインストールしました。
Arduino Yun Mini | BME680 |
---|---|
3.3V | 1. VIN |
D3(SCL) | 2. SCL |
D2(SDA) | 3. SDA |
GND | 4. GND |
Temperature = 29.03 *C
Pressure = 1006.25 hPa
Humidity = 64.55 %
Gas = 11.43 KOhms
Approx. Altitude = 16.16 m
実装編
データの保存先をつくる
今回もGCPを使います。
Firestoreだとデータポータルと連動させにくいので、BigQueryにデータを保存します。
※フィールド名にキャメルケースとスネークケース混ざってしまったのは失敗
データを受け付けるWEB APIを用意する
Functionsを使います。
第2世代が出ていたのでついそちらを使いました。
Arduinoでうまく認証させられる気がしなかったので、「未認証の呼び出しを許可」で作成しています。
また第2世代なのでHTTPSです。
import datetime
import os
import functions_framework
from google.cloud import bigquery
@functions_framework.http
def register_monitoring_data(request):
request_json = request.get_json(silent=True)
dt_now_iso = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))).isoformat()
print(request_json, dt_now_iso)
rows_to_insert = [{
'createdAt': dt_now_iso,
'temperature': request_json['temperature'], # 温度(℃)
'pressure': request_json['pressure'], # 気圧(hPa)
'humidity': request_json['humidity'], # 湿度(%)
'gas_resistance': request_json['gas_resistance'], # 気体抵抗(KOhms)
'elevation': request_json['elevation'] # 標高(m)
}]
table_id = os.environ.get('BIGQUERY_TABLE_ID', 'Specified environment variable is not set.')
client = bigquery.Client()
table = client.get_table(table_id)
errors = client.insert_rows(table, rows_to_insert)
if errors:
print("Encountered errors while inserting rows: {}".format(errors))
return 'NG {}'.format(dt_now_iso)
return 'OK {}'.format(dt_now_iso)
ランタイム環境変数にBIGQUERY_TABLE_ID
で登録先のBigQueryテーブルIDを設定します
ArduinoからJSONでデータを送る
サンプルソースをもとに、API通信を追加しました。
送信先APIがHTTPSなのですが、証明書登録が容量不足でできなかったため、証明書チェックをclient.noCheckSSL()
で無視しています。
// 計測まわり
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"
// データ送信まわり
#include <Bridge.h>
#include <HttpClient.h>
#include <Arduino_JSON.h>
#define SEALEVELPRESSURE_HPA (1008.2)
#define HTTP_URL "your url"
Adafruit_BME680 bme; // I2C
const int STATUS_LED = 13;
void setup() {
pinMode(STATUS_LED, OUTPUT);
digitalWrite(STATUS_LED, LOW);
Bridge.begin();
digitalWrite(STATUS_LED, HIGH);
delay(3000);
if (!bme.begin()) {
digitalWrite(STATUS_LED, LOW);
Serial.println("Could not find a valid BME680 sensor, check wiring!");
while (1);
}
// Set up oversampling and filter initialization
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320*C for 150 ms
}
void exec() {
if (! bme.performReading()) {
Serial.println("Failed to perform reading :(");
for(int i = 0; i < 10; i++) {
digitalWrite(STATUS_LED, HIGH);
delay(1000);
digitalWrite(STATUS_LED, LOW);
delay(1000);
}
return;
}
// データ作成
JSONVar json;
json["temperature"] = bme.temperature;
json["pressure"] = bme.pressure / 100.0;
json["humidity"] = bme.humidity;
json["gas_resistance"] = bme.gas_resistance / 1000.0;
json["elevation"] = bme.readAltitude(SEALEVELPRESSURE_HPA);
String jsonStr = JSON.stringify(json);
int str_len = jsonStr.length() + 1;
char char_array[str_len];
jsonStr.toCharArray(char_array, str_len);
// HTTP通信
HttpClient client;
client.noCheckSSL();
client.setHeader("Content-Type: application/json");
client.post(HTTP_URL, char_array);
String payload = client.readString();
Serial.println(payload);
client.close();
}
void loop() {
exec();
delay(5 * 60 * 1000UL); // 5分(ms)
}
#define HTTP_URL "your url"
にさきほど作ったFuctionsのトリガーURLを設定します。
このコードだと容量ぎりぎり98%になってしまったので、他の処理を入れるならJSONをライブラリ無で生成した方がいいかもしれません。
データを表示する
データポータル(旧Data Studio)で、BigQueryのデータを参照してグラフを表示します。
スマホで確認したかったので縦長に配置しました。
コントロール部品を設定したので、デフォルト今日のデータが表示されます。
まとめ
思っていたよりは手間がかからずモニタリングができました🤗
ソースコードはGitHubに置いてあります。
https://github.com/k-akie/room-monitor
- これからやりたいこと
- 値によってSlack通知する(アラート機能)
- センサー以外に天気などの情報も一緒に表示する
- 気体抵抗をCO2濃度に換算する