maruaのアイコン画像
marua 2023年01月04日作成 © CC BY-SA 4+
製作品 製作品 閲覧数 1064
marua 2023年01月04日作成 © CC BY-SA 4+ 製作品 製作品 閲覧数 1064

MH-Z19C使用 CO2モニタ

MH-Z19C使用 CO2モニタ

ファンヒータを使用する時期になったので、以前買っておいたCO2センサ「MH-Z19C」を使って CO2モニタを作りました。
MH-Z19Cは シリアル出力(3.3V) または PWM出力なので、シリアルポートの多い「ラズパイ Pico」を使ってみました。
AeduinoIDEで ATmega328以外のCPUを初めて使用しましたが、思った以上にすんなりプログラムを作成できました。
(Serial1.write()の引数の型NGには ハマりましたが...)

起動すると、
 (1)センサのアイドル時間(60s) 待機
 (2)3秒間隔で co2濃度を読み取り
どいう動作を実行します。
CO2濃度表示

  • 環境設定
    ArduinoIDEの環境設定については、ロジカラブログの記事を参考にしました。
    備忘録を兼ねて 以下に簡単にまとめておきます。
  1. ArduinoIDEを立ち上げ、「ファイル」-「環境設定」の「追加のボードマネージャURL」に
    https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
    を追加
    ボードマネージャURLの追加

  2. 「ツール」-「ボード」-「ボードマネージャ」でボードマネージャを立ち上げ、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 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する