11 namespace bme68x_bsec2 {
13 #define BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(a) (a == ALGORITHM_OUTPUT_CLASSIFICATION ? "Classification" : "Regression") 14 #define BME68X_BSEC2_OPERATING_AGE_LOG(o) (o == OPERATING_AGE_4D ? "4 days" : "28 days") 15 #define BME68X_BSEC2_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP")) 16 #define BME68X_BSEC2_VOLTAGE_LOG(v) (v == VOLTAGE_3_3V ? "3.3V" : "1.8V") 18 static const char *
const TAG =
"bme68x_bsec2.sensor";
20 static const std::string IAQ_ACCURACY_STATES[4] = {
"Stabilizing",
"Uncertain",
"Calibrating",
"Calibrated"};
23 ESP_LOGCONFIG(TAG,
"Setting up BME68X via BSEC2...");
28 ESP_LOGE(TAG,
"bsec_init_m failed: status %d", this->
bsec_status_);
37 ESP_LOGE(TAG,
"bme68x_init failed: status %d", this->
bme68x_status_);
44 ESP_LOGE(TAG,
"bsec_set_configuration_m failed: status %d", this->
bsec_status_);
52 ESP_LOGE(TAG,
"bsec_update_subscription_m failed: status %d", this->
bsec_status_);
60 ESP_LOGCONFIG(TAG,
"BME68X via BSEC2:");
62 ESP_LOGCONFIG(TAG,
" BSEC2 version: %d.%d.%d.%d", this->
version_.major, this->version_.minor,
63 this->version_.major_bugfix, this->version_.minor_bugfix);
65 ESP_LOGCONFIG(TAG,
" BSEC2 configuration blob:");
72 ESP_LOGE(TAG,
"Communication failed (BSEC2 status: %d, BME68X status: %d)", this->
bsec_status_,
77 ESP_LOGCONFIG(TAG,
" Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->
algorithm_output_));
79 ESP_LOGCONFIG(TAG,
" Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->
operating_age_));
80 ESP_LOGCONFIG(TAG,
" Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->
sample_rate_));
81 ESP_LOGCONFIG(TAG,
" Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->
voltage_));
99 #ifdef USE_TEXT_SENSOR 109 if (this->bsec_status_ < BSEC_OK || this->
bme68x_status_ < BME68X_OK) {
121 if (this->
queue_.size()) {
122 auto action = std::move(this->
queue_.front());
129 if (len > BSEC_MAX_PROPERTY_BLOB_SIZE) {
130 ESP_LOGE(TAG,
"Configuration is larger than BSEC_MAX_PROPERTY_BLOB_SIZE");
134 uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE];
145 return sample_rate ==
SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
149 bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
150 uint8_t num_virtual_sensors = 0;
153 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_IAQ;
155 num_virtual_sensors++;
159 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_STATIC_IAQ;
161 num_virtual_sensors++;
165 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
167 num_virtual_sensors++;
171 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
173 num_virtual_sensors++;
177 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
179 num_virtual_sensors++;
183 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
185 num_virtual_sensors++;
189 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
191 num_virtual_sensors++;
195 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
197 num_virtual_sensors++;
200 bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
201 uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
203 sensor_settings, &num_sensor_settings);
214 ESP_LOGV(TAG,
"Performing sensor run");
216 struct bme68x_conf bme68x_conf;
218 if (this->bsec_status_ < BSEC_OK) {
219 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_);
224 case BME68X_FORCED_MODE:
225 bme68x_get_conf(&bme68x_conf, &this->
bme68x_);
228 bme68x_conf.os_temp = this->
bsec_settings_.temperature_oversampling;
230 bme68x_set_conf(&bme68x_conf, &this->
bme68x_);
237 status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->
bme68x_);
238 this->
op_mode_ = BME68X_FORCED_MODE;
239 ESP_LOGV(TAG,
"Using forced mode");
242 case BME68X_PARALLEL_MODE:
244 bme68x_get_conf(&bme68x_conf, &this->
bme68x_);
247 bme68x_conf.os_temp = this->
bsec_settings_.temperature_oversampling;
249 bme68x_set_conf(&bme68x_conf, &this->
bme68x_);
256 BSEC_TOTAL_HEAT_DUR -
257 (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->
bme68x_) / INT64_C(1000));
261 status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->
bme68x_);
262 this->
op_mode_ = BME68X_PARALLEL_MODE;
263 ESP_LOGV(TAG,
"Using parallel mode");
266 case BME68X_SLEEP_MODE:
268 bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->
bme68x_);
270 ESP_LOGV(TAG,
"Using sleep mode");
275 if (this->
bsec_settings_.trigger_measurement && this->bsec_settings_.op_mode != BME68X_SLEEP_MODE) {
276 uint32_t meas_dur = 0;
277 meas_dur = bme68x_get_meas_dur(this->
op_mode_, &bme68x_conf, &this->
bme68x_);
278 ESP_LOGV(TAG,
"Queueing read in %uus", meas_dur);
279 this->
set_timeout(
"read", meas_dur / 1000, [
this, curr_time_ns]() { this->
read_(curr_time_ns); });
281 ESP_LOGV(TAG,
"Measurement not required");
282 this->
read_(curr_time_ns);
287 ESP_LOGV(TAG,
"Reading data");
290 uint8_t current_op_mode;
293 if (current_op_mode == BME68X_SLEEP_MODE) {
294 ESP_LOGV(TAG,
"Still in sleep mode, doing nothing");
300 ESP_LOGV(TAG,
"Data processing not required");
304 struct bme68x_data data[3];
309 ESP_LOGW(TAG,
"Failed to get sensor data (BME68X error code %d)", this->
bme68x_status_);
313 ESP_LOGD(TAG,
"BME68X did not provide new data");
317 for (uint8_t i = 0; i < nFields; i++) {
318 bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR];
319 uint8_t num_inputs = 0;
321 if (BSEC_CHECK_INPUT(this->
bsec_settings_.process_data, BSEC_INPUT_TEMPERATURE)) {
322 inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
323 inputs[num_inputs].signal = data[i].temperature;
324 inputs[num_inputs].time_stamp = trigger_time_ns;
327 if (BSEC_CHECK_INPUT(this->
bsec_settings_.process_data, BSEC_INPUT_HEATSOURCE)) {
328 inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
330 inputs[num_inputs].time_stamp = trigger_time_ns;
333 if (BSEC_CHECK_INPUT(this->
bsec_settings_.process_data, BSEC_INPUT_HUMIDITY)) {
334 inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
335 inputs[num_inputs].signal = data[i].humidity;
336 inputs[num_inputs].time_stamp = trigger_time_ns;
339 if (BSEC_CHECK_INPUT(this->
bsec_settings_.process_data, BSEC_INPUT_PRESSURE)) {
340 inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
341 inputs[num_inputs].signal = data[i].pressure;
342 inputs[num_inputs].time_stamp = trigger_time_ns;
345 if (BSEC_CHECK_INPUT(this->
bsec_settings_.process_data, BSEC_INPUT_GASRESISTOR)) {
346 if (data[i].
status & BME68X_GASM_VALID_MSK) {
347 inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
348 inputs[num_inputs].signal = data[i].gas_resistance;
349 inputs[num_inputs].time_stamp = trigger_time_ns;
352 ESP_LOGD(TAG,
"BME68X did not report gas data");
355 if (BSEC_CHECK_INPUT(this->
bsec_settings_.process_data, BSEC_INPUT_PROFILE_PART) &&
356 (data[i].status & BME68X_GASM_VALID_MSK)) {
357 inputs[num_inputs].sensor_id = BSEC_INPUT_PROFILE_PART;
358 inputs[num_inputs].signal = (this->
op_mode_ == BME68X_FORCED_MODE) ? 0 : data[i].gas_index;
359 inputs[num_inputs].time_stamp = trigger_time_ns;
363 if (num_inputs < 1) {
364 ESP_LOGD(TAG,
"No signal inputs available for BSEC2");
368 bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
369 uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
372 ESP_LOGW(TAG,
"BSEC2 failed to process signals (BSEC2 error code %d)", this->
bsec_status_);
375 if (num_outputs < 1) {
376 ESP_LOGD(TAG,
"No signal outputs provided by BSEC2");
380 this->
publish_(outputs, num_outputs);
385 ESP_LOGV(TAG,
"Publishing sensor states");
386 bool update_accuracy =
false;
387 uint8_t max_accuracy = 0;
388 for (uint8_t i = 0; i < num_outputs; i++) {
389 float signal = outputs[i].signal;
390 switch (outputs[i].sensor_id) {
391 case BSEC_OUTPUT_IAQ:
392 max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
393 update_accuracy =
true;
398 case BSEC_OUTPUT_STATIC_IAQ:
399 max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
400 update_accuracy =
true;
405 case BSEC_OUTPUT_CO2_EQUIVALENT:
410 case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
415 case BSEC_OUTPUT_RAW_PRESSURE:
420 case BSEC_OUTPUT_RAW_GAS:
425 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
430 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
437 if (update_accuracy) {
442 #ifdef USE_TEXT_SENSOR 453 int64_t time_ms =
millis();
464 if (!sensor || (change_only && sensor->
has_state() && sensor->
state == value)) {
471 #ifdef USE_TEXT_SENSOR 484 uint8_t
state[BSEC_MAX_STATE_BLOB_SIZE];
486 ESP_LOGV(TAG,
"Loading state");
487 uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
489 bsec_set_state_m(&this->
bsec_instance_, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer,
sizeof(work_buffer));
491 ESP_LOGW(TAG,
"Failed to load state (BSEC2 error code %d)", this->
bsec_status_);
493 ESP_LOGI(TAG,
"Loaded state");
502 ESP_LOGV(TAG,
"Saving state");
504 uint8_t
state[BSEC_MAX_STATE_BLOB_SIZE];
505 uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
506 uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
509 BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state);
511 ESP_LOGW(TAG,
"Failed fetch state for save (BSEC2 error code %d)", this->
bsec_status_);
516 ESP_LOGW(TAG,
"Failed to save state");
521 ESP_LOGI(TAG,
"Saved state");
void publish_(const bsec_output_t *outputs, uint8_t num_outputs)
SampleRate pressure_sample_rate_
const float DATA
For components that import data from directly connected sensors like DHT.
uint32_t state_save_interval_ms_
void set_config_(const uint8_t *config, u_int32_t len)
void status_set_warning(const char *message="unspecified")
sensor::Sensor * gas_resistance_sensor_
void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only=false)
ESPPreferenceObject bsec_state_
uint32_t millis_overflow_counter_
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
float temperature_offset_
bsec_library_return_t bsec_status_
uint8_t bsec_instance_[BSEC_INSTANCE_SIZE]
float get_setup_priority() const override
sensor::Sensor * iaq_accuracy_sensor_
sensor::Sensor * breath_voc_equivalent_sensor_
sensor::Sensor * humidity_sensor_
void publish_state(const std::string &state)
bool bsec2_blob_configured_
uint32_t IRAM_ATTR HOT millis()
sensor::Sensor * co2_equivalent_sensor_
float calc_sensor_sample_rate_(SampleRate sample_rate)
struct bme68x_dev bme68x_
float state
This member variable stores the last state that has passed through all filters.
void status_set_error(const char *message="unspecified")
void update_subscription_()
AlgorithmOutput algorithm_output_
struct bme68x_heatr_conf bme68x_heatr_conf_
ESPPreferences * global_preferences
sensor::Sensor * iaq_sensor_
void status_clear_warning()
void save_state_(uint8_t accuracy)
sensor::Sensor * pressure_sensor_
void publish_state(float state)
Publish a new state to the front-end.
uint8_t const * bsec2_configuration_
uint32_t bsec2_configuration_length_
text_sensor::TextSensor * iaq_accuracy_text_sensor_
bsec_bme_settings_t bsec_settings_
OperatingAge operating_age_
void status_clear_error()
SampleRate temperature_sample_rate_
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
sensor::Sensor * iaq_static_sensor_
void dump_config() override
virtual void mark_failed()
Mark this component as failed.
void queue_push_(std::function< void()> &&f)
Implementation of SPI Controller mode.
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
virtual uint32_t get_hash()=0
Base-class for all sensors.
SampleRate humidity_sample_rate_
void read_(int64_t trigger_time_ns)
std::queue< std::function< void()> > queue_
uint32_t last_state_save_ms_
esphome::sensor::Sensor * sensor
sensor::Sensor * temperature_sensor_