ESP32-DevKitCと二酸化炭素センサ SCD30モジュールを使ってCO2濃度をLCDに表示:環境モニタ(5)

窓を全開にして換気するとCO2濃度が500ppm台に下がるので、換気のタイミングを計るのに重宝しています。ESP32-DevKitCと各種センサで構築中の環境モニタにSensirion社のSCD30センサモジュールを加えてCO2濃度をLCDに表示します。

目次

追加で準備したパーツ、Sensirion(センシリオン)社のSCD30センサ

CO2センサを選ぶにあたってTVOC(Total Volatile Organic Compounds 総揮発性有機化合物の総称)測定がメインのセンサ(VOCガス濃度からCO2濃度を概算)もあるので迷いましたが、CO2 濃度測定の精度が高いSensirion社の 非分散型赤外線(NDIR)ベースの SCD30センサモジュールを購入しました。
このモジュールには基板内に同じSensirion社のSHT31センサを補正用として内蔵しているので湿度や温度も測定できますが、SCD30センサモジュールとしての動作温度は0℃~+50℃の仕様です。

SCD30 CO2 センサ モジュール基板
SCD30 CO2 センサ モジュール基板
補正用のSHT31センサ 付近の拡大
補正用のSHT31センサ 付近の拡大

追加したパーツはSCD30センサと実装用パーツです。それ以外のパーツは 温湿度・気圧センサBME280を屋内、屋外用に2個接続、LTC4331とLANケーブルでI2Cバスを10m延長:環境モニタ(4) で使ったパーツと同一です。

#今回、追加で準備したパーツ個数
1SCD30 CO2センサ モジュール
※ピンヘッダ(1×4)を準備してはんだ付け
1
2ピンヘッダ 1列タイプ 40ピン
※40ピンから4ピンをニッパでカット
1
3サンハヤト ニューブレッドボード
SAD-101
1
4ジャンパーワイヤ適量
#環境モニタ(1)~(4)で使った主なパーツ個数
1ESP32-DevKitC ESP-WROOM-32開発ボード1
2DS3231SN  I2C RTCモジュール1
32004 LCDモジュール 20×4キャラクタ 青
I2C I/F モジュール、バックライト付き、
バックライト調整用半固定ボリューム付き
2
4I2Cバス用双方向電圧レベル変換モジュール
(PCA9306)
1
5XL4015 可変DC-DCステップダウンコンバータ
(3個入り)
1
(手持ち)
6DC-POWER-JACK基板1
7USBケーブル(USB A オス to microB オス)1
(手持ち)
8コイン形リチウムイオン2次電池「LIR2032」
(4個入り)
1
9スイッチングACアダプター
9V 1.3A 100~240V
1
10BME280 温湿度・気圧センサーモジュール
Vin、I2Cレベル変換回路付き
2
11LTC4331 絶縁型I2C延長モジュール
※マスターとスレーブの2個セット
1
12LANケーブル(RJ45)10m1
13サンハヤト ニューブレッドボード SAD-1011
14サンハヤト ニューブレッドボード SAD-011
15ジャンパーワイヤ適量

ブレッドボードに実装、I2Cアドレスの確認(2022/4/21訂正)

結線図

各モジュール間をジャンパーワイヤで接続した際の結線図です。網掛け部分はmicroSDカードスロットの拡張予定箇所です。

環境モニタ(5)
環境モニタ(5)の結線図(2022/4/21訂正)

Handling and Assembly Guide for SCD30 のp2に SCD30センサの組み立て と マウントの向き についての記載があります。概要は「取り付けにはコンタクトピンのみを使用」、「SCD30はホスト基板のどの部分にも接触しないように」、「SCD30とホストPCBの接触を防ぐために推奨されるスタンドオフ高さsh以下を維持」。マウントの向きは「SCD30は、上向きにも下向きにも取り付けることができます」なので、橙色に点滅する発光面が見えるように取り付けています。

Datasheet CO2 Sensors SCD30 から、今回購入したSCD30センサモジュールの電源電圧は3.3V~5.5Vですが、I2C信号の電圧レベルは 3Vなので、 ESP32-DevKitCの SCL(GPIO22ピン)、SDL(GPIO21ピン)、3.3V(3V3ピン)、GNDピンに結線して動作させました。

