I would like to connect a CO2 sensor to the Wemos D1. I would like it to work via UART (RX, TX) and not the PWM pin. I've encountered numerous libraries on Github, but I can't seem to get it working. I've connected the following things:

enter image description here

I've tried a lot of snippets from all over the internet, and a lot of libraries on Github, but I can't seem to get it working. For the sake of this question, I am going to use this advanced library.

This is my code:

#include <Arduino.h>
#include "MHZ19.h"                                         // include main library
#include <SoftwareSerial.h>                                // Remove if using HardwareSerial or non-uno library compatable device

#define RX_PIN D1                                          // Rx pin which the MHZ19 Tx pin is attached to
#define TX_PIN D2                                          // Tx pin which the MHZ19 Rx pin is attached to
#define BAUDRATE 9600                                      // Native to the sensor (do not change)

MHZ19 myMHZ19;                                             // Constructor for MH-Z19 class
SoftwareSerial mySerial(RX_PIN, TX_PIN);                   // Uno example
//HardwareSerial mySerial(1);                              // ESP32 Example

unsigned long getDataTimer = 0;                             // Variable to store timer interval

void setup()
    Serial.begin(9600);                                     // For ESP32 baudarte is 115200 etc.
    mySerial.begin(BAUDRATE);                               // Uno example: Begin Stream with MHZ19 baudrate
    //mySerial.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN); // ESP32 Example
    myMHZ19.begin(mySerial);                                // *Important, Pass your Stream reference 
    myMHZ19.autoCalibration();                              // Turn auto calibration ON (disable with autoCalibration(false))

void loop(){
    if (millis() - getDataTimer >= 2000)                    // Check if interval has elapsed (non-blocking delay() equivilant)
        int CO2;                                            // Buffer for CO2

        /* note: getCO2() default is command "CO2 Unlimited". This returns the correct CO2 reading even 
        if below background CO2 levels or above range (useful to validate sensor). You can use the 
        usual documented command with getCO2(false) */

        CO2 = myMHZ19.getCO2();                             // Request CO2 (as ppm)
        Serial.print("CO2 (ppm): ");                      

        int8_t Temp;                                         // Buffer for temperature
        Temp = myMHZ19.getTemperature();                     // Request Temperature (as Celsius)
        Serial.print("Temperature (C): ");                  

        getDataTimer = millis();                            // Update interval

I am constantly getting this:

19:39:59.750 -> !Error: Timed out waiting for response
19:39:59.784 -> !ERROR: Failed to verify connection(1) to sensor. Failed to stablise
19:39:59.853 -> !ERROR: Initial communication errorCode recieved
19:40:00.272 -> !Error: Timed out waiting for response
19:40:00.758 -> !Error: Timed out waiting for response
19:40:00.792 -> CO2 (ppm): 0
19:40:01.280 -> !Error: Timed out waiting for response
19:40:01.315 -> Temperature (C): -17

Am I connecting something wrong or what am I doing wrong?

EDIT: Version using only the serial instead of library that also doesn't work:

#include <SoftwareSerial.h>
SoftwareSerial co2Serial(3, 1);
unsigned long startTime = millis();

void setup() {
  pinMode(9, INPUT);

void loop() {
  Serial.print("Time from start: ");
  Serial.print((millis() - startTime) / 1000);
  Serial.println(" s");
  int ppm_uart = readCO2UART();

int readCO2UART(){
  byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
  byte response[9]; // for answer

  Serial.println("Sending CO2 request...");
  co2Serial.write(cmd, 9); //request PPM CO2

  // clear the buffer
  memset(response, 0, 9);
  int i = 0;
  while (co2Serial.available() == 0) {
    Serial.print("Waiting for response ");
    Serial.println(" s");

  if (co2Serial.available() > 0) {
      co2Serial.readBytes(response, 9);

  // print out the response in hexa
  for (int i = 0; i < 9; i++) {
    Serial.print(String(response[i], HEX));
    Serial.print("   ");

  // checksum
  byte check = getCheckSum(response);
  if (response[8] != check) {
    Serial.println("Checksum not OK!");
    Serial.print("Received: ");
    Serial.print("Should be: ");

  // ppm
  int ppm_uart = 256 * (int)response[2] + response[3];
  Serial.print("PPM UART: ");

  // temp
  byte temp = response[4] - 40;
  Serial.print("Temperature? ");

  // status
  byte status = response[5];
  Serial.print("Status? ");
  if (status == 0x40) {
    Serial.println("Status OK"); 

  return ppm_uart;

byte getCheckSum(byte *packet) {
  byte i;
  unsigned char checksum = 0;
  for (i = 1; i < 8; i++) {
    checksum += packet[i];
  checksum = 0xff - checksum;
  checksum += 1;
  return checksum;
  • You do know you need to cross RX and TX with UART communications, don't you? TX connects to RX, and RX connects to TX.
    – Majenko
    Aug 18, 2019 at 17:53
  • I know, tried it both ways (drew it wrong in this picture), neither works
    – Mark D
    Aug 18, 2019 at 17:58
  • did you try to send the commands without using a MHZ19 library?
    – jsotola
    Aug 18, 2019 at 22:51
  • 1
    Yes, also no response, see the edit
    – Mark D
    Aug 19, 2019 at 17:39

1 Answer 1


I know it is rather late to respond. What worked for me working with a WEMOS D1 mini and the MH-Z19C was reassigning the Software Pins used SoftwareSerial co2Serial(D1, D2); In that case the second sketch posted works atleast for me.

The problem might be that the ESP8266 uses the hardware serial lines for connection to the USB-to-Serial bridge (usually a CH340 or CP2102 in most designs) which means you might encounter problems if you want to use the same serial line for more than one device.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.