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的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-----------