mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Add support for Sharp GP2Y1010AU0F PM2.5 sensor (#6007)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -152,6 +152,7 @@ esphome/components/ft63x6/* @gpambrozio | |||||||
| esphome/components/gcja5/* @gcormier | esphome/components/gcja5/* @gcormier | ||||||
| esphome/components/gdk101/* @Szewcson | esphome/components/gdk101/* @Szewcson | ||||||
| esphome/components/globals/* @esphome/core | esphome/components/globals/* @esphome/core | ||||||
|  | esphome/components/gp2y1010au0f/* @zry98 | ||||||
| esphome/components/gp8403/* @jesserockz | esphome/components/gp8403/* @jesserockz | ||||||
| esphome/components/gpio/* @esphome/core | esphome/components/gpio/* @esphome/core | ||||||
| esphome/components/gpio/one_wire/* @ssieb | esphome/components/gpio/one_wire/* @ssieb | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/gp2y1010au0f/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/gp2y1010au0f/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										67
									
								
								esphome/components/gp2y1010au0f/gp2y1010au0f.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								esphome/components/gp2y1010au0f/gp2y1010au0f.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | #include "gp2y1010au0f.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | #include <cinttypes> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace gp2y1010au0f { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "gp2y1010au0f"; | ||||||
|  | static const float MIN_VOLTAGE = 0.0f; | ||||||
|  | static const float MAX_VOLTAGE = 4.0f; | ||||||
|  |  | ||||||
|  | void GP2Y1010AU0FSensor::dump_config() { | ||||||
|  |   LOG_SENSOR("", "Sharp GP2Y1010AU0F PM2.5 Sensor", this); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Sampling duration: %" PRId32 " ms", this->sample_duration_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  ADC voltage multiplier: %.3f", this->voltage_multiplier_); | ||||||
|  |   LOG_UPDATE_INTERVAL(this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GP2Y1010AU0FSensor::update() { | ||||||
|  |   is_sampling_ = true; | ||||||
|  |  | ||||||
|  |   this->set_timeout("read", this->sample_duration_, [this]() { | ||||||
|  |     this->is_sampling_ = false; | ||||||
|  |     if (this->num_samples_ == 0) | ||||||
|  |       return; | ||||||
|  |  | ||||||
|  |     float mean = this->sample_sum_ / float(this->num_samples_); | ||||||
|  |     ESP_LOGD(TAG, "ADC read voltage: %.3f V (mean from %" PRId32 " samples)", mean, this->num_samples_); | ||||||
|  |  | ||||||
|  |     // PM2.5 calculation | ||||||
|  |     // ref: https://www.howmuchsnow.com/arduino/airquality/ | ||||||
|  |     int16_t pm_2_5_value = 170 * mean; | ||||||
|  |     this->publish_state(pm_2_5_value); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // reset readings | ||||||
|  |   this->num_samples_ = 0; | ||||||
|  |   this->sample_sum_ = 0.0f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void GP2Y1010AU0FSensor::loop() { | ||||||
|  |   if (!this->is_sampling_) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   // enable the internal IR LED | ||||||
|  |   this->led_output_->turn_on(); | ||||||
|  |   // wait for the sensor to stabilize | ||||||
|  |   delayMicroseconds(this->sample_wait_before_); | ||||||
|  |   // perform a single sample | ||||||
|  |   float read_voltage = this->source_->sample(); | ||||||
|  |   // disable the internal IR LED | ||||||
|  |   this->led_output_->turn_off(); | ||||||
|  |  | ||||||
|  |   if (std::isnan(read_voltage)) | ||||||
|  |     return; | ||||||
|  |   read_voltage = read_voltage * this->voltage_multiplier_ - this->voltage_offset_; | ||||||
|  |   if (read_voltage < MIN_VOLTAGE || read_voltage > MAX_VOLTAGE) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   this->num_samples_++; | ||||||
|  |   this->sample_sum_ += read_voltage; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace gp2y1010au0f | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										52
									
								
								esphome/components/gp2y1010au0f/gp2y1010au0f.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								esphome/components/gp2y1010au0f/gp2y1010au0f.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/components/voltage_sampler/voltage_sampler.h" | ||||||
|  | #include "esphome/components/output/binary_output.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace gp2y1010au0f { | ||||||
|  |  | ||||||
|  | class GP2Y1010AU0FSensor : public sensor::Sensor, public PollingComponent { | ||||||
|  |  public: | ||||||
|  |   void update() override; | ||||||
|  |   void loop() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { | ||||||
|  |     // after the base sensor has been initialized | ||||||
|  |     return setup_priority::DATA - 1.0f; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void set_adc_source(voltage_sampler::VoltageSampler *source) { source_ = source; } | ||||||
|  |   void set_voltage_refs(float offset, float multiplier) { | ||||||
|  |     this->voltage_offset_ = offset; | ||||||
|  |     this->voltage_multiplier_ = multiplier; | ||||||
|  |   } | ||||||
|  |   void set_led_output(output::BinaryOutput *output) { led_output_ = output; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   // duration in ms of the sampling phase | ||||||
|  |   uint32_t sample_duration_ = 100; | ||||||
|  |   // duration in us of the wait before sampling | ||||||
|  |   // ref: https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf | ||||||
|  |   uint32_t sample_wait_before_ = 280; | ||||||
|  |   // duration in us of the wait after sampling | ||||||
|  |   // it seems no need to delay on purpose since one ADC sampling takes longer than that (300-400 us on ESP8266) | ||||||
|  |   // uint32_t sample_wait_after_ = 40; | ||||||
|  |   // the sampling source to read voltage from | ||||||
|  |   voltage_sampler::VoltageSampler *source_; | ||||||
|  |   // ADC voltage reading offset | ||||||
|  |   float voltage_offset_ = 0.0f; | ||||||
|  |   // ADC voltage reading multiplier | ||||||
|  |   float voltage_multiplier_ = 1.0f; | ||||||
|  |   // the binary output to control the sampling LED | ||||||
|  |   output::BinaryOutput *led_output_; | ||||||
|  |  | ||||||
|  |   float sample_sum_ = 0.0f; | ||||||
|  |   uint32_t num_samples_ = 0; | ||||||
|  |   bool is_sampling_ = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace gp2y1010au0f | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										61
									
								
								esphome/components/gp2y1010au0f/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								esphome/components/gp2y1010au0f/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor, voltage_sampler, output | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_SENSOR, | ||||||
|  |     CONF_OUTPUT, | ||||||
|  |     DEVICE_CLASS_PM25, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     UNIT_MICROGRAMS_PER_CUBIC_METER, | ||||||
|  |     ICON_CHEMICAL_WEAPON, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["output"] | ||||||
|  | AUTO_LOAD = ["voltage_sampler"] | ||||||
|  | CODEOWNERS = ["@zry98"] | ||||||
|  |  | ||||||
|  | CONF_ADC_VOLTAGE_OFFSET = "adc_voltage_offset" | ||||||
|  | CONF_ADC_VOLTAGE_MULTIPLIER = "adc_voltage_multiplier" | ||||||
|  |  | ||||||
|  | gp2y1010au0f_ns = cg.esphome_ns.namespace("gp2y1010au0f") | ||||||
|  | GP2Y1010AU0FSensor = gp2y1010au0f_ns.class_( | ||||||
|  |     "GP2Y1010AU0FSensor", sensor.Sensor, cg.PollingComponent | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     sensor.sensor_schema( | ||||||
|  |         GP2Y1010AU0FSensor, | ||||||
|  |         unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, | ||||||
|  |         accuracy_decimals=0, | ||||||
|  |         device_class=DEVICE_CLASS_PM25, | ||||||
|  |         state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |         icon=ICON_CHEMICAL_WEAPON, | ||||||
|  |     ) | ||||||
|  |     .extend( | ||||||
|  |         { | ||||||
|  |             cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler), | ||||||
|  |             cv.Optional(CONF_ADC_VOLTAGE_OFFSET, default=0.0): cv.float_, | ||||||
|  |             cv.Optional(CONF_ADC_VOLTAGE_MULTIPLIER, default=1.0): cv.float_, | ||||||
|  |             cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(cv.polling_component_schema("60s")) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = await sensor.new_sensor(config) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|  |     # the ADC sensor to read voltage from | ||||||
|  |     adc_sensor = await cg.get_variable(config[CONF_SENSOR]) | ||||||
|  |     cg.add(var.set_adc_source(adc_sensor)) | ||||||
|  |     cg.add( | ||||||
|  |         var.set_voltage_refs( | ||||||
|  |             config[CONF_ADC_VOLTAGE_OFFSET], config[CONF_ADC_VOLTAGE_MULTIPLIER] | ||||||
|  |         ) | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     # the binary output to control the module's internal IR LED | ||||||
|  |     led_output = await cg.get_variable(config[CONF_OUTPUT]) | ||||||
|  |     cg.add(var.set_led_output(led_output)) | ||||||
							
								
								
									
										16
									
								
								tests/components/gp2y1010au0f/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/components/gp2y1010au0f/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | sensor: | ||||||
|  |   - platform: adc | ||||||
|  |     pin: GPIO36 | ||||||
|  |     id: adc_sensor | ||||||
|  |  | ||||||
|  |   - platform: gp2y1010au0f | ||||||
|  |     sensor: adc_sensor | ||||||
|  |     name: Dust Sensor | ||||||
|  |     adc_voltage_offset: 0.2 | ||||||
|  |     adc_voltage_multiplier: 3.3 | ||||||
|  |     output: dust_sensor_led | ||||||
|  |  | ||||||
|  | output: | ||||||
|  |   - platform: gpio | ||||||
|  |     id: dust_sensor_led | ||||||
|  |     pin: GPIO32 | ||||||
		Reference in New Issue
	
	Block a user