ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
veml6040.cpp
Go to the documentation of this file.
1#include "veml6040.h"
3#include "esphome/core/log.h"
4
5#include <cinttypes>
6#include <cmath>
7
9
10ESPHOME_LOG_TAG(TAG, "veml6040");
11
12static constexpr uint8_t CONFIG_REG = 0x00;
13static constexpr uint8_t R_DATA_REG = 0x08;
14static constexpr uint8_t G_DATA_REG = 0x09;
15static constexpr uint8_t B_DATA_REG = 0x0A;
16static constexpr uint8_t W_DATA_REG = 0x0B;
17
18static constexpr uint8_t CONFIG_REG_IT_BIT = 4;
19
20static constexpr uint16_t MAX_COUNTS = 0xFFFF;
21
22// Lux per green count at 40 ms integration time; halves for each longer integration time step
23static constexpr float LUX_PER_COUNT_40MS = 0.25168f;
24
25// Correlated colour temperature fit from the Vishay application note "Designing the VEML6040 into an
26// Application": CCT = A * ((R - B) / G + offset) ^ EXPONENT. The note gives offset = 0.5 in open air and says it
27// changes behind cover glass, so it is configurable.
28static constexpr float CCT_A = 4278.6f;
29static constexpr float CCT_EXPONENT = -1.2455f;
30
32 // Auto mode (AF = 0, TRIG = 0) with shutdown bit cleared: the device measures continuously.
33 // The high byte of the configuration register is reserved and must be written as zero.
34 const uint8_t config[] = {static_cast<uint8_t>(this->integration_time_ << CONFIG_REG_IT_BIT), 0x00};
35 if (this->write_register(CONFIG_REG, config, sizeof config) != i2c::ERROR_OK) {
36 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
37 return;
38 }
39 // The data registers hold zeros until the first integration period has finished; publish as soon as it has
40 this->set_timeout(this->integration_time_ms_(), [this]() {
41 this->data_ready_ = true;
42 this->update();
43 });
44}
45
47 ESP_LOGCONFIG(TAG,
48 "VEML6040:\n"
49 " Integration time: %" PRIu32 " ms\n"
50 " Glass attenuation factor: %.2f\n"
51 " Color temperature offset: %.3f",
53 LOG_I2C_DEVICE(this);
54 LOG_UPDATE_INTERVAL(this);
55 LOG_SENSOR(" ", "Red channel", this->red_sensor_);
56 LOG_SENSOR(" ", "Green channel", this->green_sensor_);
57 LOG_SENSOR(" ", "Blue channel", this->blue_sensor_);
58 LOG_SENSOR(" ", "White channel", this->white_sensor_);
59 LOG_SENSOR(" ", "Illuminance", this->illuminance_sensor_);
60 LOG_SENSOR(" ", "Color temperature", this->color_temperature_sensor_);
61}
62
63bool VEML6040Component::read_channel_(uint8_t reg, uint16_t &counts) {
64 uint8_t data[2];
65 if (this->read_register(reg, data, sizeof data) != i2c::ERROR_OK)
66 return false;
67 counts = encode_uint16(data[1], data[0]);
68 return true;
69}
70
72 if (!this->data_ready_)
73 return;
74
75 uint16_t red, green, blue, white;
76 if (!this->read_channel_(R_DATA_REG, red) || !this->read_channel_(G_DATA_REG, green) ||
77 !this->read_channel_(B_DATA_REG, blue) || !this->read_channel_(W_DATA_REG, white)) {
78 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
79 return;
80 }
82
83 ESP_LOGV(TAG, "Counts: red=%u green=%u blue=%u white=%u", red, green, blue, white);
84
85 if (this->red_sensor_ != nullptr)
86 this->red_sensor_->publish_state(red);
87 if (this->green_sensor_ != nullptr)
88 this->green_sensor_->publish_state(green);
89 if (this->blue_sensor_ != nullptr)
90 this->blue_sensor_->publish_state(blue);
91 if (this->white_sensor_ != nullptr)
92 this->white_sensor_->publish_state(white);
93
94 const bool saturated = red == MAX_COUNTS || green == MAX_COUNTS || blue == MAX_COUNTS;
95 if (saturated) {
96 ESP_LOGW(TAG, "Sensor saturated; use a shorter integration time");
97 }
98
99 if (this->illuminance_sensor_ != nullptr) {
100 float lux = NAN;
101 if (green != MAX_COUNTS) {
102 const float lux_per_count = LUX_PER_COUNT_40MS / static_cast<float>(1u << this->integration_time_);
103 lux = green * lux_per_count * this->glass_attenuation_factor_;
104 }
106 }
107
108 if (this->color_temperature_sensor_ != nullptr) {
109 float cct = NAN;
110 if (!saturated && green != 0) {
111 const float index =
112 (static_cast<float>(red) - static_cast<float>(blue)) / green + this->color_temperature_offset_;
113 if (index > 0.0f)
114 cct = CCT_A * powf(index, CCT_EXPONENT);
115 }
117 }
118}
119
120} // namespace esphome::veml6040
void mark_failed()
Mark this component as failed.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
void status_clear_warning()
Definition component.h:296
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
Definition i2c.cpp:37
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
Definition i2c.cpp:28
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
bool read_channel_(uint8_t reg, uint16_t &counts)
Definition veml6040.cpp:63
sensor::Sensor * illuminance_sensor_
Definition veml6040.h:47
uint32_t integration_time_ms_() const
Definition veml6040.h:41
const VEML6040IntegrationTime integration_time_
Definition veml6040.h:51
sensor::Sensor * color_temperature_sensor_
Definition veml6040.h:48
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
ESPHOME_LOG_TAG(TAG, "veml6040")
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:998