mirror of
https://github.com/esphome/esphome.git
synced 2024-11-09 16:57:47 +01: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:
parent
05ab49a615
commit
6ecf4ecac6
6 changed files with 199 additions and 0 deletions
|
@ -90,6 +90,7 @@ esphome/components/factory_reset/* @anatoly-savchenkov
|
|||
esphome/components/fastled_base/* @OttoWinter
|
||||
esphome/components/feedback/* @ianchi
|
||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||
esphome/components/fs3000/* @kahrendt
|
||||
esphome/components/globals/* @esphome/core
|
||||
esphome/components/gpio/* @esphome/core
|
||||
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
|
||||
name: "Person Sensor"
|
||||
i2c_id: i2c_bus
|
||||
- platform: fs3000
|
||||
name: "Air Velocity"
|
||||
model: 1005
|
||||
update_interval: 60s
|
||||
i2c_id: i2c_bus
|
||||
|
||||
esp32_touch:
|
||||
setup_mode: false
|
||||
iir_filter: 10ms
|
||||
|
|
Loading…
Reference in a new issue