Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

MH-Z19 sensor: disable auto-calibration and some odd fixes #592

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions src/esphome/sensor/mhz19_component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ static const char *TAG = "sensor.mhz19";
static const uint8_t MHZ19_REQUEST_LENGTH = 8;
static const uint8_t MHZ19_RESPONSE_LENGTH = 9;
static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00};
static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};

MHZ19Component::MHZ19Component(UARTComponent *parent, const std::string &co2_name, uint32_t update_interval)
: PollingComponent(update_interval), UARTDevice(parent), co2_sensor_(new MHZ19CO2Sensor(co2_name, this)) {}
Expand All @@ -26,18 +27,59 @@ uint8_t mhz19_checksum(const uint8_t *command) {

void MHZ19Component::update() {
uint8_t response[MHZ19_RESPONSE_LENGTH];
repeat:
if (!this->mhz19_write_command_(MHZ19_COMMAND_GET_PPM, response)) {
ESP_LOGW(TAG, "Reading data from MHZ19 failed!");
this->status_set_warning();
return;
}

if (response[0] != 0xFF || response[1] != 0x86) {
ESP_LOGW(TAG, "Invalid preamble from MHZ19!");
ESP_LOGW(TAG, "Invalid preamble from MHZ19! [%0x %0x %0x %0x %0x %0x %0x %0x %0x]",
response[0], response[1], response[2], response[3], response[4],
response[5], response[6], response[7], response[8]);
this->status_set_warning();
if (++this->cleanup_rx_buffer > 2) {
/* try to recover and give up on it if several attemprs were no successful until the next update cycle */
this->cleanup_rx_buffer = 0;
return;
}
goto repeat;
imammedo marked this conversation as resolved.
Show resolved Hide resolved
}
this->cleanup_rx_buffer = 0;

/* Sensor reports U(15000) during boot, ingnore reported CO2 until it boots */
uint16_t u = (response[6] << 8) + response[7];
if (u == 15000) {
ESP_LOGD(TAG, "Sensor is booting");
return;
}

/* MH-Z19B(s == 0) and MH-Z19(s != 0) */
uint8_t s = response[5];
if (response[5] == 0 && this->model_b == false) {
ESP_LOGD(TAG, "MH-Z19B detected");
this->model_b = true;
}

if (this->model_b && this->abc_disabled == false) {
uint8_t abc_ack[MHZ19_RESPONSE_LENGTH];
/*
* MH-Z19B allows to enable/disable 'automatic baseline calibration' (datasheet MH-Z19B v1.2),
* disable it to prevent sensor baseline drift in not well ventilated area
*/
ESP_LOGI(TAG, "Disabling ABC on boot");
imammedo marked this conversation as resolved.
Show resolved Hide resolved
if (!this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, abc_ack)) {
ESP_LOGW(TAG, "Failed to read ABC disable ack!");
return;
}
this->abc_disabled = true;
/*
* TODO: implement an option to recalibrate sensor, to allow for occasional
* manual recalibration
*/
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole block sounds a bit more like it should go in the setup() function. We should probably move it there.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried it first but then node hangs for good (had to restart mcu several times before I was able to flash it again) on boot. With UART0 occupied by sensor without hardware to read log from UART1 output, I don't have means to debug it.

I'd still like to move it to setup, but don't know how to fix boot issue.


uint8_t checksum = mhz19_checksum(response);
if (response[8] != checksum) {
ESP_LOGW(TAG, "MHZ19 Checksum doesn't match: 0x%02X!=0x%02X", response[8], checksum);
Expand All @@ -58,6 +100,19 @@ void MHZ19Component::update() {

bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) {
this->flush();

/*
* UART0 in NodeMCU v2, sometimes on boot has junk in RX buffer,
* check for it and drain all of it before sending commands to sensor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm the flush command should flush the RX buffer (naming is bad, but copied from arduino core). If it does not do so that would be a bug.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's make on it as well,
but got arduino/esp only a couple days ago so I'm not sure where to look for respective code
and this workaround fixed non working sensor for me.

If it's within this git repo and you can point out to related code, I'll give it a try.

*/
if (this->cleanup_rx_buffer) {
for (int i = 0; this->available(); i++) {
uint8_t junk;
this->read_byte(&junk);
ESP_LOGD(TAG, "junk in RX[%d]: %0x", i, junk);
}
}

this->write_array(command, MHZ19_REQUEST_LENGTH);
this->write_byte(mhz19_checksum(command));

Expand All @@ -74,7 +129,7 @@ MHZ19TemperatureSensor *MHZ19Component::make_temperature_sensor(const std::strin
MHZ19CO2Sensor *MHZ19Component::get_co2_sensor() const { return this->co2_sensor_; }
float MHZ19Component::get_setup_priority() const { return setup_priority::HARDWARE_LATE; }
void MHZ19Component::dump_config() {
ESP_LOGCONFIG(TAG, "MH-Z19:");
ESP_LOGCONFIG(TAG, "MH-Z19%s%s", this->model_b ? "B:" : "", this->abc_disabled ? " (auto calibration: disabled)": " (auto calibration: enabled)");
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
}
Expand Down
3 changes: 3 additions & 0 deletions src/esphome/sensor/mhz19_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class MHZ19Component : public PollingComponent, public UARTDevice {

MHZ19TemperatureSensor *temperature_sensor_{nullptr};
MHZ19CO2Sensor *co2_sensor_;
bool model_b;
bool abc_disabled;
int cleanup_rx_buffer;
};

} // namespace sensor
Expand Down