9static const char *
const TAG =
"veml7700";
10static const size_t VEML_REG_SIZE = 2;
12static float reduce_to_zero(
float a,
float b) {
return (a > b) ? (a - b) : 0; }
14template<
typename T,
size_t size> T
get_next(
const T (&array)[size],
const T
val) {
16 size_t idx = std::numeric_limits<size_t>::max();
17 while (idx == std::numeric_limits<size_t>::max() && i < size) {
18 if (array[i] ==
val) {
24 if (idx == std::numeric_limits<size_t>::max() || i + 1 >= size)
29template<
typename T,
size_t size> T
get_prev(
const T (&array)[size],
const T
val) {
31 size_t idx = std::numeric_limits<size_t>::max();
32 while (idx == std::numeric_limits<size_t>::max() && i > 0) {
33 if (array[i] ==
val) {
39 if (idx == std::numeric_limits<size_t>::max() || i == 0)
71static float get_gain_coeff(
Gain gain) {
72 static const float GAIN_FLOAT[
GAINS_COUNT] = {1.0f, 2.0f, 0.125f, 0.25f};
73 return GAIN_FLOAT[
gain & 0b11];
76static const char *get_gain_str(
Gain gain) {
77 static const char *gain_str[
GAINS_COUNT] = {
"1x",
"2x",
"1/8x",
"1/4x"};
78 return gain_str[
gain & 0b11];
84 ESP_LOGW(TAG,
"Sensor configuration failed");
97 " Integration time: %d ms",
101 " Lux compensation: %s\n"
102 " Glass attenuation factor: %f",
104 LOG_UPDATE_INTERVAL(
this);
115 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
121 ESP_LOGV(TAG,
"Update: Initiating new data collection");
133 ESP_LOGV(TAG,
"Update: Component not ready yet");
148 switch (this->state_) {
169 [
this]() { this->state_ = State::READY_TO_APPLY_ADJUSTMENTS; });
187 [
this]() { this->state_ = State::COLLECTING_DATA; });
223 ESP_LOGV(TAG,
"Configure");
229 als_conf.ALS_GAIN = this->
gain_;
231 als_conf.ALS_SD =
true;
232 ESP_LOGV(TAG,
"Shutdown before config. ALS_CONF_0 to 0x%04X", als_conf.raw);
235 ESP_LOGW(TAG,
"Failed to shutdown, I2C error %d", err);
240 als_conf.ALS_SD =
false;
241 ESP_LOGV(TAG,
"Turning on. Setting ALS_CONF_0 to 0x%04X", als_conf.raw);
244 ESP_LOGW(TAG,
"Failed to turn on, I2C error %d", err);
251 ESP_LOGV(TAG,
"Setting PSM to 0x%04X", psm.raw);
254 ESP_LOGW(TAG,
"Failed to set PSM, I2C error %d", err);
262 ESP_LOGV(TAG,
"Reconfigure time and gain (%d ms, %s) %s", get_itime_ms(time), get_gain_str(
gain),
263 shutdown ?
"Shutting down" :
"Turning back on");
269 als_conf.ALS_SD = shutdown;
270 als_conf.ALS_INT_EN =
false;
272 als_conf.ALS_IT = time;
273 als_conf.ALS_GAIN =
gain;
276 ESP_LOGW(TAG,
"%s failed", shutdown ?
"Shutdown" :
"Turn on");
285 ESP_LOGW(TAG,
"Error reading ALS register, err = %d", als_err);
290 ESP_LOGW(TAG,
"Error reading WHITE register, err = %d", white_err);
296 ESP_LOGW(TAG,
"Error reading ALS_CONF_0 register, err = %d", white_err);
298 data.actual_time = conf.ALS_IT;
299 data.actual_gain = conf.ALS_GAIN;
301 ESP_LOGV(TAG,
"Data from sensors: ALS = %d, WHITE = %d, Gain = %s, Time = %d ms",
data.als_counts,
data.white_counts,
302 get_gain_str(
data.actual_gain), get_itime_ms(
data.actual_time));
303 return std::max(als_err, white_err);
316 static constexpr uint16_t LOW_INTENSITY_THRESHOLD = 100;
317 static constexpr uint16_t HIGH_INTENSITY_THRESHOLD = 10000;
324 if (
data.als_counts <= LOW_INTENSITY_THRESHOLD) {
326 if (next_gain !=
data.actual_gain) {
327 data.actual_gain = next_gain;
331 if (next_time !=
data.actual_time) {
332 data.actual_time = next_time;
335 }
else if (
data.als_counts >= HIGH_INTENSITY_THRESHOLD) {
337 if (prev_gain !=
data.actual_gain) {
338 data.actual_gain = prev_gain;
342 if (prev_time !=
data.actual_time) {
343 data.actual_time = prev_time;
354 static const float MAX_GAIN = 2.0f;
355 static const float MAX_ITIME_MS = 800.0f;
356 static const float MAX_LX_RESOLUTION = 0.0036f;
357 float lux_resolution = (MAX_ITIME_MS / (float) get_itime_ms(
data.actual_time)) *
358 (MAX_GAIN / get_gain_coeff(
data.actual_gain)) * MAX_LX_RESOLUTION;
359 ESP_LOGV(TAG,
"Lux resolution for (%d, %s) = %.4f ", get_itime_ms(
data.actual_time), get_gain_str(
data.actual_gain),
362 data.als_lux = lux_resolution * (float)
data.als_counts;
363 data.white_lux = lux_resolution * (float)
data.white_counts;
364 data.fake_infrared_lux = reduce_to_zero(
data.white_lux,
data.als_lux);
366 ESP_LOGV(TAG,
"%s mode - ALS = %.1f lx, WHITE = %.1f lx, FAKE_IR = %.1f lx",
368 data.fake_infrared_lux);
374 auto &local_data =
data;
382 auto compensate = [&local_data](
float &lux) {
383 auto calculate_high_lux_compensation = [](
float lux_veml) ->
float {
384 return (((6.0135e-13 * lux_veml - 9.3924e-9) * lux_veml + 8.1488e-5) * lux_veml + 1.0023) * lux_veml;
388 lux = calculate_high_lux_compensation(lux);
392 compensate(
data.als_lux);
393 compensate(
data.white_lux);
394 data.fake_infrared_lux = reduce_to_zero(
data.white_lux,
data.als_lux);
396 ESP_LOGV(TAG,
"Lux compensation - ALS = %.1f lx, WHITE = %.1f lx, FAKE_IR = %.1f lx",
data.als_lux,
data.white_lux,
397 data.fake_infrared_lux);
404 ESP_LOGV(TAG,
"Glass attenuation - ALS = %.1f lx, WHITE = %.1f lx, FAKE_IR = %.1f lx",
data.als_lux,
data.white_lux,
405 data.fake_infrared_lux);
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message=nullptr)
void status_clear_warning()
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) const
writes an array of bytes to a specific register in the I²C device
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len)
reads an array of bytes from a specific register in the I²C device
void publish_state(float state)
Publish a new state to the front-end.
void publish_data_part_1_(Readings &data)
bool lux_compensation_enabled_
float glass_attenuation_factor_
void dump_config() override
void publish_data_part_2_(Readings &data)
void publish_data_part_3_(Readings &data)
IntegrationTime integration_time_
sensor::Sensor * white_counts_sensor_
void apply_glass_attenuation_(Readings &data)
sensor::Sensor * ambient_light_counts_sensor_
sensor::Sensor * white_sensor_
bool are_adjustments_required_(Readings &data)
ErrorCode reconfigure_time_and_gain_(IntegrationTime time, Gain gain, bool shutdown)
sensor::Sensor * actual_integration_time_sensor_
@ READY_TO_PUBLISH_PART_1
@ INITIAL_SETUP_COMPLETED
@ READY_TO_PUBLISH_PART_3
@ READY_TO_PUBLISH_PART_2
@ READY_TO_APPLY_ADJUSTMENTS
void apply_lux_calculation_(Readings &data)
bool automatic_mode_enabled_
sensor::Sensor * actual_gain_sensor_
struct esphome::veml7700::VEML7700Component::Readings readings_
sensor::Sensor * fake_infrared_sensor_
void apply_lux_compensation_(Readings &data)
sensor::Sensor * ambient_light_sensor_
ErrorCode read_sensor_output_(Readings &data)
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
@ ERROR_OK
No error found during execution of method.
T get_prev(const T(&array)[size], const T val)
T get_next(const T(&array)[size], const T val)
const uint8_t INTEGRATION_TIMES_COUNT
const uint8_t GAINS_COUNT
Providing packet encoding functions for exchanging data with a remote host.
void IRAM_ATTR HOT delay(uint32_t ms)
IntegrationTime actual_time