SCD30センサモジュールの消費電流は比較的大きいです(平均19mA、最大70mA)。今回LCDを2枚使っているので電源投入時の電圧低下に注意します。

ArduinoサイトにあるI2CアドレスをスキャンするスケッチArduino Playground – I2cScannerを実行すると、I2Cバス上に7個のデバイスが検出されました。
i2c_scannerのスケッチに起動時10秒程度の待機時間を加えることで、SCD30センサを検出できました。SCD30センサのI2Cスレーブアドレスは 0x61です。

#モジュールアドレス備考
12004 LCD0x26
0x27
追加
デフォルト
2DS32310x57
0x68
ARTCT24C32
RTC
3SCD300x61
4BME2800x76
0x77
デフォルト
追加
i2c_scannerのスケッチに加えた10秒の待機時間(橙色の部分)
i2c_scannerのスケッチに加えた10秒の待機時間(橙色の部分)

開発ツールarduino-esp32をインストール

arduino-esp32のインストール手順を別ページに纏めました。

あわせて読みたい
ESP32-DevKitCの開発ツール arduino-esp32 のインストール手順 Arduino IDE上で動作する ESP32-DevKitC の開発ツール arduino-esp32(Arduino core for the ESP32)をインストールした際の手順メモです。 【ESP32-DevKitC の開発ツー...

スケッチ1:ライブラリのサンプルスケッチでシリアルモニタ出力

SparkFunサイトのライブラリ「sparkfun/SparkFun_SCD30_Arduino_Library(version 1.0.16)」を利用させていただきました。 サイトの「Code」プルダウンから「SparkFun_SCD30_Arduino_Library-main.zip」をダウンロード。Arduino IDEのメニュー「スケッチ」–>「ライブラリをインクルード」–>「zip形式のライブラリをインクルード」でダウンロードしたzipファイルを指定します。

ライブラリ付属のサンプルスケッチ:Example1_BasicReadings.inoで動作確認を行いました。約2秒間隔でCO2 濃度、気温、湿度データが取得できます。
Arduino IDEのメニューから、「スケッチ例」–> 「SparkFun_SCD30_Arduino_Library」–> 「examples」–> Example1_BasicReadings を選択して実行します。

SCD30 をESP32-DevKitCに結線
SCD30 をESP32-DevKitCの SCL(GPIO22ピン)、SDL(GPIO21ピン)、3.3V(3V3ピン)、GNDピンに結線
SparkFunの サンプルスケッチ
SparkFunの サンプルスケッチ: Example1_BasicReadings.ino

スケッチ2:SCD30センサの CO2測定データをLCDに表示

シリアルモニタ出力部分をLCD表示に修正したスケッチ2です。サンプルスケッチの実行時に “Air sensor not detected. Please check wiring. Freezing…” が頻発したのでセンサ安定化のために10秒の待機時間を設けています。

ESP32_SCD30_LCD.ino  ※ここをクリックするとコード表示を開閉できます。
#include <Wire.h>
#include <SparkFun_SCD30_Arduino_Library.h>
#include <LiquidCrystal_I2C.h>

SCD30 airSensor;
LiquidCrystal_I2C lcd(0x27, 20, 4);
//LiquidCrystal_I2C lcd(0x27, 16, 2);  // 1602LCD利用時

void setup()
{
Serial.begin(115200);
Wire.begin();
lcd.init();
lcd.backlight();
lcd.clear();
// ----- SCD30 CO2センサ -----
  airSensor.begin();  // SCD30センサ初期化
// LCD表示
lcd.setCursor(0,0);
lcd.print("Wait 10 seconds");
delay(10000);  // センサ安定化のため10秒待機
while (!airSensor.dataAvailable()) {
// LCD表示
lcd.setCursor(15,0);
lcd.print(" ");
delay(500);
  }
}

