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:
swifty99 2022-05-12 06:53:33 +02:00 committed by GitHub
parent 01c4d3c225
commit bcb47c306c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 13 deletions

View file

@ -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),

View file

@ -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,8 +155,14 @@ 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) {
return; // 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;
}
} }
// 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);
} }
if (this->illuminance_sensor_ != nullptr) // do not publish values if auto gain finding ongoing, and oversaturated
this->illuminance_sensor_->publish_state(this->illuminance_); // 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)
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) {
this->integration_reg_ = integration_time; // if an integration time is 0x100, this is auto start with 154ms as this gives best starting point
this->integration_time_ = (256.f - integration_time) * 2.4f; 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;
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;

View file

@ -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};
}; };