Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sgp40 fix #2462

Merged
merged 5 commits into from Oct 10, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions esphome/components/sgp40/sensor.py
Expand Up @@ -22,6 +22,7 @@
CONF_TEMPERATURE_SOURCE = "temperature_source"
CONF_STORE_BASELINE = "store_baseline"
CONF_VOC_BASELINE = "voc_baseline"
CONF_OPTIMAL_SAMPLING = "optimal_sampling"

CONFIG_SCHEMA = (
sensor.sensor_schema(
Expand All @@ -34,6 +35,7 @@
{
cv.GenerateID(): cv.declare_id(SGP40Component),
cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
cv.Optional(CONF_OPTIMAL_SAMPLING, default=True): cv.boolean,
cv.Optional(CONF_VOC_BASELINE): cv.hex_uint16_t,
cv.Optional(CONF_COMPENSATION): cv.Schema(
{
Expand Down Expand Up @@ -62,6 +64,7 @@ async def to_code(config):
cg.add(var.set_temperature_sensor(sens))

cg.add(var.set_store_baseline(config[CONF_STORE_BASELINE]))
cg.add(var.set_use_optimal_sampling(config[CONF_OPTIMAL_SAMPLING]))

if CONF_VOC_BASELINE in config:
cg.add(var.set_voc_baseline(CONF_VOC_BASELINE))
48 changes: 41 additions & 7 deletions esphome/components/sgp40/sgp40.cpp
Expand Up @@ -77,6 +77,26 @@ void SGP40Component::setup() {
}

this->self_test_();

/* The official spec for this sensor at https://docs.rs-online.com/1956/A700000007055193.pdf
indicates this sensor should be driven at 1Hz. Comments from the developers at:
https://github.com/Sensirion/embedded-sgp/issues/136 indicate the algorithm should be a bit
resilient to slight timing variations so the software timer should be accurate enough for
this.

This block starts sampling from the sensor at 1Hz, and is done seperately from the call
to the update method. This seperation is to support getting accurate measurements but
limit the amount of communication done over wifi for power consumption or to keep the
number of records reported from being overwhelming.

At configuration the component can be configured to turn off optimal sampling in which
case the sensor will be read out with the same cadence as the update method, leading to
likely inacurate measurements, but possibly accurate enough for certain use cases.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this? I mean if you have to update close to 1Hz anyway, why should we even expose this option?

I'd say we remove that option, and if someone does come up with a real use-case we can always add it back

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't really know what your projects stance on backwards compatibility was, and I wanted to provide a way for people who may be running this on a battery or have somehow coded a flow around the incorrect behavior to retain the existing behavior. If that is not a priority I am happy to change it and the accompanying documentation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sure no problem

We try to not have breaking changes, though I think in this case the previous behavior would have been broken anyway unless it's polled at 1Hz, no? In that case it wouldn't really be a breaking change and ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, how it currently is the VOC measurement is just not an accurate reflection of reality, it may happen to be close enough for some, but will not behave correctly overtime.

Thanks for the heads up on the way the project approaches things. I am pretty new to ESPHome, and it's pretty amazing. I have a few other things I hope to contribute, so I hope to make things easier on you all reviewing things in future PRs.

*/
if (this->optimal_sampling_) {
ESP_LOGD(TAG, "Using optimal sampling, setting up background sampler");
App.scheduler.set_interval(this, "", 1000, [this](){this->update_voc_index();});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
App.scheduler.set_interval(this, "", 1000, [this](){this->update_voc_index();});
this->set_interval(1000, [this](){this->update_voc_index();});

Component already has this method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, sorry, I'm still trying to load this code base into my mental cache

}
}

void SGP40Component::self_test_() {
Expand Down Expand Up @@ -224,21 +244,32 @@ uint8_t SGP40Component::generate_crc_(const uint8_t *data, uint8_t datalen) {
return crc;
}

void SGP40Component::update() {
this->seconds_since_last_store_ += this->update_interval_ / 1000;

uint32_t voc_index = this->measure_voc_index_();
void SGP40Component::update_voc_index() {
uint32_t update_interval = this->optimal_sampling_ ? 1 : this->update_interval_ / 1000;
this->seconds_since_last_store_ += update_interval;

this->voc_index_ = this->measure_voc_index_();
if (this->samples_read_ < this->samples_to_stabalize_) {
this->samples_read_++;
ESP_LOGD(TAG, "Sensor has not collected enough samples yet. (%d/%d) VOC index is: %u", this->samples_read_,
this->samples_to_stabalize_, voc_index);
this->samples_to_stabalize_, this->voc_index_);
return;
}

}

void SGP40Component::update() {
if (!(this->optimal_sampling_)) {
this->update_voc_index();
}

if (this->samples_read_ < this->samples_to_stabalize_) {
return;
}

if (voc_index != UINT16_MAX) {
if (this->voc_index_ != UINT16_MAX) {
this->status_clear_warning();
this->publish_state(voc_index);
this->publish_state(this->voc_index_);
} else {
this->status_set_warning();
}
Expand All @@ -247,6 +278,9 @@ void SGP40Component::update() {
void SGP40Component::dump_config() {
ESP_LOGCONFIG(TAG, "SGP40:");
LOG_I2C_DEVICE(this);
ESP_LOGCONFIG(TAG, " optimal_samping: %d", this->optimal_sampling_);
ESP_LOGCONFIG(TAG, " store_baseline: %d", this->store_baseline_);

if (this->is_failed()) {
switch (this->error_code_) {
case COMMUNICATION_FAILED:
Expand Down
5 changes: 5 additions & 0 deletions esphome/components/sgp40/sgp40.h
Expand Up @@ -46,9 +46,12 @@ class SGP40Component : public PollingComponent, public sensor::Sensor, public i2

void setup() override;
void update() override;
void update_voc_index();
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void set_store_baseline(bool store_baseline) { store_baseline_ = store_baseline; }
void set_use_optimal_sampling(bool optimal_sampling) {optimal_sampling_ = optimal_sampling; }
bool get_use_optimal_sampling() const { return optimal_sampling_; }

protected:
/// Input sensor for humidity and temperature compensation.
Expand All @@ -70,8 +73,10 @@ class SGP40Component : public PollingComponent, public sensor::Sensor, public i2
VocAlgorithmParams voc_algorithm_params_;
bool self_test_complete_;
bool store_baseline_;
bool optimal_sampling_ = 1;
int32_t state0_;
int32_t state1_;
int32_t voc_index_ = 0;
uint8_t samples_read_ = 0;
uint8_t samples_to_stabalize_ = static_cast<int8_t>(VOC_ALGORITHM_INITIAL_BLACKOUT) * 2;

Expand Down