mirror of
https://github.com/esphome/esphome.git
synced 2026-10-22 13:56:10 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f76c9f1d6 | ||
|
|
99d063455d | ||
|
|
39d8ecf6e6 |
@@ -1,6 +1,7 @@
|
||||
#include "sen6x.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
@@ -9,6 +10,14 @@ namespace esphome::sen6x {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "sen6x");
|
||||
|
||||
// Indexed by Sen6xType
|
||||
PROGMEM_STRING_TABLE(TypeStrings, "SEN62", "SEN63C", "SEN65", "SEN66", "SEN68", "SEN69C", "UNKNOWN");
|
||||
|
||||
static SEN6XComponent::Sen6xType infer_type_from_product_name(const char *product_name) {
|
||||
const int16_t type = progmem_string_table_find<TypeStrings>(product_name, strnlen(product_name, 32));
|
||||
return type < 0 ? SEN6XComponent::UNKNOWN : static_cast<SEN6XComponent::Sen6xType>(type);
|
||||
}
|
||||
|
||||
#ifdef SEN6X_STATUS_SENSOR_COUNT
|
||||
// Bit of the status register behind each kind; the CO2 error bit is 9 on SEN66 and 12 on SEN63C/SEN69C
|
||||
static uint8_t status_bit(Sen6xStatus status, SEN6XComponent::Sen6xType type) {
|
||||
@@ -133,33 +142,36 @@ void SEN6XComponent::setup() {
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
this->serial_number_ = SEN6XComponent::sensirion_convert_to_string_in_place(raw_serial_number, 16);
|
||||
ESP_LOGI(TAG, "Serial number: %s", this->serial_number_.c_str());
|
||||
// The last byte of serial_number_ stays 0, so a full 32-byte serial is still terminated
|
||||
memcpy(this->serial_number_, SEN6XComponent::sensirion_convert_to_string_in_place(raw_serial_number, 16),
|
||||
sizeof(this->serial_number_) - 1);
|
||||
ESP_LOGI(TAG, "Serial number: %s", this->serial_number_);
|
||||
|
||||
// Step 2: Read product name in next loop iteration
|
||||
this->set_timeout(0, [this]() {
|
||||
uint16_t raw_product_name[16];
|
||||
// One spare zero word so a full 32-byte name is still terminated
|
||||
uint16_t raw_product_name[17]{};
|
||||
if (!this->get_register(SEN6X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
return;
|
||||
}
|
||||
|
||||
this->product_name_ = SEN6XComponent::sensirion_convert_to_string_in_place(raw_product_name, 16);
|
||||
const char *product_name = SEN6XComponent::sensirion_convert_to_string_in_place(raw_product_name, 16);
|
||||
|
||||
Sen6xType inferred_type = this->infer_type_from_product_name_(this->product_name_);
|
||||
Sen6xType inferred_type = infer_type_from_product_name(product_name);
|
||||
if (this->sen6x_type_ == UNKNOWN) {
|
||||
this->sen6x_type_ = inferred_type;
|
||||
if (inferred_type == UNKNOWN) {
|
||||
ESP_LOGE(TAG, "Unknown product '%s'", this->product_name_.c_str());
|
||||
ESP_LOGE(TAG, "Unknown product '%s'", product_name);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Type inferred from product: %s", this->product_name_.c_str());
|
||||
ESP_LOGD(TAG, "Type inferred from product: %s", product_name);
|
||||
} else if (this->sen6x_type_ != inferred_type && inferred_type != UNKNOWN) {
|
||||
ESP_LOGW(TAG, "Configured type (used) mismatches product '%s'", this->product_name_.c_str());
|
||||
ESP_LOGW(TAG, "Configured type (used) mismatches product '%s'", product_name);
|
||||
}
|
||||
ESP_LOGI(TAG, "Product: %s", this->product_name_.c_str());
|
||||
ESP_LOGI(TAG, "Product: %s", product_name);
|
||||
|
||||
// Validate configured sensors against detected type and disable unsupported ones
|
||||
const bool has_voc_nox = (this->sen6x_type_ == SEN65 || this->sen6x_type_ == SEN66 ||
|
||||
@@ -329,13 +341,13 @@ bool SEN6XComponent::write_tuning_parameters_(uint16_t i2c_command, const GasTun
|
||||
void SEN6XComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"sen6x:\n"
|
||||
" Product: %s\n"
|
||||
" Type: %s\n"
|
||||
" Serial: %s\n"
|
||||
" Firmware: %u.%u\n"
|
||||
" Address: 0x%02X\n"
|
||||
" Startup delay: %" PRIu32 " ms",
|
||||
this->product_name_.c_str(), this->serial_number_.c_str(), this->firmware_version_major_,
|
||||
this->firmware_version_minor_, this->address_, this->startup_delay_ms_);
|
||||
LOG_STR_ARG(TypeStrings::get_log_str(this->sen6x_type_, TypeStrings::LAST_INDEX)), this->serial_number_,
|
||||
this->firmware_version_major_, this->firmware_version_minor_, this->address_, this->startup_delay_ms_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
if (this->temperature_compensation_.has_value()) {
|
||||
const auto &comp = this->temperature_compensation_.value();
|
||||
@@ -812,20 +824,4 @@ void SEN6XComponent::activate_sht_heater() {
|
||||
this->set_command_wait_(SHT_HEATER_DELAY);
|
||||
}
|
||||
|
||||
SEN6XComponent::Sen6xType SEN6XComponent::infer_type_from_product_name_(const std::string &product_name) {
|
||||
if (product_name == "SEN62")
|
||||
return SEN62;
|
||||
if (product_name == "SEN63C")
|
||||
return SEN63C;
|
||||
if (product_name == "SEN65")
|
||||
return SEN65;
|
||||
if (product_name == "SEN66")
|
||||
return SEN66;
|
||||
if (product_name == "SEN68")
|
||||
return SEN68;
|
||||
if (product_name == "SEN69C")
|
||||
return SEN69C;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
} // namespace esphome::sen6x
|
||||
|
||||
@@ -86,9 +86,9 @@ class SEN6XComponent final : public PollingComponent, public sensirion_common::S
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
|
||||
enum Sen6xType { SEN62, SEN63C, SEN65, SEN66, SEN68, SEN69C, UNKNOWN };
|
||||
enum Sen6xType : uint8_t { SEN62, SEN63C, SEN65, SEN66, SEN68, SEN69C, UNKNOWN };
|
||||
|
||||
void set_type(const std::string &type) { sen6x_type_ = infer_type_from_product_name_(type); }
|
||||
void set_type(Sen6xType type) { this->sen6x_type_ = type; }
|
||||
void set_voc_algorithm_tuning(uint16_t index_offset, uint16_t learning_time_offset_hours,
|
||||
uint16_t learning_time_gain_hours, uint16_t gating_max_duration_minutes,
|
||||
uint16_t std_initial, uint16_t gain_factor) {
|
||||
@@ -139,7 +139,6 @@ class SEN6XComponent final : public PollingComponent, public sensirion_common::S
|
||||
SETUP_STEP_COUNT,
|
||||
};
|
||||
|
||||
Sen6xType infer_type_from_product_name_(const std::string &product_name);
|
||||
void run_next_setup_step_();
|
||||
bool write_setup_step_(SetupStep step);
|
||||
void finish_setup_();
|
||||
@@ -167,8 +166,7 @@ class SEN6XComponent final : public PollingComponent, public sensirion_common::S
|
||||
void parse_and_publish_number_concentration_();
|
||||
#endif
|
||||
|
||||
std::string product_name_;
|
||||
std::string serial_number_;
|
||||
char serial_number_[33]{};
|
||||
#ifdef SEN6X_STATUS_SENSOR_COUNT
|
||||
StaticVector<StatusSensor, SEN6X_STATUS_SENSOR_COUNT> status_sensors_;
|
||||
#endif
|
||||
|
||||
@@ -78,6 +78,15 @@ sen6x_ns = cg.esphome_ns.namespace("sen6x")
|
||||
SEN6XComponent = sen6x_ns.class_(
|
||||
"SEN6XComponent", cg.PollingComponent, sensirion_common.SensirionI2CDevice
|
||||
)
|
||||
Sen6xType = sen6x_ns.namespace("SEN6XComponent").enum("Sen6xType")
|
||||
SEN6X_TYPES = {
|
||||
"SEN62": Sen6xType.SEN62,
|
||||
"SEN63C": Sen6xType.SEN63C,
|
||||
"SEN65": Sen6xType.SEN65,
|
||||
"SEN66": Sen6xType.SEN66,
|
||||
"SEN68": Sen6xType.SEN68,
|
||||
"SEN69C": Sen6xType.SEN69C,
|
||||
}
|
||||
|
||||
|
||||
_PMC_KEYS = (CONF_PMC_0_5, CONF_PMC_1_0, CONF_PMC_2_5, CONF_PMC_4_0, CONF_PMC_10_0)
|
||||
@@ -133,9 +142,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(SEN6XComponent),
|
||||
cv.Optional(CONF_TYPE): cv.one_of(
|
||||
"SEN62", "SEN63C", "SEN65", "SEN66", "SEN68", "SEN69C", upper=True
|
||||
),
|
||||
cv.Optional(CONF_TYPE): cv.enum(SEN6X_TYPES, upper=True),
|
||||
cv.Optional(CONF_PM_1_0): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
|
||||
icon=ICON_CHEMICAL_WEAPON,
|
||||
|
||||
Reference in New Issue
Block a user