M5StackとGrove ダストセンサを使い、空気中のホコリの量を測定します。
今回使用するGrove ダストセンサは、 直径1マイクロメートル(μm)以上の粒子を検出し、粒子状物質の濃度に対応したパルスを発生します。
ちなみに、大気汚染で注目されているPM2.5は2.5μmの粒子状物質、スギ花粉症の原因となるスギ花粉は30〜40μmの大きさとのことなので、 このGrove ダストセンサはPM2.5が検出可能なようです。
Grove ダストセンサには4ピンのGroveコネクタがついていて、各ピンはデジタル出力、未使用、5V、グランドです。
M5Stack BasicやGrayにはGroveポートがありますが、I2C接続用です。 M5Stack内部でIP5306というI2Cで通信する電源制御用チップにもつながっているので、I2C以外の方法で使えません。つまり、GroveダストセンサのGroveコネクタはM5Stack BasicやGrayのGroveポートには挿せません。そこでGroveダストセンサをM5StackのGPIO2、5V、GNDに接続しました。
Groveダストセンサ | M5Stack |
---|---|
デジタル出力 | GPIO2 |
未使用 | ー |
5V | 5V |
グランド | GND |
空気中のホコリの量と合わせて温度と湿度も測るように、温湿度センサSi7021も接続しました。 写真ではダストセンサーを水平に置いていますが、製造元のSeeed社のサイトには垂直に立てて使うようにと書かれているので、設置するときは垂直に置きます。
Grove ダストセンサは、測定時間中にパルスがLowレベルになった時間の比率が、粒子状物質の濃度に対応します。
図はShinyei Particle Sensor Model PPD42NS データーシートより
Grove ダストセンサにアクセスしてホコリの粒子量を得るプログラムは、Seeed studui 社のサイトのサンプルコードを使いました。
void loop() { | |
duration = pulseIn(pin, LOW); // パルスがLowである時間を測る | |
lowpulseoccupancy = lowpulseoccupancy+duration; // Lowである時間を積算する | |
if ((millis()-starttime) > sampletime_ms) { // 前回の測定から60秒経過したら | |
ratio = lowpulseoccupancy/(sampletime_ms*10.0); // 積算時間 / 60秒の比率の100倍を計算する | |
concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // 比率から粒子量を計算する | |
} | |
} |
pulseInはパルス幅を測るArduinoのシステム関数です。
pulseIn(pin, value[, timeout]);
pinで指定するピンがHIGHまたはLOWである時間を測り、結果をマイクロ秒で返します。 timeoutを指定した場合、timeout μ秒経過すると0が返ります。
pulseIn関数でパルスがLowである時間を測り、lowpulseoccupancyという変数に積算していき、 sampletime_ms秒経ったら比率(ratio)を計算します。
次の式で比率(ratio)から粒子の濃度(concentration)を計算しています。
concentration = 1.1 * ratio ^3 - 3.8 * ratio ^2 + 520 * ratio + 0.62
温度と湿度の測定はSi7021というデジタル温湿度センサを使いました。 Si7021はI2Cでマイコンと通信します。
使い方は簡単で、最初にI2C通信のための Wire.h
と Adafruit_Si7021.h
というヘッダファイルをインクルードし、
Adafruit_Si7021
オブジェクトを初期化します。
setup関数内で Wire.begin
メソッドでI2Cを初期化し、 begin
メソッドでセンサを初期化し、
loop関数で readTemperature
メソッドと readHumidity
メソッドで温度と湿度のデータを読みます。
Si7021のアクセスに関係する部分を抜き出すと、次のようになります。
#include <Wire.h>
#include "Adafruit_Si7021.h"
Adafruit_Si7021 sensor = Adafruit_Si7021(); // Adafruit_Si7021オブジェクトを初期化する
void setup() {
Wire.begin(); // I2Cを初期化する
sensor.begin(); // Si7021を初期化する
}
void loop() {
float temp = sensor.readTemperature(); // Si7021から温度を読む
float humid = sensor.readHumidity(); // Si7021から湿度を読む
}
データの可視化にはIoTデータ可視化サービスAmbientを使いました。 Ambientを使うにはユーザー登録(無料)が必要です。登録の流れなどは「1.温度、湿度、気圧を測定し、記録する」をご覧ください。
Ambientにデータを送って可視化するためには、最初に Ambient.h
ヘッダファイルをインクルードし、Ambient
オブジェクトを作ります。
setup関数でチャネルIDとライトキーを指定してAmbientオブジェクトを初期設定し、loop関数で set
メソッドで送りたいデータをセットし、send
メソッドで送信します。
これで送ったデータがリアルタイムにグラフ化されます。
Ambientにデータを送信する部分を抜き出すと、次のようになります。
#include "Ambient.h"
Ambient ambient;
unsigned int channelId = 100; // AmbientのチャネルID
const char* writeKey = "writeKey"; // ライトキー
void setup() {
ambient.begin(channelId, writeKey, &client); // チャネルIDとライトキーを指定してAmbientの初期化
}
void loop() {
ambient.set(1, temp);
ambient.set(2, humid);
ambient.set(3, concentration);
ambient.send();
}
60秒ごとにホコリの粒子量と温度、湿度を測定し、クラウドサービスAmbientに送信してデータを可視化するプログラムの全体は次のようになります。
#include <M5Stack.h> | |
#include <Wire.h> | |
#include "Adafruit_Si7021.h" | |
#include "Ambient.h" | |
Adafruit_Si7021 sensor = Adafruit_Si7021(); // Adafruit_Si7021オブジェクトを初期化する | |
WiFiClient client; | |
Ambient ambient; | |
const char* ssid = "ssid"; | |
const char* password = "password"; | |
unsigned int channelId = 100; // AmbientのチャネルID | |
const char* writeKey = "writeKey"; // ライトキー | |
int pin = 2; | |
unsigned long duration; | |
unsigned long starttime; | |
unsigned long sampletime_ms = 60000; //sampe 60s ; | |
unsigned long lowpulseoccupancy = 0; | |
float ratio = 0; | |
float concentration = 0; | |
void setup() { | |
M5.begin(); | |
M5.Lcd.setTextSize(2); | |
Serial.begin(115200); | |
while (!Serial) ; | |
Wire.begin(); // I2Cを初期化する | |
if (!sensor.begin()) { // Si7021を初期化する | |
M5.Lcd.println("Did not find Si7021 sensor!"); | |
while (true) { | |
delay(0); | |
} | |
} | |
WiFi.begin(ssid, password); // Wi-Fi APに接続 | |
while (WiFi.status() != WL_CONNECTED) { // Wi-Fi AP接続待ち | |
delay(500); | |
Serial.print("."); | |
} | |
Serial.print("WiFi connected\r\nIP address: "); | |
Serial.println(WiFi.localIP()); | |
ambient.begin(channelId, writeKey, &client); // チャネルIDとライトキーを指定してAmbientの初期化 | |
pinMode(pin, INPUT); | |
starttime = millis();//get the current time; | |
} | |
void loop() { | |
duration = pulseIn(pin, LOW); // パルスがLowである時間を測る | |
lowpulseoccupancy = lowpulseoccupancy+duration; // Lowである時間を積算する | |
if ((millis()-starttime) > sampletime_ms) { // 前回の測定から60秒経過したら | |
float temp = sensor.readTemperature(); // Si7021から温度を読む | |
float humid = sensor.readHumidity(); // Si7021から湿度を読む | |
M5.Lcd.setCursor(10, 20); | |
M5.Lcd.print(temp); | |
M5.Lcd.setCursor(10, 40); | |
M5.Lcd.print(humid); | |
ratio = lowpulseoccupancy/(sampletime_ms*10.0); // 積算時間 / 60秒の比率の100倍を計算する | |
concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // 比率から粒子量を計算する | |
Serial.println(concentration); | |
M5.Lcd.setCursor(10, 60); | |
M5.Lcd.print(concentration); | |
ambient.set(1, temp); | |
ambient.set(2, humid); | |
ambient.set(3, concentration); | |
ambient.send(); | |
lowpulseoccupancy = 0; | |
starttime = millis(); | |
} | |
} |
ダストセンサを自宅の居間において、何日か測定しました。
左のグラフは青が温度、赤が湿度、右のグラフはオレンジがホコリの量で、単位は0.01立方フィートあたりのホコリの粒子量です。
9月18日は旅行にでていて、朝から不在で、18時40分ごろ帰宅して、部屋の空気を入れ替えました。そのタイミングで温度が下がり、相対湿度が上がっています。粒子量も少し増えています。20時ぐらいに夕食だったので、そのときに粒子量が増えています。
不思議なのは6時過ぎから10時ぐらいまでと12時ぐらいの粒子量の増加です。この時間は家には誰もいないのですが、粒子量が増えています。温度上昇と似た増え方をしているので、何か相関があるのかもしれません。
翌19日は7時ごろ起床しました。就寝中は粒子量が少なく、人が活動を始めると粒子量が増加しています。
ホコリの量は精密な作業をおこなう工場などでは重要な指標です。Grove ダストセンサは原稿執筆時点で1,987円と比較的安価です。ホコリの量と作業品質の相関を調べるならこういったセンサで十分意味のあるデータが取れそうです。