ESPHome  2024.9.0
bme68x_bsec2.cpp
Go to the documentation of this file.
1 #include "esphome/core/defines.h"
2 #include "esphome/core/helpers.h"
3 #include "esphome/core/log.h"
4 
5 #ifdef USE_BSEC2
6 #include "bme68x_bsec2.h"
7 
8 #include <string>
9 
10 namespace esphome {
11 namespace bme68x_bsec2 {
12 
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")
17 
18 static const char *const TAG = "bme68x_bsec2.sensor";
19 
20 static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
21 
23  ESP_LOGCONFIG(TAG, "Setting up BME68X via BSEC2...");
24 
25  this->bsec_status_ = bsec_init_m(&this->bsec_instance_);
26  if (this->bsec_status_ != BSEC_OK) {
27  this->mark_failed();
28  ESP_LOGE(TAG, "bsec_init_m failed: status %d", this->bsec_status_);
29  return;
30  }
31 
32  bsec_get_version_m(&this->bsec_instance_, &this->version_);
33 
34  this->bme68x_status_ = bme68x_init(&this->bme68x_);
35  if (this->bme68x_status_ != BME68X_OK) {
36  this->mark_failed();
37  ESP_LOGE(TAG, "bme68x_init failed: status %d", this->bme68x_status_);
38  return;
39  }
40  if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
42  if (this->bsec_status_ != BSEC_OK) {
43  this->mark_failed();
44  ESP_LOGE(TAG, "bsec_set_configuration_m failed: status %d", this->bsec_status_);
45  return;
46  }
47  }
48 
49  this->update_subscription_();
50  if (this->bsec_status_ != BSEC_OK) {
51  this->mark_failed();
52  ESP_LOGE(TAG, "bsec_update_subscription_m failed: status %d", this->bsec_status_);
53  return;
54  }
55 
56  this->load_state_();
57 }
58 
60  ESP_LOGCONFIG(TAG, "BME68X via BSEC2:");
61 
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);
64 
65  ESP_LOGCONFIG(TAG, " BSEC2 configuration blob:");
66  ESP_LOGCONFIG(TAG, " Configured: %s", YESNO(this->bsec2_blob_configured_));
67  if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
68  ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_);
69  }
70 
71  if (this->is_failed()) {
72  ESP_LOGE(TAG, "Communication failed (BSEC2 status: %d, BME68X status: %d)", this->bsec_status_,
73  this->bme68x_status_);
74  }
75 
77  ESP_LOGCONFIG(TAG, " Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_));
78  }
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_));
82  ESP_LOGCONFIG(TAG, " State save interval: %ims", this->state_save_interval_ms_);
83  ESP_LOGCONFIG(TAG, " Temperature offset: %.2f", this->temperature_offset_);
84 
85 #ifdef USE_SENSOR
86  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
87  ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
88  LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
89  ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
90  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
91  ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
92  LOG_SENSOR(" ", "Gas resistance", this->gas_resistance_sensor_);
93  LOG_SENSOR(" ", "CO2 equivalent", this->co2_equivalent_sensor_);
94  LOG_SENSOR(" ", "Breath VOC equivalent", this->breath_voc_equivalent_sensor_);
95  LOG_SENSOR(" ", "IAQ", this->iaq_sensor_);
96  LOG_SENSOR(" ", "IAQ static", this->iaq_static_sensor_);
97  LOG_SENSOR(" ", "Numeric IAQ accuracy", this->iaq_accuracy_sensor_);
98 #endif
99 #ifdef USE_TEXT_SENSOR
100  LOG_TEXT_SENSOR(" ", "IAQ accuracy", this->iaq_accuracy_text_sensor_);
101 #endif
102 }
103 
105 
107  this->run_();
108 
109  if (this->bsec_status_ < BSEC_OK || this->bme68x_status_ < BME68X_OK) {
110  this->status_set_error();
111  } else {
112  this->status_clear_error();
113  }
114  if (this->bsec_status_ > BSEC_OK || this->bme68x_status_ > BME68X_OK) {
115  this->status_set_warning();
116  } else {
117  this->status_clear_warning();
118  }
119  // Process a single action from the queue. These are primarily sensor state publishes
120  // that in totality take too long to send in a single call.
121  if (this->queue_.size()) {
122  auto action = std::move(this->queue_.front());
123  this->queue_.pop();
124  action();
125  }
126 }
127 
128 void BME68xBSEC2Component::set_config_(const uint8_t *config, uint32_t len) {
129  if (len > BSEC_MAX_PROPERTY_BLOB_SIZE) {
130  ESP_LOGE(TAG, "Configuration is larger than BSEC_MAX_PROPERTY_BLOB_SIZE");
131  this->mark_failed();
132  return;
133  }
134  uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE];
135  this->bsec_status_ = bsec_set_configuration_m(&this->bsec_instance_, config, len, work_buffer, sizeof(work_buffer));
136  if (this->bsec_status_ == BSEC_OK) {
137  this->bsec2_blob_configured_ = true;
138  }
139 }
140 
142  if (sample_rate == SAMPLE_RATE_DEFAULT) {
143  sample_rate = this->sample_rate_;
144  }
145  return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
146 }
147 
149  bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
150  uint8_t num_virtual_sensors = 0;
151 #ifdef USE_SENSOR
152  if (this->iaq_sensor_) {
153  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_IAQ;
154  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
155  num_virtual_sensors++;
156  }
157 
158  if (this->iaq_static_sensor_) {
159  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_STATIC_IAQ;
160  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
161  num_virtual_sensors++;
162  }
163 
164  if (this->co2_equivalent_sensor_) {
165  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
166  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
167  num_virtual_sensors++;
168  }
169 
170  if (this->breath_voc_equivalent_sensor_) {
171  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
172  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
173  num_virtual_sensors++;
174  }
175 
176  if (this->pressure_sensor_) {
177  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
178  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
179  num_virtual_sensors++;
180  }
181 
182  if (this->gas_resistance_sensor_) {
183  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
184  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
185  num_virtual_sensors++;
186  }
187 
188  if (this->temperature_sensor_) {
189  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
190  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
191  num_virtual_sensors++;
192  }
193 
194  if (this->humidity_sensor_) {
195  virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
196  virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
197  num_virtual_sensors++;
198  }
199 #endif
200  bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
201  uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
202  this->bsec_status_ = bsec_update_subscription_m(&this->bsec_instance_, virtual_sensors, num_virtual_sensors,
203  sensor_settings, &num_sensor_settings);
204 }
205 
207  int64_t curr_time_ns = this->get_time_ns_();
208  if (curr_time_ns < this->next_call_ns_) {
209  return;
210  }
211  this->op_mode_ = this->bsec_settings_.op_mode;
212  uint8_t status;
213 
214  ESP_LOGV(TAG, "Performing sensor run");
215 
216  struct bme68x_conf bme68x_conf;
217  this->bsec_status_ = bsec_sensor_control_m(&this->bsec_instance_, curr_time_ns, &this->bsec_settings_);
218  if (this->bsec_status_ < BSEC_OK) {
219  ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_);
220  return;
221  }
222  this->next_call_ns_ = this->bsec_settings_.next_call;
223 
224  if (this->bsec_settings_.trigger_measurement) {
225  bme68x_get_conf(&bme68x_conf, &this->bme68x_);
226 
227  bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
228  bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
229  bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
230  bme68x_set_conf(&bme68x_conf, &this->bme68x_);
231 
232  switch (this->bsec_settings_.op_mode) {
233  case BME68X_FORCED_MODE:
234  this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
235  this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature;
236  this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration;
237 
238  status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_);
239  status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
240  status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
241  this->op_mode_ = BME68X_FORCED_MODE;
242  this->sleep_mode_ = false;
243  ESP_LOGV(TAG, "Using forced mode");
244 
245  break;
246  case BME68X_PARALLEL_MODE:
247  if (this->op_mode_ != this->bsec_settings_.op_mode) {
248  this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
249  this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile;
250  this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile;
251  this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len;
252  this->bme68x_heatr_conf_.shared_heatr_dur =
253  BSEC_TOTAL_HEAT_DUR -
254  (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000));
255 
256  status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
257 
258  status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
259  this->op_mode_ = BME68X_PARALLEL_MODE;
260  this->sleep_mode_ = false;
261  ESP_LOGV(TAG, "Using parallel mode");
262  }
263  break;
264  case BME68X_SLEEP_MODE:
265  if (!this->sleep_mode_) {
266  bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_);
267  this->sleep_mode_ = true;
268  ESP_LOGV(TAG, "Using sleep mode");
269  }
270  break;
271  }
272 
273  uint32_t meas_dur = 0;
274  meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
275  ESP_LOGV(TAG, "Queueing read in %uus", meas_dur);
276  this->set_timeout("read", meas_dur / 1000, [this, curr_time_ns]() { this->read_(curr_time_ns); });
277  } else {
278  ESP_LOGV(TAG, "Measurement not required");
279  this->read_(curr_time_ns);
280  }
281 }
282 
283 void BME68xBSEC2Component::read_(int64_t trigger_time_ns) {
284  ESP_LOGV(TAG, "Reading data");
285 
286  if (this->bsec_settings_.trigger_measurement) {
287  uint8_t current_op_mode;
288  this->bme68x_status_ = bme68x_get_op_mode(&current_op_mode, &this->bme68x_);
289 
290  if (current_op_mode == BME68X_SLEEP_MODE) {
291  ESP_LOGV(TAG, "Still in sleep mode, doing nothing");
292  return;
293  }
294  }
295 
296  if (!this->bsec_settings_.process_data) {
297  ESP_LOGV(TAG, "Data processing not required");
298  return;
299  }
300 
301  struct bme68x_data data[3];
302  uint8_t nFields = 0;
303  this->bme68x_status_ = bme68x_get_data(this->op_mode_, &data[0], &nFields, &this->bme68x_);
304 
305  if (this->bme68x_status_ != BME68X_OK) {
306  ESP_LOGW(TAG, "Failed to get sensor data (BME68X error code %d)", this->bme68x_status_);
307  return;
308  }
309  if (nFields < 1) {
310  ESP_LOGD(TAG, "BME68X did not provide new data");
311  return;
312  }
313 
314  for (uint8_t i = 0; i < nFields; i++) {
315  bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance
316  uint8_t num_inputs = 0;
317 
318  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_TEMPERATURE)) {
319  inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
320  inputs[num_inputs].signal = data[i].temperature;
321  inputs[num_inputs].time_stamp = trigger_time_ns;
322  num_inputs++;
323  }
324  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HEATSOURCE)) {
325  inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
326  inputs[num_inputs].signal = this->temperature_offset_;
327  inputs[num_inputs].time_stamp = trigger_time_ns;
328  num_inputs++;
329  }
330  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HUMIDITY)) {
331  inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
332  inputs[num_inputs].signal = data[i].humidity;
333  inputs[num_inputs].time_stamp = trigger_time_ns;
334  num_inputs++;
335  }
336  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PRESSURE)) {
337  inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
338  inputs[num_inputs].signal = data[i].pressure;
339  inputs[num_inputs].time_stamp = trigger_time_ns;
340  num_inputs++;
341  }
342  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_GASRESISTOR)) {
343  if (data[i].status & BME68X_GASM_VALID_MSK) {
344  inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
345  inputs[num_inputs].signal = data[i].gas_resistance;
346  inputs[num_inputs].time_stamp = trigger_time_ns;
347  num_inputs++;
348  } else {
349  ESP_LOGD(TAG, "BME68X did not report gas data");
350  }
351  }
352  if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PROFILE_PART) &&
353  (data[i].status & BME68X_GASM_VALID_MSK)) {
354  inputs[num_inputs].sensor_id = BSEC_INPUT_PROFILE_PART;
355  inputs[num_inputs].signal = (this->op_mode_ == BME68X_FORCED_MODE) ? 0 : data[i].gas_index;
356  inputs[num_inputs].time_stamp = trigger_time_ns;
357  num_inputs++;
358  }
359 
360  if (num_inputs < 1) {
361  ESP_LOGD(TAG, "No signal inputs available for BSEC2");
362  return;
363  }
364 
365  bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
366  uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
367  this->bsec_status_ = bsec_do_steps_m(&this->bsec_instance_, inputs, num_inputs, outputs, &num_outputs);
368  if (this->bsec_status_ != BSEC_OK) {
369  ESP_LOGW(TAG, "BSEC2 failed to process signals (BSEC2 error code %d)", this->bsec_status_);
370  return;
371  }
372  if (num_outputs < 1) {
373  ESP_LOGD(TAG, "No signal outputs provided by BSEC2");
374  return;
375  }
376 
377  this->publish_(outputs, num_outputs);
378  }
379 }
380 
381 void BME68xBSEC2Component::publish_(const bsec_output_t *outputs, uint8_t num_outputs) {
382  ESP_LOGV(TAG, "Publishing sensor states");
383  bool update_accuracy = false;
384  uint8_t max_accuracy = 0;
385  for (uint8_t i = 0; i < num_outputs; i++) {
386  float signal = outputs[i].signal;
387  switch (outputs[i].sensor_id) {
388  case BSEC_OUTPUT_IAQ:
389  max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
390  update_accuracy = true;
391 #ifdef USE_SENSOR
392  this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); });
393 #endif
394  break;
395  case BSEC_OUTPUT_STATIC_IAQ:
396  max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
397  update_accuracy = true;
398 #ifdef USE_SENSOR
399  this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_static_sensor_, signal); });
400 #endif
401  break;
402  case BSEC_OUTPUT_CO2_EQUIVALENT:
403 #ifdef USE_SENSOR
404  this->queue_push_([this, signal]() { this->publish_sensor_(this->co2_equivalent_sensor_, signal); });
405 #endif
406  break;
407  case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
408 #ifdef USE_SENSOR
409  this->queue_push_([this, signal]() { this->publish_sensor_(this->breath_voc_equivalent_sensor_, signal); });
410 #endif
411  break;
412  case BSEC_OUTPUT_RAW_PRESSURE:
413 #ifdef USE_SENSOR
414  this->queue_push_([this, signal]() { this->publish_sensor_(this->pressure_sensor_, signal / 100.0f); });
415 #endif
416  break;
417  case BSEC_OUTPUT_RAW_GAS:
418 #ifdef USE_SENSOR
419  this->queue_push_([this, signal]() { this->publish_sensor_(this->gas_resistance_sensor_, signal); });
420 #endif
421  break;
422  case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
423 #ifdef USE_SENSOR
424  this->queue_push_([this, signal]() { this->publish_sensor_(this->temperature_sensor_, signal); });
425 #endif
426  break;
427  case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
428 #ifdef USE_SENSOR
429  this->queue_push_([this, signal]() { this->publish_sensor_(this->humidity_sensor_, signal); });
430 #endif
431  break;
432  }
433  }
434  if (update_accuracy) {
435 #ifdef USE_SENSOR
436  this->queue_push_(
437  [this, max_accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, max_accuracy, true); });
438 #endif
439 #ifdef USE_TEXT_SENSOR
440  this->queue_push_([this, max_accuracy]() {
441  this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[max_accuracy]);
442  });
443 #endif
444  // Queue up an opportunity to save state
445  this->queue_push_([this, max_accuracy]() { this->save_state_(max_accuracy); });
446  }
447 }
448 
450  int64_t time_ms = millis();
451  if (this->last_time_ms_ > time_ms) {
452  this->millis_overflow_counter_++;
453  }
454  this->last_time_ms_ = time_ms;
455 
456  return (time_ms + ((int64_t) this->millis_overflow_counter_ << 32)) * INT64_C(1000000);
457 }
458 
459 #ifdef USE_SENSOR
460 void BME68xBSEC2Component::publish_sensor_(sensor::Sensor *sensor, float value, bool change_only) {
461  if (!sensor || (change_only && sensor->has_state() && sensor->state == value)) {
462  return;
463  }
464  sensor->publish_state(value);
465 }
466 #endif
467 
468 #ifdef USE_TEXT_SENSOR
470  if (!sensor || (sensor->has_state() && sensor->state == value)) {
471  return;
472  }
473  sensor->publish_state(value);
474 }
475 #endif
476 
478  uint32_t hash = this->get_hash();
479  this->bsec_state_ = global_preferences->make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
480 
481  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
482  if (this->bsec_state_.load(&state)) {
483  ESP_LOGV(TAG, "Loading state");
484  uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
485  this->bsec_status_ =
486  bsec_set_state_m(&this->bsec_instance_, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, sizeof(work_buffer));
487  if (this->bsec_status_ != BSEC_OK) {
488  ESP_LOGW(TAG, "Failed to load state (BSEC2 error code %d)", this->bsec_status_);
489  }
490  ESP_LOGI(TAG, "Loaded state");
491  }
492 }
493 
494 void BME68xBSEC2Component::save_state_(uint8_t accuracy) {
495  if (accuracy < 3 || (millis() - this->last_state_save_ms_ < this->state_save_interval_ms_)) {
496  return;
497  }
498 
499  ESP_LOGV(TAG, "Saving state");
500 
501  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
502  uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
503  uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
504 
505  this->bsec_status_ = bsec_get_state_m(&this->bsec_instance_, 0, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer,
506  BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state);
507  if (this->bsec_status_ != BSEC_OK) {
508  ESP_LOGW(TAG, "Failed fetch state for save (BSEC2 error code %d)", this->bsec_status_);
509  return;
510  }
511 
512  if (!this->bsec_state_.save(&state)) {
513  ESP_LOGW(TAG, "Failed to save state");
514  return;
515  }
516  this->last_state_save_ms_ = millis();
517 
518  ESP_LOGI(TAG, "Saved state");
519 }
520 
521 } // namespace bme68x_bsec2
522 } // namespace esphome
523 #endif
void publish_(const bsec_output_t *outputs, uint8_t num_outputs)
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
void set_config_(const uint8_t *config, u_int32_t len)
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only=false)
bool is_failed() const
Definition: component.cpp:143
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
uint8_t bsec_instance_[BSEC_INSTANCE_SIZE]
Definition: bme68x_bsec2.h:112
void publish_state(const std::string &state)
Definition: text_sensor.cpp:9
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
bool save(const T *src)
Definition: preferences.h:21
float calc_sensor_sample_rate_(SampleRate sample_rate)
float state
This member variable stores the last state that has passed through all filters.
Definition: sensor.h:131
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
struct bme68x_heatr_conf bme68x_heatr_conf_
Definition: bme68x_bsec2.h:114
ESPPreferences * global_preferences
void status_clear_warning()
Definition: component.cpp:166
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
text_sensor::TextSensor * iaq_accuracy_text_sensor_
Definition: bme68x_bsec2.h:157
void status_clear_error()
Definition: component.cpp:172
uint8_t status
Definition: bl0942.h:74
std::string size_t len
Definition: helpers.h:292
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
void queue_push_(std::function< void()> &&f)
Definition: bme68x_bsec2.h:107
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
Definition: sensor.cpp:97
Base-class for all sensors.
Definition: sensor.h:57
void read_(int64_t trigger_time_ns)
std::queue< std::function< void()> > queue_
Definition: bme68x_bsec2.h:124
esphome::sensor::Sensor * sensor
Definition: statsd.h:37
bool state
Definition: fan.h:34