mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
TSL2591 automatic gain control (#3071)
This commit is contained in:
parent
511c8de6f3
commit
c5974b8833
3 changed files with 101 additions and 19 deletions
|
@ -56,18 +56,19 @@ INTEGRATION_TIMES = {
|
||||||
600: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_600MS,
|
600: TSL2591IntegrationTime.TSL2591_INTEGRATION_TIME_600MS,
|
||||||
}
|
}
|
||||||
|
|
||||||
TSL2591Gain = tsl2591_ns.enum("TSL2591Gain")
|
TSL2591ComponentGain = tsl2591_ns.enum("TSL2591ComponentGain")
|
||||||
GAINS = {
|
GAINS = {
|
||||||
"1X": TSL2591Gain.TSL2591_GAIN_LOW,
|
"1X": TSL2591ComponentGain.TSL2591_CGAIN_LOW,
|
||||||
"LOW": TSL2591Gain.TSL2591_GAIN_LOW,
|
"LOW": TSL2591ComponentGain.TSL2591_CGAIN_LOW,
|
||||||
"25X": TSL2591Gain.TSL2591_GAIN_MED,
|
"25X": TSL2591ComponentGain.TSL2591_CGAIN_MED,
|
||||||
"MED": TSL2591Gain.TSL2591_GAIN_MED,
|
"MED": TSL2591ComponentGain.TSL2591_CGAIN_MED,
|
||||||
"MEDIUM": TSL2591Gain.TSL2591_GAIN_MED,
|
"MEDIUM": TSL2591ComponentGain.TSL2591_CGAIN_MED,
|
||||||
"400X": TSL2591Gain.TSL2591_GAIN_HIGH,
|
"400X": TSL2591ComponentGain.TSL2591_CGAIN_HIGH,
|
||||||
"HIGH": TSL2591Gain.TSL2591_GAIN_HIGH,
|
"HIGH": TSL2591ComponentGain.TSL2591_CGAIN_HIGH,
|
||||||
"9500X": TSL2591Gain.TSL2591_GAIN_MAX,
|
"9500X": TSL2591ComponentGain.TSL2591_CGAIN_MAX,
|
||||||
"MAX": TSL2591Gain.TSL2591_GAIN_MAX,
|
"MAX": TSL2591ComponentGain.TSL2591_CGAIN_MAX,
|
||||||
"MAXIMUM": TSL2591Gain.TSL2591_GAIN_MAX,
|
"MAXIMUM": TSL2591ComponentGain.TSL2591_CGAIN_MAX,
|
||||||
|
"AUTO": TSL2591ComponentGain.TSL2591_CGAIN_AUTO,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ CONFIG_SCHEMA = (
|
||||||
CONF_INTEGRATION_TIME, default="100ms"
|
CONF_INTEGRATION_TIME, default="100ms"
|
||||||
): validate_integration_time,
|
): validate_integration_time,
|
||||||
cv.Optional(CONF_NAME, default="TLS2591"): cv.string,
|
cv.Optional(CONF_NAME, default="TLS2591"): cv.string,
|
||||||
cv.Optional(CONF_GAIN, default="MEDIUM"): cv.enum(GAINS, upper=True),
|
cv.Optional(CONF_GAIN, default="AUTO"): cv.enum(GAINS, upper=True),
|
||||||
cv.Optional(CONF_POWER_SAVE_MODE, default=True): cv.boolean,
|
cv.Optional(CONF_POWER_SAVE_MODE, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_DEVICE_FACTOR, default=53.0): cv.float_with_unit(
|
cv.Optional(CONF_DEVICE_FACTOR, default=53.0): cv.float_with_unit(
|
||||||
"device_factor", "", True
|
"device_factor", "", True
|
||||||
|
|
|
@ -43,6 +43,8 @@ void TSL2591Component::disable_if_power_saving_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSL2591Component::setup() {
|
void TSL2591Component::setup() {
|
||||||
|
if (this->component_gain_ == TSL2591_CGAIN_AUTO)
|
||||||
|
this->gain_ = TSL2591_GAIN_MED;
|
||||||
uint8_t address = this->address_;
|
uint8_t address = this->address_;
|
||||||
ESP_LOGI(TAG, "Setting up TSL2591 sensor at I2C address 0x%02X", address);
|
ESP_LOGI(TAG, "Setting up TSL2591 sensor at I2C address 0x%02X", address);
|
||||||
uint8_t id;
|
uint8_t id;
|
||||||
|
@ -73,26 +75,30 @@ void TSL2591Component::dump_config() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, " Name: %s", this->name_);
|
ESP_LOGCONFIG(TAG, " Name: %s", this->name_);
|
||||||
TSL2591Gain raw_gain = this->gain_;
|
TSL2591ComponentGain raw_gain = this->component_gain_;
|
||||||
int gain = 0;
|
int gain = 0;
|
||||||
std::string gain_word = "unknown";
|
std::string gain_word = "unknown";
|
||||||
switch (raw_gain) {
|
switch (raw_gain) {
|
||||||
case TSL2591_GAIN_LOW:
|
case TSL2591_CGAIN_LOW:
|
||||||
gain = 1;
|
gain = 1;
|
||||||
gain_word = "low";
|
gain_word = "low";
|
||||||
break;
|
break;
|
||||||
case TSL2591_GAIN_MED:
|
case TSL2591_CGAIN_MED:
|
||||||
gain = 25;
|
gain = 25;
|
||||||
gain_word = "medium";
|
gain_word = "medium";
|
||||||
break;
|
break;
|
||||||
case TSL2591_GAIN_HIGH:
|
case TSL2591_CGAIN_HIGH:
|
||||||
gain = 400;
|
gain = 400;
|
||||||
gain_word = "high";
|
gain_word = "high";
|
||||||
break;
|
break;
|
||||||
case TSL2591_GAIN_MAX:
|
case TSL2591_CGAIN_MAX:
|
||||||
gain = 9500;
|
gain = 9500;
|
||||||
gain_word = "maximum";
|
gain_word = "maximum";
|
||||||
break;
|
break;
|
||||||
|
case TSL2591_CGAIN_AUTO:
|
||||||
|
gain = -1;
|
||||||
|
gain_word = "auto";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
ESP_LOGCONFIG(TAG, " Gain: %dx (%s)", gain, gain_word.c_str());
|
ESP_LOGCONFIG(TAG, " Gain: %dx (%s)", gain, gain_word.c_str());
|
||||||
TSL2591IntegrationTime raw_timing = this->integration_time_;
|
TSL2591IntegrationTime raw_timing = this->integration_time_;
|
||||||
|
@ -129,6 +135,9 @@ void TSL2591Component::process_update_() {
|
||||||
if (this->calculated_lux_sensor_ != nullptr) {
|
if (this->calculated_lux_sensor_ != nullptr) {
|
||||||
this->calculated_lux_sensor_->publish_state(lux);
|
this->calculated_lux_sensor_->publish_state(lux);
|
||||||
}
|
}
|
||||||
|
if (this->component_gain_ == TSL2591_CGAIN_AUTO) {
|
||||||
|
this->automatic_gain_update(full);
|
||||||
|
}
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +192,7 @@ void TSL2591Component::set_integration_time(TSL2591IntegrationTime integration_t
|
||||||
this->integration_time_ = integration_time;
|
this->integration_time_ = integration_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TSL2591Component::set_gain(TSL2591Gain gain) { this->gain_ = gain; }
|
void TSL2591Component::set_gain(TSL2591ComponentGain gain) { this->component_gain_ = gain; }
|
||||||
|
|
||||||
void TSL2591Component::set_device_and_glass_attenuation_factors(float device_factor, float glass_attenuation_factor) {
|
void TSL2591Component::set_device_and_glass_attenuation_factors(float device_factor, float glass_attenuation_factor) {
|
||||||
this->device_factor_ = device_factor;
|
this->device_factor_ = device_factor;
|
||||||
|
@ -366,5 +375,56 @@ float TSL2591Component::get_calculated_lux(uint16_t full_spectrum, uint16_t infr
|
||||||
return std::max(lux, 0.0F);
|
return std::max(lux, 0.0F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Calculates and updates the sensor gain setting, trying to keep the full spectrum counts near
|
||||||
|
* the middle of the range
|
||||||
|
*
|
||||||
|
* It's hard to tell how far down to turn the gain when it's at the top of the scale, so decrease
|
||||||
|
* the gain by up to 2 steps if it's near the top to be sure we get a good reading next time.
|
||||||
|
* Increase gain by max 2 steps per reading.
|
||||||
|
*
|
||||||
|
* If integration time is 100 MS, divide the upper thresholds by 2 to account for ADC saturation
|
||||||
|
*
|
||||||
|
* @param full_spectrum The ADC reading for TSL2591 channel 0.
|
||||||
|
*
|
||||||
|
* 1/3 FS = 21,845
|
||||||
|
*/
|
||||||
|
void TSL2591Component::automatic_gain_update(uint16_t full_spectrum) {
|
||||||
|
TSL2591Gain new_gain = this->gain_;
|
||||||
|
uint fs_divider = (this->integration_time_ == TSL2591_INTEGRATION_TIME_100MS) ? 2 : 1;
|
||||||
|
|
||||||
|
switch (this->gain_) {
|
||||||
|
case TSL2591_GAIN_LOW:
|
||||||
|
if (full_spectrum < 54) // 1/3 FS / GAIN_HIGH
|
||||||
|
new_gain = TSL2591_GAIN_HIGH;
|
||||||
|
else if (full_spectrum < 875) // 1/3 FS / GAIN_MED
|
||||||
|
new_gain = TSL2591_GAIN_MED;
|
||||||
|
break;
|
||||||
|
case TSL2591_GAIN_MED:
|
||||||
|
if (full_spectrum < 57) // 1/3 FS / (GAIN_MAX/GAIN_MED)
|
||||||
|
new_gain = TSL2591_GAIN_MAX;
|
||||||
|
else if (full_spectrum < 1365) // 1/3 FS / (GAIN_HIGH/GAIN_MED)
|
||||||
|
new_gain = TSL2591_GAIN_HIGH;
|
||||||
|
else if (full_spectrum > 62000 / fs_divider) // 2/3 FS / (GAIN_LOW/GAIN_MED) clipped to 95% FS
|
||||||
|
new_gain = TSL2591_GAIN_LOW;
|
||||||
|
break;
|
||||||
|
case TSL2591_GAIN_HIGH:
|
||||||
|
if (full_spectrum < 920) // 1/3 FS / (GAIN_MAX/GAIN_HIGH)
|
||||||
|
new_gain = TSL2591_GAIN_MAX;
|
||||||
|
else if (full_spectrum > 62000 / fs_divider) // 2/3 FS / (GAIN_MED/GAIN_HIGH) clipped to 95% FS
|
||||||
|
new_gain = TSL2591_GAIN_LOW;
|
||||||
|
break;
|
||||||
|
case TSL2591_GAIN_MAX:
|
||||||
|
if (full_spectrum > 62000 / fs_divider) // 2/3 FS / (GAIN_MED/GAIN_HIGH) clipped to 95% FS
|
||||||
|
new_gain = TSL2591_GAIN_MED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->gain_ != new_gain) {
|
||||||
|
this->gain_ = new_gain;
|
||||||
|
this->set_integration_time_and_gain(this->integration_time_, this->gain_);
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Gain setting: %d", this->gain_);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tsl2591
|
} // namespace tsl2591
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -21,6 +21,19 @@ enum TSL2591IntegrationTime {
|
||||||
TSL2591_INTEGRATION_TIME_600MS = 0b101,
|
TSL2591_INTEGRATION_TIME_600MS = 0b101,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Enum listing all gain settings for the TSL2591 component.
|
||||||
|
*
|
||||||
|
* Enum constants are used by the component to allow auto gain, not directly to registers
|
||||||
|
* Higher values are better for low light situations, but can increase noise.
|
||||||
|
*/
|
||||||
|
enum TSL2591ComponentGain {
|
||||||
|
TSL2591_CGAIN_LOW, // 1x
|
||||||
|
TSL2591_CGAIN_MED, // 25x
|
||||||
|
TSL2591_CGAIN_HIGH, // 400x
|
||||||
|
TSL2591_CGAIN_MAX, // 9500x
|
||||||
|
TSL2591_CGAIN_AUTO
|
||||||
|
};
|
||||||
|
|
||||||
/** Enum listing all gain settings for the TSL2591.
|
/** Enum listing all gain settings for the TSL2591.
|
||||||
*
|
*
|
||||||
* Specific values of the enum constants are register values taken from the TSL2591 datasheet.
|
* Specific values of the enum constants are register values taken from the TSL2591 datasheet.
|
||||||
|
@ -200,6 +213,13 @@ class TSL2591Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
*/
|
*/
|
||||||
void disable();
|
void disable();
|
||||||
|
|
||||||
|
/** Updates the gain setting based on the most recent full spectrum reading
|
||||||
|
*
|
||||||
|
* This gets called on update and tries to keep the ADC readings in the middle of the range
|
||||||
|
*/
|
||||||
|
|
||||||
|
void automatic_gain_update(uint16_t full_spectrum);
|
||||||
|
|
||||||
// ========== INTERNAL METHODS ==========
|
// ========== INTERNAL METHODS ==========
|
||||||
// (In most use cases you won't need these. They're for ESPHome integration use.)
|
// (In most use cases you won't need these. They're for ESPHome integration use.)
|
||||||
/** Used by ESPHome framework. */
|
/** Used by ESPHome framework. */
|
||||||
|
@ -213,7 +233,7 @@ class TSL2591Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
/** Used by ESPHome framework. Does NOT actually set the value on the device. */
|
/** Used by ESPHome framework. Does NOT actually set the value on the device. */
|
||||||
void set_integration_time(TSL2591IntegrationTime integration_time);
|
void set_integration_time(TSL2591IntegrationTime integration_time);
|
||||||
/** Used by ESPHome framework. Does NOT actually set the value on the device. */
|
/** Used by ESPHome framework. Does NOT actually set the value on the device. */
|
||||||
void set_gain(TSL2591Gain gain);
|
void set_gain(TSL2591ComponentGain gain);
|
||||||
/** Used by ESPHome framework. */
|
/** Used by ESPHome framework. */
|
||||||
void setup() override;
|
void setup() override;
|
||||||
/** Used by ESPHome framework. */
|
/** Used by ESPHome framework. */
|
||||||
|
@ -230,6 +250,7 @@ class TSL2591Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
sensor::Sensor *visible_sensor_;
|
sensor::Sensor *visible_sensor_;
|
||||||
sensor::Sensor *calculated_lux_sensor_;
|
sensor::Sensor *calculated_lux_sensor_;
|
||||||
TSL2591IntegrationTime integration_time_;
|
TSL2591IntegrationTime integration_time_;
|
||||||
|
TSL2591ComponentGain component_gain_;
|
||||||
TSL2591Gain gain_;
|
TSL2591Gain gain_;
|
||||||
bool power_save_mode_enabled_;
|
bool power_save_mode_enabled_;
|
||||||
float device_factor_;
|
float device_factor_;
|
||||||
|
|
Loading…
Reference in a new issue