8static const char *
const TAG =
"veml7700";
9static const size_t VEML_REG_SIZE = 2;
11static float reduce_to_zero(
float a,
float b) {
return (a > b) ? (a - b) : 0; }
13template<
typename T,
size_t size> T
get_next(
const T (&array)[size],
const T
val) {
16 while (idx == -1 && i < size) {
17 if (array[i] ==
val) {
23 if (idx == -1 || i + 1 >= size)
28template<
typename T,
size_t size> T
get_prev(
const T (&array)[size],
const T
val) {
31 while (idx == -1 && i > 0) {
32 if (array[i] ==
val) {
38 if (idx == -1 || i == 0)
70static float get_gain_coeff(
Gain gain) {
71 static const float GAIN_FLOAT[
GAINS_COUNT] = {1.0f, 2.0f, 0.125f, 0.25f};
72 return GAIN_FLOAT[
gain & 0b11];
75static const char *get_gain_str(
Gain gain) {
76 static const char *gain_str[
GAINS_COUNT] = {
"1x",
"2x",
"1/8x",
"1/4x"};
77 return gain_str[
gain & 0b11];
83 ESP_LOGW(TAG,
"Sensor configuration failed");
96 " Integration time: %d ms",
100 " Lux compensation: %s\n"
101 " Glass attenuation factor: %f",
103 LOG_UPDATE_INTERVAL(
this);
114 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
120 ESP_LOGV(TAG,
"Update: Initiating new data collection");
132 ESP_LOGV(TAG,
"Update: Component not ready yet");
147 switch (this->state_) {
168 [
this]() { this->state_ = State::READY_TO_APPLY_ADJUSTMENTS; });
186 [
this]() { this->state_ = State::COLLECTING_DATA; });
222 ESP_LOGV(TAG,
"Configure");
228 als_conf.ALS_GAIN = this->
gain_;
230 als_conf.ALS_SD =
true;
231 ESP_LOGV(TAG,
"Shutdown before config. ALS_CONF_0 to 0x%04X", als_conf.raw);
234 ESP_LOGW(TAG,
"Failed to shutdown, I2C error %d", err);
239 als_conf.ALS_SD =
false;
240 ESP_LOGV(TAG,
"Turning on. Setting ALS_CONF_0 to 0x%04X", als_conf.raw);
243 ESP_LOGW(TAG,
"Failed to turn on, I2C error %d", err);
250 ESP_LOGV(TAG,
"Setting PSM to 0x%04X", psm.raw);
253 ESP_LOGW(TAG,
"Failed to set PSM, I2C error %d", err);
261 ESP_LOGV(TAG,
"Reconfigure time and gain (%d ms, %s) %s", get_itime_ms(time), get_gain_str(
gain),
262 shutdown ?
"Shutting down" :
"Turning back on");
268 als_conf.ALS_SD = shutdown;
269 als_conf.ALS_INT_EN =
false;
271 als_conf.ALS_IT = time;
272 als_conf.ALS_GAIN =
gain;
275 ESP_LOGW(TAG,
"%s failed", shutdown ?
"Shutdown" :
"Turn on");
284 ESP_LOGW(TAG,
"Error reading ALS register, err = %d", als_err);
289 ESP_LOGW(TAG,
"Error reading WHITE register, err = %d", white_err);
295 ESP_LOGW(TAG,
"Error reading ALS_CONF_0 register, err = %d", white_err);
297 data.actual_time = conf.ALS_IT;
298 data.actual_gain = conf.ALS_GAIN;
300 ESP_LOGV(TAG,
"Data from sensors: ALS = %d, WHITE = %d, Gain = %s, Time = %d ms",
data.als_counts,
data.white_counts,
301 get_gain_str(
data.actual_gain), get_itime_ms(
data.actual_time));
302 return std::max(als_err, white_err);
315 static constexpr uint16_t LOW_INTENSITY_THRESHOLD = 100;
316 static constexpr uint16_t HIGH_INTENSITY_THRESHOLD = 10000;
323 if (
data.als_counts <= LOW_INTENSITY_THRESHOLD) {
325 if (next_gain !=
data.actual_gain) {
326 data.actual_gain = next_gain;
330 if (next_time !=
data.actual_time) {
331 data.actual_time = next_time;
334 }
else if (
data.als_counts >= HIGH_INTENSITY_THRESHOLD) {
336 if (prev_gain !=
data.actual_gain) {
337 data.actual_gain = prev_gain;
341 if (prev_time !=
data.actual_time) {
342 data.actual_time = prev_time;
353 static const float MAX_GAIN = 2.0f;
354 static const float MAX_ITIME_MS = 800.0f;
355 static const float MAX_LX_RESOLUTION = 0.0036f;
356 float lux_resolution = (MAX_ITIME_MS / (float) get_itime_ms(
data.actual_time)) *
357 (MAX_GAIN / get_gain_coeff(
data.actual_gain)) * MAX_LX_RESOLUTION;
358 ESP_LOGV(TAG,
"Lux resolution for (%d, %s) = %.4f ", get_itime_ms(
data.actual_time), get_gain_str(
data.actual_gain),
361 data.als_lux = lux_resolution * (float)
data.als_counts;
362 data.white_lux = lux_resolution * (float)
data.white_counts;
363 data.fake_infrared_lux = reduce_to_zero(
data.white_lux,
data.als_lux);
365 ESP_LOGV(TAG,
"%s mode - ALS = %.1f lx, WHITE = %.1f lx, FAKE_IR = %.1f lx",
367 data.fake_infrared_lux);
373 auto &local_data =
data;
381 auto compensate = [&local_data](
float &lux) {
382 auto calculate_high_lux_compensation = [](
float lux_veml) ->
float {
383 return (((6.0135e-13 * lux_veml - 9.3924e-9) * lux_veml + 8.1488e-5) * lux_veml + 1.0023) * lux_veml;
387 lux = calculate_high_lux_compensation(lux);
391 compensate(
data.als_lux);
392 compensate(
data.white_lux);
393 data.fake_infrared_lux = reduce_to_zero(
data.white_lux,
data.als_lux);
395 ESP_LOGV(TAG,
"Lux compensation - ALS = %.1f lx, WHITE = %.1f lx, FAKE_IR = %.1f lx",
data.als_lux,
data.white_lux,
396 data.fake_infrared_lux);
403 ESP_LOGV(TAG,
"Glass attenuation - ALS = %.1f lx, WHITE = %.1f lx, FAKE_IR = %.1f lx",
data.als_lux,
data.white_lux,
404 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