From 97819156111e6edef5a330d0d8cbf9fa1b86abf5 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Sun, 5 May 2019 16:00:44 +0200 Subject: [PATCH] mhz19: disable autocalibration for MH-Z19B by default 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 make sensor under-report CO2 value, 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 defaut and add 'automatic_baseline_calibration' option to allow override from config. Signed-off-by: Igor Mammedov --- esphome/components/mhz19/mhz19.cpp | 18 ++++++++++++++++++ esphome/components/mhz19/mhz19.h | 2 ++ esphome/components/mhz19/sensor.py | 6 +++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/esphome/components/mhz19/mhz19.cpp b/esphome/components/mhz19/mhz19.cpp index 4de209629d..fb6fa0ba49 100644 --- a/esphome/components/mhz19/mhz19.cpp +++ b/esphome/components/mhz19/mhz19.cpp @@ -7,6 +7,8 @@ namespace mhz19 { static const char *TAG = "mhz19"; static const uint8_t MHZ19_PDU_LENGTH = 9; static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; +static const uint8_t MHZ19_COMMAND_ABC_ENABLE[] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; +static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; uint8_t mhz19_checksum(const uint8_t *command) { uint8_t sum = 0; @@ -40,6 +42,21 @@ void MHZ19Component::setup() { this->model_b_ = true; } + if (this->model_b_) { + /* + * 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 areas + */ + if (this->abc_enabled_ == false) { + ESP_LOGI(TAG, "Disabling ABC on boot"); + /* per spec response isn't expected but sensor replies anyway. + * Read reply out and discard it so it won't get in the way of following commands */ + this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, response); + } else { + ESP_LOGI(TAG, "Enabling ABC on boot"); + this->mhz19_write_command_(MHZ19_COMMAND_ABC_ENABLE, response); + } + } setup_done = true; } @@ -123,6 +140,7 @@ bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *respo float MHZ19Component::get_setup_priority() const { return setup_priority::DATA; } void MHZ19Component::dump_config() { ESP_LOGCONFIG(TAG, "MH-Z19%s:", this->model_b_ ? "B" : ""); + ESP_LOGCONFIG(TAG, " Automatic calibration: %s", this->abc_enabled_ ? " enabled" : "disabled"); LOG_SENSOR(" ", "CO2", this->co2_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); } diff --git a/esphome/components/mhz19/mhz19.h b/esphome/components/mhz19/mhz19.h index 956f070527..eb748df9dd 100644 --- a/esphome/components/mhz19/mhz19.h +++ b/esphome/components/mhz19/mhz19.h @@ -17,6 +17,7 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice { void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } + void set_abc(bool enable_abc) { abc_enabled_ = enable_abc; } protected: bool mhz19_write_command_(const uint8_t *command, uint8_t *response); @@ -24,6 +25,7 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice { sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *co2_sensor_{nullptr}; bool model_b_; + bool abc_enabled_; }; } // namespace mhz19 diff --git a/esphome/components/mhz19/sensor.py b/esphome/components/mhz19/sensor.py index 368426e6f7..8e85c28312 100644 --- a/esphome/components/mhz19/sensor.py +++ b/esphome/components/mhz19/sensor.py @@ -2,10 +2,11 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_PERIODIC_TABLE_CO2, \ - UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_THERMOMETER + UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_THERMOMETER, UNIT_EMPTY, ICON_EMPTY DEPENDENCIES = ['uart'] +CONF_ABC = "automatic_baseline_calibration" mhz19_ns = cg.esphome_ns.namespace('mhz19') MHZ19Component = mhz19_ns.class_('MHZ19Component', cg.PollingComponent, uart.UARTDevice) @@ -13,6 +14,7 @@ CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(MHZ19Component), cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 0), + cv.Optional(CONF_ABC, default=False): cv.boolean, }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) @@ -28,3 +30,5 @@ def to_code(config): if CONF_TEMPERATURE in config: sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) cg.add(var.set_temperature_sensor(sens)) + + cg.add(var.set_abc(config[CONF_ABC]))