mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	FS3000 sensor (#4502)
* Add support for FS3000 sensor. * add fs3000 to test yaml * Clean up code with clang. * Clean up sensor.py file. * Update CODEOWNERS file. * Apply suggestions from code review regarding sensor.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Apply suggestions from code review for basic issues regarding C++ code - removed unnecessary default for FS3000Model - use "this->" before any sensor update Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Move model setup to overall setup function. * Remove unneeded CONF_ID from sensor.py * Run clang-format * Move set_model code to header file now that it is simplified * Update fs3000.h --------- Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -90,6 +90,7 @@ esphome/components/factory_reset/* @anatoly-savchenkov | |||||||
| esphome/components/fastled_base/* @OttoWinter | esphome/components/fastled_base/* @OttoWinter | ||||||
| esphome/components/feedback/* @ianchi | esphome/components/feedback/* @ianchi | ||||||
| esphome/components/fingerprint_grow/* @OnFreund @loongyh | esphome/components/fingerprint_grow/* @OnFreund @loongyh | ||||||
|  | esphome/components/fs3000/* @kahrendt | ||||||
| esphome/components/globals/* @esphome/core | esphome/components/globals/* @esphome/core | ||||||
| esphome/components/gpio/* @esphome/core | esphome/components/gpio/* @esphome/core | ||||||
| esphome/components/gps/* @coogle | esphome/components/gps/* @coogle | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/fs3000/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/fs3000/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										107
									
								
								esphome/components/fs3000/fs3000.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								esphome/components/fs3000/fs3000.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | |||||||
|  | #include "fs3000.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace fs3000 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "fs3000"; | ||||||
|  |  | ||||||
|  | void FS3000Component::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up FS3000..."); | ||||||
|  |  | ||||||
|  |   if (model_ == FIVE) { | ||||||
|  |     // datasheet gives 9 points to interpolate from for the 1005 model | ||||||
|  |     static const uint16_t RAW_DATA_POINTS_1005[9] = {409, 915, 1522, 2066, 2523, 2908, 3256, 3572, 3686}; | ||||||
|  |     static const float MPS_DATA_POINTS_1005[9] = {0.0, 1.07, 2.01, 3.0, 3.97, 4.96, 5.98, 6.99, 7.23}; | ||||||
|  |  | ||||||
|  |     std::copy(RAW_DATA_POINTS_1005, RAW_DATA_POINTS_1005 + 9, this->raw_data_points_); | ||||||
|  |     std::copy(MPS_DATA_POINTS_1005, MPS_DATA_POINTS_1005 + 9, this->mps_data_points_); | ||||||
|  |   } else if (model_ == FIFTEEN) { | ||||||
|  |     // datasheet gives 13 points to extrapolate from for the 1015 model | ||||||
|  |     static const uint16_t RAW_DATA_POINTS_1015[13] = {409,  1203, 1597, 1908, 2187, 2400, 2629, | ||||||
|  |                                                       2801, 3006, 3178, 3309, 3563, 3686}; | ||||||
|  |     static const float MPS_DATA_POINTS_1015[13] = {0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 13.0, 15.0}; | ||||||
|  |  | ||||||
|  |     std::copy(RAW_DATA_POINTS_1015, RAW_DATA_POINTS_1015 + 13, this->raw_data_points_); | ||||||
|  |     std::copy(MPS_DATA_POINTS_1015, MPS_DATA_POINTS_1015 + 13, this->mps_data_points_); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void FS3000Component::update() { | ||||||
|  |   // 5 bytes of data read from fs3000 sensor | ||||||
|  |   //  byte 1 - checksum | ||||||
|  |   //  byte 2 - (lower 4 bits) high byte of sensor reading | ||||||
|  |   //  byte 3 - (8 bits) low byte of sensor reading | ||||||
|  |   //  byte 4 - generic checksum data | ||||||
|  |   //  byte 5 - generic checksum data | ||||||
|  |  | ||||||
|  |   uint8_t data[5]; | ||||||
|  |  | ||||||
|  |   if (!this->read_bytes_raw(data, 5)) { | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     ESP_LOGW(TAG, "Error reading data from FS3000"); | ||||||
|  |     this->publish_state(NAN); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // checksum passes if the modulo 256 sum of the five bytes is 0 | ||||||
|  |   uint8_t checksum = 0; | ||||||
|  |   for (uint8_t i : data) { | ||||||
|  |     checksum += i; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (checksum != 0) { | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     ESP_LOGW(TAG, "Checksum failure when reading from FS3000"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // raw value information is 12 bits | ||||||
|  |   uint16_t raw_value = (data[1] << 8) | data[2]; | ||||||
|  |   ESP_LOGV(TAG, "Got raw reading=%i", raw_value); | ||||||
|  |  | ||||||
|  |   // convert and publish the raw value into m/s using the table of data points in the datasheet | ||||||
|  |   this->publish_state(fit_raw_(raw_value)); | ||||||
|  |  | ||||||
|  |   this->status_clear_warning(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void FS3000Component::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "FS3000:"); | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  |   LOG_UPDATE_INTERVAL(this); | ||||||
|  |   LOG_SENSOR("  ", "Air Velocity", this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float FS3000Component::fit_raw_(uint16_t raw_value) { | ||||||
|  |   // converts a raw value read from the FS3000 into a speed in m/s based on the | ||||||
|  |   // reference data points given in the datasheet | ||||||
|  |   // fits raw reading using a linear interpolation between each data point | ||||||
|  |  | ||||||
|  |   uint8_t end = 8;  // assume model 1005, which has 9 data points | ||||||
|  |   if (this->model_ == FIFTEEN) | ||||||
|  |     end = 12;  // model 1015 has 13 data points | ||||||
|  |  | ||||||
|  |   if (raw_value <= this->raw_data_points_[0]) {  // less than smallest data point returns first data point | ||||||
|  |     return this->mps_data_points_[0]; | ||||||
|  |   } else if (raw_value >= this->raw_data_points_[end]) {  // greater than largest data point returns max speed | ||||||
|  |     return this->mps_data_points_[end]; | ||||||
|  |   } else { | ||||||
|  |     uint8_t i = 0; | ||||||
|  |  | ||||||
|  |     // determine between which data points does the reading fall, i-1 and i | ||||||
|  |     while (raw_value > this->raw_data_points_[i]) { | ||||||
|  |       ++i; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // calculate the slope of the secant line between the two data points that surrounds the reading | ||||||
|  |     float slope = (this->mps_data_points_[i] - this->mps_data_points_[i - 1]) / | ||||||
|  |                   (this->raw_data_points_[i] - this->raw_data_points_[i - 1]); | ||||||
|  |  | ||||||
|  |     // return the interpolated value for the reading | ||||||
|  |     return (float(raw_value - this->raw_data_points_[i - 1])) * slope + this->mps_data_points_[i - 1]; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace fs3000 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										35
									
								
								esphome/components/fs3000/fs3000.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphome/components/fs3000/fs3000.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace fs3000 { | ||||||
|  |  | ||||||
|  | // FS3000 has two models, 1005 and 1015 | ||||||
|  | //  1005 has a max speed detection of 7.23 m/s | ||||||
|  | //  1015 has a max speed detection of 15 m/s | ||||||
|  | enum FS3000Model { FIVE, FIFTEEN }; | ||||||
|  |  | ||||||
|  | class FS3000Component : public PollingComponent, public i2c::I2CDevice, public sensor::Sensor { | ||||||
|  |  public: | ||||||
|  |   void setup() override; | ||||||
|  |   void update() override; | ||||||
|  |  | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |  | ||||||
|  |   void set_model(FS3000Model model) { this->model_ = model; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   FS3000Model model_{}; | ||||||
|  |  | ||||||
|  |   uint16_t raw_data_points_[13]; | ||||||
|  |   float mps_data_points_[13]; | ||||||
|  |  | ||||||
|  |   float fit_raw_(uint16_t raw_value); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace fs3000 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										50
									
								
								esphome/components/fs3000/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								esphome/components/fs3000/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | # initially based off of TMP117 component | ||||||
|  |  | ||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import i2c, sensor | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_MODEL, | ||||||
|  |     DEVICE_CLASS_WIND_SPEED, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["i2c"] | ||||||
|  | CODEOWNERS = ["@kahrendt"] | ||||||
|  |  | ||||||
|  | fs3000_ns = cg.esphome_ns.namespace("fs3000") | ||||||
|  |  | ||||||
|  | FS3000Model = fs3000_ns.enum("MODEL") | ||||||
|  | FS3000_MODEL_OPTIONS = { | ||||||
|  |     "1005": FS3000Model.FIVE, | ||||||
|  |     "1015": FS3000Model.FIFTEEN, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | FS3000Component = fs3000_ns.class_( | ||||||
|  |     "FS3000Component", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     sensor.sensor_schema( | ||||||
|  |         FS3000Component, | ||||||
|  |         unit_of_measurement="m/s", | ||||||
|  |         accuracy_decimals=2, | ||||||
|  |         device_class=DEVICE_CLASS_WIND_SPEED, | ||||||
|  |         state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |     ) | ||||||
|  |     .extend( | ||||||
|  |         { | ||||||
|  |             cv.Required(CONF_MODEL): cv.enum(FS3000_MODEL_OPTIONS, lower=True), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(cv.polling_component_schema("60s")) | ||||||
|  |     .extend(i2c.i2c_device_schema(0x28)) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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_model(config[CONF_MODEL])) | ||||||
| @@ -1224,6 +1224,12 @@ sensor: | |||||||
|   - platform: sen21231 |   - platform: sen21231 | ||||||
|     name: "Person Sensor" |     name: "Person Sensor" | ||||||
|     i2c_id: i2c_bus |     i2c_id: i2c_bus | ||||||
|  |   - platform: fs3000 | ||||||
|  |     name: "Air Velocity" | ||||||
|  |     model: 1005 | ||||||
|  |     update_interval: 60s | ||||||
|  |     i2c_id: i2c_bus | ||||||
|  |  | ||||||
| esp32_touch: | esp32_touch: | ||||||
|   setup_mode: false |   setup_mode: false | ||||||
|   iir_filter: 10ms |   iir_filter: 10ms | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user