From 5e2d4e332aaa40559e1120577cdec1c3fb990e0b Mon Sep 17 00:00:00 2001 From: Tyler Menezes Date: Thu, 29 Jul 2021 05:24:36 -0400 Subject: [PATCH] Add T6615 (#1170) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/t6615/__init__.py | 0 esphome/components/t6615/sensor.py | 48 +++++++++++++++++ esphome/components/t6615/t6615.cpp | 81 ++++++++++++++++++++++++++++ esphome/components/t6615/t6615.h | 42 +++++++++++++++ tests/test5.yaml | 17 ++++-- 6 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 esphome/components/t6615/__init__.py create mode 100644 esphome/components/t6615/sensor.py create mode 100644 esphome/components/t6615/t6615.cpp create mode 100644 esphome/components/t6615/t6615.h diff --git a/CODEOWNERS b/CODEOWNERS index 40883d39b0..b3a4668f3a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -122,6 +122,7 @@ esphome/components/st7789v/* @kbx81 esphome/components/substitutions/* @esphome/core esphome/components/sun/* @OttoWinter esphome/components/switch/* @esphome/core +esphome/components/t6615/* @tylermenezes esphome/components/tca9548a/* @andreashergert1984 esphome/components/tcl112/* @glmnet esphome/components/teleinfo/* @0hax diff --git a/esphome/components/t6615/__init__.py b/esphome/components/t6615/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/t6615/sensor.py b/esphome/components/t6615/sensor.py new file mode 100644 index 0000000000..efe4994a97 --- /dev/null +++ b/esphome/components/t6615/sensor.py @@ -0,0 +1,48 @@ +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, + DEVICE_CLASS_CARBON_DIOXIDE, + ICON_EMPTY, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_MILLION, +) + +CODEOWNERS = ["@tylermenezes"] +DEPENDENCIES = ["uart"] + +t6615_ns = cg.esphome_ns.namespace("t6615") +T6615Component = t6615_ns.class_("T6615Component", cg.PollingComponent, uart.UARTDevice) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(T6615Component), + cv.Required(CONF_CO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, + ICON_EMPTY, + 0, + DEVICE_CLASS_CARBON_DIOXIDE, + STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) + +FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( + "t6615", baud_rate=19200, require_rx=True, require_tx=True +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + + if CONF_CO2 in config: + sens = await sensor.new_sensor(config[CONF_CO2]) + cg.add(var.set_co2_sensor(sens)) diff --git a/esphome/components/t6615/t6615.cpp b/esphome/components/t6615/t6615.cpp new file mode 100644 index 0000000000..09ff61827c --- /dev/null +++ b/esphome/components/t6615/t6615.cpp @@ -0,0 +1,81 @@ +#include "t6615.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace t6615 { + +static const char *const TAG = "t6615"; + +static const uint8_t T6615_RESPONSE_BUFFER_LENGTH = 32; +static const uint8_t T6615_MAGIC = 0xFF; +static const uint8_t T6615_ADDR_HOST = 0xFA; +static const uint8_t T6615_ADDR_SENSOR = 0xFE; +static const uint8_t T6615_COMMAND_GET_PPM[] = {0x02, 0x03}; +static const uint8_t T6615_COMMAND_GET_SERIAL[] = {0x02, 0x01}; +static const uint8_t T6615_COMMAND_GET_VERSION[] = {0x02, 0x0D}; +static const uint8_t T6615_COMMAND_GET_ELEVATION[] = {0x02, 0x0F}; +static const uint8_t T6615_COMMAND_GET_ABC[] = {0xB7, 0x00}; +static const uint8_t T6615_COMMAND_ENABLE_ABC[] = {0xB7, 0x01}; +static const uint8_t T6615_COMMAND_DISABLE_ABC[] = {0xB7, 0x02}; +static const uint8_t T6615_COMMAND_SET_ELEVATION[] = {0x03, 0x0F}; + +void T6615Component::loop() { + if (!this->available()) + return; + + // Read header + uint8_t header[3]; + this->read_array(header, 3); + if (header[0] != T6615_MAGIC || header[1] != T6615_ADDR_HOST) { + ESP_LOGW(TAG, "Reading data from T6615 failed!"); + while (this->available()) + this->read(); // Clear the incoming buffer + this->status_set_warning(); + return; + } + + // Read body + uint8_t length = header[2]; + uint8_t response[T6615_RESPONSE_BUFFER_LENGTH]; + this->read_array(response, length); + + this->status_clear_warning(); + + switch (this->command_) { + case T6615Command::GET_PPM: { + const uint16_t ppm = encode_uint16(response[0], response[1]); + ESP_LOGD(TAG, "T6615 Received CO₂=%uppm", ppm); + this->co2_sensor_->publish_state(ppm); + break; + } + default: + break; + } + + this->command_ = T6615Command::NONE; +} + +void T6615Component::update() { this->query_ppm_(); } + +void T6615Component::query_ppm_() { + if (this->co2_sensor_ == nullptr || this->command_ != T6615Command::NONE) { + return; + } + + this->command_ = T6615Command::GET_PPM; + + this->write_byte(T6615_MAGIC); + this->write_byte(T6615_ADDR_SENSOR); + this->write_byte(sizeof(T6615_COMMAND_GET_PPM)); + this->write_array(T6615_COMMAND_GET_PPM, sizeof(T6615_COMMAND_GET_PPM)); +} + +float T6615Component::get_setup_priority() const { return setup_priority::DATA; } +void T6615Component::dump_config() { + ESP_LOGCONFIG(TAG, "T6615:"); + LOG_SENSOR(" ", "CO2", this->co2_sensor_); + this->check_uart_settings(19200); +} + +} // namespace t6615 +} // namespace esphome diff --git a/esphome/components/t6615/t6615.h b/esphome/components/t6615/t6615.h new file mode 100644 index 0000000000..a7da3b4cf6 --- /dev/null +++ b/esphome/components/t6615/t6615.h @@ -0,0 +1,42 @@ +#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 t6615 { + +enum class T6615Command : uint8_t { + NONE = 0, + GET_PPM, + GET_SERIAL, + GET_VERSION, + GET_ELEVATION, + GET_ABC, + ENABLE_ABC, + DISABLE_ABC, + SET_ELEVATION, +}; + +class T6615Component : public PollingComponent, public uart::UARTDevice { + public: + float get_setup_priority() const override; + + void loop() override; + void update() override; + void dump_config() override; + + void set_co2_sensor(sensor::Sensor *co2_sensor) { this->co2_sensor_ = co2_sensor; } + + protected: + void query_ppm_(); + + T6615Command command_ = T6615Command::NONE; + + sensor::Sensor *co2_sensor_{nullptr}; +}; + +} // namespace t6615 +} // namespace esphome diff --git a/tests/test5.yaml b/tests/test5.yaml index e117b3244f..23f8ae6c4d 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -19,11 +19,18 @@ ota: logger: uart: - tx_pin: 1 - rx_pin: 3 - baud_rate: 9600 + - id: uart1 + tx_pin: 1 + rx_pin: 3 + baud_rate: 9600 + - id: uart2 + tx_pin: 17 + rx_pin: 16 + baud_rate: 19200 + modbus: + uart_id: uart1 binary_sensor: - platform: gpio @@ -109,3 +116,7 @@ sensor: name: "SelecEM2M Maximum Demand Reactive Power" maximum_demand_apparent_power: name: "SelecEM2M Maximum Demand Apparent Power" + - platform: t6615 + uart_id: uart2 + co2: + name: CO2 Sensor