mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 14:43:51 +00:00 
			
		
		
		
	Add support for ltr390
This commit is contained in:
		
							
								
								
									
										0
									
								
								esphome/components/ltr390/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ltr390/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										220
									
								
								esphome/components/ltr390/ltr390.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								esphome/components/ltr390/ltr390.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | ||||
| #include "ltr390.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include <bitset> | ||||
|  | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ltr390 { | ||||
|  | ||||
| static const char *TAG = "ltr390"; | ||||
|  | ||||
| static const float gain_values_[5] = {1.0, 3.0, 6.0, 9.0, 18.0}; | ||||
| static const float resolution_values_[6] = {4.0, 2.0, 1.0, 0.5, 0.25, 0.125}; | ||||
| static const uint32_t mode_addresses_[2] = {0x0D, 0x10}; | ||||
|  | ||||
| bool LTR390Component::enabled(void) { | ||||
|   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(void) { | ||||
|   std::bitset<8> crtl_value (this->ctrl_reg_->get()); | ||||
|  | ||||
|   crtl_value[LTR390_CTRL_RST] = 1; | ||||
|   *this->ctrl_reg_ = crtl_value.to_ulong(); | ||||
|  | ||||
|   delay(10); | ||||
|   // Read after reset | ||||
|   crtl_value = std::bitset<8>(this->ctrl_reg_->get()); | ||||
|   if (crtl_value.to_ulong()) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void LTR390Component::set_mode(ltr390_mode_t mode) { | ||||
|   std::bitset<8> crtl_value (this->ctrl_reg_->get()); | ||||
|   crtl_value[LTR390_CTRL_MODE] = mode; | ||||
|   *this->ctrl_reg_ = crtl_value.to_ulong(); | ||||
| } | ||||
|  | ||||
| ltr390_mode_t LTR390Component::get_mode(void) { | ||||
|   std::bitset<8> crtl_value (this->ctrl_reg_->get()); | ||||
|   return (ltr390_mode_t)(int)crtl_value[LTR390_CTRL_MODE]; | ||||
| } | ||||
|  | ||||
| void LTR390Component::set_gain(ltr390_gain_t gain) { | ||||
|   *this->gain_reg_ = gain; | ||||
| } | ||||
|  | ||||
| ltr390_gain_t LTR390Component::get_gain(void) { | ||||
|   std::bitset<8> gain_value (this->gain_reg_->get()); | ||||
|   return (ltr390_gain_t)gain_value.to_ulong(); | ||||
| } | ||||
|  | ||||
| void LTR390Component::set_resolution(ltr390_resolution_t 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(); | ||||
| } | ||||
|  | ||||
| ltr390_resolution_t LTR390Component::get_resolution(void) { | ||||
|   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 (ltr390_resolution_t)output_value.to_ulong(); | ||||
| } | ||||
|  | ||||
| bool LTR390Component::new_data_available(void) { | ||||
|   std::bitset<8> status_value (this->status_reg_->get()); | ||||
|   return (bool)status_value[3]; | ||||
| } | ||||
|  | ||||
| uint32_t little_endian_bytes_to_int(uint8_t *buffer, uint8_t num_bytes) { | ||||
|   uint32_t value = 0; | ||||
|  | ||||
|   for (int i = 0; i < num_bytes; i++) { | ||||
|     value <<= 8; | ||||
|     value |= buffer[num_bytes - i - 1]; | ||||
|   } | ||||
|  | ||||
|   return value; | ||||
| } | ||||
|  | ||||
| uint32_t LTR390Component::read_sensor_data(ltr390_mode_t mode) { | ||||
|   const uint8_t num_bytes = 3; | ||||
|   uint8_t buffer[num_bytes]; | ||||
|  | ||||
|   while (!this->new_data_available()) { | ||||
|     ESP_LOGD(TAG, "WAITING FOR DATA"); | ||||
|     delay(2); | ||||
|   } | ||||
|  | ||||
|   this->read_bytes(mode_addresses_[mode], buffer, num_bytes); | ||||
|  | ||||
|   return little_endian_bytes_to_int(buffer, num_bytes); | ||||
| } | ||||
|  | ||||
| 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); | ||||
|  | ||||
|   this->reset(); | ||||
|  | ||||
|   this->enable(true); | ||||
|   ESP_LOGD(TAG, "%s", this->enabled() ? "ENABLED" : "DISABLED"); | ||||
|   if (!this->enabled()) { | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // Set gain | ||||
|   this->set_gain(this->gain_); | ||||
|  | ||||
|   // Set resolution | ||||
|   this->set_resolution(this->res_); | ||||
|  | ||||
|   // Set sensor read state | ||||
|   this->reading = false; | ||||
|  | ||||
|   // Create a list of modes and corresponding read functions | ||||
|   this->mode_funcs_ = new std::vector<std::tuple<ltr390_mode_t, std::function<void(void)> > >(); | ||||
|  | ||||
|   // 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))); | ||||
|   } | ||||
|  | ||||
|   // 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))); | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| void LTR390Component::dump_config() { | ||||
|     LOG_I2C_DEVICE(this); | ||||
| } | ||||
|  | ||||
| void LTR390Component::read_als() { | ||||
|   uint32_t als = this->read_sensor_data(LTR390_MODE_ALS); | ||||
|  | ||||
|   if (this->light_sensor_ != nullptr) { | ||||
|     float lux = (0.6 * als) / (gain_values_[this->gain_] * resolution_values_[this->res_]) * this->wfac_; | ||||
|     this->light_sensor_->publish_state(lux); | ||||
|   } | ||||
|  | ||||
|   if (this->als_sensor_ != nullptr) { | ||||
|     this->als_sensor_->publish_state(als); | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| void LTR390Component::read_uvs() { | ||||
|   uint32_t uv = this->read_sensor_data(LTR390_MODE_UVS); | ||||
|  | ||||
|   if (this->uvi_sensor_ != nullptr) { | ||||
|     this->uvi_sensor_->publish_state(uv/LTR390_SENSITIVITY * this->wfac_); | ||||
|   } | ||||
|  | ||||
|   if (this->uv_sensor_ != nullptr) { | ||||
|     this->uv_sensor_->publish_state(uv); | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| void LTR390Component::read_mode(int mode_index) { | ||||
|  | ||||
|   // Set mode | ||||
|   this->set_mode(std::get<0>(this->mode_funcs_->at(mode_index))); | ||||
|  | ||||
|   // After the sensor integration time do the following | ||||
|   this->set_timeout(resolution_values_[this->res_] * 100, [this, mode_index]() { | ||||
|     // Read from the sensor | ||||
|     std::get<1>(this->mode_funcs_->at(mode_index))(); | ||||
|  | ||||
|     // If there are more modes to read then begin the next | ||||
|     // otherwise stop | ||||
|     if (mode_index + 1 < this->mode_funcs_->size()) { | ||||
|         this->read_mode(mode_index + 1); | ||||
|     } else { | ||||
|       this->reading = false; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
| } | ||||
|  | ||||
| void LTR390Component::update() { | ||||
|  | ||||
|   if (!this->reading) { | ||||
|     this->reading = true; | ||||
|     this->read_mode(0); | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| }  // namespace ltr390 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										115
									
								
								esphome/components/ltr390/ltr390.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								esphome/components/ltr390/ltr390.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| #include <map> | ||||
| #include <atomic> | ||||
| #include <tuple> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ltr390 { | ||||
|  | ||||
| typedef enum { | ||||
|   LTR390_CTRL_EN = 1, | ||||
|   LTR390_CTRL_MODE = 3, | ||||
|   LTR390_CTRL_RST = 4, | ||||
| } ltr390_ctrl_t; | ||||
|  | ||||
| // enums from https://github.com/adafruit/Adafruit_LTR390/ | ||||
|  | ||||
| #define LTR390_MAIN_CTRL 0x00       ///< Main control register | ||||
| #define LTR390_MEAS_RATE 0x04       ///< Resolution and data rate | ||||
| #define LTR390_GAIN 0x05            ///< ALS and UVS gain range | ||||
| #define LTR390_PART_ID 0x06         ///< Part id/revision register | ||||
| #define LTR390_MAIN_STATUS 0x07     ///< Main status register | ||||
|  | ||||
| #define LTR390_SENSITIVITY 2300.0 | ||||
|  | ||||
| // Sensing modes | ||||
| typedef enum { | ||||
|   LTR390_MODE_ALS, | ||||
|   LTR390_MODE_UVS, | ||||
| } ltr390_mode_t; | ||||
|  | ||||
| // Sensor gain levels | ||||
| typedef enum { | ||||
|   LTR390_GAIN_1 = 0, | ||||
|   LTR390_GAIN_3, // Default | ||||
|   LTR390_GAIN_6, | ||||
|   LTR390_GAIN_9, | ||||
|   LTR390_GAIN_18, | ||||
| } ltr390_gain_t; | ||||
|  | ||||
| // Sensor resolution | ||||
| typedef enum { | ||||
|   LTR390_RESOLUTION_20BIT, | ||||
|   LTR390_RESOLUTION_19BIT, | ||||
|   LTR390_RESOLUTION_18BIT, // Default | ||||
|   LTR390_RESOLUTION_17BIT, | ||||
|   LTR390_RESOLUTION_16BIT, | ||||
|   LTR390_RESOLUTION_13BIT, | ||||
| } ltr390_resolution_t; | ||||
|  | ||||
| class LTR390Component : public PollingComponent, public i2c::I2CDevice { | ||||
|   public: | ||||
|     void setup() override; | ||||
|     void dump_config() override; | ||||
|     void update() override; | ||||
|     float get_setup_priority() const override { return setup_priority::DATA; } | ||||
|  | ||||
|     void set_gain_value(ltr390_gain_t gain) { this->gain_ = gain; } | ||||
|     void set_res_value(ltr390_resolution_t res) { this->res_ = res; } | ||||
|     void set_wfac_value(float wfac) { this->wfac_ = wfac; } | ||||
|  | ||||
|     void set_light_sensor(sensor::Sensor *light_sensor) { this->light_sensor_ = light_sensor; } | ||||
|     void set_als_sensor(sensor::Sensor *als_sensor) { this->als_sensor_ = als_sensor; } | ||||
|     void set_uvi_sensor(sensor::Sensor *uvi_sensor) { this->uvi_sensor_ = uvi_sensor; } | ||||
|     void set_uv_sensor(sensor::Sensor *uv_sensor) { this->uv_sensor_ = uv_sensor; } | ||||
|  | ||||
|   protected: | ||||
|     bool enabled(); | ||||
|     void enable(bool en); | ||||
|  | ||||
|     bool reset(void); | ||||
|  | ||||
|     void set_mode(ltr390_mode_t mode); | ||||
|     ltr390_mode_t get_mode(void); | ||||
|  | ||||
|     void set_gain(ltr390_gain_t gain); | ||||
|     ltr390_gain_t get_gain(void); | ||||
|  | ||||
|     void set_resolution(ltr390_resolution_t res); | ||||
|     ltr390_resolution_t get_resolution(void); | ||||
|  | ||||
|     bool new_data_available(void); | ||||
|     uint32_t read_sensor_data(ltr390_mode_t mode); | ||||
|  | ||||
|     void read_als(void); | ||||
|     void read_uvs(void); | ||||
|  | ||||
|     void read_mode(int mode_index); | ||||
|  | ||||
|     std::atomic<bool> reading; | ||||
|  | ||||
|     std::vector< std::tuple< ltr390_mode_t, std::function<void(void)> > > *mode_funcs_; | ||||
|  | ||||
|     i2c::I2CRegister *ctrl_reg_; | ||||
|     i2c::I2CRegister *status_reg_; | ||||
|     i2c::I2CRegister *gain_reg_; | ||||
|     i2c::I2CRegister *res_reg_; | ||||
|  | ||||
|     ltr390_gain_t gain_; | ||||
|     ltr390_resolution_t res_; | ||||
|     float wfac_; | ||||
|  | ||||
|     sensor::Sensor *light_sensor_{nullptr}; | ||||
|     sensor::Sensor *als_sensor_{nullptr}; | ||||
|  | ||||
|     sensor::Sensor *uvi_sensor_{nullptr}; | ||||
|     sensor::Sensor *uv_sensor_{nullptr}; | ||||
|  | ||||
| }; | ||||
|  | ||||
| }  // namespace ltr390 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										75
									
								
								esphome/components/ltr390/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								esphome/components/ltr390/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, sensor | ||||
| from esphome.const import CONF_ID, CONF_GAIN, CONF_RESOLUTION, UNIT_LUX, ICON_BRIGHTNESS_5 | ||||
|  | ||||
| DEPENDENCIES = ['i2c'] | ||||
|  | ||||
| ltr390_ns = cg.esphome_ns.namespace('ltr390') | ||||
|  | ||||
| LTR390Component = ltr390_ns.class_('LTR390Component', cg.PollingComponent, i2c.I2CDevice) | ||||
|  | ||||
| CONF_LIGHT = 'light' | ||||
| CONF_ALS = 'als' | ||||
| CONF_UVI = 'uvi' | ||||
| CONF_UV = 'uv' | ||||
| CONF_WFAC = 'wfac' | ||||
|  | ||||
| UNIT_COUNTS = '#' | ||||
| UNIT_UVI = 'UVI' | ||||
|  | ||||
| ltr390_gain_t = ltr390_ns.enum('ltr390_gain_t') | ||||
| GAIN_OPTIONS = { | ||||
|     "X1": ltr390_gain_t.LTR390_GAIN_1, | ||||
|     "X3": ltr390_gain_t.LTR390_GAIN_3, | ||||
|     "X6": ltr390_gain_t.LTR390_GAIN_6, | ||||
|     "X9": ltr390_gain_t.LTR390_GAIN_9, | ||||
|     "X18": ltr390_gain_t.LTR390_GAIN_18, | ||||
| } | ||||
|  | ||||
| ltr390_resolution_t = ltr390_ns.enum('ltr390_resolution_t') | ||||
| RES_OPTIONS = { | ||||
|     20: ltr390_resolution_t.LTR390_RESOLUTION_20BIT, | ||||
|     19: ltr390_resolution_t.LTR390_RESOLUTION_19BIT, | ||||
|     18: ltr390_resolution_t.LTR390_RESOLUTION_18BIT, | ||||
|     17: ltr390_resolution_t.LTR390_RESOLUTION_17BIT, | ||||
|     16: ltr390_resolution_t.LTR390_RESOLUTION_16BIT, | ||||
|     13: ltr390_resolution_t.LTR390_RESOLUTION_13BIT, | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema({ | ||||
|     cv.GenerateID(): cv.declare_id(LTR390Component), | ||||
|  | ||||
|     cv.Optional(CONF_LIGHT): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1), | ||||
|     cv.Optional(CONF_ALS): sensor.sensor_schema(UNIT_COUNTS, ICON_BRIGHTNESS_5, 1), | ||||
|  | ||||
|     cv.Optional(CONF_UVI): sensor.sensor_schema(UNIT_UVI, ICON_BRIGHTNESS_5, 5), | ||||
|     cv.Optional(CONF_UV): sensor.sensor_schema(UNIT_COUNTS, ICON_BRIGHTNESS_5, 1), | ||||
|  | ||||
|     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), | ||||
|  | ||||
| }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x53)) | ||||
|  | ||||
| TYPES = { | ||||
|     CONF_LIGHT: 'set_light_sensor', | ||||
|     CONF_ALS: 'set_als_sensor', | ||||
|     CONF_UVI: 'set_uvi_sensor', | ||||
|     CONF_UV: 'set_uv_sensor', | ||||
| } | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield cg.register_component(var, config) | ||||
|     yield 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])) | ||||
|  | ||||
|     for key, funcName in TYPES.items(): | ||||
|  | ||||
|         if key in config: | ||||
|             sens = yield sensor.new_sensor(config[key]) | ||||
|             cg.add(getattr(var, funcName)(sens)) | ||||
		Reference in New Issue
	
	Block a user