Working Principle
The Winsen MH-Z19B is a Non-Dispersive Infrared (NDIR) CO2 sensor. It exploits the fact that CO2 molecules absorb infrared light at 4.26 µm. Inside the sensor, an IR LED shines through a gas sample chamber. A dual-wavelength detector measures absorption at 4.26 µm (CO2-sensitive) and a reference wavelength. The ratio cancels out ageing and contamination effects.
Analogy: Imagine shining a torch through a tube of smoke. The thicker the smoke, the dimmer the light on the other side. The MH-Z19B does this with invisible infrared light and CO2 molecules.
Electrical Characteristics
| Parameter | Value |
|---|---|
| Supply Voltage | 4.5 V – 5.5 V DC |
| Measurement Range | 400 – 5000 ppm (or 0–10000 ppm variant) |
| Accuracy | ±50 ppm + 5% of reading |
| Response Time (T90) | <120 seconds |
| Interface | UART (9600 baud) and PWM output |
| Auto-Calibration | ABC logic (24-hour cycle, assumes 400 ppm baseline) |
| Current | <18 mA average |
| Lifespan | >5 years |
Interfacing with an MCU
The MH-Z19B has a 4-pin header: Vin, GND, TX, RX for UART communication at 9600 baud. It also outputs a PWM signal proportional to CO2 concentration. Use 5 V power — the sensor's IR emitter requires it.
Calibration
The MH-Z19B has Automatic Baseline Correction (ABC):
- ABC enabled (default): Over 24-hour cycles, the sensor assumes the lowest reading is outdoor-air baseline (~400 ppm) and self-corrects. Best for occupied spaces that are regularly ventilated.
- ABC disabled: For greenhouses or industrial environments where CO2 never drops to 400 ppm, disable ABC and perform manual zero-point calibration in fresh outdoor air.
- Manual zero calibration: Expose to 400 ppm air for 20+ minutes, then send the calibration command via UART.
Code Example
/*
* MH-Z19B CO2 Sensor — Arduino UART
* Wiring: TX→Pin10(RX), RX→Pin11(TX), Vin→5V
*/
#include <SoftwareSerial.h>
SoftwareSerial co2Serial(10, 11); /* RX=10, TX=11 */
byte cmd_read[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
void setup() {
Serial.begin(9600);
co2Serial.begin(9600);
Serial.println("MH-Z19B CO2 Sensor Ready");
delay(3000); /* Sensor warm-up */
}
int readCO2() {
byte response[9];
co2Serial.write(cmd_read, 9);
delay(100);
if (co2Serial.available() >= 9) {
co2Serial.readBytes(response, 9);
byte checksum = 0;
for (int i = 1; i < 8; i++) checksum += response[i];
checksum = 0xFF - checksum + 1;
if (response[0] == 0xFF && response[1] == 0x86 && response[8] == checksum) {
return (response[2] << 8) | response[3];
}
}
return -1;
}
void loop() {
int ppm = readCO2();
if (ppm >= 0) {
Serial.print("CO2: "); Serial.print(ppm); Serial.println(" ppm");
if (ppm < 800) Serial.println(" → Good air quality");
else if (ppm < 1500) Serial.println(" → Moderate — open a window");
else Serial.println(" → Poor — ventilate immediately!");
}
delay(5000);
}
Real-World Applications
Smart Classroom Ventilation
Schools install MH-Z19B sensors in every classroom to monitor CO2 levels in real time. When levels exceed 1000 ppm, the building management system automatically increases ventilation fan speed. Studies show maintaining CO2 below 1000 ppm improves student cognitive performance by 15–20%.
Limitations
- Warm-up time: Requires ~3 minutes to stabilise after power-on; pre-heat in battery designs.
- ABC assumptions: ABC logic assumes daily exposure to fresh air — incorrect in sealed environments (greenhouses).
- Cross-sensitivity: High concentrations of water vapour or volatile organics can slightly affect readings.
- 5 V required: The sensor's IR emitter needs 5 V; it cannot run on 3.3 V systems without a boost converter.