forked from esphome/esphome
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Support for Sensirion SFA30 sensor (esphome#5519)
Co-authored-by: Jesse Hills <[email protected]>
- Loading branch information
1 parent
7ddcdab
commit 6143099
Showing
6 changed files
with
223 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CODEOWNERS = ["@ghsensdev"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import esphome.codegen as cg | ||
import esphome.config_validation as cv | ||
from esphome.components import i2c, sensor, sensirion_common | ||
|
||
from esphome.const import ( | ||
CONF_ID, | ||
CONF_FORMALDEHYDE, | ||
CONF_HUMIDITY, | ||
CONF_TEMPERATURE, | ||
DEVICE_CLASS_GAS, | ||
DEVICE_CLASS_HUMIDITY, | ||
DEVICE_CLASS_TEMPERATURE, | ||
ICON_RADIATOR, | ||
ICON_WATER_PERCENT, | ||
ICON_THERMOMETER, | ||
STATE_CLASS_MEASUREMENT, | ||
UNIT_PARTS_PER_BILLION, | ||
UNIT_PERCENT, | ||
UNIT_CELSIUS, | ||
) | ||
|
||
CODEOWNERS = ["@ghsensdev"] | ||
DEPENDENCIES = ["i2c"] | ||
AUTO_LOAD = ["sensirion_common"] | ||
|
||
sfa30_ns = cg.esphome_ns.namespace("sfa30") | ||
|
||
SFA30Component = sfa30_ns.class_( | ||
"SFA30Component", cg.PollingComponent, sensirion_common.SensirionI2CDevice | ||
) | ||
|
||
CONFIG_SCHEMA = ( | ||
cv.Schema( | ||
{ | ||
cv.GenerateID(): cv.declare_id(SFA30Component), | ||
cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema( | ||
unit_of_measurement=UNIT_PARTS_PER_BILLION, | ||
icon=ICON_RADIATOR, | ||
accuracy_decimals=1, | ||
device_class=DEVICE_CLASS_GAS, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( | ||
unit_of_measurement=UNIT_PERCENT, | ||
icon=ICON_WATER_PERCENT, | ||
accuracy_decimals=2, | ||
device_class=DEVICE_CLASS_HUMIDITY, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||
unit_of_measurement=UNIT_CELSIUS, | ||
icon=ICON_THERMOMETER, | ||
accuracy_decimals=2, | ||
device_class=DEVICE_CLASS_TEMPERATURE, | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
} | ||
) | ||
.extend(cv.polling_component_schema("60s")) | ||
.extend(i2c.i2c_device_schema(0x5D)) | ||
) | ||
|
||
SENSOR_MAP = { | ||
CONF_FORMALDEHYDE: "set_formaldehyde_sensor", | ||
CONF_HUMIDITY: "set_humidity_sensor", | ||
CONF_TEMPERATURE: "set_temperature_sensor", | ||
} | ||
|
||
|
||
async def to_code(config): | ||
var = cg.new_Pvariable(config[CONF_ID]) | ||
await cg.register_component(var, config) | ||
await i2c.register_i2c_device(var, config) | ||
|
||
for key, funcName in SENSOR_MAP.items(): | ||
if sensor_config := config.get(key): | ||
sens = await sensor.new_sensor(sensor_config) | ||
cg.add(getattr(var, funcName)(sens)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
#include "sfa30.h" | ||
#include "esphome/core/log.h" | ||
|
||
namespace esphome { | ||
namespace sfa30 { | ||
|
||
static const char *const TAG = "sfa30"; | ||
|
||
static const uint16_t SFA30_CMD_GET_DEVICE_MARKING = 0xD060; | ||
static const uint16_t SFA30_CMD_START_CONTINUOUS_MEASUREMENTS = 0x0006; | ||
static const uint16_t SFA30_CMD_READ_MEASUREMENT = 0x0327; | ||
|
||
void SFA30Component::setup() { | ||
ESP_LOGCONFIG(TAG, "Setting up sfa30..."); | ||
|
||
// Serial Number identification | ||
uint16_t raw_device_marking[16]; | ||
if (!this->get_register(SFA30_CMD_GET_DEVICE_MARKING, raw_device_marking, 16, 5)) { | ||
ESP_LOGE(TAG, "Failed to read device marking"); | ||
this->error_code_ = DEVICE_MARKING_READ_FAILED; | ||
this->mark_failed(); | ||
return; | ||
} | ||
|
||
for (size_t i = 0; i < 16; i++) { | ||
this->device_marking_[i * 2] = static_cast<char>(raw_device_marking[i] >> 8); | ||
this->device_marking_[i * 2 + 1] = static_cast<char>(raw_device_marking[i] & 0xFF); | ||
} | ||
ESP_LOGD(TAG, "Device Marking: '%s'", this->device_marking_); | ||
|
||
if (!this->write_command(SFA30_CMD_START_CONTINUOUS_MEASUREMENTS)) { | ||
ESP_LOGE(TAG, "Error starting measurements."); | ||
this->error_code_ = MEASUREMENT_INIT_FAILED; | ||
this->mark_failed(); | ||
return; | ||
} | ||
|
||
ESP_LOGD(TAG, "Sensor initialized"); | ||
} | ||
|
||
void SFA30Component::dump_config() { | ||
ESP_LOGCONFIG(TAG, "sfa30:"); | ||
LOG_I2C_DEVICE(this); | ||
if (this->is_failed()) { | ||
switch (this->error_code_) { | ||
case DEVICE_MARKING_READ_FAILED: | ||
ESP_LOGW(TAG, "Unable to read device marking!"); | ||
break; | ||
case MEASUREMENT_INIT_FAILED: | ||
ESP_LOGW(TAG, "Measurement initialization failed!"); | ||
break; | ||
default: | ||
ESP_LOGW(TAG, "Unknown setup error!"); | ||
break; | ||
} | ||
} | ||
LOG_UPDATE_INTERVAL(this); | ||
ESP_LOGCONFIG(TAG, " Device Marking: '%s'", this->device_marking_); | ||
LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_); | ||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); | ||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); | ||
} | ||
|
||
void SFA30Component::update() { | ||
if (!this->write_command(SFA30_CMD_READ_MEASUREMENT)) { | ||
ESP_LOGW(TAG, "Error reading measurement!"); | ||
this->status_set_warning(); | ||
return; | ||
} | ||
|
||
this->set_timeout(5, [this]() { | ||
uint16_t raw_data[3]; | ||
if (!this->read_data(raw_data, 3)) { | ||
ESP_LOGW(TAG, "Error reading measurement data!"); | ||
this->status_set_warning(); | ||
return; | ||
} | ||
|
||
if (this->formaldehyde_sensor_ != nullptr) { | ||
const float formaldehyde = raw_data[0] / 5.0f; | ||
this->formaldehyde_sensor_->publish_state(formaldehyde); | ||
} | ||
|
||
if (this->humidity_sensor_ != nullptr) { | ||
const float humidity = raw_data[1] / 100.0f; | ||
this->humidity_sensor_->publish_state(humidity); | ||
} | ||
|
||
if (this->temperature_sensor_ != nullptr) { | ||
const float temperature = raw_data[2] / 200.0f; | ||
this->temperature_sensor_->publish_state(temperature); | ||
} | ||
|
||
this->status_clear_warning(); | ||
}); | ||
} | ||
|
||
} // namespace sfa30 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#pragma once | ||
|
||
#include "esphome/core/component.h" | ||
#include "esphome/components/sensor/sensor.h" | ||
#include "esphome/components/sensirion_common/i2c_sensirion.h" | ||
|
||
namespace esphome { | ||
namespace sfa30 { | ||
|
||
class SFA30Component : public PollingComponent, public sensirion_common::SensirionI2CDevice { | ||
enum ErrorCode { DEVICE_MARKING_READ_FAILED, MEASUREMENT_INIT_FAILED, UNKNOWN }; | ||
|
||
public: | ||
float get_setup_priority() const override { return setup_priority::DATA; } | ||
void setup() override; | ||
void dump_config() override; | ||
void update() override; | ||
|
||
void set_formaldehyde_sensor(sensor::Sensor *formaldehyde) { this->formaldehyde_sensor_ = formaldehyde; } | ||
void set_humidity_sensor(sensor::Sensor *humidity) { this->humidity_sensor_ = humidity; } | ||
void set_temperature_sensor(sensor::Sensor *temperature) { this->temperature_sensor_ = temperature; } | ||
|
||
protected: | ||
char device_marking_[32] = {0}; | ||
|
||
ErrorCode error_code_{UNKNOWN}; | ||
|
||
sensor::Sensor *formaldehyde_sensor_{nullptr}; | ||
sensor::Sensor *humidity_sensor_{nullptr}; | ||
sensor::Sensor *temperature_sensor_{nullptr}; | ||
}; | ||
|
||
} // namespace sfa30 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters