M5StackとGrove ダストセンサを使い、空気中のホコリの量を測定します。

Grove ダストセンサ

今回使用するGrove ダストセンサは、 直径1マイクロメートル(μm)以上の粒子を検出し、粒子状物質の濃度に対応したパルスを発生します。

Groveダストセンサ

ちなみに、大気汚染で注目されているPM2.5は2.5μmの粒子状物質、スギ花粉症の原因となるスギ花粉は30〜40μmの大きさとのことなので、 このGrove ダストセンサはPM2.5が検出可能なようです。

Grove ダストセンサをM5Stackに接続する

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社のサイトには垂直に立てて使うようにと書かれているので、設置するときは垂直に置きます。

M5Stackとダストセンサ

プログラム

Grove ダストセンサにアクセスする

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; // 比率から粒子量を計算する
}
}
view raw dustsensor.ino hosted with ❤ by GitHub

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というデジタル温湿度センサを使いました。 Si7021はI2Cでマイコンと通信します。

使い方は簡単で、最初にI2C通信のための Wire.hAdafruit_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から湿度を読む
}

データをAmbientに送信して可視化する

データの可視化には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円と比較的安価です。ホコリの量と作業品質の相関を調べるならこういったセンサで十分意味のあるデータが取れそうです。