mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 15:38:11 +01:00
tcs34725: implement CIE1931 colors
This commit is contained in:
parent
d349a67364
commit
c53c277a0d
10 changed files with 144 additions and 3 deletions
|
@ -17,6 +17,8 @@ from esphome.const import (
|
||||||
UNIT_KELVIN,
|
UNIT_KELVIN,
|
||||||
UNIT_LUX,
|
UNIT_LUX,
|
||||||
UNIT_IRRADIANCE,
|
UNIT_IRRADIANCE,
|
||||||
|
UNIT_EMPTY,
|
||||||
|
ICON_COLOR,
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
|
@ -29,6 +31,9 @@ CONF_RED_CHANNEL_IRRADIANCE = "red_channel_irradiance"
|
||||||
CONF_GREEN_CHANNEL_IRRADIANCE = "green_channel_irradiance"
|
CONF_GREEN_CHANNEL_IRRADIANCE = "green_channel_irradiance"
|
||||||
CONF_BLUE_CHANNEL_IRRADIANCE = "blue_channel_irradiance"
|
CONF_BLUE_CHANNEL_IRRADIANCE = "blue_channel_irradiance"
|
||||||
CONF_SENSOR_SATURATION = "sensor_saturation"
|
CONF_SENSOR_SATURATION = "sensor_saturation"
|
||||||
|
CONF_CIE1931_X = "cie1931_x"
|
||||||
|
CONF_CIE1931_Y = "cie1931_y"
|
||||||
|
CONF_CIE1931_Z = "cie1931_z"
|
||||||
|
|
||||||
tcs34725_ns = cg.esphome_ns.namespace("tcs34725")
|
tcs34725_ns = cg.esphome_ns.namespace("tcs34725")
|
||||||
TCS34725Component = tcs34725_ns.class_(
|
TCS34725Component = tcs34725_ns.class_(
|
||||||
|
@ -90,6 +95,12 @@ illuminance_schema = sensor.sensor_schema(
|
||||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
)
|
)
|
||||||
|
cie1931_schema = sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_EMPTY,
|
||||||
|
icon=ICON_COLOR,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
|
@ -113,6 +124,9 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(CONF_SENSOR_SATURATION): sensor_saturation_schema,
|
cv.Optional(CONF_SENSOR_SATURATION): sensor_saturation_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_CIE1931_X): cie1931_schema,
|
||||||
|
cv.Optional(CONF_CIE1931_Y): cie1931_schema,
|
||||||
|
cv.Optional(CONF_CIE1931_Z): cie1931_schema,
|
||||||
cv.Optional(CONF_INTEGRATION_TIME, default="auto"): cv.enum(
|
cv.Optional(CONF_INTEGRATION_TIME, default="auto"): cv.enum(
|
||||||
TCS34725_INTEGRATION_TIMES, lower=True
|
TCS34725_INTEGRATION_TIMES, lower=True
|
||||||
),
|
),
|
||||||
|
@ -136,6 +150,15 @@ async def to_code(config):
|
||||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||||
cg.add(var.set_glass_attenuation_factor(config[CONF_GLASS_ATTENUATION_FACTOR]))
|
cg.add(var.set_glass_attenuation_factor(config[CONF_GLASS_ATTENUATION_FACTOR]))
|
||||||
|
|
||||||
|
if CONF_CIE1931_X in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_CIE1931_X])
|
||||||
|
cg.add(var.set_cie1931_x_sensor(sens))
|
||||||
|
if CONF_CIE1931_Y in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_CIE1931_Y])
|
||||||
|
cg.add(var.set_cie1931_y_sensor(sens))
|
||||||
|
if CONF_CIE1931_Z in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_CIE1931_Z])
|
||||||
|
cg.add(var.set_cie1931_z_sensor(sens))
|
||||||
if CONF_RED_CHANNEL_IRRADIANCE in config:
|
if CONF_RED_CHANNEL_IRRADIANCE in config:
|
||||||
sens = await sensor.new_sensor(config[CONF_RED_CHANNEL_IRRADIANCE])
|
sens = await sensor.new_sensor(config[CONF_RED_CHANNEL_IRRADIANCE])
|
||||||
cg.add(var.set_red_irradiance_sensor(sens))
|
cg.add(var.set_red_irradiance_sensor(sens))
|
||||||
|
|
|
@ -58,6 +58,9 @@ void TCS34725Component::dump_config() {
|
||||||
LOG_SENSOR(" ", "Red Channel Irradiance", this->red_irradiance_sensor_);
|
LOG_SENSOR(" ", "Red Channel Irradiance", this->red_irradiance_sensor_);
|
||||||
LOG_SENSOR(" ", "Green Channel Irradiance", this->green_irradiance_sensor_);
|
LOG_SENSOR(" ", "Green Channel Irradiance", this->green_irradiance_sensor_);
|
||||||
LOG_SENSOR(" ", "Blue Channel Irradiance", this->blue_irradiance_sensor_);
|
LOG_SENSOR(" ", "Blue Channel Irradiance", this->blue_irradiance_sensor_);
|
||||||
|
LOG_SENSOR(" ", "CIE1931 X", this->cie1931_x_sensor_);
|
||||||
|
LOG_SENSOR(" ", "CIE1931 Y", this->cie1931_y_sensor_);
|
||||||
|
LOG_SENSOR(" ", "CIE1931 Z", this->cie1931_z_sensor_);
|
||||||
LOG_SENSOR(" ", "Illuminance", this->illuminance_sensor_);
|
LOG_SENSOR(" ", "Illuminance", this->illuminance_sensor_);
|
||||||
LOG_SENSOR(" ", "Color Temperature", this->color_temperature_sensor_);
|
LOG_SENSOR(" ", "Color Temperature", this->color_temperature_sensor_);
|
||||||
}
|
}
|
||||||
|
@ -270,6 +273,64 @@ void TCS34725Component::calculate_irradiance_(uint16_t r, uint16_t g, uint16_t b
|
||||||
this->irradiance_g_, this->irradiance_b_);
|
this->irradiance_g_, this->irradiance_b_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TCS34725Component::calculate_cie1931_(uint16_t r, uint16_t g, uint16_t b, float current_saturation,
|
||||||
|
uint16_t min_raw_value) {
|
||||||
|
this->cie1931_x_ = NAN;
|
||||||
|
this->cie1931_y_ = NAN;
|
||||||
|
this->cie1931_z_ = NAN;
|
||||||
|
|
||||||
|
uint16_t min_raw_limit = get_min_raw_limit_();
|
||||||
|
float sat_limit = get_saturation_limit_();
|
||||||
|
|
||||||
|
if (min_raw_value < min_raw_limit) {
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"Sensor Saturation too low, sample with saturation %d (raw value) below limit (%d). CIE1931 colors cannot "
|
||||||
|
"be reliably calculated.",
|
||||||
|
min_raw_value, min_raw_limit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_saturation >= sat_limit) {
|
||||||
|
if (this->integration_time_auto_) {
|
||||||
|
ESP_LOGI(TAG, "Saturation too high, skip CIE1931 calculation, autogain ongoing");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG,
|
||||||
|
"Sensor Saturation too high, sample with saturation %.1f above limit (%.1f). CIE1931 colors cannot be "
|
||||||
|
"reliably calculated, reduce integration/gain or use a grey filter.",
|
||||||
|
current_saturation, sat_limit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CIE1931 transformation matrix
|
||||||
|
static const float C11 = 1.89883277f;
|
||||||
|
static const float C12 = -0.58883081f;
|
||||||
|
static const float C13 = 0.29150381f;
|
||||||
|
static const float C21 = -0.24840751f;
|
||||||
|
static const float C22 = 1.14289469f;
|
||||||
|
static const float C23 = 0.07957766f;
|
||||||
|
static const float C31 = -0.3095245f;
|
||||||
|
static const float C32 = 0.64687886f;
|
||||||
|
static const float C33 = 0.98121083f;
|
||||||
|
|
||||||
|
// Calc scaling based on integration time
|
||||||
|
float integration_time_scaling = this->integration_time_ / 24.f;
|
||||||
|
float gain_scaling = this->gain_ / 16.f;
|
||||||
|
|
||||||
|
// Adjust the raw RGB values based on the integration time scaling factor and gain
|
||||||
|
float scaled_r = (float) r / (integration_time_scaling * gain_scaling);
|
||||||
|
float scaled_g = (float) g / (integration_time_scaling * gain_scaling);
|
||||||
|
float scaled_b = (float) b / (integration_time_scaling * gain_scaling);
|
||||||
|
|
||||||
|
// Calculate CIE1931 values
|
||||||
|
this->cie1931_x_ = std::max(C11 * scaled_r + C12 * scaled_g + C13 * scaled_b, 0.0f);
|
||||||
|
this->cie1931_y_ = std::max(C21 * scaled_r + C22 * scaled_g + C23 * scaled_b, 0.0f);
|
||||||
|
this->cie1931_z_ = std::max(C31 * scaled_r + C32 * scaled_g + C33 * scaled_b, 0.0f);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Calculated CIE1931 - X: %.2f, Y: %.2f, Z: %.2f", this->cie1931_x_, this->cie1931_y_, this->cie1931_z_);
|
||||||
|
}
|
||||||
|
|
||||||
void TCS34725Component::update() {
|
void TCS34725Component::update() {
|
||||||
uint8_t data[8]; // Buffer to hold the 8 bytes (2 bytes for each of the 4 channels)
|
uint8_t data[8]; // Buffer to hold the 8 bytes (2 bytes for each of the 4 channels)
|
||||||
|
|
||||||
|
@ -307,6 +368,10 @@ void TCS34725Component::update() {
|
||||||
calculate_irradiance_(raw_r, raw_g, raw_b, current_saturation, min_raw_value);
|
calculate_irradiance_(raw_r, raw_g, raw_b, current_saturation, min_raw_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->cie1931_x_sensor_ || this->cie1931_y_sensor_ || this->cie1931_z_sensor_) {
|
||||||
|
calculate_cie1931_(raw_r, raw_g, raw_b, current_saturation, min_raw_value);
|
||||||
|
}
|
||||||
|
|
||||||
if (this->illuminance_sensor_ || this->color_temperature_sensor_) {
|
if (this->illuminance_sensor_ || this->color_temperature_sensor_) {
|
||||||
calculate_temperature_and_lux_(raw_r, raw_g, raw_b, current_saturation, min_raw_value);
|
calculate_temperature_and_lux_(raw_r, raw_g, raw_b, current_saturation, min_raw_value);
|
||||||
}
|
}
|
||||||
|
@ -318,6 +383,12 @@ void TCS34725Component::update() {
|
||||||
// - sensor oversaturated but gain and timing cannot go lower
|
// - sensor oversaturated but gain and timing cannot go lower
|
||||||
if (!this->integration_time_auto_ || current_saturation < 99.99f ||
|
if (!this->integration_time_auto_ || current_saturation < 99.99f ||
|
||||||
(this->gain_reg_ == 0 && this->integration_time_ < 200)) {
|
(this->gain_reg_ == 0 && this->integration_time_ < 200)) {
|
||||||
|
if (this->cie1931_x_sensor_ != nullptr)
|
||||||
|
this->cie1931_x_sensor_->publish_state(this->cie1931_x_);
|
||||||
|
if (this->cie1931_y_sensor_ != nullptr)
|
||||||
|
this->cie1931_y_sensor_->publish_state(this->cie1931_y_);
|
||||||
|
if (this->cie1931_z_sensor_ != nullptr)
|
||||||
|
this->cie1931_z_sensor_->publish_state(this->cie1931_z_);
|
||||||
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)
|
||||||
|
@ -334,9 +405,9 @@ void TCS34725Component::update() {
|
||||||
|
|
||||||
ESP_LOGD(TAG,
|
ESP_LOGD(TAG,
|
||||||
"Calculated: Red Irad=%.2f µW/cm², Green Irad=%.2f µW/cm², Blue Irad=%.2f µW/cm², Sensor Sat=%.2f%%, "
|
"Calculated: Red Irad=%.2f µW/cm², Green Irad=%.2f µW/cm², Blue Irad=%.2f µW/cm², Sensor Sat=%.2f%%, "
|
||||||
"Illum=%.1f lx, Color Temp=%.1f K",
|
"CIE1931 (X: %.2f, Y: %.2f, Z: %.2f), Illum=%.1f lx, Color Temp=%.1f K",
|
||||||
this->irradiance_r_, this->irradiance_g_, this->irradiance_b_, current_saturation, this->illuminance_,
|
this->irradiance_r_, this->irradiance_g_, this->irradiance_b_, current_saturation, this->cie1931_x_,
|
||||||
this->color_temperature_);
|
this->cie1931_y_, this->cie1931_z_, this->illuminance_, this->color_temperature_);
|
||||||
|
|
||||||
if (this->integration_time_auto_) {
|
if (this->integration_time_auto_) {
|
||||||
// change integration time an gain to achieve maximum resolution an dynamic range
|
// change integration time an gain to achieve maximum resolution an dynamic range
|
||||||
|
|
|
@ -56,6 +56,9 @@ class TCS34725Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
void set_color_temperature_sensor(sensor::Sensor *color_temperature_sensor) {
|
void set_color_temperature_sensor(sensor::Sensor *color_temperature_sensor) {
|
||||||
color_temperature_sensor_ = color_temperature_sensor;
|
color_temperature_sensor_ = color_temperature_sensor;
|
||||||
}
|
}
|
||||||
|
void set_cie1931_x_sensor(sensor::Sensor *cie1931_x_sensor) { cie1931_x_sensor_ = cie1931_x_sensor; }
|
||||||
|
void set_cie1931_y_sensor(sensor::Sensor *cie1931_y_sensor) { cie1931_y_sensor_ = cie1931_y_sensor; }
|
||||||
|
void set_cie1931_z_sensor(sensor::Sensor *cie1931_z_sensor) { cie1931_z_sensor_ = cie1931_z_sensor; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
@ -79,6 +82,9 @@ class TCS34725Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
sensor::Sensor *blue_irradiance_sensor_{nullptr};
|
sensor::Sensor *blue_irradiance_sensor_{nullptr};
|
||||||
sensor::Sensor *illuminance_sensor_{nullptr};
|
sensor::Sensor *illuminance_sensor_{nullptr};
|
||||||
sensor::Sensor *color_temperature_sensor_{nullptr};
|
sensor::Sensor *color_temperature_sensor_{nullptr};
|
||||||
|
sensor::Sensor *cie1931_x_sensor_{nullptr};
|
||||||
|
sensor::Sensor *cie1931_y_sensor_{nullptr};
|
||||||
|
sensor::Sensor *cie1931_z_sensor_{nullptr};
|
||||||
float integration_time_{2.4};
|
float integration_time_{2.4};
|
||||||
float gain_{1.0};
|
float gain_{1.0};
|
||||||
float glass_attenuation_{1.0};
|
float glass_attenuation_{1.0};
|
||||||
|
@ -87,11 +93,15 @@ class TCS34725Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
float irradiance_r_{NAN};
|
float irradiance_r_{NAN};
|
||||||
float irradiance_g_{NAN};
|
float irradiance_g_{NAN};
|
||||||
float irradiance_b_{NAN};
|
float irradiance_b_{NAN};
|
||||||
|
float cie1931_x_{NAN};
|
||||||
|
float cie1931_y_{NAN};
|
||||||
|
float cie1931_z_{NAN};
|
||||||
bool integration_time_auto_{true};
|
bool integration_time_auto_{true};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void calculate_temperature_and_lux_(uint16_t r, uint16_t g, uint16_t b, float current_saturation,
|
void calculate_temperature_and_lux_(uint16_t r, uint16_t g, uint16_t b, float current_saturation,
|
||||||
uint16_t min_raw_value);
|
uint16_t min_raw_value);
|
||||||
|
void calculate_cie1931_(uint16_t r, uint16_t g, uint16_t b, float current_saturation, uint16_t min_raw_value);
|
||||||
void calculate_irradiance_(uint16_t r, uint16_t g, uint16_t b, float current_saturation, uint16_t min_raw_value);
|
void calculate_irradiance_(uint16_t r, uint16_t g, uint16_t b, float current_saturation, uint16_t min_raw_value);
|
||||||
float get_saturation_limit_() const;
|
float get_saturation_limit_() const;
|
||||||
uint16_t get_min_raw_limit_() const;
|
uint16_t get_min_raw_limit_() const;
|
||||||
|
|
|
@ -978,6 +978,7 @@ ICON_CELLPHONE_ARROW_DOWN = "mdi:cellphone-arrow-down"
|
||||||
ICON_CHECK_CIRCLE_OUTLINE = "mdi:check-circle-outline"
|
ICON_CHECK_CIRCLE_OUTLINE = "mdi:check-circle-outline"
|
||||||
ICON_CHEMICAL_WEAPON = "mdi:chemical-weapon"
|
ICON_CHEMICAL_WEAPON = "mdi:chemical-weapon"
|
||||||
ICON_CHIP = "mdi:chip"
|
ICON_CHIP = "mdi:chip"
|
||||||
|
ICON_COLOR = "mdi:color"
|
||||||
ICON_COUNTER = "mdi:counter"
|
ICON_COUNTER = "mdi:counter"
|
||||||
ICON_CURRENT_AC = "mdi:current-ac"
|
ICON_CURRENT_AC = "mdi:current-ac"
|
||||||
ICON_DATABASE = "mdi:database"
|
ICON_DATABASE = "mdi:database"
|
||||||
|
|
|
@ -5,6 +5,12 @@ i2c:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: tcs34725
|
- platform: tcs34725
|
||||||
|
cie1931_x:
|
||||||
|
name: CIE1931 X
|
||||||
|
cie1931_y:
|
||||||
|
name: CIE1931 Y
|
||||||
|
cie1931_z:
|
||||||
|
name: CIE1931 Z
|
||||||
red_channel_irradiance:
|
red_channel_irradiance:
|
||||||
name: Red Channel Irradiance
|
name: Red Channel Irradiance
|
||||||
green_channel_irradiance:
|
green_channel_irradiance:
|
||||||
|
|
|
@ -5,6 +5,12 @@ i2c:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: tcs34725
|
- platform: tcs34725
|
||||||
|
cie1931_x:
|
||||||
|
name: CIE1931 X
|
||||||
|
cie1931_y:
|
||||||
|
name: CIE1931 Y
|
||||||
|
cie1931_z:
|
||||||
|
name: CIE1931 Z
|
||||||
red_channel_irradiance:
|
red_channel_irradiance:
|
||||||
name: Red Channel Irradiance
|
name: Red Channel Irradiance
|
||||||
green_channel_irradiance:
|
green_channel_irradiance:
|
||||||
|
|
|
@ -5,6 +5,12 @@ i2c:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: tcs34725
|
- platform: tcs34725
|
||||||
|
cie1931_x:
|
||||||
|
name: CIE1931 X
|
||||||
|
cie1931_y:
|
||||||
|
name: CIE1931 Y
|
||||||
|
cie1931_z:
|
||||||
|
name: CIE1931 Z
|
||||||
red_channel_irradiance:
|
red_channel_irradiance:
|
||||||
name: Red Channel Irradiance
|
name: Red Channel Irradiance
|
||||||
green_channel_irradiance:
|
green_channel_irradiance:
|
||||||
|
|
|
@ -5,6 +5,12 @@ i2c:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: tcs34725
|
- platform: tcs34725
|
||||||
|
cie1931_x:
|
||||||
|
name: CIE1931 X
|
||||||
|
cie1931_y:
|
||||||
|
name: CIE1931 Y
|
||||||
|
cie1931_z:
|
||||||
|
name: CIE1931 Z
|
||||||
red_channel_irradiance:
|
red_channel_irradiance:
|
||||||
name: Red Channel Irradiance
|
name: Red Channel Irradiance
|
||||||
green_channel_irradiance:
|
green_channel_irradiance:
|
||||||
|
|
|
@ -5,6 +5,12 @@ i2c:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: tcs34725
|
- platform: tcs34725
|
||||||
|
cie1931_x:
|
||||||
|
name: CIE1931 X
|
||||||
|
cie1931_y:
|
||||||
|
name: CIE1931 Y
|
||||||
|
cie1931_z:
|
||||||
|
name: CIE1931 Z
|
||||||
red_channel_irradiance:
|
red_channel_irradiance:
|
||||||
name: Red Channel Irradiance
|
name: Red Channel Irradiance
|
||||||
green_channel_irradiance:
|
green_channel_irradiance:
|
||||||
|
|
|
@ -5,6 +5,12 @@ i2c:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: tcs34725
|
- platform: tcs34725
|
||||||
|
cie1931_x:
|
||||||
|
name: CIE1931 X
|
||||||
|
cie1931_y:
|
||||||
|
name: CIE1931 Y
|
||||||
|
cie1931_z:
|
||||||
|
name: CIE1931 Z
|
||||||
red_channel_irradiance:
|
red_channel_irradiance:
|
||||||
name: Red Channel Irradiance
|
name: Red Channel Irradiance
|
||||||
green_channel_irradiance:
|
green_channel_irradiance:
|
||||||
|
|
Loading…
Reference in a new issue