diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index 55ab07879e..195dfef5f6 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -8,21 +8,25 @@ static const char *TAG = "scd30"; static const uint16_t SCD30_CMD_GET_FIRMWARE_VERSION = 0xd100; static const uint16_t SCD30_CMD_START_CONTINUOUS_MEASUREMENTS = 0x0010; +static const uint16_t SCD30_CMD_ALTITUDE_COMPENSATION = 0x5102; +static const uint16_t SCD30_CMD_AUTOMATIC_SELF_CALIBRATION = 0x5306; static const uint16_t SCD30_CMD_GET_DATA_READY_STATUS = 0x0202; static const uint16_t SCD30_CMD_READ_MEASUREMENT = 0x0300; /// Commands for future use static const uint16_t SCD30_CMD_STOP_MEASUREMENTS = 0x0104; static const uint16_t SCD30_CMD_MEASUREMENT_INTERVAL = 0x4600; -static const uint16_t SCD30_CMD_AUTOMATIC_SELF_CALIBRATION = 0x5306; static const uint16_t SCD30_CMD_FORCED_CALIBRATION = 0x5204; static const uint16_t SCD30_CMD_TEMPERATURE_OFFSET = 0x5403; -static const uint16_t SCD30_CMD_ALTITUDE_COMPENSATION = 0x5102; static const uint16_t SCD30_CMD_SOFT_RESET = 0xD304; void SCD30Component::setup() { ESP_LOGCONFIG(TAG, "Setting up scd30..."); +#ifdef ARDUINO_ARCH_ESP8266 + Wire.setClockStretchLimit(150000); +#endif + /// Firmware version identification if (!this->write_command_(SCD30_CMD_GET_FIRMWARE_VERSION)) { this->error_code_ = COMMUNICATION_FAILED; @@ -40,12 +44,29 @@ void SCD30Component::setup() { uint16_t(raw_firmware_version[0] & 0xFF)); /// Sensor initialization - if (!this->write_command_(SCD30_CMD_START_CONTINUOUS_MEASUREMENTS)) { + if (!this->write_command_(SCD30_CMD_START_CONTINUOUS_MEASUREMENTS, 0)) { ESP_LOGE(TAG, "Sensor SCD30 error starting continuous measurements."); this->error_code_ = MEASUREMENT_INIT_FAILED; this->mark_failed(); return; } + + // 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_)) { + ESP_LOGE(TAG, "Sensor SCD30 error starting continuous measurements."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + } + + if (!this->write_command_(SCD30_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) { + ESP_LOGE(TAG, "Sensor SCD30 error setting automatic self calibration."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } } void SCD30Component::dump_config() { @@ -67,6 +88,12 @@ void SCD30Component::dump_config() { break; } } + if (this->altitude_compensation_ == 0xFFFF) { + ESP_LOGCONFIG(TAG, " Altitude compensation: OFF"); + } else { + ESP_LOGCONFIG(TAG, " Altitude compensation: %dm", this->altitude_compensation_); + } + ESP_LOGCONFIG(TAG, " Automatic self calibration: %s", ONOFF(this->enable_asc_)); LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "CO2", this->co2_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); @@ -130,6 +157,16 @@ bool SCD30Component::write_command_(uint16_t command) { return this->write_byte(command >> 8, command & 0xFF); } +bool SCD30Component::write_command_(uint16_t command, uint16_t data) { + uint8_t raw[5]; + raw[0] = command >> 8; + raw[1] = command & 0xFF; + raw[2] = data >> 8; + raw[3] = data & 0xFF; + raw[4] = sht_crc_(raw[2], raw[3]); + return this->write_bytes_raw(raw, 5); +} + uint8_t SCD30Component::sht_crc_(uint8_t data1, uint8_t data2) { uint8_t bit; uint8_t crc = 0xFF; diff --git a/esphome/components/scd30/scd30.h b/esphome/components/scd30/scd30.h index 999e66414d..2c4ee51f8a 100644 --- a/esphome/components/scd30/scd30.h +++ b/esphome/components/scd30/scd30.h @@ -13,6 +13,8 @@ class SCD30Component : public PollingComponent, public i2c::I2CDevice { void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; } void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } void set_temperature_sensor(sensor::Sensor *temperature) { temperature_sensor_ = temperature; } + void set_automatic_self_calibration(bool asc) { enable_asc_ = asc; } + void set_altitude_compensation(uint16_t altitude) { altitude_compensation_ = altitude; } void setup() override; void update() override; @@ -21,6 +23,7 @@ class SCD30Component : public PollingComponent, public i2c::I2CDevice { protected: bool write_command_(uint16_t command); + 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); @@ -30,6 +33,8 @@ class SCD30Component : public PollingComponent, public i2c::I2CDevice { MEASUREMENT_INIT_FAILED, UNKNOWN } error_code_{UNKNOWN}; + bool enable_asc_{true}; + uint16_t altitude_compensation_{0xFFFF}; sensor::Sensor *co2_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index 7a60725276..b3de1b214a 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -1,3 +1,4 @@ +import re import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor @@ -10,12 +11,24 @@ DEPENDENCIES = ['i2c'] scd30_ns = cg.esphome_ns.namespace('scd30') SCD30Component = scd30_ns.class_('SCD30Component', cg.PollingComponent, i2c.I2CDevice) +CONF_AUTOMATIC_SELF_CALIBRATION = 'automatic_self_calibration' +CONF_ALTITUDE_COMPENSATION = 'altitude_compensation' + + +def remove_altitude_suffix(value): + return re.sub(r"\s*(?:m(?:\s+a\.s\.l)?)|(?:MAM?SL)$", '', value) + + CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SCD30Component), cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0), cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Optional(CONF_AUTOMATIC_SELF_CALIBRATION, default=True): cv.boolean, + cv.Optional(CONF_ALTITUDE_COMPENSATION): cv.All(remove_altitude_suffix, + cv.int_range(min=0, max=0xFFFF, + max_included=False)), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x61)) @@ -24,6 +37,10 @@ def to_code(config): yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) + cg.add(var.set_automatic_self_calibration(config[CONF_AUTOMATIC_SELF_CALIBRATION])) + if CONF_ALTITUDE_COMPENSATION in config: + cg.add(var.set_altitude_compensation(config[CONF_ALTITUDE_COMPENSATION])) + if CONF_CO2 in config: sens = yield sensor.new_sensor(config[CONF_CO2]) cg.add(var.set_co2_sensor(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 0533063fb8..93e6638330 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -584,6 +584,8 @@ sensor: name: "Living Room Humidity 9" address: 0x61 update_interval: 15s + automatic_self_calibration: true + altitude_compensation: 10m - platform: sgp30 eco2: name: "Workshop eCO2"