ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
apds9930.cpp
Go to the documentation of this file.
1#include "apds9930.h"
2#include "esphome/core/hal.h"
3#include "esphome/core/log.h"
4
5#include <algorithm>
6#include <cmath>
7
9
10ESPHOME_LOG_TAG(TAG, "apds9930");
11
12// Register addresses
13static constexpr uint8_t APDS9930_ENABLE = 0x00;
14static constexpr uint8_t APDS9930_ATIME = 0x01;
15static constexpr uint8_t APDS9930_PTIME = 0x02;
16static constexpr uint8_t APDS9930_PPULSE = 0x0E;
17static constexpr uint8_t APDS9930_CONTROL = 0x0F;
18static constexpr uint8_t APDS9930_ID = 0x12;
19static constexpr uint8_t APDS9930_STATUS = 0x13;
20
21// Command byte: repeated byte and auto-increment protocols
22static constexpr uint8_t APDS9930_CMD = 0x80;
23static constexpr uint8_t APDS9930_CMD_AUTO_INCREMENT = 0xA0;
24
25static constexpr uint8_t APDS9930_CHIP_ID = 0x39;
26
27// ENABLE register bits
28static constexpr uint8_t APDS9930_PON = 0x01;
29static constexpr uint8_t APDS9930_AEN = 0x02;
30static constexpr uint8_t APDS9930_PEN = 0x04;
31
32// STATUS register bits
33static constexpr uint8_t APDS9930_AVALID = 0x01;
34static constexpr uint8_t APDS9930_PVALID = 0x02;
35
36// CONTROL register fields
37static constexpr uint8_t CONTROL_PDRIVE_SHIFT = 6;
38static constexpr uint8_t CONTROL_PDIODE_SHIFT = 4;
39static constexpr uint8_t CONTROL_PGAIN_SHIFT = 2;
40static constexpr uint8_t CONTROL_AGAIN_SHIFT = 0;
41static constexpr uint8_t CONTROL_PDIODE_CH1 = 0b10; // The only supported proximity diode
42
43static constexpr uint8_t APDS9930_ATIME_VALUE = 0xED; // 19 cycles, about 52 ms
44static constexpr uint8_t APDS9930_PTIME_VALUE = 0xFF; // 2.73 ms
45static constexpr uint8_t APDS9930_PPULSE_VALUE = 0x08; // 8 pulses
46
47// Lux calculation coefficients
48static constexpr float APDS9930_DF = 52.0f;
49static constexpr float APDS9930_GA = 0.49f;
50static constexpr float APDS9930_ALS_B = 1.862f;
51static constexpr float APDS9930_ALS_C = 0.746f;
52static constexpr float APDS9930_ALS_D = 1.291f;
53static constexpr float APDS9930_ALSIT_MS = 2.73f * (256 - APDS9930_ATIME_VALUE);
54
55static constexpr uint8_t AMBIENT_GAINS[4] PROGMEM = {1, 8, 16, 120};
56static constexpr uint8_t PROXIMITY_GAINS[4] PROGMEM = {1, 2, 4, 8};
57static constexpr float LED_CURRENT_MAX_MA = 100.0f; // halved for each drive step
58
59// Lux per count at 1x gain and the fixed integration time
60static constexpr float LUX_PER_COUNT_1X = APDS9930_GA * APDS9930_DF / APDS9930_ALSIT_MS;
61// Each integration cycle adds up to 1024 counts, so this is the ceiling of both light channels
62static constexpr uint16_t ALS_SATURATION = 1024 * (256 - APDS9930_ATIME_VALUE);
63
64bool APDS9930Component::write_reg_(uint8_t reg, uint8_t value) { return this->write_byte(reg | APDS9930_CMD, value); }
65
67 uint8_t id;
68 if (!this->read_byte(APDS9930_ID | APDS9930_CMD, &id)) {
69 this->mark_failed(LOG_STR("Communication failed"));
70 return;
71 }
72 if (id != APDS9930_CHIP_ID) {
73 ESP_LOGE(TAG, "Wrong ID 0x%02X (expected 0x%02X)", id, APDS9930_CHIP_ID);
74 this->mark_failed(LOG_STR("Wrong chip ID"));
75 return;
76 }
77
78 const uint8_t control =
79 (this->led_drive_ & 0b11) << CONTROL_PDRIVE_SHIFT | CONTROL_PDIODE_CH1 << CONTROL_PDIODE_SHIFT |
80 (this->proximity_gain_ & 0b11) << CONTROL_PGAIN_SHIFT | (this->ambient_gain_ & 0b11) << CONTROL_AGAIN_SHIFT;
81
82 uint8_t enable = APDS9930_PON;
83 if (this->illuminance_sensor_ != nullptr)
84 enable |= APDS9930_AEN;
85 if (this->proximity_sensor_ != nullptr)
86 enable |= APDS9930_PEN;
87
88 if (!this->write_reg_(APDS9930_ENABLE, 0x00) || !this->write_reg_(APDS9930_ATIME, APDS9930_ATIME_VALUE) ||
89 !this->write_reg_(APDS9930_PTIME, APDS9930_PTIME_VALUE) ||
90 !this->write_reg_(APDS9930_PPULSE, APDS9930_PPULSE_VALUE) || !this->write_reg_(APDS9930_CONTROL, control) ||
91 !this->write_reg_(APDS9930_ENABLE, enable)) {
92 this->mark_failed(LOG_STR("Configuration failed"));
93 }
94}
95
96void APDS9930Component::dump_config() {
97 ESP_LOGCONFIG(TAG,
98 "APDS9930:\n"
99 " LED Drive: %.1f mA\n"
100 " Proximity Gain: %ux\n"
101 " Ambient Light Gain: %ux",
102 LED_CURRENT_MAX_MA / (1 << this->led_drive_),
103 progmem_read_byte(&PROXIMITY_GAINS[this->proximity_gain_]),
104 progmem_read_byte(&AMBIENT_GAINS[this->ambient_gain_]));
105 LOG_I2C_DEVICE(this);
106 LOG_UPDATE_INTERVAL(this);
107 LOG_SENSOR(" ", "Illuminance", this->illuminance_sensor_);
108 LOG_SENSOR(" ", "Proximity", this->proximity_sensor_);
109}
110
111void APDS9930Component::update() {
112 // STATUS, CH0DATAL/H, CH1DATAL/H, PDATAL/H
113 uint8_t raw[7];
114 if (!this->read_bytes(APDS9930_STATUS | APDS9930_CMD_AUTO_INCREMENT, raw, sizeof(raw))) {
115 this->status_set_warning(LOG_STR("Reading data failed"));
116 return;
117 }
118 this->status_clear_warning();
119 const uint8_t status = raw[0];
120
121 if (this->illuminance_sensor_ != nullptr && (status & APDS9930_AVALID) != 0) {
122 const uint16_t ch0 = encode_uint16(raw[2], raw[1]);
123 const uint16_t ch1 = encode_uint16(raw[4], raw[3]);
124 if (ch0 >= ALS_SATURATION || ch1 >= ALS_SATURATION) {
125 // A saturated channel would give a lux value that is too low, not too high
126 ESP_LOGW(TAG, "Light channel saturated, lower ambient_light_gain");
127 this->illuminance_sensor_->publish_state(NAN);
128 } else {
129 const float iac = std::max({ch0 - APDS9930_ALS_B * ch1, APDS9930_ALS_C * ch0 - APDS9930_ALS_D * ch1, 0.0f});
130 this->illuminance_sensor_->publish_state(iac * LUX_PER_COUNT_1X /
131 progmem_read_byte(&AMBIENT_GAINS[this->ambient_gain_]));
132 }
133 }
134
135 if (this->proximity_sensor_ != nullptr && (status & APDS9930_PVALID) != 0) {
136 this->proximity_sensor_->publish_state(encode_uint16(raw[6], raw[5]));
137 }
138}
139
140} // namespace esphome::apds9930
uint8_t raw[35]
Definition bl0939.h:0
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
virtual void setup()
Where the component's initialization should happen.
Definition component.cpp:84
void status_clear_warning()
Definition component.h:291
bool write_reg_(uint8_t reg, uint8_t value)
Definition apds9930.cpp:64
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:217
uint16_t id
ESPHOME_LOG_TAG(TAG, "apds9930")
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:980
uint8_t progmem_read_byte(const uint8_t *addr)
Definition hal.h:51