mirror of
https://github.com/esphome/esphome.git
synced 2025-09-03 11:52:20 +01:00
528 lines
20 KiB
C++
528 lines
20 KiB
C++
#include "esphome/core/defines.h"
|
|
#include "esphome/core/hal.h"
|
|
#include "esphome/core/helpers.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
#ifdef USE_BSEC2
|
|
#include "bme68x_bsec2.h"
|
|
|
|
#include <string>
|
|
|
|
namespace esphome {
|
|
namespace bme68x_bsec2 {
|
|
|
|
#define BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(a) (a == ALGORITHM_OUTPUT_CLASSIFICATION ? "Classification" : "Regression")
|
|
#define BME68X_BSEC2_OPERATING_AGE_LOG(o) (o == OPERATING_AGE_4D ? "4 days" : "28 days")
|
|
#define BME68X_BSEC2_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP"))
|
|
#define BME68X_BSEC2_VOLTAGE_LOG(v) (v == VOLTAGE_3_3V ? "3.3V" : "1.8V")
|
|
|
|
static const char *const TAG = "bme68x_bsec2.sensor";
|
|
|
|
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
|
|
|
|
void BME68xBSEC2Component::setup() {
|
|
ESP_LOGCONFIG(TAG, "Setting up BME68X via BSEC2...");
|
|
|
|
this->bsec_status_ = bsec_init_m(&this->bsec_instance_);
|
|
if (this->bsec_status_ != BSEC_OK) {
|
|
this->mark_failed();
|
|
ESP_LOGE(TAG, "bsec_init_m failed: status %d", this->bsec_status_);
|
|
return;
|
|
}
|
|
|
|
bsec_get_version_m(&this->bsec_instance_, &this->version_);
|
|
|
|
this->bme68x_status_ = bme68x_init(&this->bme68x_);
|
|
if (this->bme68x_status_ != BME68X_OK) {
|
|
this->mark_failed();
|
|
ESP_LOGE(TAG, "bme68x_init failed: status %d", this->bme68x_status_);
|
|
return;
|
|
}
|
|
if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
|
|
this->set_config_(this->bsec2_configuration_, this->bsec2_configuration_length_);
|
|
if (this->bsec_status_ != BSEC_OK) {
|
|
this->mark_failed();
|
|
ESP_LOGE(TAG, "bsec_set_configuration_m failed: status %d", this->bsec_status_);
|
|
return;
|
|
}
|
|
}
|
|
|
|
this->update_subscription_();
|
|
if (this->bsec_status_ != BSEC_OK) {
|
|
this->mark_failed();
|
|
ESP_LOGE(TAG, "bsec_update_subscription_m failed: status %d", this->bsec_status_);
|
|
return;
|
|
}
|
|
|
|
this->load_state_();
|
|
}
|
|
|
|
void BME68xBSEC2Component::dump_config() {
|
|
ESP_LOGCONFIG(TAG, "BME68X via BSEC2:");
|
|
|
|
ESP_LOGCONFIG(TAG, " BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor,
|
|
this->version_.major_bugfix, this->version_.minor_bugfix);
|
|
|
|
ESP_LOGCONFIG(TAG, " BSEC2 configuration blob:");
|
|
ESP_LOGCONFIG(TAG, " Configured: %s", YESNO(this->bsec2_blob_configured_));
|
|
if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
|
|
ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_);
|
|
}
|
|
|
|
if (this->is_failed()) {
|
|
ESP_LOGE(TAG, "Communication failed (BSEC2 status: %d, BME68X status: %d)", this->bsec_status_,
|
|
this->bme68x_status_);
|
|
}
|
|
|
|
if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) {
|
|
ESP_LOGCONFIG(TAG, " Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_));
|
|
}
|
|
ESP_LOGCONFIG(TAG, " Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_));
|
|
ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_));
|
|
ESP_LOGCONFIG(TAG, " Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_));
|
|
ESP_LOGCONFIG(TAG, " State save interval: %ims", this->state_save_interval_ms_);
|
|
ESP_LOGCONFIG(TAG, " Temperature offset: %.2f", this->temperature_offset_);
|
|
|
|
#ifdef USE_SENSOR
|
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
|
ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
|
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
|
ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
|
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
|
ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
|
|
LOG_SENSOR(" ", "Gas resistance", this->gas_resistance_sensor_);
|
|
LOG_SENSOR(" ", "CO2 equivalent", this->co2_equivalent_sensor_);
|
|
LOG_SENSOR(" ", "Breath VOC equivalent", this->breath_voc_equivalent_sensor_);
|
|
LOG_SENSOR(" ", "IAQ", this->iaq_sensor_);
|
|
LOG_SENSOR(" ", "IAQ static", this->iaq_static_sensor_);
|
|
LOG_SENSOR(" ", "Numeric IAQ accuracy", this->iaq_accuracy_sensor_);
|
|
#endif
|
|
#ifdef USE_TEXT_SENSOR
|
|
LOG_TEXT_SENSOR(" ", "IAQ accuracy", this->iaq_accuracy_text_sensor_);
|
|
#endif
|
|
}
|
|
|
|
float BME68xBSEC2Component::get_setup_priority() const { return setup_priority::DATA; }
|
|
|
|
void BME68xBSEC2Component::loop() {
|
|
this->run_();
|
|
|
|
if (this->bsec_status_ < BSEC_OK || this->bme68x_status_ < BME68X_OK) {
|
|
this->status_set_error();
|
|
} else {
|
|
this->status_clear_error();
|
|
}
|
|
if (this->bsec_status_ > BSEC_OK || this->bme68x_status_ > BME68X_OK) {
|
|
this->status_set_warning();
|
|
} else {
|
|
this->status_clear_warning();
|
|
}
|
|
// Process a single action from the queue. These are primarily sensor state publishes
|
|
// that in totality take too long to send in a single call.
|
|
if (this->queue_.size()) {
|
|
auto action = std::move(this->queue_.front());
|
|
this->queue_.pop();
|
|
action();
|
|
}
|
|
}
|
|
|
|
void BME68xBSEC2Component::set_config_(const uint8_t *config, uint32_t len) {
|
|
if (len > BSEC_MAX_PROPERTY_BLOB_SIZE) {
|
|
ESP_LOGE(TAG, "Configuration is larger than BSEC_MAX_PROPERTY_BLOB_SIZE");
|
|
this->mark_failed();
|
|
return;
|
|
}
|
|
uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE];
|
|
this->bsec_status_ = bsec_set_configuration_m(&this->bsec_instance_, config, len, work_buffer, sizeof(work_buffer));
|
|
if (this->bsec_status_ == BSEC_OK) {
|
|
this->bsec2_blob_configured_ = true;
|
|
}
|
|
}
|
|
|
|
float BME68xBSEC2Component::calc_sensor_sample_rate_(SampleRate sample_rate) {
|
|
if (sample_rate == SAMPLE_RATE_DEFAULT) {
|
|
sample_rate = this->sample_rate_;
|
|
}
|
|
return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
|
|
}
|
|
|
|
void BME68xBSEC2Component::update_subscription_() {
|
|
bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
|
|
uint8_t num_virtual_sensors = 0;
|
|
#ifdef USE_SENSOR
|
|
if (this->iaq_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_IAQ;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
|
num_virtual_sensors++;
|
|
}
|
|
|
|
if (this->iaq_static_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_STATIC_IAQ;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
|
num_virtual_sensors++;
|
|
}
|
|
|
|
if (this->co2_equivalent_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
|
num_virtual_sensors++;
|
|
}
|
|
|
|
if (this->breath_voc_equivalent_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
|
num_virtual_sensors++;
|
|
}
|
|
|
|
if (this->pressure_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
|
|
num_virtual_sensors++;
|
|
}
|
|
|
|
if (this->gas_resistance_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
|
num_virtual_sensors++;
|
|
}
|
|
|
|
if (this->temperature_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
|
|
num_virtual_sensors++;
|
|
}
|
|
|
|
if (this->humidity_sensor_) {
|
|
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
|
|
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
|
|
num_virtual_sensors++;
|
|
}
|
|
#endif
|
|
bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
|
|
uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
|
|
this->bsec_status_ = bsec_update_subscription_m(&this->bsec_instance_, virtual_sensors, num_virtual_sensors,
|
|
sensor_settings, &num_sensor_settings);
|
|
}
|
|
|
|
void BME68xBSEC2Component::run_() {
|
|
this->op_mode_ = this->bsec_settings_.op_mode;
|
|
int64_t curr_time_ns = this->get_time_ns_();
|
|
if (curr_time_ns < this->bsec_settings_.next_call) {
|
|
return;
|
|
}
|
|
uint8_t status;
|
|
|
|
ESP_LOGV(TAG, "Performing sensor run");
|
|
|
|
struct bme68x_conf bme68x_conf;
|
|
this->bsec_status_ = bsec_sensor_control_m(&this->bsec_instance_, curr_time_ns, &this->bsec_settings_);
|
|
if (this->bsec_status_ < BSEC_OK) {
|
|
ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_);
|
|
return;
|
|
}
|
|
|
|
switch (this->bsec_settings_.op_mode) {
|
|
case BME68X_FORCED_MODE:
|
|
bme68x_get_conf(&bme68x_conf, &this->bme68x_);
|
|
|
|
bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
|
|
bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
|
|
bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
|
|
bme68x_set_conf(&bme68x_conf, &this->bme68x_);
|
|
this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
|
|
this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature;
|
|
this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration;
|
|
|
|
// status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_);
|
|
status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
|
status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
|
|
this->op_mode_ = BME68X_FORCED_MODE;
|
|
ESP_LOGV(TAG, "Using forced mode");
|
|
|
|
break;
|
|
case BME68X_PARALLEL_MODE:
|
|
if (this->op_mode_ != this->bsec_settings_.op_mode) {
|
|
bme68x_get_conf(&bme68x_conf, &this->bme68x_);
|
|
|
|
bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
|
|
bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
|
|
bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
|
|
bme68x_set_conf(&bme68x_conf, &this->bme68x_);
|
|
|
|
this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
|
|
this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile;
|
|
this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile;
|
|
this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len;
|
|
this->bme68x_heatr_conf_.shared_heatr_dur =
|
|
BSEC_TOTAL_HEAT_DUR -
|
|
(bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000));
|
|
|
|
status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
|
|
|
status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
|
|
this->op_mode_ = BME68X_PARALLEL_MODE;
|
|
ESP_LOGV(TAG, "Using parallel mode");
|
|
}
|
|
break;
|
|
case BME68X_SLEEP_MODE:
|
|
if (this->op_mode_ != this->bsec_settings_.op_mode) {
|
|
bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_);
|
|
this->op_mode_ = BME68X_SLEEP_MODE;
|
|
ESP_LOGV(TAG, "Using sleep mode");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (this->bsec_settings_.trigger_measurement && this->bsec_settings_.op_mode != BME68X_SLEEP_MODE) {
|
|
uint32_t meas_dur = 0;
|
|
meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
|
|
ESP_LOGV(TAG, "Queueing read in %uus", meas_dur);
|
|
this->set_timeout("read", meas_dur / 1000, [this, curr_time_ns]() { this->read_(curr_time_ns); });
|
|
} else {
|
|
ESP_LOGV(TAG, "Measurement not required");
|
|
this->read_(curr_time_ns);
|
|
}
|
|
}
|
|
|
|
void BME68xBSEC2Component::read_(int64_t trigger_time_ns) {
|
|
ESP_LOGV(TAG, "Reading data");
|
|
|
|
if (this->bsec_settings_.trigger_measurement) {
|
|
uint8_t current_op_mode;
|
|
this->bme68x_status_ = bme68x_get_op_mode(¤t_op_mode, &this->bme68x_);
|
|
|
|
if (current_op_mode == BME68X_SLEEP_MODE) {
|
|
ESP_LOGV(TAG, "Still in sleep mode, doing nothing");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!this->bsec_settings_.process_data) {
|
|
ESP_LOGV(TAG, "Data processing not required");
|
|
return;
|
|
}
|
|
|
|
struct bme68x_data data[3];
|
|
uint8_t nFields = 0;
|
|
this->bme68x_status_ = bme68x_get_data(this->op_mode_, &data[0], &nFields, &this->bme68x_);
|
|
|
|
if (this->bme68x_status_ != BME68X_OK) {
|
|
ESP_LOGW(TAG, "Failed to get sensor data (BME68X error code %d)", this->bme68x_status_);
|
|
return;
|
|
}
|
|
if (nFields < 1) {
|
|
ESP_LOGD(TAG, "BME68X did not provide new data");
|
|
return;
|
|
}
|
|
|
|
for (uint8_t i = 0; i < nFields; i++) {
|
|
bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance
|
|
uint8_t num_inputs = 0;
|
|
|
|
if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_TEMPERATURE)) {
|
|
inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
|
|
inputs[num_inputs].signal = data[i].temperature;
|
|
inputs[num_inputs].time_stamp = trigger_time_ns;
|
|
num_inputs++;
|
|
}
|
|
if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HEATSOURCE)) {
|
|
inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
|
|
inputs[num_inputs].signal = this->temperature_offset_;
|
|
inputs[num_inputs].time_stamp = trigger_time_ns;
|
|
num_inputs++;
|
|
}
|
|
if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HUMIDITY)) {
|
|
inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
|
|
inputs[num_inputs].signal = data[i].humidity;
|
|
inputs[num_inputs].time_stamp = trigger_time_ns;
|
|
num_inputs++;
|
|
}
|
|
if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PRESSURE)) {
|
|
inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
|
|
inputs[num_inputs].signal = data[i].pressure;
|
|
inputs[num_inputs].time_stamp = trigger_time_ns;
|
|
num_inputs++;
|
|
}
|
|
if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_GASRESISTOR)) {
|
|
if (data[i].status & BME68X_GASM_VALID_MSK) {
|
|
inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
|
|
inputs[num_inputs].signal = data[i].gas_resistance;
|
|
inputs[num_inputs].time_stamp = trigger_time_ns;
|
|
num_inputs++;
|
|
} else {
|
|
ESP_LOGD(TAG, "BME68X did not report gas data");
|
|
}
|
|
}
|
|
if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PROFILE_PART) &&
|
|
(data[i].status & BME68X_GASM_VALID_MSK)) {
|
|
inputs[num_inputs].sensor_id = BSEC_INPUT_PROFILE_PART;
|
|
inputs[num_inputs].signal = (this->op_mode_ == BME68X_FORCED_MODE) ? 0 : data[i].gas_index;
|
|
inputs[num_inputs].time_stamp = trigger_time_ns;
|
|
num_inputs++;
|
|
}
|
|
|
|
if (num_inputs < 1) {
|
|
ESP_LOGD(TAG, "No signal inputs available for BSEC2");
|
|
return;
|
|
}
|
|
|
|
bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
|
|
uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
|
|
this->bsec_status_ = bsec_do_steps_m(&this->bsec_instance_, inputs, num_inputs, outputs, &num_outputs);
|
|
if (this->bsec_status_ != BSEC_OK) {
|
|
ESP_LOGW(TAG, "BSEC2 failed to process signals (BSEC2 error code %d)", this->bsec_status_);
|
|
return;
|
|
}
|
|
if (num_outputs < 1) {
|
|
ESP_LOGD(TAG, "No signal outputs provided by BSEC2");
|
|
return;
|
|
}
|
|
|
|
this->publish_(outputs, num_outputs);
|
|
}
|
|
}
|
|
|
|
void BME68xBSEC2Component::publish_(const bsec_output_t *outputs, uint8_t num_outputs) {
|
|
ESP_LOGV(TAG, "Publishing sensor states");
|
|
bool update_accuracy = false;
|
|
uint8_t max_accuracy = 0;
|
|
for (uint8_t i = 0; i < num_outputs; i++) {
|
|
float signal = outputs[i].signal;
|
|
switch (outputs[i].sensor_id) {
|
|
case BSEC_OUTPUT_IAQ:
|
|
max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
|
|
update_accuracy = true;
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); });
|
|
#endif
|
|
break;
|
|
case BSEC_OUTPUT_STATIC_IAQ:
|
|
max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
|
|
update_accuracy = true;
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_static_sensor_, signal); });
|
|
#endif
|
|
break;
|
|
case BSEC_OUTPUT_CO2_EQUIVALENT:
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->co2_equivalent_sensor_, signal); });
|
|
#endif
|
|
break;
|
|
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->breath_voc_equivalent_sensor_, signal); });
|
|
#endif
|
|
break;
|
|
case BSEC_OUTPUT_RAW_PRESSURE:
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->pressure_sensor_, signal / 100.0f); });
|
|
#endif
|
|
break;
|
|
case BSEC_OUTPUT_RAW_GAS:
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->gas_resistance_sensor_, signal); });
|
|
#endif
|
|
break;
|
|
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->temperature_sensor_, signal); });
|
|
#endif
|
|
break;
|
|
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_([this, signal]() { this->publish_sensor_(this->humidity_sensor_, signal); });
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
if (update_accuracy) {
|
|
#ifdef USE_SENSOR
|
|
this->queue_push_(
|
|
[this, max_accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, max_accuracy, true); });
|
|
#endif
|
|
#ifdef USE_TEXT_SENSOR
|
|
this->queue_push_([this, max_accuracy]() {
|
|
this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[max_accuracy]);
|
|
});
|
|
#endif
|
|
// Queue up an opportunity to save state
|
|
this->queue_push_([this, max_accuracy]() { this->save_state_(max_accuracy); });
|
|
}
|
|
}
|
|
|
|
int64_t BME68xBSEC2Component::get_time_ns_() {
|
|
int64_t time_ms = millis();
|
|
if (this->last_time_ms_ > time_ms) {
|
|
this->millis_overflow_counter_++;
|
|
}
|
|
this->last_time_ms_ = time_ms;
|
|
|
|
return (time_ms + ((int64_t) this->millis_overflow_counter_ << 32)) * INT64_C(1000000);
|
|
}
|
|
|
|
#ifdef USE_SENSOR
|
|
void BME68xBSEC2Component::publish_sensor_(sensor::Sensor *sensor, float value, bool change_only) {
|
|
if (!sensor || (change_only && sensor->has_state() && sensor->state == value)) {
|
|
return;
|
|
}
|
|
sensor->publish_state(value);
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_TEXT_SENSOR
|
|
void BME68xBSEC2Component::publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value) {
|
|
if (!sensor || (sensor->has_state() && sensor->state == value)) {
|
|
return;
|
|
}
|
|
sensor->publish_state(value);
|
|
}
|
|
#endif
|
|
|
|
void BME68xBSEC2Component::load_state_() {
|
|
uint32_t hash = this->get_hash();
|
|
this->bsec_state_ = global_preferences->make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
|
|
|
|
uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
|
|
if (this->bsec_state_.load(&state)) {
|
|
ESP_LOGV(TAG, "Loading state");
|
|
uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
|
|
this->bsec_status_ =
|
|
bsec_set_state_m(&this->bsec_instance_, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, sizeof(work_buffer));
|
|
if (this->bsec_status_ != BSEC_OK) {
|
|
ESP_LOGW(TAG, "Failed to load state (BSEC2 error code %d)", this->bsec_status_);
|
|
}
|
|
ESP_LOGI(TAG, "Loaded state");
|
|
}
|
|
}
|
|
|
|
void BME68xBSEC2Component::save_state_(uint8_t accuracy) {
|
|
if (accuracy < 3 || (millis() - this->last_state_save_ms_ < this->state_save_interval_ms_)) {
|
|
return;
|
|
}
|
|
|
|
ESP_LOGV(TAG, "Saving state");
|
|
|
|
uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
|
|
uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
|
|
uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
|
|
|
|
this->bsec_status_ = bsec_get_state_m(&this->bsec_instance_, 0, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer,
|
|
BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state);
|
|
if (this->bsec_status_ != BSEC_OK) {
|
|
ESP_LOGW(TAG, "Failed fetch state for save (BSEC2 error code %d)", this->bsec_status_);
|
|
return;
|
|
}
|
|
|
|
if (!this->bsec_state_.save(&state)) {
|
|
ESP_LOGW(TAG, "Failed to save state");
|
|
return;
|
|
}
|
|
this->last_state_save_ms_ = millis();
|
|
|
|
ESP_LOGI(TAG, "Saved state");
|
|
}
|
|
|
|
} // namespace bme68x_bsec2
|
|
} // namespace esphome
|
|
#endif
|