From a416375994876b9e1733b27dfaca831ba0c1a74d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Sun, 5 May 2019 15:45:54 +0200 Subject: [PATCH 1/4] mhz19: igrnore data from sensor wile it's booting Reported CO2 might be not correct while sensor is booting and while it boots it report value 15000 in response at offset 6-7. So skip reporting till this value is reported. Signed-off-by: Igor Mammedov --- Idea is taken from ESPEasy implementation. --- src/esphome/sensor/mhz19_component.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/esphome/sensor/mhz19_component.cpp b/src/esphome/sensor/mhz19_component.cpp index 00f0c3e8..5ca4d780 100644 --- a/src/esphome/sensor/mhz19_component.cpp +++ b/src/esphome/sensor/mhz19_component.cpp @@ -38,6 +38,13 @@ void MHZ19Component::update() { return; } + /* 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; + } + 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); From e834bf18a4b13bd88256a69252028c7a54b5de93 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Sun, 5 May 2019 15:55:06 +0200 Subject: [PATCH 2/4] mhz19: add detection of B sensor revision it will allow to report exact sensor type in dump config and later will be used for turning off autocalibration. Signed-off-by: Igor Mammedov --- src/esphome/sensor/mhz19_component.cpp | 9 ++++++++- src/esphome/sensor/mhz19_component.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/esphome/sensor/mhz19_component.cpp b/src/esphome/sensor/mhz19_component.cpp index 5ca4d780..18c805ad 100644 --- a/src/esphome/sensor/mhz19_component.cpp +++ b/src/esphome/sensor/mhz19_component.cpp @@ -45,6 +45,13 @@ void MHZ19Component::update() { 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; + } + 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); @@ -81,7 +88,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:", this->model_b ? "B:" : ""); LOG_SENSOR(" ", "CO2", this->co2_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); } diff --git a/src/esphome/sensor/mhz19_component.h b/src/esphome/sensor/mhz19_component.h index b3d49897..ec15e143 100644 --- a/src/esphome/sensor/mhz19_component.h +++ b/src/esphome/sensor/mhz19_component.h @@ -33,6 +33,7 @@ class MHZ19Component : public PollingComponent, public UARTDevice { MHZ19TemperatureSensor *temperature_sensor_{nullptr}; MHZ19CO2Sensor *co2_sensor_; + bool model_b; }; } // namespace sensor From df2f4dadb6f066f23126c79570f2a23dfee3e90e Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Sun, 5 May 2019 16:00:44 +0200 Subject: [PATCH 3/4] mhz19: disable autocalibration for MH-Z19B MH-Z19B allows to enable/disable 'automatic baseline calibration'[*], which is enabled by default (MH-Z19B v1.2 datasheet). It operates on too short 24hr interval. So if the place where sensor is located is not ventilated well Every day, ABC will under-report CO2 since baseline (400ppm) is moved to lowest observed value in 24hr. With such short recalibrartion window, sensor often doesn't show correct CO2 levels when ABS is left enabled. Disable it on boot unconditionally. Signed-off-by: Igor Mammedov --- src/esphome/sensor/mhz19_component.cpp | 21 ++++++++++++++++++++- src/esphome/sensor/mhz19_component.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/esphome/sensor/mhz19_component.cpp b/src/esphome/sensor/mhz19_component.cpp index 18c805ad..c0030331 100644 --- a/src/esphome/sensor/mhz19_component.cpp +++ b/src/esphome/sensor/mhz19_component.cpp @@ -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)) {} @@ -52,6 +53,24 @@ void MHZ19Component::update() { 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"); + 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 + */ + } + 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); @@ -88,7 +107,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%s:", this->model_b ? "B:" : ""); + 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_); } diff --git a/src/esphome/sensor/mhz19_component.h b/src/esphome/sensor/mhz19_component.h index ec15e143..cfa82ef1 100644 --- a/src/esphome/sensor/mhz19_component.h +++ b/src/esphome/sensor/mhz19_component.h @@ -34,6 +34,7 @@ class MHZ19Component : public PollingComponent, public UARTDevice { MHZ19TemperatureSensor *temperature_sensor_{nullptr}; MHZ19CO2Sensor *co2_sensor_; bool model_b; + bool abc_disabled; }; } // namespace sensor From 31bae8ccb31cc0dbb13b4b75afa04055810ed0d2 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Sun, 5 May 2019 21:58:01 +0200 Subject: [PATCH 4/4] mhz19: cleanup uart RX buffer before sending command When sensor is connected to hardware uart, sometimes on boot uart's RX buffer contains several bytes of junk. Workaround it by consuming all of it before sending command. Also turn on cleanup logic only if problem is detected (i.e. when reply from sensor doesn't look correct. Signed-off-by: Igor Mammedov --- src/esphome/sensor/mhz19_component.cpp | 26 ++++++++++++++++++++++++-- src/esphome/sensor/mhz19_component.h | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/esphome/sensor/mhz19_component.cpp b/src/esphome/sensor/mhz19_component.cpp index c0030331..4bd56b63 100644 --- a/src/esphome/sensor/mhz19_component.cpp +++ b/src/esphome/sensor/mhz19_component.cpp @@ -27,6 +27,7 @@ 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(); @@ -34,10 +35,18 @@ void MHZ19Component::update() { } 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(); - return; + 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; } + 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]; @@ -91,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 + */ + 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)); diff --git a/src/esphome/sensor/mhz19_component.h b/src/esphome/sensor/mhz19_component.h index cfa82ef1..34c64b5b 100644 --- a/src/esphome/sensor/mhz19_component.h +++ b/src/esphome/sensor/mhz19_component.h @@ -35,6 +35,7 @@ class MHZ19Component : public PollingComponent, public UARTDevice { MHZ19CO2Sensor *co2_sensor_; bool model_b; bool abc_disabled; + int cleanup_rx_buffer; }; } // namespace sensor