mirror of
https://github.com/esphome/esphome.git
synced 2025-01-08 22:01:44 +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
|
// According ESP32 clock stretching is typically 30ms and up to 150ms "due to
|
||||||
// internal calibration processes". The I2C peripheral only supports 13ms (at
|
// internal calibration processes". The I2C peripheral only supports 13ms (at
|
||||||
// least when running at 80MHz).
|
// 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
|
// calls. It also seems that delays in between calls makes them
|
||||||
// disappear/shorter. Hence work around with delays for ESP32.
|
// disappear/shorter. Hence work around with delays for ESP32.
|
||||||
//
|
//
|
||||||
|
@ -69,6 +69,16 @@ void SCD30Component::setup() {
|
||||||
delay(30);
|
delay(30);
|
||||||
#endif
|
#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
|
// 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->altitude_compensation_ != 0xFFFF) {
|
||||||
if (!this->write_command_(SCD30_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) {
|
if (!this->write_command_(SCD30_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) {
|
||||||
|
@ -99,6 +109,12 @@ void SCD30Component::setup() {
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
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() {
|
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, " Automatic self calibration: %s", ONOFF(this->enable_asc_));
|
||||||
ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_compensation_);
|
ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_compensation_);
|
||||||
ESP_LOGCONFIG(TAG, " Temperature offset: %.2f °C", this->temperature_offset_);
|
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(" ", "CO2", this->co2_sensor_);
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCD30Component::update() {
|
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];
|
uint16_t raw_read_status[1];
|
||||||
if (!this->read_data_(raw_read_status, 1) || raw_read_status[0] == 0x00) {
|
if (!this->read_data_(raw_read_status, 1) || raw_read_status[0] == 0x00) {
|
||||||
this->status_set_warning();
|
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) {
|
bool SCD30Component::write_command_(uint16_t command) {
|
||||||
// Warning ugly, trick the I2Ccomponent base by setting register to the first 8 bit.
|
// Warning ugly, trick the I2Ccomponent base by setting register to the first 8 bit.
|
||||||
return this->write_byte(command >> 8, command & 0xFF);
|
return this->write_byte(command >> 8, command & 0xFF);
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace esphome {
|
||||||
namespace scd30 {
|
namespace scd30 {
|
||||||
|
|
||||||
/// This class implements support for the Sensirion scd30 i2c GAS (VOC and CO2eq) sensors.
|
/// 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:
|
public:
|
||||||
void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; }
|
void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; }
|
||||||
void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
|
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);
|
ambient_pressure_compensation_ = (uint16_t)(pressure * 1000);
|
||||||
}
|
}
|
||||||
void set_temperature_offset(float offset) { temperature_offset_ = offset; }
|
void set_temperature_offset(float offset) { temperature_offset_ = offset; }
|
||||||
|
void set_update_interval(uint16_t interval) { update_interval_ = interval; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void update() override;
|
void update();
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
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 write_command_(uint16_t command, uint16_t data);
|
||||||
bool read_data_(uint16_t *data, uint8_t len);
|
bool read_data_(uint16_t *data, uint8_t len);
|
||||||
uint8_t sht_crc_(uint8_t data1, uint8_t data2);
|
uint8_t sht_crc_(uint8_t data1, uint8_t data2);
|
||||||
|
bool is_data_ready_();
|
||||||
|
|
||||||
enum ErrorCode {
|
enum ErrorCode {
|
||||||
COMMUNICATION_FAILED,
|
COMMUNICATION_FAILED,
|
||||||
|
@ -41,6 +43,7 @@ class SCD30Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
uint16_t altitude_compensation_{0xFFFF};
|
uint16_t altitude_compensation_{0xFFFF};
|
||||||
uint16_t ambient_pressure_compensation_{0x0000};
|
uint16_t ambient_pressure_compensation_{0x0000};
|
||||||
float temperature_offset_{0.0};
|
float temperature_offset_{0.0};
|
||||||
|
uint16_t update_interval_{0xFFFF};
|
||||||
|
|
||||||
sensor::Sensor *co2_sensor_{nullptr};
|
sensor::Sensor *co2_sensor_{nullptr};
|
||||||
sensor::Sensor *humidity_sensor_{nullptr};
|
sensor::Sensor *humidity_sensor_{nullptr};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from esphome import core
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
|
@ -6,6 +7,7 @@ from esphome.const import (
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
CONF_CO2,
|
CONF_CO2,
|
||||||
|
CONF_UPDATE_INTERVAL,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
@ -18,7 +20,7 @@ from esphome.const import (
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
scd30_ns = cg.esphome_ns.namespace("scd30")
|
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_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration"
|
||||||
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
|
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
|
||||||
|
@ -55,9 +57,15 @@ CONFIG_SCHEMA = (
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure,
|
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure,
|
||||||
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.temperature,
|
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))
|
.extend(i2c.i2c_device_schema(0x61))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -81,6 +89,8 @@ async def to_code(config):
|
||||||
if CONF_TEMPERATURE_OFFSET in config:
|
if CONF_TEMPERATURE_OFFSET in config:
|
||||||
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
|
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:
|
if CONF_CO2 in config:
|
||||||
sens = await sensor.new_sensor(config[CONF_CO2])
|
sens = await sensor.new_sensor(config[CONF_CO2])
|
||||||
cg.add(var.set_co2_sensor(sens))
|
cg.add(var.set_co2_sensor(sens))
|
||||||
|
|
Loading…
Reference in a new issue