here is a code-version which I have stripped down to the bare minimum to test if the module does respond at all
pre-heating time and calibrating the sensor are disabled through commenting out this part of the code
I changed the Rx/Tx-pins of the software-serial to D7/D6
I added intensive documentation about which Rx/Tx-pin has which meaning on which side to make it very clear
what needs to be connected to what
This code was uploaded and tested witha MH-Z14A-module in combination with a ESP8266-nodeMCU right before pasting the code here. So there is a high chance that this code will really work with teh same hardware
/***************************************************************************
Sample sketch for using a mh-z14a co2 with a ESP8266
for TESTING a MH_Z14A-sensor
Written by Erik Lemcke, combined out of the following samples:
https://www.letscontrolit.com/forum/viewtopic.php?f=2&t=1785&start=40, calibration sample by s3030150
https://www.home-assistant.io/blog/2015/10/11/measure-temperature-with-esp8266-and-report-to-mqtt/, home assistant mqqt by Paulus Schoutsen
modified by Stefan Ludwig (StefanL38)
wiring:
nodeMCU mh-z14a
D7(GPIO13)--> Rx
D6(GPIO12)<-- Tx
GND GND
use the nodeMCU-Vin pin to supply with 5V, the mh-z14a needs 5v
***************************************************************************/
#include <SoftwareSerial.h>
#include <SPI.h>
#include <Wire.h>
#define INTERVAL 5000
//Tx_pin of MH_Z is connected to nodeMCU-RX-pin D6 (GPIO12)
#define nodeMCU_D6_GPIO12 12
#define MH_Z14_TX nodeMCU_D6_GPIO12
//Rx_pin of MH_Z is connected to nodeMCU-TX-pin D7 (GPIO13)
#define nodeMCU_D7_GPIO13 13
#define MH_Z14_R_Pin nodeMCU_D7_GPIO13
byte mhzResp[9]; // 9 bytes bytes response
byte mhzCmdReadPPM[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
byte mhzCmdCalibrateZero[9] = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78};
byte mhzCmdABCEnable[9] = {0xFF,0x01,0x79,0xA0,0x00,0x00,0x00,0x00,0xE6};
byte mhzCmdABCDisable[9] = {0xFF,0x01,0x79,0x00,0x00,0x00,0x00,0x00,0x86};
byte mhzCmdReset[9] = {0xFF,0x01,0x8d,0x00,0x00,0x00,0x00,0x00,0x72};
byte mhzCmdMeasurementRange1000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x03,0xE8,0x7B};
byte mhzCmdMeasurementRange2000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x07,0xD0,0x8F};
byte mhzCmdMeasurementRange3000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x0B,0xB8,0xA3};
byte mhzCmdMeasurementRange5000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x13,0x88,0xCB};
int shifts = 0 ;
int co2ppm;
long previousMillis = 0;
// the softwareserial interface is defined from the view of the nodeMCU
// nodeMCUs Rx is MH_Zs Tx and vice versa nodeMCUs Tx is MH_Zs Rx
//basic definition of Software-serial is SoftwareSerial MySerial(Rx,Tx)
// from the nodeMCUs view using nodeMCU-pins
SoftwareSerial co2Serial(nodeMCU_D6_GPIO12, nodeMCU_D7_GPIO13); // define MH-Z14A
// from the MH_Zs view using the MH_Z-pins
//SoftwareSerial co2Serial(MH_Z14_TX, MH_Z14_R_Pin); // define MH-Z14A
byte checksum(byte response[9]){
byte crc = 0;
for (int i = 1; i < 8; i++) {
crc += response[i];
}
crc = 255 - crc + 1;
return crc;
}
void disableABC() {
co2Serial.write(mhzCmdABCDisable, 9);
}
void enableABC() {
co2Serial.write(mhzCmdABCEnable, 9);
}
void setRange5000() {
co2Serial.write(mhzCmdMeasurementRange5000, 9);
}
void calibrateZero(){
co2Serial.write(mhzCmdCalibrateZero, 9);
}
int readCO2() {
byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
byte response[9];
co2Serial.write(cmd, 9);
// The serial stream can get out of sync. The response starts with 0xff, try to resync.
while (co2Serial.available() > 0 && (unsigned char)co2Serial.peek() != 0xFF) {
co2Serial.read();
yield();
shifts++;
}
memset(response, 0, 9);
co2Serial.readBytes(response, 9);
for (int i = 0; i < 9; i++) {
Serial.print(" 0x");
Serial.print(response[i], HEX);
}
Serial.println(" Response OK. Shifts=" + String(shifts));
if (response[1] != 0x86)
{
Serial.println(" Invalid response from co2 sensor!");
delay(1000);
return -1;
}
if (response[8] == checksum(response)) {
int responseHigh = (int) response[2];
int responseLow = (int) response[3];
int ppm = (256 * responseHigh) + responseLow;
return ppm;
} else {
Serial.println("CRC error!");
return -1;
}
}
void setup() {
Serial.begin(115200);
Serial.println("Startup");
co2Serial.begin(9600);
Serial.println("co2Serial.begin(9600);");
unsigned long previousMillis = millis();
delay(500);
/*
Serial.println("Disabling ABC");
disableABC();
*/
Serial.println("Setting range to 5000");
setRange5000();
/*
Serial.println("Waiting half an hour before calibrating zero");
delay(1800000);
calibrateZero();
Serial.println("Zero was calibrated");
*/
Serial.println("leaving Setup");
}
long lastMsg = 0;
void loop() {
unsigned long now = millis();
//send a meaage every minute
if (now - lastMsg > 5 * 1000) {
lastMsg = now;
unsigned long currentMillis = millis();
if (abs(currentMillis - previousMillis) > INTERVAL)
{
previousMillis = currentMillis;
Serial.print("Requesting CO2 concentration...");
co2ppm = -999;
co2ppm = readCO2();
//If no proper co2 value is returned, try again
while (co2ppm == -1){
yield();
Serial.print("re-Requesting CO2 concentration...");
co2ppm = readCO2();
}
Serial.println(" PPM = " + String(co2ppm));
}
}
}
here is a code-version which I have stripped down to the bare minimum to test if the module does respond at all
pre-heating time and calibrating the sensor are disabled through commenting out this part of the code
I changed the Rx/Tx-pins of the software-serial to D7/D6
I added intensive documentation about which Rx/Tx-pin has which meaning on which side to make it very clear
what needs to be connected to what
This code was uploaded and tested witha MH-Z14A-module in combination with a ESP8266-nodeMCU right before pasting the code here. So there is a high chance that this code will really work with teh same hardware