mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 17:05:21 +01:00
Tcs34725 automatic sampling settings for improved dynamics and accuracy (#3258)
Co-authored-by: Daniel Cousens <413395+dcousens@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
01c4d3c225
commit
bcb47c306c
3 changed files with 103 additions and 13 deletions
|
@ -31,6 +31,7 @@ TCS34725Component = tcs34725_ns.class_(
|
||||||
|
|
||||||
TCS34725IntegrationTime = tcs34725_ns.enum("TCS34725IntegrationTime")
|
TCS34725IntegrationTime = tcs34725_ns.enum("TCS34725IntegrationTime")
|
||||||
TCS34725_INTEGRATION_TIMES = {
|
TCS34725_INTEGRATION_TIMES = {
|
||||||
|
"auto": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_AUTO,
|
||||||
"2.4ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_2_4MS,
|
"2.4ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_2_4MS,
|
||||||
"24ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_24MS,
|
"24ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_24MS,
|
||||||
"50ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_50MS,
|
"50ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_50MS,
|
||||||
|
@ -88,7 +89,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(CONF_CLEAR_CHANNEL): color_channel_schema,
|
cv.Optional(CONF_CLEAR_CHANNEL): color_channel_schema,
|
||||||
cv.Optional(CONF_ILLUMINANCE): illuminance_schema,
|
cv.Optional(CONF_ILLUMINANCE): illuminance_schema,
|
||||||
cv.Optional(CONF_COLOR_TEMPERATURE): color_temperature_schema,
|
cv.Optional(CONF_COLOR_TEMPERATURE): color_temperature_schema,
|
||||||
cv.Optional(CONF_INTEGRATION_TIME, default="2.4ms"): cv.enum(
|
cv.Optional(CONF_INTEGRATION_TIME, default="auto"): cv.enum(
|
||||||
TCS34725_INTEGRATION_TIMES, lower=True
|
TCS34725_INTEGRATION_TIMES, lower=True
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_GAIN, default="1X"): cv.enum(TCS34725_GAINS, upper=True),
|
cv.Optional(CONF_GAIN, default="1X"): cv.enum(TCS34725_GAINS, upper=True),
|
||||||
|
|
|
@ -136,8 +136,14 @@ void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, u
|
||||||
}
|
}
|
||||||
/* Check for saturation and mark the sample as invalid if true */
|
/* Check for saturation and mark the sample as invalid if true */
|
||||||
if (c >= sat) {
|
if (c >= sat) {
|
||||||
ESP_LOGW(TAG, "Saturation too high, discarding sample with saturation %.1f and clear %d", sat, c);
|
if (this->integration_time_auto_) {
|
||||||
return;
|
ESP_LOGI(TAG, "Saturation too high, sample discarded, autogain ongoing");
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(
|
||||||
|
TAG,
|
||||||
|
"Saturation too high, sample with saturation %.1f and clear %d treat values carefully or use grey filter",
|
||||||
|
sat, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AMS RGB sensors have no IR channel, so the IR content must be */
|
/* AMS RGB sensors have no IR channel, so the IR content must be */
|
||||||
|
@ -149,9 +155,15 @@ void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, u
|
||||||
g2 = g - ir;
|
g2 = g - ir;
|
||||||
b2 = b - ir;
|
b2 = b - ir;
|
||||||
|
|
||||||
|
// discarding super low values? not recemmonded, and avoided by using auto gain.
|
||||||
if (r2 == 0) {
|
if (r2 == 0) {
|
||||||
|
// legacy code
|
||||||
|
if (!this->integration_time_auto_) {
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"No light detected on red channel, switch to auto gain or adjust timing, values will be unreliable");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Lux Calculation (DN40 3.2)
|
// Lux Calculation (DN40 3.2)
|
||||||
|
|
||||||
|
@ -189,7 +201,7 @@ void TCS34725Component::update() {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "Raw values clear=%x red=%x green=%x blue=%x", raw_c, raw_r, raw_g, raw_b);
|
ESP_LOGV(TAG, "Raw values clear=%d red=%d green=%d blue=%d", raw_c, raw_r, raw_g, raw_b);
|
||||||
|
|
||||||
float channel_c;
|
float channel_c;
|
||||||
float channel_r;
|
float channel_r;
|
||||||
|
@ -220,20 +232,95 @@ void TCS34725Component::update() {
|
||||||
calculate_temperature_and_lux_(raw_r, raw_g, raw_b, raw_c);
|
calculate_temperature_and_lux_(raw_r, raw_g, raw_b, raw_c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do not publish values if auto gain finding ongoing, and oversaturated
|
||||||
|
// so: publish when:
|
||||||
|
// - not auto mode
|
||||||
|
// - clear not oversaturated
|
||||||
|
// - clear oversaturated but gain and timing cannot go lower
|
||||||
|
if (!this->integration_time_auto_ || raw_c < 65530 || (this->gain_reg_ == 0 && this->integration_time_ < 200)) {
|
||||||
if (this->illuminance_sensor_ != nullptr)
|
if (this->illuminance_sensor_ != nullptr)
|
||||||
this->illuminance_sensor_->publish_state(this->illuminance_);
|
this->illuminance_sensor_->publish_state(this->illuminance_);
|
||||||
|
|
||||||
if (this->color_temperature_sensor_ != nullptr)
|
if (this->color_temperature_sensor_ != nullptr)
|
||||||
this->color_temperature_sensor_->publish_state(this->color_temperature_);
|
this->color_temperature_sensor_->publish_state(this->color_temperature_);
|
||||||
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Got Red=%.1f%%,Green=%.1f%%,Blue=%.1f%%,Clear=%.1f%% Illuminance=%.1flx Color Temperature=%.1fK",
|
ESP_LOGD(TAG,
|
||||||
|
"Got Red=%.1f%%,Green=%.1f%%,Blue=%.1f%%,Clear=%.1f%% Illuminance=%.1flx Color "
|
||||||
|
"Temperature=%.1fK",
|
||||||
channel_r, channel_g, channel_b, channel_c, this->illuminance_, this->color_temperature_);
|
channel_r, channel_g, channel_b, channel_c, this->illuminance_, this->color_temperature_);
|
||||||
|
|
||||||
|
if (this->integration_time_auto_) {
|
||||||
|
// change integration time an gain to achieve maximum resolution an dynamic range
|
||||||
|
// calculate optimal integration time to achieve 70% satuaration
|
||||||
|
float integration_time_ideal;
|
||||||
|
integration_time_ideal = 60 / ((float) raw_c / 655.35) * this->integration_time_;
|
||||||
|
|
||||||
|
uint8_t gain_reg_val_new = this->gain_reg_;
|
||||||
|
// increase gain if less than 20% of white channel used and high integration time
|
||||||
|
// increase only if not already maximum
|
||||||
|
// do not use max gain, as ist will not get better
|
||||||
|
if (this->gain_reg_ < 3) {
|
||||||
|
if (((float) raw_c / 655.35 < 20.f) && (this->integration_time_ > 600.f)) {
|
||||||
|
gain_reg_val_new = this->gain_reg_ + 1;
|
||||||
|
// update integration time to new situation
|
||||||
|
integration_time_ideal = integration_time_ideal / 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrease gain, if very high clear values and integration times alreadey low
|
||||||
|
if (this->gain_reg_ > 0) {
|
||||||
|
if (70 < ((float) raw_c / 655.35) && (this->integration_time_ < 200)) {
|
||||||
|
gain_reg_val_new = this->gain_reg_ - 1;
|
||||||
|
// update integration time to new situation
|
||||||
|
integration_time_ideal = integration_time_ideal * 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// saturate integration times
|
||||||
|
float integration_time_next = integration_time_ideal;
|
||||||
|
if (integration_time_ideal > 2.4f * 256) {
|
||||||
|
integration_time_next = 2.4f * 256;
|
||||||
|
}
|
||||||
|
if (integration_time_ideal < 154) {
|
||||||
|
integration_time_next = 154;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate register value from timing
|
||||||
|
uint8_t regval_atime = (uint8_t)(256.f - integration_time_next / 2.4f);
|
||||||
|
ESP_LOGD(TAG, "Integration time: %.1fms, ideal: %.1fms regval_new %d Gain: %.f Clear channel raw: %d gain reg: %d",
|
||||||
|
this->integration_time_, integration_time_next, regval_atime, this->gain_, raw_c, this->gain_reg_);
|
||||||
|
|
||||||
|
if (this->integration_reg_ != regval_atime || gain_reg_val_new != this->gain_reg_) {
|
||||||
|
this->integration_reg_ = regval_atime;
|
||||||
|
this->gain_reg_ = gain_reg_val_new;
|
||||||
|
set_gain((TCS34725Gain) gain_reg_val_new);
|
||||||
|
if (this->write_config_register_(TCS34725_REGISTER_ATIME, this->integration_reg_) != i2c::ERROR_OK ||
|
||||||
|
this->write_config_register_(TCS34725_REGISTER_CONTROL, this->gain_reg_) != i2c::ERROR_OK) {
|
||||||
|
this->mark_failed();
|
||||||
|
ESP_LOGW(TAG, "TCS34725I update timing failed!");
|
||||||
|
} else {
|
||||||
|
this->integration_time_ = integration_time_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
}
|
}
|
||||||
void TCS34725Component::set_integration_time(TCS34725IntegrationTime integration_time) {
|
void TCS34725Component::set_integration_time(TCS34725IntegrationTime integration_time) {
|
||||||
|
// if an integration time is 0x100, this is auto start with 154ms as this gives best starting point
|
||||||
|
TCS34725IntegrationTime my_integration_time_regval;
|
||||||
|
|
||||||
|
if (integration_time == TCS34725_INTEGRATION_TIME_AUTO) {
|
||||||
|
this->integration_time_auto_ = true;
|
||||||
|
this->integration_reg_ = TCS34725_INTEGRATION_TIME_154MS;
|
||||||
|
my_integration_time_regval = TCS34725_INTEGRATION_TIME_154MS;
|
||||||
|
} else {
|
||||||
this->integration_reg_ = integration_time;
|
this->integration_reg_ = integration_time;
|
||||||
this->integration_time_ = (256.f - integration_time) * 2.4f;
|
my_integration_time_regval = integration_time;
|
||||||
|
this->integration_time_auto_ = false;
|
||||||
|
}
|
||||||
|
this->integration_time_ = (256.f - my_integration_time_regval) * 2.4f;
|
||||||
|
ESP_LOGI(TAG, "TCS34725I Integration time set to: %.1fms", this->integration_time_);
|
||||||
}
|
}
|
||||||
void TCS34725Component::set_gain(TCS34725Gain gain) {
|
void TCS34725Component::set_gain(TCS34725Gain gain) {
|
||||||
this->gain_reg_ = gain;
|
this->gain_reg_ = gain;
|
||||||
|
|
|
@ -26,6 +26,7 @@ enum TCS34725IntegrationTime {
|
||||||
TCS34725_INTEGRATION_TIME_540MS = 0x1F,
|
TCS34725_INTEGRATION_TIME_540MS = 0x1F,
|
||||||
TCS34725_INTEGRATION_TIME_600MS = 0x06,
|
TCS34725_INTEGRATION_TIME_600MS = 0x06,
|
||||||
TCS34725_INTEGRATION_TIME_614MS = 0x00,
|
TCS34725_INTEGRATION_TIME_614MS = 0x00,
|
||||||
|
TCS34725_INTEGRATION_TIME_AUTO = 0x100,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum TCS34725Gain {
|
enum TCS34725Gain {
|
||||||
|
@ -77,10 +78,11 @@ class TCS34725Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
float glass_attenuation_{1.0};
|
float glass_attenuation_{1.0};
|
||||||
float illuminance_;
|
float illuminance_;
|
||||||
float color_temperature_;
|
float color_temperature_;
|
||||||
|
bool integration_time_auto_{true};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void calculate_temperature_and_lux_(uint16_t r, uint16_t g, uint16_t b, uint16_t c);
|
void calculate_temperature_and_lux_(uint16_t r, uint16_t g, uint16_t b, uint16_t c);
|
||||||
uint8_t integration_reg_{TCS34725_INTEGRATION_TIME_2_4MS};
|
uint16_t integration_reg_;
|
||||||
uint8_t gain_reg_{TCS34725_GAIN_1X};
|
uint8_t gain_reg_{TCS34725_GAIN_1X};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue