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