Compare commits

...
3 changed files with 37 additions and 36 deletions
+24 -28
View File
@@ -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
+3 -5
View File
@@ -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
+10 -3
View File
@@ -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,