mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	APDS9306 Ambient Light Sensor (#6709)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Keith Burzinski <kbx81x@gmail.com> Co-authored-by: Mat931 <49403702+Mat931@users.noreply.github.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							5d5f3276e9
						
					
				
				
					commit
					f1aa254e48
				
			| @@ -37,6 +37,7 @@ esphome/components/am43/sensor/* @buxtronix | ||||
| esphome/components/analog_threshold/* @ianchi | ||||
| esphome/components/animation/* @syndlex | ||||
| esphome/components/anova/* @buxtronix | ||||
| esphome/components/apds9306/* @aodrenah | ||||
| esphome/components/api/* @OttoWinter | ||||
| esphome/components/as5600/* @ammmze | ||||
| esphome/components/as5600/sensor/* @ammmze | ||||
|   | ||||
							
								
								
									
										4
									
								
								esphome/components/apds9306/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								esphome/components/apds9306/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| # Based on this datasheet: | ||||
| # https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf | ||||
|  | ||||
| CODEOWNERS = ["@aodrenah"] | ||||
							
								
								
									
										151
									
								
								esphome/components/apds9306/apds9306.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								esphome/components/apds9306/apds9306.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| // Based on this datasheet: | ||||
| // https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf | ||||
|  | ||||
| #include "apds9306.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace apds9306 { | ||||
|  | ||||
| static const char *const TAG = "apds9306"; | ||||
|  | ||||
| enum {  // APDS9306 registers | ||||
|   APDS9306_MAIN_CTRL = 0x00, | ||||
|   APDS9306_ALS_MEAS_RATE = 0x04, | ||||
|   APDS9306_ALS_GAIN = 0x05, | ||||
|   APDS9306_PART_ID = 0x06, | ||||
|   APDS9306_MAIN_STATUS = 0x07, | ||||
|   APDS9306_CLEAR_DATA_0 = 0x0A,  // LSB | ||||
|   APDS9306_CLEAR_DATA_1 = 0x0B, | ||||
|   APDS9306_CLEAR_DATA_2 = 0x0C,  // MSB | ||||
|   APDS9306_ALS_DATA_0 = 0x0D,    // LSB | ||||
|   APDS9306_ALS_DATA_1 = 0x0E, | ||||
|   APDS9306_ALS_DATA_2 = 0x0F,  // MSB | ||||
|   APDS9306_INT_CFG = 0x19, | ||||
|   APDS9306_INT_PERSISTENCE = 0x1A, | ||||
|   APDS9306_ALS_THRES_UP_0 = 0x21,  // LSB | ||||
|   APDS9306_ALS_THRES_UP_1 = 0x22, | ||||
|   APDS9306_ALS_THRES_UP_2 = 0x23,   // MSB | ||||
|   APDS9306_ALS_THRES_LOW_0 = 0x24,  // LSB | ||||
|   APDS9306_ALS_THRES_LOW_1 = 0x25, | ||||
|   APDS9306_ALS_THRES_LOW_2 = 0x26,  // MSB | ||||
|   APDS9306_ALS_THRES_VAR = 0x27 | ||||
| }; | ||||
|  | ||||
| #define APDS9306_ERROR_CHECK(func, error) \ | ||||
|   if (!(func)) { \ | ||||
|     ESP_LOGE(TAG, error); \ | ||||
|     this->mark_failed(); \ | ||||
|     return; \ | ||||
|   } | ||||
| #define APDS9306_WARNING_CHECK(func, warning) \ | ||||
|   if (!(func)) { \ | ||||
|     ESP_LOGW(TAG, warning); \ | ||||
|     this->status_set_warning(); \ | ||||
|     return; \ | ||||
|   } | ||||
| #define APDS9306_WRITE_BYTE(reg, value) \ | ||||
|   ESP_LOGV(TAG, "Writing 0x%02x to 0x%02x", value, reg); \ | ||||
|   if (!this->write_byte(reg, value)) { \ | ||||
|     ESP_LOGE(TAG, "Failed writing 0x%02x to 0x%02x", value, reg); \ | ||||
|     this->mark_failed(); \ | ||||
|     return; \ | ||||
|   } | ||||
|  | ||||
| void APDS9306::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up APDS9306..."); | ||||
|  | ||||
|   uint8_t id; | ||||
|   if (!this->read_byte(APDS9306_PART_ID, &id)) {  // Part ID register | ||||
|     this->error_code_ = COMMUNICATION_FAILED; | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (id != 0xB1 && id != 0xB3) {  // 0xB1 for APDS9306 0xB3 for APDS9306-065 | ||||
|     this->error_code_ = WRONG_ID; | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // ALS resolution and measurement, see datasheet or init.py for options | ||||
|   uint8_t als_meas_rate = ((this->bit_width_ & 0x07) << 4) | (this->measurement_rate_ & 0x07); | ||||
|   APDS9306_WRITE_BYTE(APDS9306_ALS_MEAS_RATE, als_meas_rate); | ||||
|  | ||||
|   // ALS gain, see datasheet or init.py for options | ||||
|   uint8_t als_gain = (this->gain_ & 0x07); | ||||
|   APDS9306_WRITE_BYTE(APDS9306_ALS_GAIN, als_gain); | ||||
|  | ||||
|   // Set to standby mode | ||||
|   APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00); | ||||
|  | ||||
|   // Check for data, clear main status | ||||
|   uint8_t status; | ||||
|   APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed."); | ||||
|  | ||||
|   // Set to active mode | ||||
|   APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02); | ||||
|  | ||||
|   ESP_LOGCONFIG(TAG, "APDS9306 setup complete"); | ||||
| } | ||||
|  | ||||
| void APDS9306::dump_config() { | ||||
|   LOG_SENSOR("", "APDS9306", this); | ||||
|   LOG_I2C_DEVICE(this); | ||||
|  | ||||
|   if (this->is_failed()) { | ||||
|     switch (this->error_code_) { | ||||
|       case COMMUNICATION_FAILED: | ||||
|         ESP_LOGE(TAG, "Communication with APDS9306 failed!"); | ||||
|         break; | ||||
|       case WRONG_ID: | ||||
|         ESP_LOGE(TAG, "APDS9306 has invalid id!"); | ||||
|         break; | ||||
|       default: | ||||
|         ESP_LOGE(TAG, "Setting up APDS9306 registers failed!"); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ESP_LOGCONFIG(TAG, "  Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]); | ||||
|   ESP_LOGCONFIG(TAG, "  Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]); | ||||
|   ESP_LOGCONFIG(TAG, "  Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]); | ||||
|  | ||||
|   LOG_UPDATE_INTERVAL(this); | ||||
| } | ||||
|  | ||||
| void APDS9306::update() { | ||||
|   // Check for new data | ||||
|   uint8_t status; | ||||
|   APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed."); | ||||
|  | ||||
|   this->status_clear_warning(); | ||||
|  | ||||
|   if (!(status &= 0b00001000)) {  // No new data | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // Set to standby mode | ||||
|   APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00); | ||||
|  | ||||
|   // Clear MAIN STATUS | ||||
|   APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed."); | ||||
|  | ||||
|   uint8_t als_data[3]; | ||||
|   APDS9306_WARNING_CHECK(this->read_bytes(APDS9306_ALS_DATA_0, als_data, 3), "Reading ALS data has failed."); | ||||
|  | ||||
|   // Set to active mode | ||||
|   APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02); | ||||
|  | ||||
|   uint32_t light_level = 0x00 | encode_uint24(als_data[2], als_data[1], als_data[0]); | ||||
|  | ||||
|   float lux = ((float) light_level / AMBIENT_LIGHT_GAIN_VALUES[this->gain_]) * | ||||
|               (100.0f / MEASUREMENT_RATE_VALUES[this->measurement_rate_]); | ||||
|  | ||||
|   ESP_LOGD(TAG, "Got illuminance=%.1flx from", lux); | ||||
|   this->publish_state(lux); | ||||
| } | ||||
|  | ||||
| }  // namespace apds9306 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										66
									
								
								esphome/components/apds9306/apds9306.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								esphome/components/apds9306/apds9306.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| // Based on this datasheet: | ||||
| // https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace apds9306 { | ||||
|  | ||||
| enum MeasurementBitWidth : uint8_t { | ||||
|   MEASUREMENT_BIT_WIDTH_20 = 0, | ||||
|   MEASUREMENT_BIT_WIDTH_19 = 1, | ||||
|   MEASUREMENT_BIT_WIDTH_18 = 2, | ||||
|   MEASUREMENT_BIT_WIDTH_17 = 3, | ||||
|   MEASUREMENT_BIT_WIDTH_16 = 4, | ||||
|   MEASUREMENT_BIT_WIDTH_13 = 5, | ||||
| }; | ||||
| static const uint8_t MEASUREMENT_BIT_WIDTH_VALUES[] = {20, 19, 18, 17, 16, 13}; | ||||
|  | ||||
| enum MeasurementRate : uint8_t { | ||||
|   MEASUREMENT_RATE_25 = 0, | ||||
|   MEASUREMENT_RATE_50 = 1, | ||||
|   MEASUREMENT_RATE_100 = 2, | ||||
|   MEASUREMENT_RATE_200 = 3, | ||||
|   MEASUREMENT_RATE_500 = 4, | ||||
|   MEASUREMENT_RATE_1000 = 5, | ||||
|   MEASUREMENT_RATE_2000 = 6, | ||||
| }; | ||||
| static const uint16_t MEASUREMENT_RATE_VALUES[] = {25, 50, 100, 200, 500, 1000, 2000}; | ||||
|  | ||||
| enum AmbientLightGain : uint8_t { | ||||
|   AMBIENT_LIGHT_GAIN_1 = 0, | ||||
|   AMBIENT_LIGHT_GAIN_3 = 1, | ||||
|   AMBIENT_LIGHT_GAIN_6 = 2, | ||||
|   AMBIENT_LIGHT_GAIN_9 = 3, | ||||
|   AMBIENT_LIGHT_GAIN_18 = 4, | ||||
| }; | ||||
| static const uint8_t AMBIENT_LIGHT_GAIN_VALUES[] = {1, 3, 6, 9, 18}; | ||||
|  | ||||
| class APDS9306 : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   float get_setup_priority() const override { return setup_priority::BUS; } | ||||
|   void dump_config() override; | ||||
|   void update() override; | ||||
|   void set_bit_width(MeasurementBitWidth bit_width) { this->bit_width_ = bit_width; } | ||||
|   void set_measurement_rate(MeasurementRate measurement_rate) { this->measurement_rate_ = measurement_rate; } | ||||
|   void set_ambient_light_gain(AmbientLightGain gain) { this->gain_ = gain; } | ||||
|  | ||||
|  protected: | ||||
|   enum ErrorCode { | ||||
|     NONE = 0, | ||||
|     COMMUNICATION_FAILED, | ||||
|     WRONG_ID, | ||||
|   } error_code_{NONE}; | ||||
|  | ||||
|   MeasurementBitWidth bit_width_; | ||||
|   MeasurementRate measurement_rate_; | ||||
|   AmbientLightGain gain_; | ||||
| }; | ||||
|  | ||||
| }  // namespace apds9306 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										95
									
								
								esphome/components/apds9306/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								esphome/components/apds9306/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| # Based on this datasheet: | ||||
| # https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf | ||||
|  | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, sensor | ||||
| from esphome.const import ( | ||||
|     CONF_GAIN, | ||||
|     DEVICE_CLASS_ILLUMINANCE, | ||||
|     ICON_LIGHTBULB, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     UNIT_LUX, | ||||
| ) | ||||
|  | ||||
| DEPENDENCIES = ["i2c"] | ||||
|  | ||||
| CONF_APDS9306_ID = "apds9306_id" | ||||
| CONF_BIT_WIDTH = "bit_width" | ||||
| CONF_MEASUREMENT_RATE = "measurement_rate" | ||||
|  | ||||
| apds9306_ns = cg.esphome_ns.namespace("apds9306") | ||||
| APDS9306 = apds9306_ns.class_( | ||||
|     "APDS9306", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice | ||||
| ) | ||||
|  | ||||
| MeasurementBitWidth = apds9306_ns.enum("MeasurementBitWidth") | ||||
| MeasurementRate = apds9306_ns.enum("MeasurementRate") | ||||
| AmbientLightGain = apds9306_ns.enum("AmbientLightGain") | ||||
|  | ||||
| MEASUREMENT_BIT_WIDTHS = { | ||||
|     20: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_20, | ||||
|     19: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_19, | ||||
|     18: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_18, | ||||
|     17: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_17, | ||||
|     16: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_16, | ||||
|     13: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_13, | ||||
| } | ||||
|  | ||||
| MEASUREMENT_RATES = { | ||||
|     25: MeasurementRate.MEASUREMENT_RATE_25, | ||||
|     50: MeasurementRate.MEASUREMENT_RATE_50, | ||||
|     100: MeasurementRate.MEASUREMENT_RATE_100, | ||||
|     200: MeasurementRate.MEASUREMENT_RATE_200, | ||||
|     500: MeasurementRate.MEASUREMENT_RATE_500, | ||||
|     1000: MeasurementRate.MEASUREMENT_RATE_1000, | ||||
|     2000: MeasurementRate.MEASUREMENT_RATE_2000, | ||||
| } | ||||
|  | ||||
| AMBIENT_LIGHT_GAINS = { | ||||
|     1: AmbientLightGain.AMBIENT_LIGHT_GAIN_1, | ||||
|     3: AmbientLightGain.AMBIENT_LIGHT_GAIN_3, | ||||
|     6: AmbientLightGain.AMBIENT_LIGHT_GAIN_6, | ||||
|     9: AmbientLightGain.AMBIENT_LIGHT_GAIN_9, | ||||
|     18: AmbientLightGain.AMBIENT_LIGHT_GAIN_18, | ||||
| } | ||||
|  | ||||
|  | ||||
| def _validate_measurement_rate(value): | ||||
|     value = cv.positive_time_period_milliseconds(value) | ||||
|     return cv.enum(MEASUREMENT_RATES, int=True)(value.total_milliseconds) | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     sensor.sensor_schema( | ||||
|         APDS9306, | ||||
|         unit_of_measurement=UNIT_LUX, | ||||
|         accuracy_decimals=1, | ||||
|         device_class=DEVICE_CLASS_ILLUMINANCE, | ||||
|         state_class=STATE_CLASS_MEASUREMENT, | ||||
|         icon=ICON_LIGHTBULB, | ||||
|     ) | ||||
|     .extend( | ||||
|         { | ||||
|             cv.Optional(CONF_GAIN, default="1"): cv.enum(AMBIENT_LIGHT_GAINS, int=True), | ||||
|             cv.Optional(CONF_BIT_WIDTH, default="18"): cv.enum( | ||||
|                 MEASUREMENT_BIT_WIDTHS, int=True | ||||
|             ), | ||||
|             cv.Optional( | ||||
|                 CONF_MEASUREMENT_RATE, default="100ms" | ||||
|             ): _validate_measurement_rate, | ||||
|         } | ||||
|     ) | ||||
|     .extend(cv.polling_component_schema("60s")) | ||||
|     .extend(i2c.i2c_device_schema(0x52)) | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = await sensor.new_sensor(config) | ||||
|     await cg.register_component(var, config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
|  | ||||
|     cg.add(var.set_bit_width(config[CONF_BIT_WIDTH])) | ||||
|     cg.add(var.set_measurement_rate(config[CONF_MEASUREMENT_RATE])) | ||||
|     cg.add(var.set_ambient_light_gain(config[CONF_GAIN])) | ||||
							
								
								
									
										12
									
								
								tests/components/apds9306/common.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/components/apds9306/common.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| i2c: | ||||
|   - id: i2c_apds9306 | ||||
|     scl: ${scl_pin} | ||||
|     sda: ${sda_pin} | ||||
|  | ||||
| sensor: | ||||
|   - platform: apds9306 | ||||
|     name: "APDS9306 Light Level" | ||||
|     gain: 3 | ||||
|     bit_width: 16 | ||||
|     measurement_rate: 2000ms | ||||
|     update_interval: 60s | ||||
							
								
								
									
										5
									
								
								tests/components/apds9306/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/apds9306/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   scl_pin: GPIO22 | ||||
|   sda_pin: GPIO21 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/apds9306/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/apds9306/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   scl_pin: GPIO5 | ||||
|   sda_pin: GPIO4 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/apds9306/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/apds9306/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   scl_pin: GPIO5 | ||||
|   sda_pin: GPIO4 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/apds9306/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/apds9306/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   scl_pin: GPIO22 | ||||
|   sda_pin: GPIO21 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/apds9306/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/apds9306/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   scl_pin: GPIO5 | ||||
|   sda_pin: GPIO4 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/apds9306/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/apds9306/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   scl_pin: GPIO5 | ||||
|   sda_pin: GPIO4 | ||||
|  | ||||
| <<: !include common.yaml | ||||
		Reference in New Issue
	
	Block a user