ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
opt3001.cpp
Go to the documentation of this file.
1#include "opt3001.h"
2#include "esphome/core/log.h"
3
4namespace esphome::opt3001 {
5
6ESPHOME_LOG_TAG(TAG, "opt3001.sensor");
7
8static constexpr uint32_t RESULT_RETRY_TIMEOUT_ID = 0;
9static constexpr uint32_t READ_TIMEOUT_ID = 1;
10
11static const uint8_t OPT3001_REG_RESULT = 0x00;
12static const uint8_t OPT3001_REG_CONFIGURATION = 0x01;
13// See datasheet for full description of each bit.
14static const uint16_t OPT3001_CONFIGURATION_RANGE_FULL = 0b1100000000000000;
15static const uint16_t OPT3001_CONFIGURATION_CONVERSION_TIME_800 = 0b100000000000;
16static const uint16_t OPT3001_CONFIGURATION_CONVERSION_MODE_MASK = 0b11000000000;
17static const uint16_t OPT3001_CONFIGURATION_CONVERSION_MODE_SINGLE_SHOT = 0b01000000000;
18static const uint16_t OPT3001_CONFIGURATION_CONVERSION_MODE_SHUTDOWN = 0b00000000000;
19// tl;dr: Configure an automatic-ranged, 800ms single shot reading,
20// with INT processing disabled
21static const uint16_t OPT3001_CONFIGURATION_FULL_RANGE_ONE_SHOT = OPT3001_CONFIGURATION_RANGE_FULL |
22 OPT3001_CONFIGURATION_CONVERSION_TIME_800 |
23 OPT3001_CONFIGURATION_CONVERSION_MODE_SINGLE_SHOT;
24static const uint16_t OPT3001_CONVERSION_TIME_800 = 825; // give it 25 extra ms; it seems to not be ready quite often
25
26/*
27opt3001 properties:
28
29- e (exponent) = high 4 bits of result register
30- m (mantissa) = low 12 bits of result register
31- formula: (0.01 * 2^e) * m lx
32
33*/
34
35void OPT3001Sensor::read_result_(const std::function<void(float)> &f) {
36 // ensure the single shot flag is clear, indicating it's done
37 uint16_t raw_value;
38 if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
39 ESP_LOGW(TAG, "Reading configuration register failed");
40 f(NAN);
41 return;
42 }
43 raw_value = i2c::i2ctohs(raw_value);
44
45 if ((raw_value & OPT3001_CONFIGURATION_CONVERSION_MODE_MASK) != OPT3001_CONFIGURATION_CONVERSION_MODE_SHUTDOWN) {
46 // not ready; wait 10ms and try again
47 ESP_LOGW(TAG, "Data not ready; waiting 10ms");
48 this->set_timeout(RESULT_RETRY_TIMEOUT_ID, 10, [this, f]() { read_result_(f); });
49 return;
50 }
51
52 if (this->read_register(OPT3001_REG_RESULT, reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
53 ESP_LOGW(TAG, "Reading result register failed");
54 f(NAN);
55 return;
56 }
57 raw_value = i2c::i2ctohs(raw_value);
58
59 uint8_t exponent = raw_value >> 12;
60 uint16_t mantissa = raw_value & 0b111111111111;
61
62 double lx = 0.01 * pow(2.0, double(exponent)) * double(mantissa);
63 f(float(lx));
64}
65
66void OPT3001Sensor::read_lx_(const std::function<void(float)> &f) {
67 // turn on (after one-shot sensor automatically powers down)
68 uint16_t start_measurement = i2c::htoi2cs(OPT3001_CONFIGURATION_FULL_RANGE_ONE_SHOT);
69 if (this->write_register(OPT3001_REG_CONFIGURATION, reinterpret_cast<uint8_t *>(&start_measurement), 2) !=
71 ESP_LOGW(TAG, "Triggering one shot measurement failed");
72 f(NAN);
73 return;
74 }
75
76 this->set_timeout(READ_TIMEOUT_ID, OPT3001_CONVERSION_TIME_800, [this, f]() {
77 if (this->write(&OPT3001_REG_CONFIGURATION, 1) != i2c::ERROR_OK) {
78 ESP_LOGW(TAG, "Starting configuration register read failed");
79 f(NAN);
80 return;
81 }
82
83 this->read_result_(f);
84 });
85}
86
88 LOG_SENSOR("", "OPT3001", this);
89 LOG_I2C_DEVICE(this);
90
91 LOG_UPDATE_INTERVAL(this);
92}
93
95 // Set a flag and skip just in case the sensor isn't responding,
96 // and we just keep waiting for it in read_result_.
97 // This way we don't end up with potentially boundless "threads"
98 // using up memory and eventually crashing the device
99 if (this->updating_) {
100 return;
101 }
102 this->updating_ = true;
103
104 this->read_lx_([this](float val) {
105 this->updating_ = false;
106
107 if (!this->status_comm_result(!std::isnan(val))) {
108 this->publish_state(NAN);
109 return;
110 }
111 ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", LOG_STR_ARG(this->get_log_name()), val);
112 this->publish_state(val);
113 });
114}
115
116} // namespace esphome::opt3001
bool status_comm_result(bool ok)
Clears the warning when ok, else sets it with message (default "Communication failed"); returns ok.
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
const LogString * get_log_name() const
Get the name for a "%s" log argument (LOG_STR_ARG).
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 write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
Definition i2c.h:195
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:175
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 read_result_(const std::function< void(float)> &f)
Definition opt3001.cpp:35
void read_lx_(const std::function< void(float)> &f)
Definition opt3001.cpp:66
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
mopeka_std_values val[3]
uint16_t i2ctohs(uint16_t i2cshort)
Definition i2c.h:139
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
uint16_t htoi2cs(uint16_t hostshort)
Definition i2c.h:140
ESPHOME_LOG_TAG(TAG, "opt3001.sensor")
static void uint32_t