void loop()
{
// ----- SCD30 CO2センサ -----
if (airSensor.dataAvailable())
{
// lcd表示
lcd.setCursor(0,0);
lcd.print("                ");
lcd.setCursor(0,0);
lcd.print("CO2:");
lcd.print(airSensor.getCO2());
lcd.print("ppm");
lcd.setCursor(0,1);
lcd.print("                ");
lcd.setCursor(0,1);
lcd.print("T:");
lcd.print(airSensor.getTemperature(), 1);
lcd.print("'C ");
lcd.print("H:");
lcd.print(airSensor.getHumidity(), 1);
lcd.print("%");
  }
else
lcd.setCursor(15,0);
lcd.print(" ");
delay(500);
}

スケッチ2を実行中のLCD表示画面です。中央のブレッドボード上に実装しているSCD30センサモジュールの赤外線発光部が2秒間隔で橙色点滅します。

息を少し吹きかけただけでも即時CO2濃度が大きく値が変動します。応答性が良いです。

スケッチ2を実行中
スケッチ2を実行中。右側のLCDは使っていないので+5Vジャンパを外して消灯

手持ちの16行、2桁表示の1602 LCDモジュールに交換してみました。スケッチのライブラリ定義と表示文字数が16文字、2行内に収まるようにlcd.print文を修正します。

LiquidCrystal_I2C lcd(0x27, 16, 2);  // 1602LCD利用時
1602 LCDモジュールに交換してスケッチ2を実行中
1602 LCDモジュールに交換してスケッチ2を実行中

スケッチ3:CO2測定データを、時計・温湿度・気圧表示と統合して2つのLCDに表示

SCD30センサのCO2濃度と温湿度の表示は1枚目のLCD下段に、1枚目のLCD下段にあった室内BME280センサの表示は2枚目のLCD上段に変更しました。

SCD30モジュールを使ってCO2濃度をLCD表示
SCD30モジュールを使ってCO2濃度をLCD表示
ESP32_DS3232RTC_BME280x2_SCD30_LCDx2.ino  ※ここをクリックするとコード表示を開閉できます。
#include <Wire.h>
#include <WiFi.h>
#include <time.h>
#include <LiquidCrystal_I2C.h> // https://github.com/johnrickman/LiquidCrystal_I2C
#include <DS3232RTC.h>         // https://github.com/JChristensen/DS3232RTC
#include <esp_sntp.h>
#include <Adafruit_Sensor.h>   // https://github.com/adafruit/Adafruit_Sensor
#include <Adafruit_BME280.h>   // https://github.com/adafruit/Adafruit_BME280_Library
#include <SparkFun_SCD30_Arduino_Library.h>   // https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library

SCD30 airSensor;

DS3232RTC myRTC(false);
LiquidCrystal_I2C lcd(0x27, 20, 4);    // DS3231時計、SCD30表示
LiquidCrystal_I2C lcd2(0x26, 20, 4);   // 屋内BME280表示、屋外BME280表示

Adafruit_BME280 bme;   // 屋内センサ
Adafruit_BME280 bme2;  // 屋外センサ

float temp;
float pressure;
float humid;
float temp2;
float pressure2;
float humid2;
// 2022/03/27:曜日の文字数を9文字固定に変更
const char* weekStr[7] = {"Sunday   ","Monday   ","Tuesday  ","Wednesday","Thursday ","Friday   ","Saturday "};
const char* ssid      = "your ssid";
const char* password  = "your password";
const char* ntpServer = "ntp.nict.jp";
const long  gmtOffset_sec = 32400;
const int   daylightOffset_sec = 0;


