01-MH-Z19 +arduino +oled

(2017/03/16)

自製二氧化碳偵測器

本篇文章主要參考 【MH-Z19 CO2 meter】、【MH-Z19 CO2 sensor giving diferent values using UART and PWM

有關Arduino+OLED,請參考【09-OLED顯示器

  • 所需設備:

    • 紅外二氧化碳傳感器 MH-Z19 CO2檢測傳感器模塊 (淘寶,約台幣532元)

    • OLED LCD Display 0.96" I2C IIC SPI Serial 128X64(淘寶,約台幣70元)

    • Arduino Uno板子

  • 接線說明:

    • MH-Z19的Vin接Arduino的5V;GND接Arduino的GND

    • 採用PWM來讀取資料(CO2的PPM值介於0-2000ppm):MH-Z19的PWM接Arduino的D10(可自選)

    • 採用UART來讀取資料(CO2的PPM值介於0-5000ppm):MH-Z19的RX接Arduino的D3(可自選);MH-Z19的TX接Arduino的D2(可自選)

    • 本例PWM及UART都有接,並觀察所測的值的差異

  • 接線圖

  • 註:本例希望做出一個可移動的CO2偵測器,因此將所有設備放入一個筆盒內

  • 外觀圖:

    • 註:一直調整設備的位置,所以整個筆盒挖的都是洞

    • 程式部分,參考上面那兩篇文章,整合如下:(紅色字是有關MH-Z19、藍色字是有關OLED)

#include <SPI.h>

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <SoftwareSerial.h>

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)

#error("Height incorrect, please fix Adafruit_SSD1306.h!");

#endif

#define pwmPin 10 //接D10

int preheatSec = 120; //預熱時間

SoftwareSerial mySerial(2, 3); // RX, TX

byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};

unsigned char response[9];

unsigned long th, tl,ppm, ppm2, ppm3 = 0;

void setup() {

Serial.begin(9600);

mySerial.begin(9600);

pinMode(pwmPin, INPUT);

display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x64)

display.setTextColor(WHITE);

}

void loop() {

mySerial.write(cmd,9);

mySerial.readBytes(response, 9);

unsigned int responseHigh = (unsigned int) response[2];

unsigned int responseLow = (unsigned int) response[3];

ppm = (256*responseHigh)+responseLow;

//CO2 via pwm

do {

th = pulseIn(pwmPin, HIGH, 1004000) / 1000;

tl = 1004 - th;

ppm2 = 2000 * (th-2)/(th+tl-4);

ppm3 = 5000 * (th-2)/(th+tl-4);

} while (th == 0);

display.clearDisplay();

display.setCursor(0,0);

if (preheatSec > 0) {

Serial.print("Preheating");

Serial.println(preheatSec);

displayPreheating(preheatSec);

preheatSec--;

}

else {

Serial.println(ppm); //UART

Serial.println(th);

Serial.println(ppm2); //PWM (0-2000PPM)

Serial.println(ppm3); //PWM (0-5000PPM)

Serial.println("-----------");

displayPPM(ppm,ppm2);

}

delay(1000);

}

void displayPreheating(int secLeft) {

display.setTextSize(2);

display.println("PREHEATING");

display.setTextSize(1);

display.println();

display.setTextSize(5);

display.print(" ");

display.print(secLeft);

display.display();

}

void displayPPM(long ppm,long ppm2) {

display.setTextSize(2);

display.println("CO2 PPM");

display.setTextSize(1);

display.println();

display.setTextSize(2);

display.print("UART:");

display.println(ppm);

//display.println();

display.print("PWM:");

display.print(ppm2);

display.display();

}

  • 測試結果

    • 其他

        • 本設備無校正,不知哪個值較正確?

      • 從參考資料得知,UART值較合理,室內空間正常CO2濃度約為650ppm,如果高於此應該就要開個窗讓空氣流通了!(戶外約400PPM)(The UART readings at least make sense. Normal CO2 concentrations at open spaces should be around 400ppm, and a room may well be around 650ppm)

如果只要利用PWM來求得CO2的值,就變得很簡單,只剩下面紅色字的部分

#include <SPI.h>

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <SoftwareSerial.h>

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)

#error("Height incorrect, please fix Adafruit_SSD1306.h!");

#endif

#define pwmPin 10 //接D10

int preheatSec = 120; //預熱時間

unsigned long th, tl, ppm2, ppm3 = 0;

void setup() {

Serial.begin(9600);

pinMode(pwmPin, INPUT);

display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x64)

display.setTextColor(WHITE);

}

