初投稿です。
M5Stack社のM5Dialが手に入ったので、これを使って机上に置くガジェットを作ろうと画策中。その機能の一つとして部屋の空気品質測定をしようとしていて、Bosch BME688の動作確認をしたのでその覚え書きです。
使用機器・環境
- PlatformIO + VSCode
- M5Dial
- ENV Pro(M5Stack用BME688搭載環境センサユニット)
Bosch BME688について
Boschの資料はこちら。BME688で実現できる機能についてはこちらのページが参考になります。
ガスをセンシングして様々な分析ができる環境センサですが、ぶっちゃけどうやれば何ができるかわかっていません。資料によると以下の測定ができるとなっています。
- 揮発性有機化合物(VOC)
- 揮発性硫黄化合物(VSC)
- 空気質指数(IAQ)
- CO2濃度
- 温度(-40~85℃)
- 湿度(0~100%)
- 気圧(300~1100hPa)
温度、湿度、気圧以外はガスセンサを利用して実現しているようです。細かい所は置いておいて、VOCやIAQの測定についてはBoschが提供しているBSEC2 Libraryを使うと取得することができるのでこれを利用しました。今後、特定のガス(火事、ペットの排泄物等)に反応する設定を試すかもしれません。
以下のソースコードを書き込んだM5Dialからの出力は以下のような感じです。
電源投入直後はガスセンサのスタビライゼーションが完了していないため、VOCやCO2の精度がイマイチですが、しばらく放置してaccuracyが3になるとちゃんとした出力になります。
M5Dial
- 使用I2Cポート : PORT A(SCL: 15, SDA: 13)
- BME688 I2Cアドレス : 0x77 (ENV ProではSDOに+3.3Vが印加されており0x77になる)
- 使用ライブラリ
- M5Unified
- M5Dial
- BME68x Sensor library
- BSEC2 Software Library
ソースコード
基本的にはBoschのBSEC2 Software Libraryのサンプル(以下スクショにある'basic')をそのまま使っていますが、
Bsec2オブジェクトに設定するセンサリストにBSEC_OUTPUT_CO2_EQUIVALENTとBSEC_BREATH_VOC_EQUIVALENTを追記し、CO2濃度[ppm]とVOC[ppm]を取得できるようにしています。
#include <Wire.h>
#include <M5Dial.h>
#include <bme68xLibrary.h>
#include <bsec2.h>
#define SERIAL_SPEED (115200)
#define PORTA_SCL_PIN (15)
#define PORTA_SDA_PIN (13)
#define PORTB_SCL_PIN (1)
#define PORTB_SDA_PIN (2)
#define BME688_ADDR (0x77)
Bsec2 envSensor;
bsecSensor sensorList[] = {
BSEC_OUTPUT_IAQ,
BSEC_OUTPUT_CO2_EQUIVALENT,
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
BSEC_OUTPUT_RAW_TEMPERATURE,
BSEC_OUTPUT_RAW_PRESSURE,
BSEC_OUTPUT_RAW_HUMIDITY,
BSEC_OUTPUT_RAW_GAS,
BSEC_OUTPUT_STABILIZATION_STATUS,
BSEC_OUTPUT_RUN_IN_STATUS};
void checkBsecStatus(Bsec2 bsec)
{
if (bsec.status < BSEC_OK)
{
USBSerial.println("BSEC error code:" + String(bsec.status));
}
else if (bsec.status > BSEC_OK)
{
USBSerial.println("BSEC warning code:" + String(bsec.status));
}
if (bsec.sensor.status < BME68X_OK)
{
USBSerial.println("BME68X error code:" + String(bsec.sensor.status));
}
else if (bsec.sensor.status > BME68X_OK)
{
USBSerial.println("BME68X warning code:" + String(bsec.sensor.status));
}
}
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
{
if (!outputs.nOutputs)
{
return;
}
USBSerial.println("BSEC outputs:\n\ttimestamp = " + String((int)(outputs.output[0].time_stamp / INT64_C(1000000))));
for (uint8_t i = 0; i < outputs.nOutputs; i++)
{
const bsecData output = outputs.output[i];
switch (output.sensor_id)
{
case BSEC_OUTPUT_IAQ:
USBSerial.println("\tiaq = " + String(output.signal));
USBSerial.println("\t\tiaq accuracy = " + String((int)output.accuracy));
break;
case BSEC_OUTPUT_CO2_EQUIVALENT:
USBSerial.println("\tco2 = " + String(output.signal) + " ppm");
USBSerial.println("\t\tco2 accuracy = " + String((int)output.accuracy));
break;
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
USBSerial.println("\tbreath voc = " + String(output.signal) + " ppm");
USBSerial.println("\t\tbreath voc accuracy = " + String((int)output.accuracy));
break;
case BSEC_OUTPUT_RAW_TEMPERATURE:
USBSerial.println("\ttemperature = " + String(output.signal) + " degC");
break;
case BSEC_OUTPUT_RAW_PRESSURE:
USBSerial.println("\tpressure = " + String(output.signal / 100.0) + " hPa");
break;
case BSEC_OUTPUT_RAW_HUMIDITY:
USBSerial.println("\thumidity = " + String(output.signal) + " %RH");
break;
case BSEC_OUTPUT_RAW_GAS:
USBSerial.println("\tgas resistance = " + String(output.signal));
break;
case BSEC_OUTPUT_STABILIZATION_STATUS:
USBSerial.println("\tstabilization status = " + String(output.signal));
break;
case BSEC_OUTPUT_RUN_IN_STATUS:
USBSerial.println("\trun in status = " + String(output.signal));
break;
default:
break;
}
}
}
void setup()
{
auto cfg = M5.config();
M5Dial.begin(false, false);
USBSerial.begin(SERIAL_SPEED);
Wire.begin(PORTA_SDA_PIN, PORTA_SCL_PIN);
if (!envSensor.begin(BME688_ADDR, Wire))
{
checkBsecStatus(envSensor);
}
if (!envSensor.updateSubscription(sensorList, ARRAY_LEN(sensorList), BSEC_SAMPLE_RATE_LP))
{
checkBsecStatus(envSensor);
}
envSensor.attachCallback(newDataCallback);
USBSerial.println("BSEC library version " +
String(envSensor.version.major) + "." + String(envSensor.version.minor) + "." + String(envSensor.version.major_bugfix) + "." + String(envSensor.version.minor_bugfix));
}
void loop()
{
if (!envSensor.run())
{
checkBsecStatus(envSensor);
}
}
[env:m5stack-stamps3]
platform = espressif32
board = m5stack-stamps3
framework = arduino
lib_deps =
m5stack/M5Dial@^1.0.2
m5stack/M5Unified@^0.1.12
boschsensortec/BME68x Sensor library@^1.1.40407
boschsensortec/BSEC2 Software Library@^1.3.2200
platform_packages = framework-arduinoespressif32 @ https://github.com/bsergei/arduino-esp32.git#issue-8185
monitor_speed = 115200
参考資料
- M5Stack社 ENV Pro Unit
- GitHub Bosch BSEC2 Library
- M5Dial Basic Library
- いろんな匂いを識別できる匂いセンサを作る (Bosch BME688利用)
あとがき
M5Stackとそのツールチェーンを使えばRTOS、ライブラリを組み合わせて100行程度でこの機能を実現できるって素晴らしいですね。