From 0ea97df1afa0cf5fbd8cfd34028749a3422ef170 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 25 Jan 2023 09:58:44 +1300 Subject: [PATCH] Add MICS-4514 gas sensor (#4316) --- CODEOWNERS | 1 + esphome/components/mics_4514/__init__.py | 0 esphome/components/mics_4514/mics_4514.cpp | 145 +++++++++++++++++++++ esphome/components/mics_4514/mics_4514.h | 34 +++++ esphome/components/mics_4514/sensor.py | 71 ++++++++++ tests/test2.yaml | 14 ++ 6 files changed, 265 insertions(+) create mode 100644 esphome/components/mics_4514/__init__.py create mode 100644 esphome/components/mics_4514/mics_4514.cpp create mode 100644 esphome/components/mics_4514/mics_4514.h create mode 100644 esphome/components/mics_4514/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index cdcf1ae2d0..c9b59c099a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -145,6 +145,7 @@ esphome/components/mcp9808/* @k7hpn esphome/components/md5/* @esphome/core esphome/components/mdns/* @esphome/core esphome/components/media_player/* @jesserockz +esphome/components/mics_4514/* @jesserockz esphome/components/midea/* @dudanov esphome/components/midea_ir/* @dudanov esphome/components/mitsubishi/* @RubyBailey diff --git a/esphome/components/mics_4514/__init__.py b/esphome/components/mics_4514/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/mics_4514/mics_4514.cpp b/esphome/components/mics_4514/mics_4514.cpp new file mode 100644 index 0000000000..8dc1a9f639 --- /dev/null +++ b/esphome/components/mics_4514/mics_4514.cpp @@ -0,0 +1,145 @@ +#include "mics_4514.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mics_4514 { + +static const char *const TAG = "mics_4514"; + +static const uint8_t SENSOR_REGISTER = 0x04; +static const uint8_t POWER_MODE_REGISTER = 0x0a; + +void MICS4514Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up MICS 4514..."); + uint8_t power_mode; + this->read_register(POWER_MODE_REGISTER, &power_mode, 1); + if (power_mode == 0x00) { + ESP_LOGCONFIG(TAG, "Waking up MICS 4514"); + power_mode = 0x01; + this->write_register(POWER_MODE_REGISTER, &power_mode, 1); + delay(100); // NOLINT + this->set_timeout("warmup", 3 * 60 * 1000, [this]() { + this->warmed_up_ = true; + ESP_LOGCONFIG(TAG, "MICS 4514 setup complete."); + }); + this->status_set_warning(); + return; + } + ESP_LOGCONFIG(TAG, "Device already awake."); + this->warmed_up_ = true; + ESP_LOGCONFIG(TAG, "MICS 4514 setup complete."); +} +void MICS4514Component::dump_config() { + ESP_LOGCONFIG(TAG, "MICS 4514:"); + LOG_I2C_DEVICE(this); + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_); + LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_); + LOG_SENSOR(" ", "Methane", this->methane_sensor_); + LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_); + LOG_SENSOR(" ", "Hydrogen", this->hydrogen_sensor_); + LOG_SENSOR(" ", "Ammonia", this->ammonia_sensor_); +} +float MICS4514Component::get_setup_priority() const { return setup_priority::DATA; } +void MICS4514Component::update() { + if (!this->warmed_up_) { + return; + } + uint8_t data[6]; + if (this->read_register(SENSOR_REGISTER, data, 6) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + this->status_clear_warning(); + ESP_LOGV(TAG, "Got data: %02X %02X %02X %02X %02X %02X", data[0], data[1], data[2], data[3], data[4], data[5]); + uint16_t ox = encode_uint16(data[0], data[1]); + uint16_t red = encode_uint16(data[2], data[3]); + uint16_t power = encode_uint16(data[4], data[5]); + + if (this->initial_) { + this->initial_ = false; + this->ox_calibration_ = (float) (power - ox); + this->red_calibration_ = (float) (power - red); + return; + } + + float red_f = (float) (power - red) / this->red_calibration_; + float ox_f = (float) (power - ox) / this->ox_calibration_; + + if (this->carbon_monoxide_sensor_ != nullptr) { + float co = 0.0f; + if (red_f <= 0.425f) { + co = (0.425f - red_f) / 0.000405f; + if (co < 1.0f) + co = 0.0f; + if (co > 1000.0f) + co = 1000.0f; + } + this->carbon_monoxide_sensor_->publish_state(co); + } + + if (this->nitrogen_dioxide_sensor_ != nullptr) { + float nitrogendioxide = 0.0f; + if (ox_f >= 1.1f) { + nitrogendioxide = (ox_f - 0.045f) / 6.13f; + if (nitrogendioxide < 0.1f) + nitrogendioxide = 0.0f; + if (nitrogendioxide > 10.0f) + nitrogendioxide = 10.0f; + } + this->nitrogen_dioxide_sensor_->publish_state(nitrogendioxide); + } + + if (this->methane_sensor_ != nullptr) { + float methane = 0.0f; + if (red_f <= 0.786f) { + methane = (0.786f - red_f) / 0.000023f; + if (methane < 1000.0f) + methane = 0.0f; + if (methane > 25000.0f) + methane = 25000.0f; + } + this->methane_sensor_->publish_state(methane); + } + + if (this->ethanol_sensor_ != nullptr) { + float ethanol = 0.0f; + if (red_f <= 0.306f) { + ethanol = (0.306f - red_f) / 0.00057f; + if (ethanol < 10.0f) + ethanol = 0.0f; + if (ethanol > 500.0f) + ethanol = 500.0f; + } + this->ethanol_sensor_->publish_state(ethanol); + } + + if (this->hydrogen_sensor_ != nullptr) { + float hydrogen = 0.0f; + if (red_f <= 0.279f) { + hydrogen = (0.279f - red_f) / 0.00026f; + if (hydrogen < 1.0f) + hydrogen = 0.0f; + if (hydrogen > 1000.0f) + hydrogen = 1000.0f; + } + this->hydrogen_sensor_->publish_state(hydrogen); + } + + if (this->ammonia_sensor_ != nullptr) { + float ammonia = 0.0f; + if (red_f <= 0.8f) { + ammonia = (0.8f - red_f) / 0.0015f; + if (ammonia < 1.0f) + ammonia = 0.0f; + if (ammonia > 500.0f) + ammonia = 500.0f; + } + this->ammonia_sensor_->publish_state(ammonia); + } +} + +} // namespace mics_4514 +} // namespace esphome diff --git a/esphome/components/mics_4514/mics_4514.h b/esphome/components/mics_4514/mics_4514.h new file mode 100644 index 0000000000..d2fefc3630 --- /dev/null +++ b/esphome/components/mics_4514/mics_4514.h @@ -0,0 +1,34 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace mics_4514 { + +class MICS4514Component : public PollingComponent, public i2c::I2CDevice { + SUB_SENSOR(carbon_monoxide) + SUB_SENSOR(nitrogen_dioxide) + SUB_SENSOR(methane) + SUB_SENSOR(ethanol) + SUB_SENSOR(hydrogen) + SUB_SENSOR(ammonia) + + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void update() override; + + protected: + bool warmed_up_{false}; + bool initial_{true}; + + float ox_calibration_{0}; + float red_calibration_{0}; +}; + +} // namespace mics_4514 +} // namespace esphome diff --git a/esphome/components/mics_4514/sensor.py b/esphome/components/mics_4514/sensor.py new file mode 100644 index 0000000000..80c3524f66 --- /dev/null +++ b/esphome/components/mics_4514/sensor.py @@ -0,0 +1,71 @@ +import esphome.codegen as cg +import esphome.config_validation as cv + +from esphome.components import sensor, i2c + +from esphome.const import ( + CONF_ID, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_MILLION, +) + +CODEOWNERS = ["@jesserockz"] +DEPENDENCIES = ["i2c"] + +CONF_CARBON_MONOXIDE = "carbon_monoxide" +CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide" +CONF_METHANE = "methane" +CONF_ETHANOL = "ethanol" +CONF_HYDROGEN = "hydrogen" +CONF_AMMONIA = "ammonia" + + +mics_4514_ns = cg.esphome_ns.namespace("mics_4514") +MICS4514Component = mics_4514_ns.class_( + "MICS4514Component", cg.PollingComponent, i2c.I2CDevice +) + +SENSORS = [ + CONF_CARBON_MONOXIDE, + CONF_METHANE, + CONF_ETHANOL, + CONF_HYDROGEN, + CONF_AMMONIA, +] + +common_sensor_schema = sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=2, +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MICS4514Component), + cv.Optional(CONF_NITROGEN_DIOXIDE): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=2, + ), + } + ) + .extend({cv.Optional(sensor_type): common_sensor_schema for sensor_type in SENSORS}) + .extend(i2c.i2c_device_schema(0x75)) + .extend(cv.polling_component_schema("60s")) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + for sensor_type in SENSORS: + if sensor_type in config: + sens = await sensor.new_sensor(config[sensor_type]) + cg.add(getattr(var, f"set_{sensor_type}_sensor")(sens)) + + if CONF_NITROGEN_DIOXIDE in config: + sens = await sensor.new_sensor(config[CONF_NITROGEN_DIOXIDE]) + cg.add(var.set_nitrogen_dioxide_sensor(sens)) diff --git a/tests/test2.yaml b/tests/test2.yaml index 2c94333c03..3e6a186320 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -381,6 +381,20 @@ sensor: temperature_sensor: ha_hello_world_temperature ph: name: Ufire pH + - platform: mics_4514 + update_interval: 60s + nitrogen_dioxide: + name: MICS-4514 NO2 + carbon_monoxide: + name: MICS-4514 CO + methane: + name: MICS-4514 CH4 + hydrogen: + name: MICS-4514 H2 + ethanol: + name: MICS-4514 C2H5OH + ammonia: + name: MICS-4514 NH3 time: - platform: homeassistant