void loop() {

//CO2 via pwm

do {

th = pulseIn(pwmPin, HIGH, 1004000) / 1000;

tl = 1004 - th;

ppm2 = 2000 * (th-2)/(th+tl-4);

ppm3 = 5000 * (th-2)/(th+tl-4);

} while (th == 0);

display.clearDisplay();

display.setCursor(0,0);

if (preheatSec > 0) {

Serial.print("Preheating");

Serial.println(preheatSec);

displayPreheating(preheatSec);

preheatSec--;

}

else {

Serial.println(th);

Serial.println(ppm2); //PWM (0-2000PPM)

Serial.println(ppm3); //PWM (0-5000PPM)

Serial.println("-----------");

displayPPM(ppm2,ppm3);

}

delay(1000);

}

void displayPreheating(int secLeft) {

display.setTextSize(2);

display.println("PREHEATING");

display.setTextSize(1);

display.println();

display.setTextSize(5);

display.print(" ");

display.print(secLeft);

display.display();

}

void displayPPM(long ppm,long ppm2) {

display.setTextSize(2);

display.println("CO2 PPM");

display.setTextSize(1);

display.println();

display.setTextSize(2);

display.print("PWM:");

display.println(ppm2);

//display.println();

display.print("PWM:");

display.print(ppm3);

display.display();

}

參考資料:

針對第一篇文章【MH-Z19 CO2 meter】的程式,如下(失敗)

#include <SPI.h>

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)

#error("Height incorrect, please fix Adafruit_SSD1306.h!");

#endif

#define pwmPin 7

int preheatSec = 120;

int prevVal = LOW;

long th, tl, h, l, ppm = 0;

void PWM_ISR() {

long tt = millis();

int val = digitalRead(pwmPin);

if (val == HIGH) {

if (val != prevVal) {

h = tt;

tl = h - l;

prevVal = val;

}

} else {

if (val != prevVal) {

l = tt;

th = l - h;

prevVal = val;

ppm = 2000 * (th - 2) / (th + tl - 4);

}

}

}

void setup() {

Serial.begin(115200);

pinMode(pwmPin, INPUT);

attachInterrupt(digitalPinToInterrupt(pwmPin), PWM_ISR, CHANGE);

display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x64)

display.setTextColor(WHITE);

}

void displayPreheating(int secLeft) {

display.setTextSize(2);

display.println("PREHEATING");

display.setTextSize(1);

display.println();

display.setTextSize(5);

display.print(" ");

display.print(secLeft);

display.display();

}

void displayPPM(long ppm) {

display.setTextSize(2);

display.println("CO2 PPM");

display.setTextSize(1);

display.println();

display.setTextSize(5);

if (ppm < 1000) {

display.print(" ");

}

display.print(ppm);

display.display();

Serial.println(ppm);

}

void loop() {

display.clearDisplay();

display.setCursor(0,0);

if (preheatSec > 0) {

displayPreheating(preheatSec);

preheatSec--;

}

else {

displayPPM(ppm);

}

delay(1000);

}

結果:120秒預熱後,CO2 PPM都是呈現0,不知問題在哪裡???

針對第二篇文章MH-Z19 CO2 sensor giving diferent values using UART and PWM的程式,如下

#include <SoftwareSerial.h>

#define pwmPin 10

SoftwareSerial mySerial(A0, A1); // RX, TX

byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};

unsigned char response[9];

unsigned long th, tl,ppm, ppm2, ppm3 = 0;

void setup() {

Serial.begin(9600);

mySerial.begin(9600);

pinMode(pwmPin, INPUT);

}

void loop() {

mySerial.write(cmd,9);

mySerial.readBytes(response, 9);

unsigned int responseHigh = (unsigned int) response[2];

unsigned int responseLow = (unsigned int) response[3];

ppm = (256*responseHigh)+responseLow;

//CO2 via pwm

do {

th = pulseIn(pwmPin, HIGH, 1004000) / 1000;

tl = 1004 - th;

ppm2 = 2000 * (th-2)/(th+tl-4);

ppm3 = 5000 * (th-2)/(th+tl-4);

} while (th == 0);

Serial.println(ppm);

Serial.println(th);

Serial.println(ppm2);

Serial.println(ppm3);

Serial.println("-----------");

delay(5000);

}

459 <- ppm (UART)93 <- Milliseconds UART is HIGH 182 <- ppm2 (PWM) with 2000ppm as limit 455 <- ppm3 (PWM) with 5000ppm as limit -----------46093182455-----------46093182455-----------