11static const char *
const TAG =
"ltr501";
13static const uint8_t MAX_TRIES = 5;
14static const uint8_t MAX_SENSITIVITY_ADJUSTMENTS = 10;
21bool operator==(
const GainTimePair &lhs,
const GainTimePair &rhs) {
22 return lhs.gain == rhs.gain && lhs.time == rhs.time;
25bool operator!=(
const GainTimePair &lhs,
const GainTimePair &rhs) {
26 return lhs.gain != rhs.gain || lhs.time != rhs.time;
29template<
typename T,
size_t size> T
get_next(
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 <
size) {
33 if (array[i] ==
val) {
39 if (idx == std::numeric_limits<size_t>::max() || i + 1 >=
size)
44template<
typename T,
size_t size> T
get_prev(
const T (&array)[
size],
const T
val) {
46 size_t idx = std::numeric_limits<size_t>::max();
47 while (idx == std::numeric_limits<size_t>::max() && i > 0) {
48 if (array[i] ==
val) {
54 if (idx == std::numeric_limits<size_t>::max() || i == 0)
60 static const uint16_t ALS_INT_TIME[4] = {100, 50, 200, 400};
61 return ALS_INT_TIME[time & 0b11];
65 static const uint16_t ALS_MEAS_RATE[8] = {50, 100, 200, 500, 1000, 2000, 2000, 2000};
66 return ALS_MEAS_RATE[rate & 0b111];
72 static const float PS_GAIN[4] = {1, 4, 8, 16};
73 return PS_GAIN[
gain & 0b11];
82 auto get_device_type = [](
LtrType typ) {
98 " Automatic mode: %s\n"
100 " Integration time: %d ms\n"
101 " Measurement repeat rate: %d ms\n"
102 " Glass attenuation factor: %f\n"
103 " Proximity gain: %.0fx\n"
104 " Proximity cooldown time: %d s\n"
105 " Proximity high threshold: %d\n"
106 " Proximity low threshold: %d",
112 LOG_UPDATE_INTERVAL(
this);
120 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
126 ESP_LOGW(TAG,
"Update. ALS data not available. Change configuration to ALS or ALS_PS.");
130 ESP_LOGV(TAG,
"Update. Initiating new ALS data collection.");
142 ESP_LOGV(TAG,
"Update. Component not ready yet.");
149 switch (this->state_) {
151 err = this->
write(
nullptr, 0);
153 ESP_LOGW(TAG,
"i2c connection failed");
177 ESP_LOGV(TAG,
"Reading sensor data assuming gain = %.0fx, time = %d ms",
182 }
else if (this->
tries_ >= MAX_TRIES) {
183 ESP_LOGW(TAG,
"Can't get data after several tries. Aborting.");
198 ESP_LOGD(TAG,
"Reconfiguring sensitivity: gain = %.0fx, time = %d ms", get_gain_coeff(this->
als_readings_.
gain),
240 ESP_LOGD(TAG,
"Proximity high threshold triggered. Value = %d, Trigger level = %d", ps_data,
246 ESP_LOGD(TAG,
"Proximity low threshold triggered. Value = %d, Trigger level = %d", ps_data,
255 if (manuf_id != 0x05) {
256 ESP_LOGW(TAG,
"Unknown manufacturer ID: 0x%02X", manuf_id);
271 if (part_id.part_number_id != 0x08) {
272 ESP_LOGW(TAG,
"Unknown part number ID: 0x%02X. LTR-501/301 shall have 0x08. It might not work properly.",
273 part_id.part_number_id);
281 ESP_LOGV(TAG,
"Resetting");
288 uint8_t tries = MAX_TRIES;
290 ESP_LOGV(TAG,
"Waiting chip to reset");
293 }
while (als_ctrl.sw_reset && tries--);
295 if (als_ctrl.sw_reset) {
296 ESP_LOGW(TAG,
"Reset failed");
303 als_ctrl.als_mode_active =
true;
304 als_ctrl.gain = this->
gain_;
306 ESP_LOGV(TAG,
"Setting active mode and gain reg 0x%02X", als_ctrl.raw);
310 uint8_t tries = MAX_TRIES;
312 ESP_LOGV(TAG,
"Waiting for ALS device to become active");
315 }
while (!als_ctrl.als_mode_active && tries--);
317 if (!als_ctrl.als_mode_active) {
318 ESP_LOGW(TAG,
"Failed to activate ALS device");
329 ps_ctrl.ps_mode_xxx =
true;
336 if (!als_status.ps_new_data) {
351 als_ctrl.gain =
gain;
357 if (read_als_ctrl.gain !=
gain) {
358 ESP_LOGW(TAG,
"Failed to set gain. We will try one more time.");
367 meas.integration_time = time;
373 if (read_meas.integration_time != time) {
374 ESP_LOGW(TAG,
"Failed to set integration time. We will try one more time.");
383 if (!als_status.als_new_data)
385 ESP_LOGV(TAG,
"Data ready, reported gain is %.0fx", get_gain_coeff(als_status.gain));
386 if (data.gain != als_status.gain) {
387 ESP_LOGW(TAG,
"Actual gain differs from requested (%.0f)", get_gain_coeff(data.gain));
390 data.gain = als_status.gain;
404 ESP_LOGD(TAG,
"Got sensor data: CH1 = %d, CH0 = %d", data.ch1, data.ch0);
413 ESP_LOGW(TAG,
"Too many sensitivity adjustments done. Something wrong with the sensor. Stopping.");
417 ESP_LOGV(TAG,
"Adjusting sensitivity, run #%d", data.number_of_adjustments);
420 static const GainTimePair GAIN_TIME_PAIRS[] = {
426 GainTimePair current_pair = {data.gain, data.integration_time};
435 if (data.ch1 == 1 && data.ch0 == 0) {
436 ESP_LOGV(TAG,
"Looks like sensor got saturated (?) CH1 = 1, CH0 = 0, Gain 150x");
440 }
else if (data.ch1 == 65535 && data.ch0 == 0) {
441 ESP_LOGV(TAG,
"Looks like sensor got saturated (?) CH1 = 65535, CH0 = 0, Gain 150x");
443 }
else if (data.ch1 > 1000 && data.ch0 == 0) {
444 ESP_LOGV(TAG,
"Looks like sensor got saturated (?) CH1 = %d, CH0 = 0, Gain 150x", data.ch1);
449 static const uint16_t LOW_INTENSITY_THRESHOLD_1 = 100;
450 static const uint16_t LOW_INTENSITY_THRESHOLD_200 = 2000;
451 static const uint16_t HIGH_INTENSITY_THRESHOLD = 25000;
453 if (data.ch0 <= (data.gain ==
AlsGain501::GAIN_1 ? LOW_INTENSITY_THRESHOLD_1 : LOW_INTENSITY_THRESHOLD_200) ||
455 GainTimePair next_pair =
get_next(GAIN_TIME_PAIRS, current_pair);
456 if (next_pair != current_pair) {
457 data.gain = next_pair.gain;
458 data.integration_time = next_pair.time;
459 ESP_LOGV(TAG,
"Low illuminance. Increasing sensitivity.");
463 }
else if (data.ch0 >= HIGH_INTENSITY_THRESHOLD || data.ch1 >= HIGH_INTENSITY_THRESHOLD) {
464 GainTimePair prev_pair =
get_prev(GAIN_TIME_PAIRS, current_pair);
465 if (prev_pair != current_pair) {
466 data.gain = prev_pair.gain;
467 data.integration_time = prev_pair.time;
468 ESP_LOGV(TAG,
"High illuminance. Decreasing sensitivity.");
472 ESP_LOGD(TAG,
"Illuminance is good enough.");
475 ESP_LOGD(TAG,
"Can't adjust sensitivity anymore.");
480 if ((data.ch0 == 0xFFFF) || (data.ch1 == 0xFFFF)) {
481 ESP_LOGW(TAG,
"Sensors got saturated");
486 if ((data.ch0 == 0x0000) && (data.ch1 == 0x0000)) {
487 ESP_LOGW(TAG,
"Sensors blacked out");
492 float ch0 = data.ch0;
493 float ch1 = data.ch1;
494 float ratio = ch1 / (ch0 + ch1);
495 float als_gain = get_gain_coeff(data.gain);
496 float als_time = ((float) get_itime_ms(data.integration_time)) / 100.0f;
504 lux = 1.7743 * ch0 + 1.1059 * ch1;
505 }
else if (ratio < 0.64) {
506 lux = 3.7725 * ch0 - 1.3363 * ch1;
507 }
else if (ratio < 0.85) {
508 lux = 1.6903 * ch0 - 0.1693 * ch1;
510 ESP_LOGW(TAG,
"Impossible ch1/(ch0 + ch1) ratio");
514 lux = inv_pfactor * lux / als_gain / als_time;
517 ESP_LOGD(TAG,
"Lux calculation: ratio %.3f, gain %.0fx, int time %.1f, inv_pfactor %.3f, lux %.3f", ratio, als_gain,
518 als_time, inv_pfactor, lux);
void mark_failed()
Mark this component as failed.
void status_set_warning()
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
void status_clear_warning()
ErrorCode write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
uint8_t get() const
returns the register value
LtrDataAvail is_als_data_ready_(AlsReadings &data)
void configure_gain_(AlsGain501 gain)
void publish_data_part_1_(AlsReadings &data)
void publish_data_part_2_(AlsReadings &data)
void check_and_trigger_ps_()
void configure_integration_time_(IntegrationTime501 time)
sensor::Sensor * actual_integration_time_sensor_
uint32_t last_ps_high_trigger_time_
CallbackManager< void()> on_ps_high_trigger_callback_
float glass_attenuation_factor_
IntegrationTime501 integration_time_
struct esphome::ltr501::LTRAlsPs501Component::AlsReadings als_readings_
void read_sensor_data_(AlsReadings &data)
MeasurementRepeatRate repeat_rate_
bool automatic_mode_enabled_
CallbackManager< void()> on_ps_low_trigger_callback_
void dump_config() override
uint32_t last_ps_low_trigger_time_
void apply_lux_calculation_(AlsReadings &data)
uint16_t ps_threshold_high_
sensor::Sensor * proximity_counts_sensor_
sensor::Sensor * infrared_counts_sensor_
bool are_adjustments_required_(AlsReadings &data)
uint16_t ps_threshold_low_
sensor::Sensor * full_spectrum_counts_sensor_
sensor::Sensor * actual_gain_sensor_
sensor::Sensor * ambient_light_sensor_
uint16_t ps_cooldown_time_s_
bool check_part_number_()
void publish_state(float state)
Publish a new state to the front-end.
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
@ ERROR_OK
No error found during execution of method.
bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs)
T get_next(const T(&array)[size], const T val)
bool operator==(const GainTimePair &lhs, const GainTimePair &rhs)
T get_prev(const T(&array)[size], const T val)
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
uint8_t number_of_adjustments
IntegrationTime501 integration_time
MeasurementRepeatRate measurement_repeat_rate
PsMeasurementRate ps_measurement_rate