mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +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_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_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 sum = 0; | ||||
| @@ -17,6 +20,14 @@ uint8_t mhz19_checksum(const uint8_t *command) { | ||||
|   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() { | ||||
|   uint8_t response[MHZ19_RESPONSE_LENGTH]; | ||||
|   if (!this->mhz19_write_command_(MHZ19_COMMAND_GET_PPM, response)) { | ||||
| @@ -50,6 +61,21 @@ void MHZ19Component::update() { | ||||
|     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) { | ||||
|   this->flush(); | ||||
|   this->write_array(command, MHZ19_REQUEST_LENGTH); | ||||
| @@ -67,6 +93,12 @@ void MHZ19Component::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "MH-Z19:"); | ||||
|   LOG_SENSOR("  ", "CO2", this->co2_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 | ||||
|   | ||||
| @@ -1,27 +1,64 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace mhz19 { | ||||
|  | ||||
| enum MHZ19ABCLogic { MHZ19_ABC_NONE = 0, MHZ19_ABC_ENABLED, MHZ19_ABC_DISABLED }; | ||||
|  | ||||
| class MHZ19Component : public PollingComponent, public uart::UARTDevice { | ||||
|  public: | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   void setup() override; | ||||
|   void update() 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_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: | ||||
|   bool mhz19_write_command_(const uint8_t *command, uint8_t *response); | ||||
|  | ||||
|   sensor::Sensor *temperature_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 | ||||
|   | ||||
| @@ -1,18 +1,26 @@ | ||||
| import esphome.codegen as cg | ||||
| 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.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_PERIODIC_TABLE_CO2, \ | ||||
|     UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_THERMOMETER | ||||
|  | ||||
| DEPENDENCIES = ['uart'] | ||||
|  | ||||
| CONF_AUTOMATIC_BASELINE_CALIBRATION = 'automatic_baseline_calibration' | ||||
|  | ||||
| mhz19_ns = cg.esphome_ns.namespace('mhz19') | ||||
| 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({ | ||||
|     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_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean, | ||||
| }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) | ||||
|  | ||||
|  | ||||
| @@ -28,3 +36,22 @@ def to_code(config): | ||||
|     if CONF_TEMPERATURE in config: | ||||
|         sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) | ||||
|         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: | ||||
|       name: "MH-Z19 Temperature" | ||||
|     update_interval: 15s | ||||
|     automatic_baseline_calibration: false | ||||
|   - platform: mpu6050 | ||||
|     address: 0x68 | ||||
|     accel_x: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user