void setup() {
struct tm timeInfo;
Serial.begin(115200);
myRTC.begin();
lcd.init();
lcd.backlight();
lcd2.init();
lcd2.backlight();
// ----- SCD30 CO2センサ -----
  airSensor.begin();  // SCD30センサ初期化
// LCD表示
lcd.setCursor(0,2);
lcd.print("Wait 10 seconds");
delay(10000);  // センサ安定化のため10秒待機
while (!airSensor.dataAvailable()) {
// LCD表示
lcd.setCursor(19,2);
lcd.print(" ");
delay(500);
}
// ----- DS3231時計 -----
//WiFi接続
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
lcd.setCursor(0,0);
lcd.print("."); // 進捗表示
delay(500);
}
// WiFi接続の表示
lcd.clear();
lcd.print("WiFi connected");
delay(2000);
lcd.clear();
// NTPサーバからJST取得
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
lcd.clear();
lcd.print("JST synchro.");
delay(2000);
lcd.clear();
// 内蔵RTCの時刻がNTP時刻に合うまで待機
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET) {
lcd.print(">"); // 進捗表示
delay(1000);
}
//内蔵RTC時刻 = NTP時刻の表示
lcd.clear();
lcd.print("Time matched");
delay(2000);
lcd.clear();
// 内蔵RTCの時刻の取得
getLocalTime(&timeInfo);
// 内蔵RTCの時刻をDS3231に時刻設定
setTime(timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec,
timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900);
myRTC.set(now());
//WiFi切断
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
// ----- BME280温湿度・圧力計 -----
bool status;
status = bme.begin(0x76);
while (!status) {
Serial.println("屋内BME280 sensorが使えません");
delay(1000);
}
status = bme2.begin(0x77);
while (!status) {
Serial.println("屋外BME280 sensorが使えません");
delay(1000);
}
}


void loop(void){
// ----- SCD30 CO2センサ -----
if (airSensor.dataAvailable())
{
// lcd表示
lcd.setCursor(0,2);
lcd.print("                    ");
lcd.setCursor(0,2);
lcd.print("co2:");
lcd.print(airSensor.getCO2());
lcd.print("ppm");
lcd.setCursor(0,3);
lcd.print("                    ");
lcd.setCursor(0,3);
lcd.print("  T:");
lcd.print(airSensor.getTemperature(), 1);
lcd.print("'C   ");
lcd.print("H:");
lcd.print(airSensor.getHumidity(), 1);
lcd.print("%");
lcd.setCursor(19,2);
}
else
lcd.setCursor(19,2);
lcd.print(" ");
// ----- DS3231時計 -----
// RTCから時刻取得
tmElements_t tm;
myRTC.read(tm);
// 時計LCD表示
lcd.setCursor(0,0);
lcd.print(tm.Year + 1970);
lcd.print("/");
lcdzeroSup(tm.Month);
lcd.print("/");
lcdzeroSup(tm.Day);
lcd.print(" ");
lcd.setCursor(11,0);
lcd.print(weekStr[tm.Wday - 1]);
lcd.setCursor(0,1);
lcdzeroSup(tm.Hour);
lcd.print(":");
lcdzeroSup(tm.Minute);
lcd.print(":");
lcdzeroSup(tm.Second);
// ----- BME280温湿度・圧力計 -----
// 屋内センサ
temp=bme.readTemperature();
pressure=bme.readPressure() / 100.0F;
humid=bme.readHumidity();
lcd2.setCursor(0,0);
lcd2.print("Press1:");
lcd2.print(pressure);
lcd2.print("hPa");
lcd2.setCursor(0,1);
lcd2.print("T:");
lcd2.print(temp);
lcd2.print("'C / H:");
lcd2.print(humid);
lcd2.print("%");
// 屋外センサ
temp2=bme2.readTemperature();
pressure2=bme2.readPressure() / 100.0F;
humid2=bme2.readHumidity();
lcd2.setCursor(0,2);
lcd2.print("Press2:");
lcd2.print(pressure2);
lcd2.print("hPa");
lcd2.setCursor(0,3);
lcd2.print("T:");
lcd2.print(temp2);
lcd2.print("'C / H:");
lcd2.print(humid2);
lcd2.print("%");
//Tera Termログ出力用
Serial.print(temp);
Serial.print(",");
Serial.print(pressure);
Serial.print(",");
Serial.print(humid);
Serial.print(",");
Serial.print(temp2);
Serial.print(",");
Serial.print(pressure2);
Serial.print(",");
Serial.println(humid2);
// delay()値を調整
delay(100);
}


// ----- DS3231時計 -----
// 先頭のゼロ(0)を空白に置換
void lcdzeroSup(int digit)
{
if(digit < 10)
lcd.print(' ');
lcd.print(digit);
}
よかったらシェアしてね!
  • URLをコピーしました!
目次