Add pressure compensation during runtime (#2493)

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
Martin 2021-10-14 11:24:57 +02:00 committed by GitHub
parent 4896f870f0
commit 8823024509
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 54 deletions

View file

@ -1,4 +1,5 @@
#include "scd4x.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
@ -38,6 +39,7 @@ void SCD4XComponent::setup() {
return;
}
uint32_t stop_measurement_delay = 0;
// In order to query the device periodic measurement must be ceased
if (raw_read_status[0]) {
ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement");
@ -46,68 +48,72 @@ void SCD4XComponent::setup() {
this->mark_failed();
return;
}
// According to the SCD4x datasheet the sensor will only respond to other commands after waiting 500 ms after
// issuing the stop_periodic_measurement command
stop_measurement_delay = 500;
}
this->set_timeout(stop_measurement_delay, [this]() {
if (!this->write_command_(SCD4X_CMD_GET_SERIAL_NUMBER)) {
ESP_LOGE(TAG, "Failed to write get serial command");
this->error_code_ = COMMUNICATION_FAILED;
this->mark_failed();
return;
}
if (!this->write_command_(SCD4X_CMD_GET_SERIAL_NUMBER)) {
ESP_LOGE(TAG, "Failed to write get serial command");
this->error_code_ = COMMUNICATION_FAILED;
this->mark_failed();
return;
}
uint16_t raw_serial_number[3];
if (!this->read_data_(raw_serial_number, 3)) {
ESP_LOGE(TAG, "Failed to read serial number");
this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED;
this->mark_failed();
return;
}
ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", (uint16_t(raw_serial_number[0]) >> 8),
uint16_t(raw_serial_number[0] & 0xFF), (uint16_t(raw_serial_number[1]) >> 8));
uint16_t raw_serial_number[3];
if (!this->read_data_(raw_serial_number, 3)) {
ESP_LOGE(TAG, "Failed to read serial number");
this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED;
this->mark_failed();
return;
}
ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", (uint16_t(raw_serial_number[0]) >> 8),
uint16_t(raw_serial_number[0] & 0xFF), (uint16_t(raw_serial_number[1]) >> 8));
if (!this->write_command_(SCD4X_CMD_TEMPERATURE_OFFSET,
(uint16_t)(temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) {
ESP_LOGE(TAG, "Error setting temperature offset.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
// If pressure compensation available use it
// else use altitude
if (ambient_pressure_compensation_) {
if (!this->write_command_(SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION, ambient_pressure_compensation_)) {
ESP_LOGE(TAG, "Error setting ambient pressure compensation.");
if (!this->write_command_(SCD4X_CMD_TEMPERATURE_OFFSET,
(uint16_t)(temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) {
ESP_LOGE(TAG, "Error setting temperature offset.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
} else {
if (!this->write_command_(SCD4X_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) {
ESP_LOGE(TAG, "Error setting altitude compensation.");
// If pressure compensation available use it
// else use altitude
if (ambient_pressure_compensation_) {
if (!this->update_ambient_pressure_compensation_(ambient_pressure_)) {
ESP_LOGE(TAG, "Error setting ambient pressure compensation.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
} else {
if (!this->write_command_(SCD4X_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) {
ESP_LOGE(TAG, "Error setting altitude compensation.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
}
if (!this->write_command_(SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) {
ESP_LOGE(TAG, "Error setting automatic self calibration.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
}
if (!this->write_command_(SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) {
ESP_LOGE(TAG, "Error setting automatic self calibration.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
// Finally start sensor measurements
if (!this->write_command_(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) {
ESP_LOGE(TAG, "Error starting continuous measurements.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
// Finally start sensor measurements
if (!this->write_command_(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) {
ESP_LOGE(TAG, "Error starting continuous measurements.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
initialized_ = true;
ESP_LOGD(TAG, "Sensor initialized");
initialized_ = true;
ESP_LOGD(TAG, "Sensor initialized");
});
});
}
@ -150,6 +156,13 @@ void SCD4XComponent::update() {
return;
}
if (this->ambient_pressure_source_ != nullptr) {
float pressure = this->ambient_pressure_source_->state / 1000.0f;
if (!std::isnan(pressure)) {
set_ambient_pressure_compensation(this->ambient_pressure_source_->state / 1000.0f);
}
}
// Check if data is ready
if (!this->write_command_(SCD4X_CMD_GET_DATA_READY_STATUS)) {
this->status_set_warning();
@ -191,6 +204,28 @@ void SCD4XComponent::update() {
this->status_clear_warning();
}
// Note pressure in bar here. Convert to hPa
void SCD4XComponent::set_ambient_pressure_compensation(float pressure_in_bar) {
ambient_pressure_compensation_ = true;
uint16_t new_ambient_pressure = (uint16_t)(pressure_in_bar * 1000);
// remove millibar from comparison to avoid frequent updates +/- 10 millibar doesn't matter
if (initialized_ && (new_ambient_pressure / 10 != ambient_pressure_ / 10)) {
update_ambient_pressure_compensation_(new_ambient_pressure);
ambient_pressure_ = new_ambient_pressure;
} else {
ESP_LOGD(TAG, "ambient pressure compensation skipped - no change required");
}
}
bool SCD4XComponent::update_ambient_pressure_compensation_(uint16_t pressure_in_hpa) {
if (this->write_command_(SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION, pressure_in_hpa)) {
ESP_LOGD(TAG, "setting ambient pressure compensation to %d hPa", pressure_in_hpa);
return true;
} else {
ESP_LOGE(TAG, "Error setting ambient pressure compensation.");
return false;
}
}
uint8_t SCD4XComponent::sht_crc_(uint8_t data1, uint8_t data2) {
uint8_t bit;

View file

@ -18,10 +18,8 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice {
void set_automatic_self_calibration(bool asc) { enable_asc_ = asc; }
void set_altitude_compensation(uint16_t altitude) { altitude_compensation_ = altitude; }
void set_ambient_pressure_compensation(float pressure) {
ambient_pressure_compensation_ = true;
ambient_pressure_ = (uint16_t)(pressure * 1000);
}
void set_ambient_pressure_compensation(float pressure_in_bar);
void set_ambient_pressure_source(sensor::Sensor *pressure) { ambient_pressure_source_ = pressure; }
void set_temperature_offset(float offset) { temperature_offset_ = offset; };
void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; }
@ -33,6 +31,7 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice {
bool read_data_(uint16_t *data, uint8_t len);
bool write_command_(uint16_t command);
bool write_command_(uint16_t command, uint16_t data);
bool update_ambient_pressure_compensation_(uint16_t pressure_in_hpa);
ERRORCODE error_code_;
@ -47,6 +46,8 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice {
sensor::Sensor *co2_sensor_{nullptr};
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
// used for compensation
sensor::Sensor *ambient_pressure_source_{nullptr};
};
} // namespace scd4x

View file

@ -29,6 +29,7 @@ CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration"
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation"
CONF_TEMPERATURE_OFFSET = "temperature_offset"
CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE = "ambient_pressure_compensation_source"
CONFIG_SCHEMA = (
cv.Schema(
@ -62,6 +63,9 @@ CONFIG_SCHEMA = (
),
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION): cv.pressure,
cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.temperature,
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE): cv.use_id(
sensor.Sensor
),
}
)
.extend(cv.polling_component_schema("60s"))
@ -92,7 +96,10 @@ async def to_code(config):
cg.add(getattr(var, funcName)(config[key]))
for key, funcName in SENSOR_MAP.items():
if key in config:
sens = await sensor.new_sensor(config[key])
cg.add(getattr(var, funcName)(sens))
if CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE in config:
sens = await cg.get_variable(config[CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE])
cg.add(var.set_ambient_pressure_source(sens))