mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	MH-Z19 calibration support (#683)
* Allow configuration to enable or disable automatic baseline calibration on boot * Add actions to enable or disable automatic baseline calibration * Add action to calibrate zero point
This commit is contained in:
		
				
					committed by
					
						 Brandon Davidson
						Brandon Davidson
					
				
			
			
				
	
			
			
			
						parent
						
							15a7d2ef75
						
					
				
				
					commit
					c5db457700
				
			| @@ -8,6 +8,9 @@ static const char *TAG = "mhz19"; | |||||||
| static const uint8_t MHZ19_REQUEST_LENGTH = 8; | static const uint8_t MHZ19_REQUEST_LENGTH = 8; | ||||||
| static const uint8_t MHZ19_RESPONSE_LENGTH = 9; | 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_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||||
|  | static const uint8_t MHZ19_COMMAND_ABC_ENABLE[] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00}; | ||||||
|  | static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||||
|  | static const uint8_t MHZ19_COMMAND_CALIBRATE_ZERO[] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||||
|  |  | ||||||
| uint8_t mhz19_checksum(const uint8_t *command) { | uint8_t mhz19_checksum(const uint8_t *command) { | ||||||
|   uint8_t sum = 0; |   uint8_t sum = 0; | ||||||
| @@ -17,6 +20,14 @@ uint8_t mhz19_checksum(const uint8_t *command) { | |||||||
|   return 0xFF - sum + 0x01; |   return 0xFF - sum + 0x01; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void MHZ19Component::setup() { | ||||||
|  |   if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) { | ||||||
|  |     this->abc_enable(); | ||||||
|  |   } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) { | ||||||
|  |     this->abc_disable(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| void MHZ19Component::update() { | void MHZ19Component::update() { | ||||||
|   uint8_t response[MHZ19_RESPONSE_LENGTH]; |   uint8_t response[MHZ19_RESPONSE_LENGTH]; | ||||||
|   if (!this->mhz19_write_command_(MHZ19_COMMAND_GET_PPM, response)) { |   if (!this->mhz19_write_command_(MHZ19_COMMAND_GET_PPM, response)) { | ||||||
| @@ -50,6 +61,21 @@ void MHZ19Component::update() { | |||||||
|     this->temperature_sensor_->publish_state(temp); |     this->temperature_sensor_->publish_state(temp); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void MHZ19Component::calibrate_zero() { | ||||||
|  |   ESP_LOGD(TAG, "MHZ19 Calibrating zero point"); | ||||||
|  |   this->mhz19_write_command_(MHZ19_COMMAND_CALIBRATE_ZERO, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void MHZ19Component::abc_enable() { | ||||||
|  |   ESP_LOGD(TAG, "MHZ19 Enabling automatic baseline calibration"); | ||||||
|  |   this->mhz19_write_command_(MHZ19_COMMAND_ABC_ENABLE, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void MHZ19Component::abc_disable() { | ||||||
|  |   ESP_LOGD(TAG, "MHZ19 Disabling automatic baseline calibration"); | ||||||
|  |   this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
| bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) { | bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) { | ||||||
|   this->flush(); |   this->flush(); | ||||||
|   this->write_array(command, MHZ19_REQUEST_LENGTH); |   this->write_array(command, MHZ19_REQUEST_LENGTH); | ||||||
| @@ -67,6 +93,12 @@ void MHZ19Component::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "MH-Z19:"); |   ESP_LOGCONFIG(TAG, "MH-Z19:"); | ||||||
|   LOG_SENSOR("  ", "CO2", this->co2_sensor_); |   LOG_SENSOR("  ", "CO2", this->co2_sensor_); | ||||||
|   LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); |   LOG_SENSOR("  ", "Temperature", this->temperature_sensor_); | ||||||
|  |  | ||||||
|  |   if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  Automatic baseline calibration enabled on boot"); | ||||||
|  |   } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  Automatic baseline calibration disabled on boot"); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace mhz19 | }  // namespace mhz19 | ||||||
|   | |||||||
| @@ -1,27 +1,64 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/automation.h" | ||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
| #include "esphome/components/uart/uart.h" | #include "esphome/components/uart/uart.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace mhz19 { | namespace mhz19 { | ||||||
|  |  | ||||||
|  | enum MHZ19ABCLogic { MHZ19_ABC_NONE = 0, MHZ19_ABC_ENABLED, MHZ19_ABC_DISABLED }; | ||||||
|  |  | ||||||
| class MHZ19Component : public PollingComponent, public uart::UARTDevice { | class MHZ19Component : public PollingComponent, public uart::UARTDevice { | ||||||
|  public: |  public: | ||||||
|   float get_setup_priority() const override; |   float get_setup_priority() const override; | ||||||
|  |  | ||||||
|  |   void setup() override; | ||||||
|   void update() override; |   void update() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|  |  | ||||||
|  |   void calibrate_zero(); | ||||||
|  |   void abc_enable(); | ||||||
|  |   void abc_disable(); | ||||||
|  |  | ||||||
|   void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } |   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_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } | ||||||
|  |   void set_abc_enabled(bool abc_enabled) { abc_boot_logic_ = abc_enabled ? MHZ19_ABC_ENABLED : MHZ19_ABC_DISABLED; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   bool mhz19_write_command_(const uint8_t *command, uint8_t *response); |   bool mhz19_write_command_(const uint8_t *command, uint8_t *response); | ||||||
|  |  | ||||||
|   sensor::Sensor *temperature_sensor_{nullptr}; |   sensor::Sensor *temperature_sensor_{nullptr}; | ||||||
|   sensor::Sensor *co2_sensor_{nullptr}; |   sensor::Sensor *co2_sensor_{nullptr}; | ||||||
|  |   MHZ19ABCLogic abc_boot_logic_{MHZ19_ABC_NONE}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class MHZ19CalibrateZeroAction : public Action<Ts...> { | ||||||
|  |  public: | ||||||
|  |   MHZ19CalibrateZeroAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} | ||||||
|  |   void play(Ts... x) override { this->mhz19_->calibrate_zero(); } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   MHZ19Component *mhz19_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class MHZ19ABCEnableAction : public Action<Ts...> { | ||||||
|  |  public: | ||||||
|  |   MHZ19ABCEnableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} | ||||||
|  |   void play(Ts... x) override { this->mhz19_->abc_enable(); } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   MHZ19Component *mhz19_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class MHZ19ABCDisableAction : public Action<Ts...> { | ||||||
|  |  public: | ||||||
|  |   MHZ19ABCDisableAction(MHZ19Component *mhz19) : mhz19_(mhz19) {} | ||||||
|  |   void play(Ts... x) override { this->mhz19_->abc_disable(); } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   MHZ19Component *mhz19_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace mhz19 | }  // namespace mhz19 | ||||||
|   | |||||||
| @@ -1,18 +1,26 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome import automation | ||||||
|  | from esphome.automation import maybe_simple_id | ||||||
| from esphome.components import sensor, uart | from esphome.components import sensor, uart | ||||||
| from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_PERIODIC_TABLE_CO2, \ | 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 | ||||||
|  |  | ||||||
| DEPENDENCIES = ['uart'] | DEPENDENCIES = ['uart'] | ||||||
|  |  | ||||||
|  | CONF_AUTOMATIC_BASELINE_CALIBRATION = 'automatic_baseline_calibration' | ||||||
|  |  | ||||||
| mhz19_ns = cg.esphome_ns.namespace('mhz19') | mhz19_ns = cg.esphome_ns.namespace('mhz19') | ||||||
| MHZ19Component = mhz19_ns.class_('MHZ19Component', cg.PollingComponent, uart.UARTDevice) | MHZ19Component = mhz19_ns.class_('MHZ19Component', cg.PollingComponent, uart.UARTDevice) | ||||||
|  | MHZ19CalibrateZeroAction = mhz19_ns.class_('MHZ19CalibrateZeroAction', automation.Action) | ||||||
|  | MHZ19ABCEnableAction = mhz19_ns.class_('MHZ19ABCEnableAction', automation.Action) | ||||||
|  | MHZ19ABCDisableAction = mhz19_ns.class_('MHZ19ABCDisableAction', automation.Action) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.Schema({ | CONFIG_SCHEMA = cv.Schema({ | ||||||
|     cv.GenerateID(): cv.declare_id(MHZ19Component), |     cv.GenerateID(): cv.declare_id(MHZ19Component), | ||||||
|     cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0), |     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_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 0), | ||||||
|  |     cv.Optional(CONF_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean, | ||||||
| }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) | }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -28,3 +36,22 @@ def to_code(config): | |||||||
|     if CONF_TEMPERATURE in config: |     if CONF_TEMPERATURE in config: | ||||||
|         sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) |         sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) | ||||||
|         cg.add(var.set_temperature_sensor(sens)) |         cg.add(var.set_temperature_sensor(sens)) | ||||||
|  |  | ||||||
|  |     if CONF_AUTOMATIC_BASELINE_CALIBRATION in config: | ||||||
|  |         cg.add(var.set_abc_enabled(config[CONF_AUTOMATIC_BASELINE_CALIBRATION])) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CALIBRATION_ACTION_SCHEMA = maybe_simple_id({ | ||||||
|  |     cv.Required(CONF_ID): cv.use_id(MHZ19Component), | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action('mhz19.calibrate_zero', MHZ19CalibrateZeroAction, | ||||||
|  |                             CALIBRATION_ACTION_SCHEMA) | ||||||
|  | @automation.register_action('mhz19.abc_enable', MHZ19ABCEnableAction, | ||||||
|  |                             CALIBRATION_ACTION_SCHEMA) | ||||||
|  | @automation.register_action('mhz19.abc_disable', MHZ19ABCDisableAction, | ||||||
|  |                             CALIBRATION_ACTION_SCHEMA) | ||||||
|  | def mhz19_calibration_to_code(config, action_id, template_arg, args): | ||||||
|  |     paren = yield cg.get_variable(config[CONF_ID]) | ||||||
|  |     yield cg.new_Pvariable(action_id, template_arg, paren) | ||||||
|   | |||||||
| @@ -449,6 +449,7 @@ sensor: | |||||||
|     temperature: |     temperature: | ||||||
|       name: "MH-Z19 Temperature" |       name: "MH-Z19 Temperature" | ||||||
|     update_interval: 15s |     update_interval: 15s | ||||||
|  |     automatic_baseline_calibration: false | ||||||
|   - platform: mpu6050 |   - platform: mpu6050 | ||||||
|     address: 0x68 |     address: 0x68 | ||||||
|     accel_x: |     accel_x: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user