11 static const char *
const TAG =
"ltr501";
13 static const uint8_t MAX_TRIES = 5;
14 static const uint8_t MAX_SENSITIVITY_ADJUSTMENTS = 10;
21 bool operator==(
const GainTimePair &lhs,
const GainTimePair &rhs) {
22 return lhs.gain == rhs.gain && lhs.time == rhs.time;
25 bool operator!=(
const GainTimePair &lhs,
const GainTimePair &rhs) {
26 return lhs.gain != rhs.gain || lhs.time != rhs.time;
29 template<
typename T,
size_t size> T
get_next(
const T (&array)[size],
const T
val) {
32 while (idx == -1 && i < size) {
33 if (array[i] == val) {
39 if (idx == -1 || i + 1 >= size)
44 template<
typename T,
size_t size> T
get_prev(
const T (&array)[size],
const T
val) {
47 while (idx == -1 && i > 0) {
48 if (array[i] == val) {
54 if (idx == -1 || i == 0)
60 static const uint16_t ALS_INT_TIME[4] = {100, 50, 200, 400};
61 return ALS_INT_TIME[time & 0
b11];
65 static const uint16_t ALS_MEAS_RATE[8] = {50, 100, 200, 500, 1000, 2000, 2000, 2000};
66 return ALS_MEAS_RATE[rate & 0b111];
71 static float get_ps_gain_coeff(
PsGain501 gain) {
72 static const float PS_GAIN[4] = {1, 4, 8, 16};
73 return PS_GAIN[gain & 0
b11];
77 ESP_LOGCONFIG(TAG,
"Setting up LTR-501/301/558");
79 this->set_timeout(100, [
this]() { this->state_ = State::DELAYED_SETUP; });
83 auto get_device_type = [](
LtrType typ) {
97 ESP_LOGCONFIG(TAG,
" Device type: %s", get_device_type(this->ltr_type_));
98 ESP_LOGCONFIG(TAG,
" Automatic mode: %s", ONOFF(this->automatic_mode_enabled_));
99 ESP_LOGCONFIG(TAG,
" Gain: %.0fx", get_gain_coeff(this->gain_));
100 ESP_LOGCONFIG(TAG,
" Integration time: %d ms", get_itime_ms(this->integration_time_));
101 ESP_LOGCONFIG(TAG,
" Measurement repeat rate: %d ms", get_meas_time_ms(this->repeat_rate_));
102 ESP_LOGCONFIG(TAG,
" Glass attenuation factor: %f", this->glass_attenuation_factor_);
103 ESP_LOGCONFIG(TAG,
" Proximity gain: %.0fx", get_ps_gain_coeff(this->ps_gain_));
104 ESP_LOGCONFIG(TAG,
" Proximity cooldown time: %d s", this->ps_cooldown_time_s_);
105 ESP_LOGCONFIG(TAG,
" Proximity high threshold: %d", this->ps_threshold_high_);
106 ESP_LOGCONFIG(TAG,
" Proximity low threshold: %d", this->ps_threshold_low_);
108 LOG_UPDATE_INTERVAL(
this);
110 LOG_SENSOR(
" ",
"ALS calculated lux", this->ambient_light_sensor_);
111 LOG_SENSOR(
" ",
"CH1 Infrared counts", this->infrared_counts_sensor_);
112 LOG_SENSOR(
" ",
"CH0 Visible+IR counts", this->full_spectrum_counts_sensor_);
113 LOG_SENSOR(
" ",
"Actual gain", this->actual_gain_sensor_);
115 if (this->is_failed()) {
116 ESP_LOGE(TAG,
"Communication with I2C LTR-501/301/558 failed!");
121 if (!this->is_als_()) {
122 ESP_LOGW(TAG,
"Update. ALS data not available. Change configuration to ALS or ALS_PS.");
125 if (this->is_ready() && this->is_als_() && this->state_ ==
State::IDLE) {
126 ESP_LOGV(TAG,
"Update. Initiating new ALS data collection.");
128 this->state_ = this->automatic_mode_enabled_ ? State::COLLECTING_DATA_AUTO : State::WAITING_FOR_DATA;
130 this->als_readings_.ch0 = 0;
131 this->als_readings_.ch1 = 0;
132 this->als_readings_.gain = this->gain_;
133 this->als_readings_.integration_time = this->integration_time_;
134 this->als_readings_.lux = 0;
135 this->als_readings_.number_of_adjustments = 0;
138 ESP_LOGV(TAG,
"Update. Component not ready yet.");
144 static uint8_t tries{0};
146 switch (this->state_) {
147 case State::DELAYED_SETUP:
148 err = this->write(
nullptr, 0);
150 ESP_LOGW(TAG,
"i2c connection failed");
153 this->configure_reset_();
154 if (this->is_als_()) {
155 this->configure_als_();
156 this->configure_integration_time_(this->integration_time_);
158 if (this->is_ps_()) {
159 this->configure_ps_();
166 if (this->is_ps_()) {
167 this->check_and_trigger_ps_();
171 case State::WAITING_FOR_DATA:
174 ESP_LOGV(TAG,
"Reading sensor data assuming gain = %.0fx, time = %d ms",
175 get_gain_coeff(this->als_readings_.gain), get_itime_ms(this->als_readings_.integration_time));
176 this->read_sensor_data_(this->als_readings_);
177 this->apply_lux_calculation_(this->als_readings_);
178 this->state_ = State::DATA_COLLECTED;
179 }
else if (tries >= MAX_TRIES) {
180 ESP_LOGW(TAG,
"Can't get data after several tries. Aborting.");
182 this->status_set_warning();
190 case State::COLLECTING_DATA_AUTO:
191 case State::DATA_COLLECTED:
193 if (this->state_ == State::COLLECTING_DATA_AUTO || this->are_adjustments_required_(this->als_readings_)) {
194 this->state_ = State::ADJUSTMENT_IN_PROGRESS;
195 ESP_LOGD(TAG,
"Reconfiguring sensitivity: gain = %.0fx, time = %d ms", get_gain_coeff(this->als_readings_.gain),
196 get_itime_ms(this->als_readings_.integration_time));
197 this->configure_integration_time_(this->als_readings_.integration_time);
198 this->configure_gain_(this->als_readings_.gain);
200 this->set_timeout(2 * get_meas_time_ms(this->repeat_rate_),
201 [
this]() { this->state_ = State::WAITING_FOR_DATA; });
203 this->state_ = State::READY_TO_PUBLISH;
207 case State::ADJUSTMENT_IN_PROGRESS:
211 case State::READY_TO_PUBLISH:
212 this->publish_data_part_1_(this->als_readings_);
213 this->state_ = State::KEEP_PUBLISHING;
216 case State::KEEP_PUBLISHING:
217 this->publish_data_part_2_(this->als_readings_);
218 this->status_clear_warning();
228 static uint32_t last_high_trigger_time{0};
229 static uint32_t last_low_trigger_time{0};
230 uint16_t ps_data = this->read_ps_data_();
233 if (ps_data != this->ps_readings_) {
234 this->ps_readings_ = ps_data;
236 if (ps_data > this->ps_threshold_high_ && now - last_high_trigger_time >= this->ps_cooldown_time_s_ * 1000) {
237 last_high_trigger_time = now;
238 ESP_LOGD(TAG,
"Proximity high threshold triggered. Value = %d, Trigger level = %d", ps_data,
239 this->ps_threshold_high_);
240 this->on_ps_high_trigger_callback_.call();
241 }
else if (ps_data < this->ps_threshold_low_ && now - last_low_trigger_time >= this->ps_cooldown_time_s_ * 1000) {
242 last_low_trigger_time = now;
243 ESP_LOGD(TAG,
"Proximity low threshold triggered. Value = %d, Trigger level = %d", ps_data,
244 this->ps_threshold_low_);
245 this->on_ps_low_trigger_callback_.call();
252 if (manuf_id != 0x05) {
253 ESP_LOGW(TAG,
"Unknown manufacturer ID: 0x%02X", manuf_id);
268 if (part_id.part_number_id != 0x08) {
269 ESP_LOGW(TAG,
"Unknown part number ID: 0x%02X. LTR-501/301 shall have 0x08. It might not work properly.",
270 part_id.part_number_id);
271 this->status_set_warning();
278 ESP_LOGV(TAG,
"Resetting");
285 uint8_t tries = MAX_TRIES;
287 ESP_LOGV(TAG,
"Waiting chip to reset");
289 als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get();
290 }
while (als_ctrl.sw_reset && tries--);
292 if (als_ctrl.sw_reset) {
293 ESP_LOGW(TAG,
"Reset failed");
300 als_ctrl.als_mode_active =
true;
301 als_ctrl.gain = this->gain_;
303 ESP_LOGV(TAG,
"Setting active mode and gain reg 0x%02X", als_ctrl.raw);
307 uint8_t tries = MAX_TRIES;
309 ESP_LOGV(TAG,
"Waiting for ALS device to become active...");
311 als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get();
312 }
while (!als_ctrl.als_mode_active && tries--);
314 if (!als_ctrl.als_mode_active) {
315 ESP_LOGW(TAG,
"Failed to activate ALS device");
326 ps_ctrl.ps_mode_xxx =
true;
333 if (!als_status.ps_new_data) {
334 return this->ps_readings_;
348 als_ctrl.gain =
gain;
353 read_als_ctrl.
raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get();
354 if (read_als_ctrl.gain != gain) {
355 ESP_LOGW(TAG,
"Failed to set gain. We will try one more time.");
356 this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw;
364 meas.integration_time = time;
369 read_meas.
raw = this->reg((uint8_t) CommandRegisters::MEAS_RATE).get();
370 if (read_meas.integration_time != time) {
371 ESP_LOGW(TAG,
"Failed to set integration time. We will try one more time.");
372 this->reg((uint8_t) CommandRegisters::MEAS_RATE) = meas.raw;
380 if (!als_status.als_new_data)
382 ESP_LOGV(TAG,
"Data ready, reported gain is %.0fx", get_gain_coeff(als_status.gain));
383 if (data.
gain != als_status.gain) {
384 ESP_LOGW(TAG,
"Actual gain differs from requested (%.0f)", get_gain_coeff(data.
gain));
387 data.
gain = als_status.gain;
401 ESP_LOGD(TAG,
"Got sensor data: CH1 = %d, CH0 = %d", data.
ch1, data.
ch0);
405 if (!this->automatic_mode_enabled_)
410 ESP_LOGW(TAG,
"Too many sensitivity adjustments done. Something wrong with the sensor. Stopping.");
417 static const GainTimePair GAIN_TIME_PAIRS[] = {
432 if (data.
ch1 == 1 && data.
ch0 == 0) {
433 ESP_LOGV(TAG,
"Looks like sensor got saturated (?) CH1 = 1, CH0 = 0, Gain 150x");
437 }
else if (data.
ch1 == 65535 && data.
ch0 == 0) {
438 ESP_LOGV(TAG,
"Looks like sensor got saturated (?) CH1 = 65535, CH0 = 0, Gain 150x");
440 }
else if (data.
ch1 > 1000 && data.
ch0 == 0) {
441 ESP_LOGV(TAG,
"Looks like sensor got saturated (?) CH1 = %d, CH0 = 0, Gain 150x", data.
ch1);
446 static const uint16_t LOW_INTENSITY_THRESHOLD_1 = 100;
447 static const uint16_t LOW_INTENSITY_THRESHOLD_200 = 2000;
448 static const uint16_t HIGH_INTENSITY_THRESHOLD = 25000;
452 GainTimePair next_pair =
get_next(GAIN_TIME_PAIRS, current_pair);
453 if (next_pair != current_pair) {
454 data.
gain = next_pair.gain;
456 ESP_LOGV(TAG,
"Low illuminance. Increasing sensitivity.");
460 }
else if (data.
ch0 >= HIGH_INTENSITY_THRESHOLD || data.
ch1 >= HIGH_INTENSITY_THRESHOLD) {
461 GainTimePair prev_pair =
get_prev(GAIN_TIME_PAIRS, current_pair);
462 if (prev_pair != current_pair) {
463 data.
gain = prev_pair.gain;
465 ESP_LOGV(TAG,
"High illuminance. Decreasing sensitivity.");
469 ESP_LOGD(TAG,
"Illuminance is good enough.");
472 ESP_LOGD(TAG,
"Can't adjust sensitivity anymore.");
477 if ((data.
ch0 == 0xFFFF) || (data.
ch1 == 0xFFFF)) {
478 ESP_LOGW(TAG,
"Sensors got saturated");
483 if ((data.
ch0 == 0x0000) && (data.
ch1 == 0x0000)) {
484 ESP_LOGW(TAG,
"Sensors blacked out");
489 float ch0 = data.
ch0;
490 float ch1 = data.
ch1;
491 float ratio = ch1 / (ch0 + ch1);
492 float als_gain = get_gain_coeff(data.
gain);
494 float inv_pfactor = this->glass_attenuation_factor_;
501 lux = 1.7743 * ch0 + 1.1059 * ch1;
502 }
else if (ratio < 0.64) {
503 lux = 3.7725 * ch0 - 1.3363 * ch1;
504 }
else if (ratio < 0.85) {
505 lux = 1.6903 * ch0 - 0.1693 * ch1;
507 ESP_LOGW(TAG,
"Impossible ch1/(ch0 + ch1) ratio");
511 lux = inv_pfactor * lux / als_gain / als_time;
514 ESP_LOGD(TAG,
"Lux calculation: ratio %.3f, gain %.0fx, int time %.1f, inv_pfactor %.3f, lux %.3f", ratio, als_gain,
515 als_time, inv_pfactor, lux);
519 if (this->proximity_counts_sensor_ !=
nullptr) {
520 this->proximity_counts_sensor_->publish_state(this->ps_readings_);
522 if (this->ambient_light_sensor_ !=
nullptr) {
523 this->ambient_light_sensor_->publish_state(data.
lux);
525 if (this->infrared_counts_sensor_ !=
nullptr) {
526 this->infrared_counts_sensor_->publish_state(data.
ch1);
528 if (this->full_spectrum_counts_sensor_ !=
nullptr) {
529 this->full_spectrum_counts_sensor_->publish_state(data.
ch0);
534 if (this->actual_gain_sensor_ !=
nullptr) {
535 this->actual_gain_sensor_->publish_state(get_gain_coeff(data.
gain));
537 if (this->actual_integration_time_sensor_ !=
nullptr) {
538 this->actual_integration_time_sensor_->publish_state(get_itime_ms(data.
integration_time));
void apply_lux_calculation_(AlsReadings &data)
bool are_adjustments_required_(AlsReadings &data)
void publish_data_part_2_(AlsReadings &data)
bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs)
IntegrationTime501 integration_time
T get_next(const T(&array)[size], const T val)
PsMeasurementRate ps_measurement_rate
uint8_t number_of_adjustments
void read_sensor_data_(AlsReadings &data)
MeasurementRepeatRate measurement_repeat_rate
uint32_t IRAM_ATTR HOT millis()
void dump_config() override
bool operator==(const GainTimePair &lhs, const GainTimePair &rhs)
No error found during execution of method.
bool check_part_number_()
DataAvail is_als_data_ready_(AlsReadings &data)
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
void configure_integration_time_(IntegrationTime501 time)
void publish_data_part_1_(AlsReadings &data)
Implementation of SPI Controller mode.
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
void check_and_trigger_ps_()
T get_prev(const T(&array)[size], const T val)
void configure_gain_(AlsGain501 gain)
void IRAM_ATTR HOT delay(uint32_t ms)