「Parts:Sensor:MH-Z19C」の版間の差分
184行目: | 184行目: | ||
<blockquote> | <blockquote> | ||
{| class="wikitable" | {| class="wikitable" | ||
− | |<syntaxhighlight lang=" | + | |<syntaxhighlight lang="c++" style="border: 1px dashed gray;"> |
− | /////// MHZ19C /////// | + | /////// MHZ19C /////// |
− | HardwareSerial mhz19(2); | + | HardwareSerial mhz19(2); |
− | + | ||
− | void mhz19_init(void){ | + | void mhz19_init(void){ |
− | delay(200); //電解コンデンサがチャージ完了するまで待つ | + | delay(200); //電解コンデンサがチャージ完了するまで待つ |
− | digitalWrite(pin_XCL103EN, 1); //mhz19の電源をONにする | + | digitalWrite(pin_XCL103EN, 1); //mhz19の電源をONにする |
− | delay(100); //電源が安定するのを待つ | + | delay(100); //電源が安定するのを待つ |
− | mhz19.begin(9600); | + | mhz19.begin(9600); |
− | //MHZ19C動作開始 ※5V用電解コンデンサは100μFにする事!470μFにするとXCL103が時々スタートしなくなる | + | //MHZ19C動作開始 ※5V用電解コンデンサは100μFにする事!470μFにするとXCL103が時々スタートしなくなる |
− | digitalWrite(pin_whiteLED, 0); //calibで点灯、それ以外は消灯 | + | digitalWrite(pin_whiteLED, 0); //calibで点灯、それ以外は消灯 |
− | //mhz19setCalMode(1); //calmode=0:autocal=OFF / calmode=1:autocal=ON | + | //mhz19setCalMode(1); //calmode=0:autocal=OFF / calmode=1:autocal=ON |
− | } | + | } |
− | + | ||
− | //返値:co2濃度、mhz19temp=温度、mhz19status=status | + | //返値:co2濃度、mhz19temp=温度、mhz19status=status |
int mhz19readCO2() { // MH-Z19CからCO2濃度の値を読む<BR/> | int mhz19readCO2() { // MH-Z19CからCO2濃度の値を読む<BR/> | ||
− | uint16_t co2Val; | + | uint16_t co2Val; |
− | byte ReadCO2Cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; | + | byte ReadCO2Cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; |
byte RetVal[9]; | byte RetVal[9]; | ||
− | while (mhz19.available())mhz19.read(); // 受信バッファクリア | + | while (mhz19.available())mhz19.read(); // 受信バッファクリア |
− | memset(RetVal, 0x00, sizeof RetVal); // 変数をクリア | + | memset(RetVal, 0x00, sizeof RetVal); // 変数をクリア |
− | mhz19.write(ReadCO2Cmd, sizeof ReadCO2Cmd); // 測定コマンド送信 | + | mhz19.write(ReadCO2Cmd, sizeof ReadCO2Cmd); // 測定コマンド送信 |
− | mhz19.readBytes((char *)RetVal, sizeof RetVal); // 測定結果をMHZ19Cから受信 | + | mhz19.readBytes((char *)RetVal, sizeof RetVal); // 測定結果をMHZ19Cから受信 |
− | if ((RetVal[0] == 0xff) && (RetVal[1] == 0x86)) { // 正常に読めた? 0xff=255 0x86=134 | + | if ((RetVal[0] == 0xff) && (RetVal[1] == 0x86)) { // 正常に読めた? 0xff=255 0x86=134 |
− | co2Val = RetVal[2] * 256 + RetVal[3]; // CO2濃度を計算 | + | co2Val = RetVal[2] * 256 + RetVal[3]; // CO2濃度を計算 |
− | mhz19temp = RetVal[4];// - 40; // 内部温度 ※参考用 | + | mhz19temp = RetVal[4];// - 40; // 内部温度 ※参考用 |
− | mhz19status = RetVal[5]; // Status | + | mhz19status = RetVal[5]; // Status |
− | } else { | + | } else { |
− | co2Val = 0; // 異常値の場合 0 | + | co2Val = 0; // 異常値の場合 0 |
}<BR/> | }<BR/> | ||
− | return co2Val; //返り値がCO2濃度:400-5000ppm。エラーなら0ppm | + | return co2Val; //返り値がCO2濃度:400-5000ppm。エラーなら0ppm |
− | } | + | } |
− | + | ||
− | //calmode=0 : セルフキャリブレーションON (ABC function) | + | //calmode=0 : セルフキャリブレーションON (ABC function) |
− | //calmode=1 : セルフキャリブレーションOFF | + | //calmode=1 : セルフキャリブレーションOFF |
− | //calmode=2 : 今すぐキャリブレーション実施 | + | //calmode=2 : 今すぐキャリブレーション実施 |
− | //calmode=3 : コマンドによるキャリブレーション ※HD端子を7秒LOWにする代わり (使用禁止:うまく動作できてない) | + | //calmode=3 : コマンドによるキャリブレーション ※HD端子を7秒LOWにする代わり (使用禁止:うまく動作できてない) |
− | //NOTE:ZERO POINT is 400PPM, PLS MAKE SURE THE SENSOR HAD BEEN WORKED UNDER 400PPM FOR OVER 20MINUTES | + | //NOTE:ZERO POINT is 400PPM, PLS MAKE SURE THE SENSOR HAD BEEN WORKED UNDER 400PPM FOR OVER 20MINUTES |
− | void mhz19setCalMode(int calMode) { // ゼロ調モードを設定 | + | void mhz19setCalMode(int calMode) { // ゼロ調モードを設定 |
− | byte autocalOn[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; | + | byte autocalOn[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; |
− | byte autocalOff[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; | + | byte autocalOff[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; |
− | byte cmdCalib[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78}; | + | byte cmdCalib[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78}; |
− | byte RetVal[9]; | + | byte RetVal[9]; |
− | + | ||
− | switch(calMode){ //Self-Calibration mode | + | switch(calMode){ //Self-Calibration mode |
− | case 0: | + | case 0: |
− | mhz19.write(autocalOff, sizeof autocalOff); // OFF | + | mhz19.write(autocalOff, sizeof autocalOff); // OFF |
− | Serial.println("CO2 Automatic Calibration mode : OFF"); | + | Serial.println("CO2 Automatic Calibration mode : OFF"); |
− | break; | + | break; |
− | case 1: | + | case 1: |
− | mhz19.write(autocalOn, sizeof autocalOn); // ON | + | mhz19.write(autocalOn, sizeof autocalOn); // ON |
− | Serial.println("CO2 Automatic Calibration mode : ON"); | + | Serial.println("CO2 Automatic Calibration mode : ON"); |
− | break; | + | break; |
− | case 2: | + | case 2: |
− | mhz19.write(cmdCalib, sizeof cmdCalib); // キャリブレーション | + | mhz19.write(cmdCalib, sizeof cmdCalib); // キャリブレーション |
− | Serial.println("Cal"); | + | Serial.println("Cal"); |
− | calibration(); | + | calibration(); |
− | break; | + | break; |
− | case 3: | + | case 3: |
− | mhz19.write(cmdCalib, sizeof cmdCalib); // コマンドによるキャリブレーション[NG] | + | mhz19.write(cmdCalib, sizeof cmdCalib); // コマンドによるキャリブレーション[NG] |
− | Serial.println("CO2 Calibration by command : [x]"); | + | Serial.println("CO2 Calibration by command : [x]"); |
− | break; | + | break; |
− | default: | + | default: |
− | //何もしない | + | //何もしない |
− | break; | + | break; |
− | } | + | } |
− | mhz19.readBytes((char *)RetVal, sizeof RetVal); // clear read buffer | + | mhz19.readBytes((char *)RetVal, sizeof RetVal); // clear read buffer |
− | } | + | } |
− | + | ||
− | void setup(){ | + | void setup(){ |
− | mhz19_init(); | + | mhz19_init(); |
− | } | + | } |
− | void loop(){ | + | void loop(){ |
− | int co2 = mhz19readCO2(); | + | int co2 = mhz19readCO2(); |
− | sprintf(buf, "%d",co2); | + | sprintf(buf, "%d",co2); |
− | Serial.println(buf); | + | Serial.println(buf); |
− | delay(2000); | + | delay(2000); |
} | } | ||
|} | |} | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
== 経験からの対策 == | == 経験からの対策 == | ||
418行目: | 417行目: | ||
</blockquote> | </blockquote> | ||
+ | |} |
2022年11月6日 (日) 15:06時点における版
Name MH-Z19C メーカー Winsen Datasheet MH-Z19C コマンド:CO2濃度受信, SelfCalibration MH-Z19B ABC Logic Functionの説明(automatic calibration)
Accuracy CO2 ±(50ppm+5%) Temp ±---℃ 3D data f3d(Fusion360), step Driver mhz19_uart ※ESP32はHardwareSerial-UART2 販売店 日本 秋月電子通商 \2480 Amazon \2799
中国 Aliexpress winsen \2272/5p, \2360/1p Aliexpress mili technology $15.7/1p
Pin Function
仕様の補足
暖機時間 (Warm-up time)
購入直後の最初の起動時 24時間 条件 常温
毎回の起動時 1分(この間は出力データもデタラメな値を示すので破棄する必要がある)
CO2測定間隔
測定間隔 2秒 PWM出力間隔 1秒 ただし次の測定値が確定するまで前の値を出力する。
デジタル出力 随時 ただし次の測定値が確定するまで前の値を出力する。
セルフキャリブレーション
センサーが一定期間連続して動作した後、検出したCO2濃度に応じて自動的にゼロ点を校正する機能
- 校正周期 :電源投入時から 24 時間毎
- 校正するCO2濃度: 400ppm
- 想定する用途 :オフィス環境、家庭環境
- 適さない用途 :農業用温室、農場、冷蔵倉庫
拭き取り
ケースを拭き取れる溶剤 なし 拭き取り方法 なし
外装の材質 非公開フィルタの材質 非公開
※メーカーは拭き掃除を許可していない。
部材の情報
ケース 非公開 コネクタ出しタイプのコネクタ品名 非公開
- ※推測:JVT connector
コマンド
コマンドによるキャリブレーション機能入り。 そのコマンド仕様はMH-Z19/B等過去機種の仕様を参照。
電源電圧
MHZ19Cは2秒周期で0.4秒のランプ点灯を繰り返す。
精度保証範囲 5±0.1V 動作範囲 5±0.5V
- ランプが点灯する瞬間の大電流が流れるため、5V電源電圧が10ms程度の間Δ0.3V以上dropする事がある。
- ここで言う電圧範囲は、どのタイミングの値の事を言っているのかも非公開。
CPU
内部にはCPU(16MHz)が入っている。
通信用ライブラリ
とりあえずこれで動くけど、コマンドによるキャリブレーションが機能していない。
以下、理解して補足すれば使えるプログラムです。
/////// MHZ19C /////// HardwareSerial mhz19(2); void mhz19_init(void){ delay(200); //電解コンデンサがチャージ完了するまで待つ digitalWrite(pin_XCL103EN, 1); //mhz19の電源をONにする delay(100); //電源が安定するのを待つ mhz19.begin(9600); //MHZ19C動作開始 ※5V用電解コンデンサは100μFにする事!470μFにするとXCL103が時々スタートしなくなる digitalWrite(pin_whiteLED, 0); //calibで点灯、それ以外は消灯 //mhz19setCalMode(1); //calmode=0:autocal=OFF / calmode=1:autocal=ON } //返値:co2濃度、mhz19temp=温度、mhz19status=status int mhz19readCO2() { // MH-Z19CからCO2濃度の値を読む<BR/> uint16_t co2Val; byte ReadCO2Cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; byte RetVal[9]; while (mhz19.available())mhz19.read(); // 受信バッファクリア memset(RetVal, 0x00, sizeof RetVal); // 変数をクリア mhz19.write(ReadCO2Cmd, sizeof ReadCO2Cmd); // 測定コマンド送信 mhz19.readBytes((char *)RetVal, sizeof RetVal); // 測定結果をMHZ19Cから受信 if ((RetVal[0] == 0xff) && (RetVal[1] == 0x86)) { // 正常に読めた? 0xff=255 0x86=134 co2Val = RetVal[2] * 256 + RetVal[3]; // CO2濃度を計算 mhz19temp = RetVal[4];// - 40; // 内部温度 ※参考用 mhz19status = RetVal[5]; // Status } else { co2Val = 0; // 異常値の場合 0 }<BR/> return co2Val; //返り値がCO2濃度:400-5000ppm。エラーなら0ppm } //calmode=0 : セルフキャリブレーションON (ABC function) //calmode=1 : セルフキャリブレーションOFF //calmode=2 : 今すぐキャリブレーション実施 //calmode=3 : コマンドによるキャリブレーション ※HD端子を7秒LOWにする代わり (使用禁止:うまく動作できてない) //NOTE:ZERO POINT is 400PPM, PLS MAKE SURE THE SENSOR HAD BEEN WORKED UNDER 400PPM FOR OVER 20MINUTES void mhz19setCalMode(int calMode) { // ゼロ調モードを設定 byte autocalOn[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; byte autocalOff[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; byte cmdCalib[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78}; byte RetVal[9]; switch(calMode){ //Self-Calibration mode case 0: mhz19.write(autocalOff, sizeof autocalOff); // OFF Serial.println("CO2 Automatic Calibration mode : OFF"); break; case 1: mhz19.write(autocalOn, sizeof autocalOn); // ON Serial.println("CO2 Automatic Calibration mode : ON"); break; case 2: mhz19.write(cmdCalib, sizeof cmdCalib); // キャリブレーション Serial.println("Cal"); calibration(); break; case 3: mhz19.write(cmdCalib, sizeof cmdCalib); // コマンドによるキャリブレーション[NG] Serial.println("CO2 Calibration by command : [x]"); break; default: //何もしない break; } mhz19.readBytes((char *)RetVal, sizeof RetVal); // clear read buffer } void setup(){ mhz19_init(); } void loop(){ int co2 = mhz19readCO2(); sprintf(buf, "%d",co2); Serial.println(buf); delay(2000); } |}経験からの対策
通信Error
Core Debug LevelをDebug/Verboseとすると、通信するたびにDebug情報としてエラーの表示が現れる。
[E] addApbChangeCallback(): duplicate func=4000F8B7C arg=3FFBDF14
調査結果
mhz19_uartで通信するとRead/Write両方で上記エラーが毎回発生するけど機能上は問題ない。作者HP
対策
調査中。ノイズ
症状
- ESP32とMH-Z19CをUART&PWMの配線で接続すると、ESP32が定期的にリブートした。
- リブートするのは起動から必ず一定の時間になっている。
調査結果
- ・起動からリブートまでの経過時間:100秒。1秒の誤差もない。
- ・電流との相関:CO2測定のためのランプ点滅周期とは無関係なタイミングでリブートする。
- ・Error表示(UART):リブート時のシリアル出力に出るメッセージでは、Flash Readができない記述になっている。
- ・効果的な対策①:ESP32のTXD端子出口と、ESP32のPWM受信端子に直列に100Ωを入れると問題が消えた。
- 反射波が影響している可能性が高い。
- ・効果的な対策②:ESP32との接続配線を全て外すと問題が消えた。
原因の推測
- MH-Z19Cとの通信部分に外部からのノイズが乗りやすい。
- MH-Z19Cが不要なノイズを作っている可能性が高い。
- Wifi/Bluetoothかもしれない。(使ってないけど)
対策
- ノイズが乗っても共振や反射波で誤動作レベルになりにくいように、終端抵抗を入れてみる。
- ESP32との通信部分(送信側)の配線は全て両端(ESP32側/MH-Z19C側)に100Ωを入れて反射波を抑える。
結果
- この対策で問題が起きなくなった。
- ※ただし、確認済の機能や評価条件が少ないため、まだ要確認。
電源電圧の低下
症状
- MH-Z19C内のランプが光るタイミングで+5.0V電源電圧が約2V付近まで10μs程度の間だけ低下する。
- この時の電源はXCL103モジュール(秋月)+入出力に330μF追加。
調査結果
- ・電圧低下のタイミングは常にランプが光り始める時。
- ・ランプの駆動は、2秒周期で毎回0.4秒だけ発光する。Max125mA。
- ・電源モジュールをTDK製のCC10-0505SF-Eに変更するとドロップ時間が200ms程度にまで伸びて悪化した。
- ・瞬間的な電流消費対策:電源IC(XCL103)の手前にインダクタ100μHを追加すると
- CPU側の電圧ドロップはなくなったが、Rebootは改善されなかった。
- ・起動時にRebootするのはENにReset-IC(2.9V)を入れた事と、SPI-FLASHの動作不安定さが主な原因だった。
- ESP32のENにReset-ICを入れるとBootメッセージがUARTに出力されたり色々な事が起こるため除去した方が気楽。
- ・XCL103の入力側に330μH+470μFを追加するとXCL103が起動しなくなった。
- 追加条件を変えてみた。
問題なかった組み合わせ
○:異常なし ×:Reboot10μF 100μF 330μF 470μF 0μH ○ ○ × × 10μH ○ ○ ○ 33μH ○ ○ 100μH ○ ○ ○
- 100μH以下以下であれば問題ない。
- ・PCのUSBポートから電源を取っていたのを、単独USB充電器に変更してみたが、変化なし。
- つまり、PCの電源起因の可能性は低い。
- ・USBケーブルを15cmまで短くしてみたが、変化なし。
原因の推測
- ①スイッチング電源は電流0からの応答ではレスポンスが極端に悪化する特性がある。
- その場合、微量な電流を常に負荷に流すようにすると改善されるはず。
- ②入出力のコンデンサが大きいとチャージする時間が長くなるため、レスポンスが落ちて戻りにくくなる。
- ③XCL103の短絡保護回路が働いて出力を止めている。
- ④ESP32に追加で付けたReset-ICが誤動作している。
対策
- ①漏れ電流:MH-Z19C用の+5V電源(+5VP)にLEDを付けて微弱な電流を流す。
- 白LED+100Ωで17.87mA(±5%で 17.13~18.69mA)が常に流れる。
- ②電流立ち上がりで高周波ノイズを除去しやすいように、
- MH-Z19C手前のパスコン(XCL103出力側)をCE10μF×3に変更してみる。
- ③XCL103入力インダクタ・コンデンサを減らして、安全動作領域になるように変更する。
- ④Reset-IC除去。
結果
- ① 問題なく動作した。
- ② 問題なく動作した。
- ③ 10μH+470μFが最もドロップ電圧とドロップ時間が少なかったため最適。
- ④ 起動時にRebootしなくなった。
参考資料
他のCO2センサーとの比較表 https://canair.io/docs/co2_comparative.html
販売店
秋月電子通商 2480円
Winsen社製品の正規取り扱い店。 長期供給保証なし。在庫のみ。量産非対応。
技術サポート不可。(技術サポートは中国Winsen社が担当)
注意事項
戻るAliexpressに類似品が多く出回っている。仕様が全く異なるため要注意。
Winsen製であっても似たパッケージ・似たSPECが多い。
購入時には、pinタイプ/コネクタタイプ、測定範囲(400-5000ppm / 400-2000ppm)を選ぶ。
ただし、コネクタの品名は非公開。