mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 15:38:11 +01:00
Add pressure compensation during runtime (#2493)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
parent
ec683fc227
commit
15b5ea43a7
3 changed files with 97 additions and 54 deletions
|
@ -1,4 +1,5 @@
|
||||||
#include "scd4x.h"
|
#include "scd4x.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
@ -38,6 +39,7 @@ void SCD4XComponent::setup() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t stop_measurement_delay = 0;
|
||||||
// In order to query the device periodic measurement must be ceased
|
// In order to query the device periodic measurement must be ceased
|
||||||
if (raw_read_status[0]) {
|
if (raw_read_status[0]) {
|
||||||
ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement");
|
ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement");
|
||||||
|
@ -46,68 +48,72 @@ void SCD4XComponent::setup() {
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
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)) {
|
uint16_t raw_serial_number[3];
|
||||||
ESP_LOGE(TAG, "Failed to write get serial command");
|
if (!this->read_data_(raw_serial_number, 3)) {
|
||||||
this->error_code_ = COMMUNICATION_FAILED;
|
ESP_LOGE(TAG, "Failed to read serial number");
|
||||||
this->mark_failed();
|
this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED;
|
||||||
return;
|
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->write_command_(SCD4X_CMD_TEMPERATURE_OFFSET,
|
||||||
if (!this->read_data_(raw_serial_number, 3)) {
|
(uint16_t)(temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) {
|
||||||
ESP_LOGE(TAG, "Failed to read serial number");
|
ESP_LOGE(TAG, "Error setting temperature offset.");
|
||||||
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.");
|
|
||||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!this->write_command_(SCD4X_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) {
|
// If pressure compensation available use it
|
||||||
ESP_LOGE(TAG, "Error setting altitude compensation.");
|
// 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->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->write_command_(SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) {
|
// Finally start sensor measurements
|
||||||
ESP_LOGE(TAG, "Error setting automatic self calibration.");
|
if (!this->write_command_(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) {
|
||||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
ESP_LOGE(TAG, "Error starting continuous measurements.");
|
||||||
this->mark_failed();
|
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
||||||
return;
|
this->mark_failed();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Finally start sensor measurements
|
initialized_ = true;
|
||||||
if (!this->write_command_(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) {
|
ESP_LOGD(TAG, "Sensor initialized");
|
||||||
ESP_LOGE(TAG, "Error starting continuous measurements.");
|
});
|
||||||
this->error_code_ = MEASUREMENT_INIT_FAILED;
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized_ = true;
|
|
||||||
ESP_LOGD(TAG, "Sensor initialized");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +156,13 @@ void SCD4XComponent::update() {
|
||||||
return;
|
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
|
// Check if data is ready
|
||||||
if (!this->write_command_(SCD4X_CMD_GET_DATA_READY_STATUS)) {
|
if (!this->write_command_(SCD4X_CMD_GET_DATA_READY_STATUS)) {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
@ -191,6 +204,28 @@ void SCD4XComponent::update() {
|
||||||
|
|
||||||
this->status_clear_warning();
|
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 SCD4XComponent::sht_crc_(uint8_t data1, uint8_t data2) {
|
||||||
uint8_t bit;
|
uint8_t bit;
|
||||||
|
|
|
@ -18,10 +18,8 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
|
||||||
void set_automatic_self_calibration(bool asc) { enable_asc_ = asc; }
|
void set_automatic_self_calibration(bool asc) { enable_asc_ = asc; }
|
||||||
void set_altitude_compensation(uint16_t altitude) { altitude_compensation_ = altitude; }
|
void set_altitude_compensation(uint16_t altitude) { altitude_compensation_ = altitude; }
|
||||||
void set_ambient_pressure_compensation(float pressure) {
|
void set_ambient_pressure_compensation(float pressure_in_bar);
|
||||||
ambient_pressure_compensation_ = true;
|
void set_ambient_pressure_source(sensor::Sensor *pressure) { ambient_pressure_source_ = pressure; }
|
||||||
ambient_pressure_ = (uint16_t)(pressure * 1000);
|
|
||||||
}
|
|
||||||
void set_temperature_offset(float offset) { temperature_offset_ = offset; };
|
void set_temperature_offset(float offset) { temperature_offset_ = offset; };
|
||||||
|
|
||||||
void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; }
|
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 read_data_(uint16_t *data, uint8_t len);
|
||||||
bool write_command_(uint16_t command);
|
bool write_command_(uint16_t command);
|
||||||
bool write_command_(uint16_t command, uint16_t data);
|
bool write_command_(uint16_t command, uint16_t data);
|
||||||
|
bool update_ambient_pressure_compensation_(uint16_t pressure_in_hpa);
|
||||||
|
|
||||||
ERRORCODE error_code_;
|
ERRORCODE error_code_;
|
||||||
|
|
||||||
|
@ -47,6 +46,8 @@ class SCD4XComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
sensor::Sensor *co2_sensor_{nullptr};
|
sensor::Sensor *co2_sensor_{nullptr};
|
||||||
sensor::Sensor *temperature_sensor_{nullptr};
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
sensor::Sensor *humidity_sensor_{nullptr};
|
sensor::Sensor *humidity_sensor_{nullptr};
|
||||||
|
// used for compensation
|
||||||
|
sensor::Sensor *ambient_pressure_source_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace scd4x
|
} // namespace scd4x
|
||||||
|
|
|
@ -29,6 +29,7 @@ CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration"
|
||||||
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
|
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
|
||||||
CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation"
|
CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation"
|
||||||
CONF_TEMPERATURE_OFFSET = "temperature_offset"
|
CONF_TEMPERATURE_OFFSET = "temperature_offset"
|
||||||
|
CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE = "ambient_pressure_compensation_source"
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
|
@ -62,6 +63,9 @@ CONFIG_SCHEMA = (
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION): cv.pressure,
|
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION): cv.pressure,
|
||||||
cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.temperature,
|
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"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
@ -92,7 +96,10 @@ async def to_code(config):
|
||||||
cg.add(getattr(var, funcName)(config[key]))
|
cg.add(getattr(var, funcName)(config[key]))
|
||||||
|
|
||||||
for key, funcName in SENSOR_MAP.items():
|
for key, funcName in SENSOR_MAP.items():
|
||||||
|
|
||||||
if key in config:
|
if key in config:
|
||||||
sens = await sensor.new_sensor(config[key])
|
sens = await sensor.new_sensor(config[key])
|
||||||
cg.add(getattr(var, funcName)(sens))
|
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))
|
||||||
|
|
Loading…
Reference in a new issue