diff --git a/CODEOWNERS b/CODEOWNERS index a53bf63c69..6e3801d1f3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -80,6 +80,7 @@ esphome/components/hbridge/light/* @DotNetDann esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/homeassistant/* @OttoWinter +esphome/components/honeywellabp/* @RubyBailey esphome/components/hrxl_maxsonar_wr/* @netmikey esphome/components/i2c/* @esphome/core esphome/components/improv_serial/* @esphome/core diff --git a/esphome/components/honeywellabp/__init__.py b/esphome/components/honeywellabp/__init__.py new file mode 100644 index 0000000000..03440d675d --- /dev/null +++ b/esphome/components/honeywellabp/__init__.py @@ -0,0 +1 @@ +"""Support for Honeywell ABP""" diff --git a/esphome/components/honeywellabp/honeywellabp.cpp b/esphome/components/honeywellabp/honeywellabp.cpp new file mode 100644 index 0000000000..910c39e8c8 --- /dev/null +++ b/esphome/components/honeywellabp/honeywellabp.cpp @@ -0,0 +1,102 @@ +#include "honeywellabp.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace honeywellabp { + +static const char *const TAG = "honeywellabp"; + +const float MIN_COUNT = 1638.4; // 1638 counts (10% of 2^14 counts or 0x0666) +const float MAX_COUNT = 14745.6; // 14745 counts (90% of 2^14 counts or 0x3999) + +void HONEYWELLABPSensor::setup() { + ESP_LOGD(TAG, "Setting up Honeywell ABP Sensor "); + this->spi_setup(); +} + +uint8_t HONEYWELLABPSensor::readsensor_() { + // Polls the sensor for new data. + // transfer 4 bytes (the last two are temperature only used by some sensors) + this->enable(); + buf_[0] = this->read_byte(); + buf_[1] = this->read_byte(); + buf_[2] = this->read_byte(); + buf_[3] = this->read_byte(); + this->disable(); + + // Check the status codes: + // status = 0 : normal operation + // status = 1 : device in command mode + // status = 2 : stale data + // status = 3 : diagnostic condition + status_ = buf_[0] >> 6 & 0x3; + ESP_LOGV(TAG, "Sensor status %d", status_); + + // if device is normal and there is new data, bitmask and save the raw data + if (status_ == 0) { + // 14 - bit pressure is the last 6 bits of byte 0 (high bits) & all of byte 1 (lowest 8 bits) + pressure_count_ = ((uint16_t)(buf_[0]) << 8 & 0x3F00) | ((uint16_t)(buf_[1]) & 0xFF); + // 11 - bit temperature is all of byte 2 (lowest 8 bits) and the first three bits of byte 3 + temperature_count_ = (((uint16_t)(buf_[2]) << 3) & 0x7F8) | (((uint16_t)(buf_[3]) >> 5) & 0x7); + ESP_LOGV(TAG, "Sensor pressure_count_ %d", pressure_count_); + ESP_LOGV(TAG, "Sensor temperature_count_ %d", temperature_count_); + } + return status_; +} + +// returns status +uint8_t HONEYWELLABPSensor::readstatus_() { return status_; } + +// The pressure value from the most recent reading in raw counts +int HONEYWELLABPSensor::rawpressure_() { return pressure_count_; } + +// The temperature value from the most recent reading in raw counts +int HONEYWELLABPSensor::rawtemperature_() { return temperature_count_; } + +// Converts a digital pressure measurement in counts to pressure measured +float HONEYWELLABPSensor::countstopressure_(const int counts, const float min_pressure, const float max_pressure) { + return ((((float) counts - MIN_COUNT) * (max_pressure - min_pressure)) / (MAX_COUNT - MIN_COUNT)) + min_pressure; +} + +// Converts a digital temperature measurement in counts to temperature in C +// This will be invalid if sensore daoes not have temperature measurement capability +float HONEYWELLABPSensor::countstotemperatures_(const int counts) { return (((float) counts / 2047.0) * 200.0) - 50.0; } + +// Pressure value from the most recent reading in units +float HONEYWELLABPSensor::read_pressure_() { + return countstopressure_(pressure_count_, honeywellabp_min_pressure_, honeywellabp_max_pressure_); +} + +// Temperature value from the most recent reading in degrees C +float HONEYWELLABPSensor::read_temperature_() { return countstotemperatures_(temperature_count_); } + +void HONEYWELLABPSensor::update() { + ESP_LOGV(TAG, "Update Honeywell ABP Sensor"); + if (readsensor_() == 0) { + if (this->pressure_sensor_ != nullptr) + this->pressure_sensor_->publish_state(read_pressure_() * 1.0); + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(read_temperature_() * 1.0); + } +} + +float HONEYWELLABPSensor::get_setup_priority() const { return setup_priority::LATE; } + +void HONEYWELLABPSensor::dump_config() { + // LOG_SENSOR("", "HONEYWELLABP", this); + LOG_PIN(" CS Pin: ", this->cs_); + ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", honeywellabp_min_pressure_); + ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", honeywellabp_max_pressure_); + LOG_UPDATE_INTERVAL(this); +} + +void HONEYWELLABPSensor::set_honeywellabp_min_pressure(float min_pressure) { + this->honeywellabp_min_pressure_ = min_pressure; +} + +void HONEYWELLABPSensor::set_honeywellabp_max_pressure(float max_pressure) { + this->honeywellabp_max_pressure_ = max_pressure; +} + +} // namespace honeywellabp +} // namespace esphome diff --git a/esphome/components/honeywellabp/honeywellabp.h b/esphome/components/honeywellabp/honeywellabp.h new file mode 100644 index 0000000000..44d5952ca6 --- /dev/null +++ b/esphome/components/honeywellabp/honeywellabp.h @@ -0,0 +1,45 @@ +// for Honeywell ABP sensor +// adapting code from https://github.com/vwls/Honeywell_pressure_sensors +#pragma once + +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/spi/spi.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace honeywellabp { + +class HONEYWELLABPSensor : public PollingComponent, + public spi::SPIDevice { + public: + void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } + void setup() override; + void update() override; + float get_setup_priority() const override; + void dump_config() override; + void set_honeywellabp_min_pressure(float min_pressure); + void set_honeywellabp_max_pressure(float max_pressure); + + protected: + float honeywellabp_min_pressure_ = 0.0; + float honeywellabp_max_pressure_ = 0.0; + uint8_t buf_[4]; // buffer to hold sensor data + uint8_t status_ = 0; // byte to hold status information. + int pressure_count_ = 0; // hold raw pressure data (14 - bit, 0 - 16384) + int temperature_count_ = 0; // hold raw temperature data (11 - bit, 0 - 2048) + sensor::Sensor *pressure_sensor_; + sensor::Sensor *temperature_sensor_; + uint8_t readsensor_(); + uint8_t readstatus_(); + int rawpressure_(); + int rawtemperature_(); + float countstopressure_(int counts, float min_pressure, float max_pressure); + float countstotemperatures_(int counts); + float read_pressure_(); + float read_temperature_(); +}; + +} // namespace honeywellabp +} // namespace esphome diff --git a/esphome/components/honeywellabp/sensor.py b/esphome/components/honeywellabp/sensor.py new file mode 100644 index 0000000000..720a96b93c --- /dev/null +++ b/esphome/components/honeywellabp/sensor.py @@ -0,0 +1,70 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.components import spi +from esphome.const import ( + CONF_ID, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, +) + +DEPENDENCIES = ["spi"] +CODEOWNERS = ["@RubyBailey"] + +CONF_MIN_PRESSURE = "min_pressure" +CONF_MAX_PRESSURE = "max_pressure" + +honeywellabp_ns = cg.esphome_ns.namespace("honeywellabp") +HONEYWELLABPSensor = honeywellabp_ns.class_( + "HONEYWELLABPSensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HONEYWELLABPSensor), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + unit_of_measurement="psi", + accuracy_decimals=1, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ).extend( + { + cv.Required(CONF_MIN_PRESSURE): cv.float_, + cv.Required(CONF_MAX_PRESSURE): cv.float_, + } + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(spi.spi_device_schema(cs_pin_required=True)) +) + + +async def to_code(config): + + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await spi.register_spi_device(var, config) + + if CONF_PRESSURE in config: + conf = config[CONF_PRESSURE] + sens = await sensor.new_sensor(conf) + cg.add(var.set_pressure_sensor(sens)) + cg.add(var.set_honeywellabp_min_pressure(conf[CONF_MIN_PRESSURE])) + cg.add(var.set_honeywellabp_max_pressure(conf[CONF_MAX_PRESSURE])) + + if CONF_TEMPERATURE in config: + conf = config[CONF_TEMPERATURE] + sens = await sensor.new_sensor(conf) + cg.add(var.set_temperature_sensor(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 65169e2fd9..8cd01f1d6f 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -600,6 +600,14 @@ sensor: oversampling: 8x update_interval: 15s i2c_id: i2c_bus + - platform: honeywellabp + pressure: + name: "Honeywell pressure" + min_pressure: 0 + max_pressure: 15 + temperature: + name: "Honeywell temperature" + cs_pin: GPIO5 - platform: qmc5883l address: 0x0D field_strength_x: