eriknl1982 / esp8266_mh-z14a-mqtt

Sample for reading a mh-z14a co2 sensor with a ESP8266 and sending the result over MQTT
4 stars 1 forks source link

stripped down Code-Version for testing that is working #2

Open StefanL38 opened 3 years ago

StefanL38 commented 3 years ago

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));
    }
  }
}