mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
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:
parent
b12c7432e0
commit
7eee3cdc7f
3 changed files with 46 additions and 12 deletions
|
@ -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);
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue