convert SCD30 into Component, polls dataready register (#2308)

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Geoffrey Van Landeghem 2021-10-31 03:29:22 +01:00 committed by GitHub
parent b12c7432e0
commit 7eee3cdc7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 12 deletions

View file

@ -60,7 +60,7 @@ void SCD30Component::setup() {
// According ESP32 clock stretching is typically 30ms and up to 150ms "due to
// internal calibration processes". The I2C peripheral only supports 13ms (at
// least when running at 80MHz).
// In practise it seems that clock stretching occurs during this calibration
// In practice it seems that clock stretching occurs during this calibration
// calls. It also seems that delays in between calls makes them
// disappear/shorter. Hence work around with delays for ESP32.
//
@ -69,6 +69,16 @@ void SCD30Component::setup() {
delay(30);
#endif
if (!this->write_command_(SCD30_CMD_MEASUREMENT_INTERVAL, update_interval_)) {
ESP_LOGE(TAG, "Sensor SCD30 error setting update interval.");
this->error_code_ = MEASUREMENT_INIT_FAILED;
this->mark_failed();
return;
}
#ifdef USE_ESP32
delay(30);
#endif
// The start measurement command disables the altitude compensation, if any, so we only set it if it's turned on
if (this->altitude_compensation_ != 0xFFFF) {
if (!this->write_command_(SCD30_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) {
@ -99,6 +109,12 @@ void SCD30Component::setup() {
this->mark_failed();
return;
}
// check each 500ms if data is ready, and read it in that case
this->set_interval("status-check", 500, [this]() {
if (this->is_data_ready_())
this->update();
});
}
void SCD30Component::dump_config() {
@ -128,19 +144,13 @@ void SCD30Component::dump_config() {
ESP_LOGCONFIG(TAG, " Automatic self calibration: %s", ONOFF(this->enable_asc_));
ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_compensation_);
ESP_LOGCONFIG(TAG, " Temperature offset: %.2f °C", this->temperature_offset_);
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG, " Update interval: %ds", this->update_interval_);
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
void SCD30Component::update() {
/// Check if measurement is ready before reading the value
if (!this->write_command_(SCD30_CMD_GET_DATA_READY_STATUS)) {
this->status_set_warning();
return;
}
uint16_t raw_read_status[1];
if (!this->read_data_(raw_read_status, 1) || raw_read_status[0] == 0x00) {
this->status_set_warning();
@ -186,6 +196,17 @@ void SCD30Component::update() {
});
}
bool SCD30Component::is_data_ready_() {
if (!this->write_command_(SCD30_CMD_GET_DATA_READY_STATUS)) {
return false;
}
uint16_t is_data_ready;
if (!this->read_data_(&is_data_ready, 1)) {
return false;
}
return is_data_ready == 1;
}
bool SCD30Component::write_command_(uint16_t command) {
// Warning ugly, trick the I2Ccomponent base by setting register to the first 8 bit.
return this->write_byte(command >> 8, command & 0xFF);

View file

@ -8,7 +8,7 @@ namespace esphome {
namespace scd30 {
/// This class implements support for the Sensirion scd30 i2c GAS (VOC and CO2eq) sensors.
class SCD30Component : public PollingComponent, public i2c::I2CDevice {
class SCD30Component : public Component, public i2c::I2CDevice {
public:
void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; }
void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
@ -19,9 +19,10 @@ class SCD30Component : public PollingComponent, public i2c::I2CDevice {
ambient_pressure_compensation_ = (uint16_t)(pressure * 1000);
}
void set_temperature_offset(float offset) { temperature_offset_ = offset; }
void set_update_interval(uint16_t interval) { update_interval_ = interval; }
void setup() override;
void update() override;
void update();
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
@ -30,6 +31,7 @@ class SCD30Component : public PollingComponent, public i2c::I2CDevice {
bool write_command_(uint16_t command, uint16_t data);
bool read_data_(uint16_t *data, uint8_t len);
uint8_t sht_crc_(uint8_t data1, uint8_t data2);
bool is_data_ready_();
enum ErrorCode {
COMMUNICATION_FAILED,
@ -41,6 +43,7 @@ class SCD30Component : public PollingComponent, public i2c::I2CDevice {
uint16_t altitude_compensation_{0xFFFF};
uint16_t ambient_pressure_compensation_{0x0000};
float temperature_offset_{0.0};
uint16_t update_interval_{0xFFFF};
sensor::Sensor *co2_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};

View file

@ -1,3 +1,4 @@
from esphome import core
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
@ -6,6 +7,7 @@ from esphome.const import (
CONF_HUMIDITY,
CONF_TEMPERATURE,
CONF_CO2,
CONF_UPDATE_INTERVAL,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
@ -18,7 +20,7 @@ from esphome.const import (
DEPENDENCIES = ["i2c"]
scd30_ns = cg.esphome_ns.namespace("scd30")
SCD30Component = scd30_ns.class_("SCD30Component", cg.PollingComponent, i2c.I2CDevice)
SCD30Component = scd30_ns.class_("SCD30Component", cg.Component, i2c.I2CDevice)
CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration"
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
@ -55,9 +57,15 @@ CONFIG_SCHEMA = (
),
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure,
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.temperature,
cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.All(
cv.positive_time_period_seconds,
cv.Range(
min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800)
),
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x61))
)
@ -81,6 +89,8 @@ async def to_code(config):
if CONF_TEMPERATURE_OFFSET in config:
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
if CONF_CO2 in config:
sens = await sensor.new_sensor(config[CONF_CO2])
cg.add(var.set_co2_sensor(sens))