diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index da791ec633..b65854533c 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -139,8 +139,6 @@ class I2CDevice; class I2CMultiplexer; class I2CRegister { public: - I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} - I2CRegister &operator=(uint8_t value); I2CRegister &operator=(const std::vector &value); I2CRegister &operator&=(uint8_t value); @@ -149,6 +147,10 @@ class I2CRegister { uint8_t get(); protected: + friend class I2CDevice; + + I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} + I2CDevice *parent_; uint8_t register_; }; diff --git a/esphome/components/ltr390/ltr390.cpp b/esphome/components/ltr390/ltr390.cpp index 2f766db637..cb1fdf3e8c 100644 --- a/esphome/components/ltr390/ltr390.cpp +++ b/esphome/components/ltr390/ltr390.cpp @@ -5,83 +5,12 @@ namespace esphome { namespace ltr390 { -static const char *TAG = "ltr390"; +static const char *const TAG = "ltr390"; static const float GAINVALUES[5] = {1.0, 3.0, 6.0, 9.0, 18.0}; static const float RESOLUTIONVALUE[6] = {4.0, 2.0, 1.0, 0.5, 0.25, 0.125}; static const uint32_t MODEADDRESSES[2] = {0x0D, 0x10}; -bool LTR390Component::enabled_() { - std::bitset<8> crtl_value(this->ctrl_reg_->get()); - return (bool) crtl_value[LTR390_CTRL_EN]; -} - -void LTR390Component::enable_(bool en) { - std::bitset<8> crtl_value(this->ctrl_reg_->get()); - crtl_value[LTR390_CTRL_EN] = en; - *this->ctrl_reg_ = crtl_value.to_ulong(); -} - -bool LTR390Component::reset_() { - std::bitset<8> crtl_value(this->ctrl_reg_->get()); - - crtl_value[LTR390_CTRL_RST] = true; - *this->ctrl_reg_ = crtl_value.to_ulong(); - - delay(10); - - // Read after reset - crtl_value = std::bitset<8>(this->ctrl_reg_->get()); - - return !(bool) crtl_value.to_ulong(); -} - -void LTR390Component::set_mode_(LTR390MODE mode) { - std::bitset<8> crtl_value(this->ctrl_reg_->get()); - crtl_value[LTR390_CTRL_MODE] = mode; - *this->ctrl_reg_ = crtl_value.to_ulong(); -} - -LTR390MODE LTR390Component::get_mode_() { - std::bitset<8> crtl_value(this->ctrl_reg_->get()); - return (LTR390MODE)(int) crtl_value[LTR390_CTRL_MODE]; -} - -void LTR390Component::set_gain_(LTR390GAIN gain) { *this->gain_reg_ = gain; } - -LTR390GAIN LTR390Component::get_gain_() { - std::bitset<8> gain_value(this->gain_reg_->get()); - return (LTR390GAIN) gain_value.to_ulong(); -} - -void LTR390Component::set_resolution_(LTR390RESOLUTION res) { - std::bitset<8> res_value(this->res_reg_->get()); - - std::bitset<3> new_res_value(res); - - for (int i = 0; i < 3; i++) { - res_value[4 + i] = new_res_value[i]; - } - - *this->res_reg_ = res_value.to_ulong(); -} - -LTR390RESOLUTION LTR390Component::get_resolution_() { - std::bitset<8> res_value(this->res_reg_->get()); - - std::bitset<3> output_value(0); - for (int i = 0; i < 3; i++) { - output_value[i] = res_value[4 + i]; - } - - return (LTR390RESOLUTION) output_value.to_ulong(); -} - -bool LTR390Component::new_data_available_() { - std::bitset<8> status_value(this->status_reg_->get()); - return (bool) status_value[3]; -} - uint32_t little_endian_bytes_to_int(const uint8_t *buffer, uint8_t num_bytes) { uint32_t value = 0; @@ -93,22 +22,39 @@ uint32_t little_endian_bytes_to_int(const uint8_t *buffer, uint8_t num_bytes) { return value; } -uint32_t LTR390Component::read_sensor_data_(LTR390MODE mode) { +optional LTR390Component::read_sensor_data_(LTR390MODE mode) { const uint8_t num_bytes = 3; uint8_t buffer[num_bytes]; - while (!this->new_data_available_()) { - ESP_LOGD(TAG, "WAITING FOR DATA"); + // Wait until data available + const uint32_t now = millis(); + while (true) { + std::bitset<8> status = this->reg(LTR390_MAIN_STATUS).get(); + bool available = status[3]; + if (available) + break; + + if (millis() - now > 100) { + ESP_LOGW(TAG, "Sensor didn't return any data, aborting"); + return {}; + } + ESP_LOGD(TAG, "Waiting for data"); delay(2); } - this->read_bytes(MODEADDRESSES[mode], buffer, num_bytes); + if (!this->read_bytes(MODEADDRESSES[mode], buffer, num_bytes)) { + ESP_LOGW(TAG, "Reading data from sensor failed!"); + return {}; + } return little_endian_bytes_to_int(buffer, num_bytes); } void LTR390Component::read_als_() { - uint32_t als = this->read_sensor_data_(LTR390_MODE_ALS); + auto val = this->read_sensor_data_(LTR390_MODE_ALS); + if (!val.has_value()) + return; + uint32_t als = *val; if (this->light_sensor_ != nullptr) { float lux = (0.6 * als) / (GAINVALUES[this->gain_] * RESOLUTIONVALUE[this->res_]) * this->wfac_; @@ -121,7 +67,10 @@ void LTR390Component::read_als_() { } void LTR390Component::read_uvs_() { - uint32_t uv = this->read_sensor_data_(LTR390_MODE_UVS); + auto val = this->read_sensor_data_(LTR390_MODE_UVS); + if (!val.has_value()) + return; + uint32_t uv = *val; if (this->uvi_sensor_ != nullptr) { this->uvi_sensor_->publish_state(uv / LTR390_SENSITIVITY * this->wfac_); @@ -134,16 +83,20 @@ void LTR390Component::read_uvs_() { void LTR390Component::read_mode_(int mode_index) { // Set mode - this->set_mode_(std::get<0>(this->mode_funcs_->at(mode_index))); + LTR390MODE mode = std::get<0>(this->mode_funcs_[mode_index]); + + std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_MODE] = mode; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); // After the sensor integration time do the following - this->set_timeout(((unsigned int) RESOLUTIONVALUE[this->res_]) * 100, [this, mode_index]() { + this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100, [this, mode_index]() { // Read from the sensor - std::get<1>(this->mode_funcs_->at(mode_index))(); + std::get<1>(this->mode_funcs_[mode_index])(); // If there are more modes to read then begin the next // otherwise stop - if (mode_index + 1 < this->mode_funcs_->size()) { + if (mode_index + 1 < this->mode_funcs_.size()) { this->read_mode_(mode_index + 1); } else { this->reading_ = false; @@ -154,40 +107,48 @@ void LTR390Component::read_mode_(int mode_index) { void LTR390Component::setup() { ESP_LOGCONFIG(TAG, "Setting up ltr390..."); - this->ctrl_reg_ = new i2c::I2CRegister(this, LTR390_MAIN_CTRL); - this->status_reg_ = new i2c::I2CRegister(this, LTR390_MAIN_STATUS); - this->gain_reg_ = new i2c::I2CRegister(this, LTR390_GAIN); - this->res_reg_ = new i2c::I2CRegister(this, LTR390_MEAS_RATE); + // reset + std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_RST] = true; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); + delay(10); - this->reset_(); + // Enable + ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_EN] = true; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); - this->enable_(true); - ESP_LOGD(TAG, "%s", this->enabled_() ? "ENABLED" : "DISABLED"); - if (!this->enabled_()) { + // check enabled + ctrl = this->reg(LTR390_MAIN_CTRL).get(); + bool enabled = ctrl[LTR390_CTRL_EN]; + + if (!enabled) { + ESP_LOGW(TAG, "Sensor didn't respond with enabled state"); this->mark_failed(); return; } // Set gain - this->set_gain_(this->gain_); + this->reg(LTR390_GAIN) = gain_; // Set resolution - this->set_resolution_(this->res_); + uint8_t res = this->reg(LTR390_MEAS_RATE).get(); + // resolution is in bits 5-7 + res &= ~0b01110000; + res |= res << 4; + this->reg(LTR390_MEAS_RATE) = res; // Set sensor read state this->reading_ = false; - // Create a list of modes and corresponding read functions - this->mode_funcs_ = new std::vector > >(); - // If we need the light sensor then add to the list if (this->light_sensor_ != nullptr || this->als_sensor_ != nullptr) { - this->mode_funcs_->push_back(std::make_tuple(LTR390_MODE_ALS, std::bind(<R390Component::read_als_, this))); + this->mode_funcs_.emplace_back(LTR390_MODE_ALS, std::bind(<R390Component::read_als_, this)); } // If we need the UV sensor then add to the list if (this->uvi_sensor_ != nullptr || this->uv_sensor_ != nullptr) { - this->mode_funcs_->push_back(std::make_tuple(LTR390_MODE_UVS, std::bind(<R390Component::read_uvs_, this))); + this->mode_funcs_.emplace_back(LTR390_MODE_UVS, std::bind(<R390Component::read_uvs_, this)); } } diff --git a/esphome/components/ltr390/ltr390.h b/esphome/components/ltr390/ltr390.h index 7bb61503ed..d607a3e55f 100644 --- a/esphome/components/ltr390/ltr390.h +++ b/esphome/components/ltr390/ltr390.h @@ -1,10 +1,9 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/optional.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" -#include -#include #include namespace esphome { @@ -23,8 +22,7 @@ static const uint8_t LTR390_MEAS_RATE = 0x04; static const uint8_t LTR390_GAIN = 0x05; static const uint8_t LTR390_PART_ID = 0x06; static const uint8_t LTR390_MAIN_STATUS = 0x07; - -#define LTR390_SENSITIVITY 2300.0 +static const float LTR390_SENSITIVITY = 2300.0; // Sensing modes enum LTR390MODE { @@ -68,36 +66,17 @@ class LTR390Component : public PollingComponent, public i2c::I2CDevice { void set_uv_sensor(sensor::Sensor *uv_sensor) { this->uv_sensor_ = uv_sensor; } protected: - bool enabled_(); - void enable_(bool en); - - bool reset_(); - - void set_mode_(LTR390MODE mode); - LTR390MODE get_mode_(); - - void set_gain_(LTR390GAIN gain); - LTR390GAIN get_gain_(); - - void set_resolution_(LTR390RESOLUTION res); - LTR390RESOLUTION get_resolution_(); - - bool new_data_available_(); - uint32_t read_sensor_data_(LTR390MODE mode); + optional read_sensor_data_(LTR390MODE mode); void read_als_(); void read_uvs_(); void read_mode_(int mode_index); - std::atomic reading_; + bool reading_; - std::vector > > *mode_funcs_; - - i2c::I2CRegister *ctrl_reg_; - i2c::I2CRegister *status_reg_; - i2c::I2CRegister *gain_reg_; - i2c::I2CRegister *res_reg_; + // a list of modes and corresponding read functions + std::vector>> mode_funcs_; LTR390GAIN gain_; LTR390RESOLUTION res_; diff --git a/esphome/components/ltr390/sensor.py b/esphome/components/ltr390/sensor.py index 2ca203502d..0e70f7bb1b 100644 --- a/esphome/components/ltr390/sensor.py +++ b/esphome/components/ltr390/sensor.py @@ -20,10 +20,10 @@ LTR390Component = ltr390_ns.class_( "LTR390Component", cg.PollingComponent, i2c.I2CDevice ) -CONF_ALS = "als" -CONF_UVI = "uvi" +CONF_AMBIENT_LIGHT = "ambient_light" +CONF_UV_INDEX = "uv_index" CONF_UV = "uv" -CONF_WFAC = "wfac" +CONF_WINDOW_CORRECTION_FACTOR = "window_correction_factor" UNIT_COUNTS = "#" UNIT_UVI = "UVI" @@ -47,17 +47,17 @@ RES_OPTIONS = { 13: LTR390RESOLUTION.LTR390_RESOLUTION_13BIT, } -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(LTR390Component), cv.Optional(CONF_LIGHT): sensor.sensor_schema( UNIT_LUX, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE ), - cv.Optional(CONF_ALS): sensor.sensor_schema( + cv.Optional(CONF_AMBIENT_LIGHT): sensor.sensor_schema( UNIT_COUNTS, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE ), - cv.Optional(CONF_UVI): sensor.sensor_schema( + cv.Optional(CONF_UV_INDEX): sensor.sensor_schema( UNIT_UVI, ICON_BRIGHTNESS_5, 5, DEVICE_CLASS_ILLUMINANCE ), cv.Optional(CONF_UV): sensor.sensor_schema( @@ -65,32 +65,34 @@ CONFIG_SCHEMA = ( ), cv.Optional(CONF_GAIN, default="X3"): cv.enum(GAIN_OPTIONS), cv.Optional(CONF_RESOLUTION, default=18): cv.enum(RES_OPTIONS), - cv.Optional(CONF_WFAC, default=1.0): cv.float_range(min=1.0), + cv.Optional(CONF_WINDOW_CORRECTION_FACTOR, default=1.0): cv.float_range( + min=1.0 + ), } ) .extend(cv.polling_component_schema("60s")) - .extend(i2c.i2c_device_schema(0x53)) + .extend(i2c.i2c_device_schema(0x53)), + cv.has_at_least_one_key(CONF_LIGHT, CONF_AMBIENT_LIGHT, CONF_UV_INDEX, CONF_UV), ) TYPES = { CONF_LIGHT: "set_light_sensor", - CONF_ALS: "set_als_sensor", - CONF_UVI: "set_uvi_sensor", + CONF_AMBIENT_LIGHT: "set_als_sensor", + CONF_UV_INDEX: "set_uvi_sensor", CONF_UV: "set_uv_sensor", } -def to_code(config): +async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) - yield i2c.register_i2c_device(var, config) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) cg.add(var.set_gain_value(config[CONF_GAIN])) cg.add(var.set_res_value(config[CONF_RESOLUTION])) - cg.add(var.set_wfac_value(config[CONF_WFAC])) + cg.add(var.set_wfac_value(config[CONF_WINDOW_CORRECTION_FACTOR])) for key, funcName in TYPES.items(): - if key in config: - sens = yield sensor.new_sensor(config[key]) + sens = await sensor.new_sensor(config[key]) cg.add(getattr(var, funcName)(sens)) diff --git a/tests/test2.yaml b/tests/test2.yaml index 9c99416133..1cdbc3a5a2 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -239,15 +239,15 @@ sensor: - platform: ltr390 uv: name: "LTR390 UV" - uvi: + uv_index: name: "LTR390 UVI" light: name: "LTR390 Light" - als: + ambient_light: name: "LTR390 ALS" gain: "X3" resolution: 18 - wfac: 1.0 + window_correction_factor: 1.0 address: 0x53 update_interval: 60s - platform: sgp40