MHZ19 CO2 Sensor

Sming library supporting the MH-Z19 and MH-Z19B CO2 sensors.

Essentially a re-write of to use Hardware serial port with callbacks.

See for some very detailed and helpful background to the device.

Functionality is split into two main classes:


Access the sensor via serial port


Asynchronously decodes PWM signal from sensor

ESP8266 Connections

Communication requires use of UART0, as UART1 is output-only.

The samples in this library reconfigure the default Serial class to use UART1 for debug output.

UART0 is reconfigured to use the alternate pins by calling HardwareSerial::swap(). Connect as follows:





GPIO14 (D5)


GPIO13 (D7)



GPIO15 (D8)


GPIO2 (D4)


Route UART1 output to USB

Note: A similar approach is used for I2S, see Tone Generator.


API Documentation

namespace MHZ19


using MeasurementCallback = Delegate<void(Measurement &m)>


enum class DetectionRange

Device may be configured to output CO2 PPM values in various ranges.


enumerator PPM_2000
enumerator PPM_5000
enumerator PPM_10000
enum Command

Available commands.

Support differs by device variant


enumerator CMD_GasConcentration

-, B, C

enumerator CMD_CalibrateZeroPoint

-, B

enumerator CMD_CalibrateSpanPoint

-, B

enumerator CMD_SelfCalbrationOnOff

B, C.

enumerator CMD_SetDetectionRange


enum class Error


enumerator success
enumerator incompleteResponse
enumerator invalidResponse
enumerator timeout


unsigned pwmRead(uint8_t pwmPin, DetectionRange range)

Read PWM output from sensor.


This will hang CPU for 1-2 seconds. Use PwmReader instead.

  • pwmPin – GPIO to which the sensor is connected

  • range – Range sensor is configured for

class PwmReader
#include <PwmReader.h>

Reads input pulse width asynchronously.

The ESP8266 lacks any timing capture hardware but as the pulse output from the MHZ19 is very slow it can be decoded using interrupts with high accuracy.

Once started, the PwmReader runs continuously and the last value can be obtained by calling getMeasurement.

Public Types

using Callback = Delegate<void(uint16_t ppm)>

Callback for regular readings.

Public Functions

void begin(uint8_t pin, DetectionRange range)

Start the PWM reader.

Runs continuously in background until end() is called.

  • pin – GPIO to read PWM signal on

  • range – Configured device range

void end()

Stop the PWM reader.

uint16_t getMeasurement() const

Obtain the most recent measurement.

inline void setCallback(Callback callback)

Set a callback to be invoked whenever a valid reading is obtained.

bool suspend()

Temporarily suspend readings and disable interrupts on the PWM pin.

Return values:

bool – true on success, false if reader not initialised

bool resume()

Resume reading after a call to suspend()

Return values:

bool – true on success, false if reader not initialised

union Pulse
#include <PwmReader.h>

Used internally to measure a high/low pulse pair.

Public Functions

inline uint16_t getCycleTime() const
inline bool isValid() const
inline uint16_t calculatePpm(DetectionRange range)

Public Members

uint16_t low
uint16_t high
struct MHZ19::PwmReader::Pulse::[anonymous] [anonymous]
uint32_t value
struct Request
#include <Uart.h>
struct Response
#include <Uart.h>
struct Measurement
#include <Uart.h>
class Uart
#include <Uart.h>

Access MHZ19 sensor via serial port.

Public Functions

inline Uart(HardwareSerial &serial)

Use device in UART mode.


serial – Port and pins must be pre-configured

inline void setSensorNumber(uint8_t num)

Change sensor number field.

Original datasheet identifies first byte as sensor number, others as ‘reserved’. Provided if this value changes in future hardware versions.

inline uint8_t getSensorNumber() const

Get currently configured sensor number.

void setAutoCalibration(bool enable)

Enable/disable zero-point auto-calibration feature. On by default.

Calibrates 400ppm zero point reference automatically at power-on and every 24 hours. Suitable for home/office environment only. Refer to datasheet for details.

void calibrateZero()

Calibrate zero point manually.

Sensor must be in stable 400ppm CO2 environment. Refer to datasheet for detailed procedure.

void calibrateSpan(uint16_t ppm)

Calibrate span point.

Typical calibration value is 2000ppm, but must be above 1000ppm. Refer to datasheet for detailed procedure.

void setDetectionRange(DetectionRange range)

MHZ-19B has configurable detection range.

bool getMeasurement(MeasurementCallback callback)

Read measurement from device.


Used by

SoC support

  • esp32

  • esp32c2

  • esp32c3

  • esp32s2

  • esp32s3

  • esp8266

  • host

  • rp2040