MH-Z19C使用 CO2モニタ
ファンヒータを使用する時期になったので、以前買っておいたCO2センサ「MH-Z19C」を使って CO2モニタを作りました。
MH-Z19Cは シリアル出力(3.3V) または PWM出力なので、シリアルポートの多い「ラズパイ Pico」を使ってみました。
AeduinoIDEで ATmega328以外のCPUを初めて使用しましたが、思った以上にすんなりプログラムを作成できました。
(Serial1.write()の引数の型NGには ハマりましたが...)
起動すると、
(1)センサのアイドル時間(60s) 待機
(2)3秒間隔で co2濃度を読み取り
どいう動作を実行します。
- 環境設定
ArduinoIDEの環境設定については、ロジカラブログの記事を参考にしました。
備忘録を兼ねて 以下に簡単にまとめておきます。
-
ArduinoIDEを立ち上げ、「ファイル」-「環境設定」の「追加のボードマネージャURL」に
「https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json」
を追加
-
「ツール」-「ボード」-「ボードマネージャ」でボードマネージャを立ち上げ、Philhowerさん作成の「Raspberry Pi Pico/RP2040」をインストール。
3.「ツール」-「ボード」から、「Raspberry Pi RP2040 Boards」-「Raspberry pi pico」を選択
-
主な使用部品
| No. | 品名 | 型番 | 備考 |
|:---:---:---:---
| 1 | Raspberry Pi Pico | | 秋月電子 M-16132 |
| 2 | CO2センサ | MH-Z19C | 秋月電子 M-16142 |
| 3 | I2C接続液晶 | AE-AQM1602A | 秋月電子 K-08896 | -
ソースコード
CO2モニタ
#include <Wire.h>
#define LCD_ADRS 0x3E //Slave address of LCD(Arduino ver.)
String VRAM1 = " "; // 16文字
String VRAM2 = " "; // 16文字
unsigned long Lchika_tim0;
unsigned long Lchika_timH = 1000;
unsigned long Lchika_timL = 1000;
byte Lchika_task, Lchika_cnt;
unsigned long co2_tim0;
byte co2_task, co2_RD_pos, co2_cnt, co2_enum;
byte co2_RDat[10];
word co2_resalt;
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
Wire.begin();
init_LCD();
Lchika_timH = 500;
Lchika_timL = 500;
VRAM1 = " 0:Wait";
LCD_DISP_16_1();
Serial.begin(9600); // USB
Serial1.begin(9600); // UART1
co2_tim0 = millis(); // (co2_tim0)に基準時間セット
co2_enum = 0; // エラーNo. = 0: エラー無し
}
// the loop function runs over and over again forever
void loop() {
Lchika();
read_CO2();
}
// ■■ Read CO2 data ■■
void read_CO2() {
byte sum;
switch (co2_task) {
case 0: // pre Heat
if ( (millis() - co2_tim0) > 1000) {
co2_cnt ++;
VRAM1 = " 0:Wait "+ String(co2_cnt);
LCD_DISP_16_1();
co2_task ++;
}
break;
case 1: //
if ( co2_cnt < 60 ) {
co2_tim0 = millis(); // (co2_tim0)に基準時間セット
co2_task = 0;
} else {
VRAM1 = " 1:STT";
LCD_DISP_16_1();
Serial.println(" CO2 sens start");
Lchika_timH = 1000;
Lchika_timL = 1000;
co2_task ++;
}
break;
case 2: // send Read cmd
if (Serial1.available() ) { Serial1.read(); break; } // 受信バッファ 強制読出し
send_CO2_read(); // Read comd
co2_RD_pos = 0;
co2_tim0 = millis(); // (co2_tim0)に基準時間セット
co2_task ++;
break;
case 3: // 受信完了待ち
if (Serial1.available() ) {
co2_RDat[co2_RD_pos] = Serial1.read();
co2_RD_pos ++;
if (co2_RD_pos >=9) {co2_task ++;}
}
if ( (millis() - co2_tim0) > 10000) {co2_task = 99; } // 10s経過で 再送
break;
case 4: // 受信データ処理
sum = 0;
for (int i=0; i<8; i++ ) { sum += co2_RDat[i]; }
if (co2_RDat[0] != 0xff) { co2_task = 99; co2_enum = 11; break; }
if (co2_RDat[1] != 0x86) { co2_task = 99; co2_enum = 12; break; }
if ((0xff - sum) != co2_RDat[8]) { co2_task = 99; co2_enum = 19; break; }
co2_resalt = co2_RDat[2]*256 + co2_RDat[3];
Serial.print(co2_resalt);
Serial.print(", ");
Serial.print(sum,HEX);
Serial.print(", ");
Serial.print(co2_RDat[8],HEX);
Serial.println();
VRAM1 = " 3:Receive " + String(co2_resalt);
LCD_DISP_16_1();
co2_tim0 = millis(); // (co2_tim0)に基準時間セット
co2_task ++;
break;
case 5:
if ( (millis() - co2_tim0) > 3000) {co2_task = 2; } // 3s経過で 再読込み
break;
case 6:
co2_task = 2;
break;
case 99:
Serial.print("NG ");
Serial.print('E');
Serial.print(co2_enum);
Serial.print(" sum=");
Serial.print(sum);
Serial.print(" , ");
Serial.print(co2_RDat[8], HEX);
Serial.print(" , ");
Serial.print(co2_RDat[0], HEX);
Serial.println();
co2_tim0 = millis(); // (co2_tim0)に基準時間セット
co2_task = 5;
break;
default:
co2_task = 0;
break;
}
}
void send_CO2_read() {
Serial1.write(0xff); // 1
Serial1.write(0x01); // 2
Serial1.write(0x86); // 3
Serial1.write((byte) 0x00); // 4
Serial1.write((byte) 0x00); // 5
Serial1.write((byte) 0x00); // 6
Serial1.write((byte) 0x00); // 7
Serial1.write((byte) 0x00); // 8
Serial1.write(0x79); // 9(SUM)
}
// ■■ L-chka ■■
void Lchika() {
switch(Lchika_task) {
case 0:
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
Lchika_tim0 = millis();
Lchika_task++;
//Serial.println(Lchika_cnt ++);
break;
case 1:
if ( (millis() - Lchika_tim0) > Lchika_timH) { Lchika_task ++; }
break;
case 2:
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
Lchika_tim0 = millis();
Lchika_task++;
break;
case 3:
if ( (millis() - Lchika_tim0) > Lchika_timL) { Lchika_task ++; }
break;
default:
Lchika_task = 0;
break;
}
}
// ■■ LCD control ■■
void writeLcdCmd(byte t_command) { //コマンド書き込み
Wire.beginTransmission(LCD_ADRS);
Wire.write(0x00);
Wire.write(t_command);
Wire.endTransmission();
delay(10);
}
void writeLcdDat(byte t_data) { //データ書き込み
Wire.beginTransmission(LCD_ADRS);
Wire.write(0x40);
Wire.write(t_data);
Wire.endTransmission();
delay(1);
}
void LCD_DISP_16_1(void) { //上段の行にデータ書き込み
writeLcdCmd(0x00 + 0x80); //
for (int i = 0; i < 16; i++) {
writeLcdDat(VRAM1[i]);
}
}
void LCD_DISP_16_2(void) { //下段の行にデータ書き込み
writeLcdCmd(0x40 + 0x80); //
for (int i = 0; i < 16; i++) {
writeLcdDat(VRAM2[i]);
}
}
void init_LCD() {
delay(100); //液晶初期化
writeLcdCmd(0x38); delay(20); // FUNCTION SET
writeLcdCmd(0x39); delay(20); // IS=1
writeLcdCmd(0x14); delay(20); // INT OSC FREQUENCY
writeLcdCmd(0x73); delay(20); //7faONTRAST SET0,1,2,3(3.3V=0x73,5V=0x7A)
writeLcdCmd(0x56); delay(20); // 54CONTRAST SET 4,5 (3.3V=0x56, 5V= 0x54)
writeLcdCmd(0x6C); delay(20); // F0LLOWER CONTROL
writeLcdCmd(0x38); delay(20); // IS=0
writeLcdCmd(0x01); delay(20); // Clear Display
writeLcdCmd(0x0C); delay(20); // Display ON
}
投稿者の人気記事
-
marua
さんが
2023/01/04
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する