t-hom’s diary

主にVBAネタを扱っているブログです。

Arduino UNOで複数の環境センサー(温度、湿度、気圧、PM2.5、CO2)からデータ取得

今回はマイコンボードArduinoを使って複数の環境センサーからデータを取得するコードを紹介。この記事はシリアルモニター上で観測するところまでなので、これだけで普段使いのツールを作れるというものではないことはご承知いただきたい。

作成の動機

在宅勤務が続くなか、出社時と同等またはそれ以上のパフォーマンスを出すためには環境の見直しが重要だと考えた。オフィスでは事務所衛生基準規則等によって会社側が環境を整えてくれるが、在宅だと自分でなんとかする必要がある。よって、常時環境を監視して基準を外れた場合は対策を促すアラートを出すような仕組みを構築したいと思った。

既製品ではなく自作する動機としては、取得したデータを自分の思い通りに活用したい為。

記事にした動機

センサー単品のサンプルは探せばいくらでも出てくるが、複数取り扱うとなると一気に参考サイトが減る。センサー類の特性によっては読み取り開始から終了までの待ち時間があったり、何十秒か連続で読み取りを継続しなければならなかったりといったプログラム上の制約が出てくるので、これらをうまく取り扱う方法を紹介したいと思った。

下準備

温度・湿度・気圧センサーのハンダジャンパー

購入した温度・湿度・気圧センサーはハンダジャンパーでArduino側の通信ピンを選択できるようになってるらしい。
チップ上の3接点のうち、左と中央をハンダで繋げておく。
f:id:t-hom:20201108103718p:plain
普通は、初期状態で左と中央が繋がっているらしくそれを前提にした解説が多いのだが、私の購入した製品はどこにもジャンパーされてなかった。

Arduinoへライブラリのインストール

ツール→ライブラリを管理からAdafruit BME280 Libraryをインストールしておく。
f:id:t-hom:20201108110154p:plain

配線図

※CO2センサーは端子のないところにつながっているように見えるが、実際にはそこに端子がある。Fritzingというツールで図を作成した際、このセンサーの別のバージョンしかデータを入手できなかったため代用した。
f:id:t-hom:20201108132112p:plain

Arduino UNO内蔵型ブレッドボードを利用して検証した実際の配線がこちら。
ごちゃってて何の参考にもならないと思うが一応。。
f:id:t-hom:20201108113845p:plain

コード

このコードは私が書いたというよりは、ただ参考サイトのコードを組み合わせただけ。
変数名とかも基本的にはそのままだし本当はそれぞれ処理を関数化したいけど一旦動いたところまでで記事にしておこうと思いとりいそぎ公開。後日リファクタリングしたい。

//for CO2
#include <SoftwareSerial.h>

//for BME
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define SEALEVELPRESSURE_HPA (1013.25)

//for CO2
SoftwareSerial swSer(8, 7);
uint8_t cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
uint8_t reset[9] = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78};
uint8_t res[9] = {};
uint8_t idx = 0;
bool flag = false;
uint16_t co2=0;

//for BME
Adafruit_BME280 bme;

//for DustSensor
int pin = 9;
unsigned long t0;
unsigned long ts = 30000; // 30000ms
unsigned long lowOc = 0;
float ratio = 0;
float concent = 0;

void setup() {
  //for Common
  Serial.begin(9600);

  //for CO2
  swSer.begin(9600);
  //swSer.write(reset,9);//calib
  //delay(60000UL);

  //for BME
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  //for DustSensor
  pinMode(9,INPUT);
  t0 = millis();
}

void loop() {
  uint16_t val=0;
  float t;
  swSer.write(cmd,9);

  Serial.println("--------------------------------");
  //BME
  Serial.print("Temperature = ");
  Serial.print(bme.readTemperature());
  Serial.println("*C");

  Serial.print("Pressure = ");
  Serial.print(bme.readPressure() / 100.0F);
  Serial.println("hPa");

  Serial.print("Humidity = ");
  Serial.print(bme.readHumidity());
  Serial.println("%");

  //DustSensor
  t0 = millis();
  lowOc = 0;
  while((millis() -t0) <= ts) {
    lowOc += pulseIn(pin, LOW);
  }
  ratio = lowOc/(ts*10.0);
  concent = 1.1 * pow(ratio,3) - 3.8 * pow(ratio,2) + 520 * ratio + 0.62;
  Serial.println("Particulates = " + String(concent) + "pcs/0.01cf");
  
  
  delay(30000);

  //CO2
  while(swSer.available()==0){
    Serial.println("bad com");
  }
  while(swSer.available()>0){
    res[idx++]=swSer.read();
    flag=true;
  }
  idx = 0;
  if(flag){
    flag=false;
    co2 = 0;
    //delay(100);
    co2 += (uint16_t)res[2] <<8;
    //delay(100);
    co2 += res[3];
    t = res[4];
  }
  Serial.print("CO2 = ");
  Serial.print(co2);
  Serial.println("ppm");
}

組み合わせの解説

個別のセンサー処理部分は参考サイトの解説か、ご自身で調べていただくとして、組み合わせ部分だけざっくり説明する。
まず、各センサーごとにデータ取得のタイミングが異なる。即時取得できるセンサーばかりであれば単純に順次取得すれば良いのだが、各参考ページやコードを見ているとそうでもないので一工夫する必要がある。

以下に今回使用したセンサーごとのデータ取得タイミングを記す。

センサー名 データ取得タイミング
温度・湿度・気圧センサー 即時データ取得可能
ダストセンサー 30秒間連続で信号を監視し続け、そのうち信号がLowになっていた時間を累計することで計測
CO2センサー 観測開始のコマンドを発行してから60秒程度Waitしてからデータ取得

順次取得でもできなくはないが、CO2センサーが60秒ただ待ち続けるのは時間のロスなので待ってる間にダストセンサーのデータ収集をするというロジックにした。

タイムラインがこちら。
f:id:t-hom:20201108113230p:plain

実行結果

シリアルモニターを起動しておくとこんな感じでデータが取得できる。
f:id:t-hom:20201108114450p:plain

なおCO2センサーのキャリブレーションは特にやってないので値のズレは生じていると思う。CO2濃度が400ppmに近い屋外でキャリブレーションするらしいのだが、値が大きくずれた場所でやってしまうと逆に狂いが大きくなるらしいので下手にいじらず出荷状態で使っている。

記事にするかどうかは別として今後の改良・応用プラン

  • データ表示のタイミングをそろえる
  • コードのリファクタリング
  • Arduinoとセンサー類をまとめて格納するケースの作成
  • ラズパイへデータ送信してリアルタイムのグラフ表示
  • 電光掲示板に取得した情報を流す
  • 基準値越えをアラームで警告

以上

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。