mirror of
https://github.com/sigmasternchen/MH-Z-CO2-Sensors
synced 2025-03-15 14:48:55 +00:00

The literals for the preheating time are interpreted as int. On Arduino Uno an int is 2 bytes in size, ranging from -32,768 to 32,767. Therefore, the calculated number of 3*60*1000=180000 does not fit in. Added upper L to force the literal to long.
253 lines
6.2 KiB
C++
253 lines
6.2 KiB
C++
/* MHZ library
|
|
|
|
By Tobias Schürg
|
|
*/
|
|
|
|
#include "MHZ.h"
|
|
|
|
const int MHZ14A = 14;
|
|
const int MHZ19B = 19;
|
|
|
|
const unsigned long MHZ14A_PREHEATING_TIME = 3L * 60L * 1000L;
|
|
const unsigned long MHZ19B_PREHEATING_TIME = 3L * 60L * 1000L;
|
|
|
|
const unsigned long MHZ14A_RESPONSE_TIME = 60 * 1000;
|
|
const unsigned long MHZ19B_RESPONSE_TIME = 120 * 1000;
|
|
|
|
const int STATUS_NO_RESPONSE = -2;
|
|
const int STATUS_CHECKSUM_MISMATCH = -3;
|
|
const int STATUS_INCOMPLETE = -4;
|
|
const int STATUS_NOT_READY = -5;
|
|
const int STATUS_PWM_NOT_CONFIGURED = -6;
|
|
const int STATUS_SERIAL_NOT_CONFIGURED = -7;
|
|
|
|
unsigned long lastRequest = 0;
|
|
|
|
bool SerialConfigured = true;
|
|
bool PwmConfigured = true;
|
|
|
|
MHZ::MHZ(uint8_t rxpin, uint8_t txpin, uint8_t pwmpin, uint8_t type) {
|
|
SoftwareSerial * ss = new SoftwareSerial(rxpin, txpin);
|
|
_pwmpin = pwmpin;
|
|
_type = type;
|
|
|
|
ss->begin(9600);
|
|
_serial = ss;
|
|
}
|
|
|
|
MHZ::MHZ(uint8_t rxpin, uint8_t txpin, uint8_t type) {
|
|
SoftwareSerial * ss = new SoftwareSerial(rxpin, txpin);
|
|
_type = type;
|
|
|
|
ss->begin(9600);
|
|
_serial = ss;
|
|
|
|
PwmConfigured = false;
|
|
}
|
|
|
|
MHZ::MHZ(uint8_t pwmpin, uint8_t type) {
|
|
_pwmpin = pwmpin;
|
|
_type = type;
|
|
|
|
SerialConfigured = false;
|
|
}
|
|
|
|
MHZ::MHZ(Stream * serial, uint8_t pwmpin, uint8_t type) {
|
|
_serial = serial;
|
|
_pwmpin = pwmpin;
|
|
_type = type;
|
|
}
|
|
|
|
MHZ::MHZ(Stream * serial, uint8_t type) {
|
|
_serial = serial;
|
|
_type = type;
|
|
|
|
PwmConfigured = false;
|
|
}
|
|
|
|
/**
|
|
* Enables or disables the debug mode (more logging).
|
|
*/
|
|
void MHZ::setDebug(boolean enable) {
|
|
debug = enable;
|
|
if (debug) {
|
|
Serial.println(F("MHZ: debug mode ENABLED"));
|
|
} else {
|
|
Serial.println(F("MHZ: debug mode DISABLED"));
|
|
}
|
|
}
|
|
|
|
boolean MHZ::isPreHeating() {
|
|
if (_type == MHZ14A) {
|
|
return millis() < (MHZ14A_PREHEATING_TIME);
|
|
} else if (_type == MHZ19B) {
|
|
return millis() < (MHZ19B_PREHEATING_TIME);
|
|
} else {
|
|
Serial.println(F("MHZ::isPreHeating() => UNKNOWN SENSOR"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
boolean MHZ::isReady() {
|
|
if (isPreHeating()) return false;
|
|
if (_type == MHZ14A)
|
|
return lastRequest < millis() - MHZ14A_RESPONSE_TIME;
|
|
else if (_type == MHZ19B)
|
|
return lastRequest < millis() - MHZ19B_RESPONSE_TIME;
|
|
else {
|
|
Serial.print(F("MHZ::isReady() => UNKNOWN SENSOR \""));
|
|
Serial.print(_type);
|
|
Serial.println(F("\""));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
int MHZ::readCO2UART() {
|
|
if (!SerialConfigured) {
|
|
if (debug) Serial.println(F("-- serial is not configured"));
|
|
return STATUS_SERIAL_NOT_CONFIGURED;
|
|
}
|
|
if (!isReady()) return STATUS_NOT_READY;
|
|
if (debug) Serial.println(F("-- read CO2 uart ---"));
|
|
byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
|
|
byte response[9]; // for answer
|
|
|
|
if (debug) Serial.print(F(" >> Sending CO2 request"));
|
|
_serial->write(cmd, 9); // request PPM CO2
|
|
lastRequest = millis();
|
|
|
|
// clear the buffer
|
|
memset(response, 0, 9);
|
|
|
|
int waited = 0;
|
|
while (_serial->available() == 0) {
|
|
if (debug) Serial.print(".");
|
|
delay(100); // wait a short moment to avoid false reading
|
|
if (waited++ > 10) {
|
|
if (debug) Serial.println(F("No response after 10 seconds"));
|
|
_serial->flush();
|
|
return STATUS_NO_RESPONSE;
|
|
}
|
|
}
|
|
if (debug) Serial.println();
|
|
|
|
// The serial stream can get out of sync. The response starts with 0xff, try
|
|
// to resync.
|
|
// TODO: I think this might be wrong any only happens during initialization?
|
|
boolean skip = false;
|
|
while (_serial->available() > 0 && (unsigned char)_serial->peek() != 0xFF) {
|
|
if (!skip) {
|
|
Serial.print(F("MHZ: - skipping unexpected readings:"));
|
|
skip = true;
|
|
}
|
|
Serial.print(" ");
|
|
Serial.print(_serial->peek(), HEX);
|
|
_serial->read();
|
|
}
|
|
if (skip) Serial.println();
|
|
|
|
if (_serial->available() > 0) {
|
|
int count = _serial->readBytes(response, 9);
|
|
if (count < 9) {
|
|
_serial->flush();
|
|
return STATUS_INCOMPLETE;
|
|
}
|
|
} else {
|
|
_serial->flush();
|
|
return STATUS_INCOMPLETE;
|
|
}
|
|
|
|
if (debug) {
|
|
// print out the response in hexa
|
|
Serial.print(F(" << "));
|
|
for (int i = 0; i < 9; i++) {
|
|
Serial.print(response[i], HEX);
|
|
Serial.print(F(" "));
|
|
}
|
|
Serial.println(F(""));
|
|
}
|
|
|
|
// checksum
|
|
byte check = getCheckSum(response);
|
|
if (response[8] != check) {
|
|
Serial.println(F("MHZ: Checksum not OK!"));
|
|
Serial.print(F("MHZ: Received: "));
|
|
Serial.println(response[8], HEX);
|
|
Serial.print(F("MHZ: Should be: "));
|
|
Serial.println(check, HEX);
|
|
temperature = STATUS_CHECKSUM_MISMATCH;
|
|
_serial->flush();
|
|
return STATUS_CHECKSUM_MISMATCH;
|
|
}
|
|
|
|
int ppm_uart = 256 * (int)response[2] + response[3];
|
|
|
|
temperature = response[4] - 44; // - 40;
|
|
|
|
byte status = response[5];
|
|
if (debug) {
|
|
Serial.print(F(" # PPM UART: "));
|
|
Serial.println(ppm_uart);
|
|
Serial.print(F(" # Temperature? "));
|
|
Serial.println(temperature);
|
|
}
|
|
|
|
// Is always 0 for version 14a and 19b
|
|
// Version 19a?: status != 0x40
|
|
if (debug && status != 0) {
|
|
Serial.print(F(" ! Status maybe not OK ! "));
|
|
Serial.println(status, HEX);
|
|
} else if (debug) {
|
|
Serial.print(F(" Status OK: "));
|
|
Serial.println(status, HEX);
|
|
}
|
|
|
|
_serial->flush();
|
|
return ppm_uart;
|
|
}
|
|
|
|
int MHZ::getLastTemperature() {
|
|
if (!SerialConfigured) {
|
|
if (debug) Serial.println(F("-- serial is not configured"));
|
|
return STATUS_SERIAL_NOT_CONFIGURED;
|
|
}
|
|
if (isPreHeating()) return STATUS_NOT_READY;
|
|
return temperature;
|
|
}
|
|
|
|
byte MHZ::getCheckSum(byte* packet) {
|
|
if (!SerialConfigured) {
|
|
if (debug) Serial.println(F("-- serial is not configured"));
|
|
return STATUS_SERIAL_NOT_CONFIGURED;
|
|
}
|
|
if (debug) Serial.println(F(" getCheckSum()"));
|
|
byte i;
|
|
unsigned char checksum = 0;
|
|
for (i = 1; i < 8; i++) {
|
|
checksum += packet[i];
|
|
}
|
|
checksum = 0xff - checksum;
|
|
checksum += 1;
|
|
return checksum;
|
|
}
|
|
|
|
int MHZ::readCO2PWM() {
|
|
if (!PwmConfigured) {
|
|
if (debug) Serial.println(F("-- pwm is not configured "));
|
|
return STATUS_PWM_NOT_CONFIGURED;
|
|
}
|
|
//if (!isReady()) return STATUS_NOT_READY; not needed?
|
|
if (debug) Serial.print(F("-- reading CO2 from pwm "));
|
|
unsigned long th, tl, ppm_pwm = 0;
|
|
do {
|
|
if (debug) Serial.print(".");
|
|
th = pulseIn(_pwmpin, HIGH, 1004000) / 1000;
|
|
tl = 1004 - th;
|
|
ppm_pwm = 2000 * (th - 2) / (th + tl - 4);
|
|
} while (th == 0);
|
|
if (debug) {
|
|
Serial.print(F("\n # PPM PWM: "));
|
|
Serial.println(ppm_pwm);
|
|
}
|
|
return ppm_pwm;
|
|
}
|