ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
bme680_bsec.cpp
Go to the documentation of this file.
1#include "bme680_bsec.h"
3#include "esphome/core/log.h"
4#include <string>
5
7#ifdef USE_BSEC
8ESPHOME_LOG_TAG(TAG, "bme680_bsec.sensor");
9
10static constexpr uint32_t READ_TIMEOUT_ID = 0;
11
12static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
13
14std::vector<BME680BSECComponent *>
15 BME680BSECComponent::instances; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
16uint8_t BME680BSECComponent::work_buffer_[BSEC_MAX_WORKBUFFER_SIZE] = {0};
17
19 uint8_t new_idx = BME680BSECComponent::instances.size();
20 BME680BSECComponent::instances.push_back(this);
21
22 this->bsec_state_data_valid_ = false;
23
24 // Initialize the bme680_ structure (passed-in to the bme680_* functions) and the BME680 device
25 this->bme680_.dev_id =
26 new_idx; // This is a "Place holder to store the id of the device structure" (see bme680_defs.h).
27 // This will be passed-in as first parameter to the next "read" and "write" function pointers.
28 // We currently use the index of the object in the BME680BSECComponent::instances vector to identify
29 // the different devices in the system.
30 this->bme680_.intf = BME680_I2C_INTF;
34 this->bme680_.amb_temp = 25;
35
36 this->bme680_status_ = bme680_init(&this->bme680_);
37 if (this->bme680_status_ != BME680_OK) {
38 this->mark_failed();
39 return;
40 }
41
42 // Initialize the BSEC library
43 if (this->reinit_bsec_lib_() != 0) {
44 this->mark_failed();
45 return;
46 }
47
48 // Load the BSEC library state from storage
49 this->load_state_();
50}
51
53 if (this->sample_rate_ == SAMPLE_RATE_ULP) {
55 const uint8_t config[] = {
56#include "config/generic_33v_300s_28d/bsec_iaq.txt"
57 };
58 this->bsec_status_ =
59 bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
60 } else { // SUPPLY_VOLTAGE_1V8
61 const uint8_t config[] = {
62#include "config/generic_18v_300s_28d/bsec_iaq.txt"
63 };
64 this->bsec_status_ =
65 bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
66 }
67 } else { // SAMPLE_RATE_LP
69 const uint8_t config[] = {
70#include "config/generic_33v_3s_28d/bsec_iaq.txt"
71 };
72 this->bsec_status_ =
73 bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
74 } else { // SUPPLY_VOLTAGE_1V8
75 const uint8_t config[] = {
76#include "config/generic_18v_3s_28d/bsec_iaq.txt"
77 };
78 this->bsec_status_ =
79 bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
80 }
81 }
82}
83
85 if (sample_rate == SAMPLE_RATE_DEFAULT) {
86 sample_rate = this->sample_rate_;
87 }
88 return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
89}
90
92 bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
93 int num_virtual_sensors = 0;
94
95 if (this->iaq_sensor_) {
96 virtual_sensors[num_virtual_sensors].sensor_id =
97 this->iaq_mode_ == IAQ_MODE_STATIC ? BSEC_OUTPUT_STATIC_IAQ : BSEC_OUTPUT_IAQ;
98 virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
99 num_virtual_sensors++;
100 }
101
102 if (this->co2_equivalent_sensor_) {
103 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
104 virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
105 num_virtual_sensors++;
106 }
107
109 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
110 virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
111 num_virtual_sensors++;
112 }
113
114 if (this->pressure_sensor_) {
115 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
116 virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
117 num_virtual_sensors++;
118 }
119
120 if (this->gas_resistance_sensor_) {
121 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
122 virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
123 num_virtual_sensors++;
124 }
125
126 if (this->temperature_sensor_) {
127 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
128 virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
129 num_virtual_sensors++;
130 }
131
132 if (this->humidity_sensor_) {
133 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
134 virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
135 num_virtual_sensors++;
136 }
137
138 bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
139 uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
140 this->bsec_status_ =
141 bsec_update_subscription(virtual_sensors, num_virtual_sensors, sensor_settings, &num_sensor_settings);
142 ESP_LOGV(TAG, "%s: updating subscription for %d virtual sensors (out=%d sensors)", this->device_id_,
143 num_virtual_sensors, num_sensor_settings);
144}
145
147 ESP_LOGCONFIG(TAG, "%s via BSEC:", this->device_id_);
148
149 bsec_version_t version;
150 bsec_get_version(&version);
151 ESP_LOGCONFIG(TAG, " BSEC Version: %d.%d.%d.%d", version.major, version.minor, version.major_bugfix,
152 version.minor_bugfix);
153
154 LOG_I2C_DEVICE(this);
155
156 if (this->is_failed()) {
157 ESP_LOGE(TAG, "Communication failed (BSEC Status: %d, BME680 Status: %d)", this->bsec_status_,
158 this->bme680_status_);
159 }
160
161 ESP_LOGCONFIG(TAG,
162 " Temperature Offset: %.2f\n"
163 " IAQ Mode: %s\n"
164 " Supply Voltage: %sV\n"
165 " Sample Rate: %s\n"
166 " State Save Interval: %" PRIu32 "ms",
168 this->iaq_mode_ == IAQ_MODE_STATIC ? LOG_STR_LITERAL("Static") : LOG_STR_LITERAL("Mobile"),
169 this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? LOG_STR_LITERAL("3.3") : LOG_STR_LITERAL("1.8"),
170 BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_);
171
172 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
173 ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
174 LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
175 ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
176 LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
177 ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
178 LOG_SENSOR(" ", "Gas Resistance", this->gas_resistance_sensor_);
179 LOG_SENSOR(" ", "IAQ", this->iaq_sensor_);
180 LOG_SENSOR(" ", "Numeric IAQ Accuracy", this->iaq_accuracy_sensor_);
181 LOG_TEXT_SENSOR(" ", "IAQ Accuracy", this->iaq_accuracy_text_sensor_);
182 LOG_SENSOR(" ", "CO2 Equivalent", this->co2_equivalent_sensor_);
183 LOG_SENSOR(" ", "Breath VOC Equivalent", this->breath_voc_equivalent_sensor_);
184}
185
187 this->run_();
188
190 this->status_set_error();
191 } else {
192 this->status_clear_error();
193 }
195
196 // Process a single action from the queue. These are primarily sensor state publishes
197 // that in totality take too long to send in a single call.
198 if (this->queue_.size()) {
199 auto action = std::move(this->queue_.front());
200 this->queue_.pop();
201 action();
202 }
203}
204
206 int64_t curr_time_ns = this->get_time_ns_();
207 if (curr_time_ns < this->next_call_ns_) {
208 return;
209 }
210
211 ESP_LOGV(TAG, "%s: Performing sensor run", this->device_id_);
212
213 // Restore BSEC library state
214 // The reinit_bsec_lib_ method is computationally expensive: it takes 1200รท2900 microseconds on a ESP32.
215 // It can be skipped entirely when there is only one device (since the BSEC library won't be shared)
216 if (BME680BSECComponent::instances.size() > 1) {
217 int res = this->reinit_bsec_lib_();
218 if (res != 0)
219 return;
220 }
221
222 this->bsec_status_ = bsec_sensor_control(curr_time_ns, &this->bme680_settings_);
223 if (this->bsec_status_ < BSEC_OK) {
224 ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC Error Code %d)", this->bsec_status_);
225 return;
226 }
227 this->next_call_ns_ = this->bme680_settings_.next_call;
228
229 if (this->bme680_settings_.trigger_measurement) {
230 this->bme680_.tph_sett.os_temp = this->bme680_settings_.temperature_oversampling;
231 this->bme680_.tph_sett.os_pres = this->bme680_settings_.pressure_oversampling;
232 this->bme680_.tph_sett.os_hum = this->bme680_settings_.humidity_oversampling;
233 this->bme680_.gas_sett.run_gas = this->bme680_settings_.run_gas;
234 this->bme680_.gas_sett.heatr_temp = this->bme680_settings_.heater_temperature;
235 this->bme680_.gas_sett.heatr_dur = this->bme680_settings_.heating_duration;
236 this->bme680_.power_mode = BME680_FORCED_MODE;
237 uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
238 this->bme680_status_ = bme680_set_sensor_settings(desired_settings, &this->bme680_);
239 if (this->bme680_status_ != BME680_OK) {
240 ESP_LOGW(TAG, "Failed to set sensor settings (BME680 Error Code %d)", this->bme680_status_);
241 return;
242 }
243
244 this->bme680_status_ = bme680_set_sensor_mode(&this->bme680_);
245 if (this->bme680_status_ != BME680_OK) {
246 ESP_LOGW(TAG, "Failed to set sensor mode (BME680 Error Code %d)", this->bme680_status_);
247 return;
248 }
249
250 uint16_t meas_dur = 0;
251 bme680_get_profile_dur(&meas_dur, &this->bme680_);
252
253 // Since we are about to go "out of scope" in the loop, take a snapshot of the state now so we can restore it later
254 // TODO: it would be interesting to see if this is really needed here, or if it's needed only after each
255 // bsec_do_steps() call
256 if (BME680BSECComponent::instances.size() > 1)
257 this->snapshot_state_();
258
259 ESP_LOGV(TAG, "Queueing read in %ums", meas_dur);
260 this->set_timeout(READ_TIMEOUT_ID, meas_dur, [this]() { this->read_(); });
261 } else {
262 ESP_LOGV(TAG, "Measurement not required");
263 this->read_();
264 }
265}
266
268 ESP_LOGV(TAG, "%s: Reading data", this->device_id_);
269 int64_t curr_time_ns = this->get_time_ns_();
270
271 if (this->bme680_settings_.trigger_measurement) {
272 uint32_t start = millis();
273 while (this->bme680_.power_mode != BME680_SLEEP_MODE) {
274 if (millis() - start > 50) {
275 ESP_LOGE(TAG, "Timeout waiting for BME680 to enter sleep mode");
276 return;
277 }
278 this->bme680_status_ = bme680_get_sensor_mode(&this->bme680_);
279 if (this->bme680_status_ != BME680_OK) {
280 ESP_LOGE(TAG, "Failed to get sensor mode (BME680 Error Code %d)", this->bme680_status_);
281 return;
282 }
283 }
284 }
285
286 if (!this->bme680_settings_.process_data) {
287 ESP_LOGV(TAG, "Data processing not required");
288 return;
289 }
290
291 struct bme680_field_data data;
292 this->bme680_status_ = bme680_get_sensor_data(&data, &this->bme680_);
293
294 if (this->bme680_status_ != BME680_OK) {
295 ESP_LOGW(TAG, "Failed to get sensor data (BME680 Error Code %d)", this->bme680_status_);
296 return;
297 }
298 if (!(data.status & BME680_NEW_DATA_MSK)) {
299 ESP_LOGD(TAG, "BME680 did not report new data");
300 return;
301 }
302
303 bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance
304 uint8_t num_inputs = 0;
305
306 if (this->bme680_settings_.process_data & BSEC_PROCESS_TEMPERATURE) {
307 inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
308 inputs[num_inputs].signal = data.temperature / 100.0f;
309 inputs[num_inputs].time_stamp = curr_time_ns;
310 num_inputs++;
311
312 // Temperature offset from the real temperature due to external heat sources
313 inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
314 inputs[num_inputs].signal = this->temperature_offset_;
315 inputs[num_inputs].time_stamp = curr_time_ns;
316 num_inputs++;
317 }
318 if (this->bme680_settings_.process_data & BSEC_PROCESS_HUMIDITY) {
319 inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
320 inputs[num_inputs].signal = data.humidity / 1000.0f;
321 inputs[num_inputs].time_stamp = curr_time_ns;
322 num_inputs++;
323 }
324 if (this->bme680_settings_.process_data & BSEC_PROCESS_PRESSURE) {
325 inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
326 inputs[num_inputs].signal = data.pressure;
327 inputs[num_inputs].time_stamp = curr_time_ns;
328 num_inputs++;
329 }
330 if (this->bme680_settings_.process_data & BSEC_PROCESS_GAS) {
331 if (data.status & BME680_GASM_VALID_MSK) {
332 inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
333 inputs[num_inputs].signal = data.gas_resistance;
334 inputs[num_inputs].time_stamp = curr_time_ns;
335 num_inputs++;
336 } else {
337 ESP_LOGD(TAG, "BME680 did not report gas data");
338 }
339 }
340 if (num_inputs < 1) {
341 ESP_LOGD(TAG, "No signal inputs available for BSEC");
342 return;
343 }
344
345 // Restore BSEC library state
346 // The reinit_bsec_lib_ method is computationally expensive: it takes 1200รท2900 microseconds on a ESP32.
347 // It can be skipped entirely when there is only one device (since the BSEC library won't be shared)
349 int res = this->reinit_bsec_lib_();
350 if (res != 0)
351 return;
352 // Now that the BSEC library has been re-initialized, bsec_sensor_control *NEEDS* to be called in order to support
353 // multiple devices with a different set of enabled sensors (even if the bme680_settings_ data is not used)
354 this->bsec_status_ = bsec_sensor_control(curr_time_ns, &this->bme680_settings_);
355 if (this->bsec_status_ < BSEC_OK) {
356 ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC Error Code %d)", this->bsec_status_);
357 return;
358 }
359 }
360
361 bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
362 uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
363 this->bsec_status_ = bsec_do_steps(inputs, num_inputs, outputs, &num_outputs);
364 if (this->bsec_status_ != BSEC_OK) {
365 ESP_LOGW(TAG, "BSEC failed to process signals (BSEC Error Code %d)", this->bsec_status_);
366 return;
367 }
368 ESP_LOGV(TAG, "%s: after bsec_do_steps: num_inputs=%d num_outputs=%d", this->device_id_, num_inputs, num_outputs);
369
370 // Since we are about to go "out of scope" in the loop, take a snapshot of the state now so we can restore it later
372 this->snapshot_state_();
373
374 if (num_outputs < 1) {
375 ESP_LOGD(TAG, "No signal outputs provided by BSEC");
376 return;
377 }
378
379 this->publish_(outputs, num_outputs);
380}
381
382void BME680BSECComponent::publish_(const bsec_output_t *outputs, uint8_t num_outputs) {
383 ESP_LOGV(TAG, "%s: Queuing sensor state publish actions", this->device_id_);
384 for (uint8_t i = 0; i < num_outputs; i++) {
385 float signal = outputs[i].signal;
386 switch (outputs[i].sensor_id) {
387 case BSEC_OUTPUT_IAQ:
388 case BSEC_OUTPUT_STATIC_IAQ: {
389 uint8_t accuracy = std::min<uint8_t>(outputs[i].accuracy, std::size(IAQ_ACCURACY_STATES) - 1);
390 this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); });
391 this->queue_push_([this, accuracy]() {
392 this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[accuracy]);
393 });
394 this->queue_push_([this, accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, accuracy, true); });
395
396 // Queue up an opportunity to save state
397 this->queue_push_([this, accuracy]() { this->save_state_(accuracy); });
398 } break;
399 case BSEC_OUTPUT_CO2_EQUIVALENT:
400 this->queue_push_([this, signal]() { this->publish_sensor_(this->co2_equivalent_sensor_, signal); });
401 break;
402 case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
403 this->queue_push_([this, signal]() { this->publish_sensor_(this->breath_voc_equivalent_sensor_, signal); });
404 break;
405 case BSEC_OUTPUT_RAW_PRESSURE:
406 this->queue_push_([this, signal]() { this->publish_sensor_(this->pressure_sensor_, signal / 100.0f); });
407 break;
408 case BSEC_OUTPUT_RAW_GAS:
409 this->queue_push_([this, signal]() { this->publish_sensor_(this->gas_resistance_sensor_, signal); });
410 break;
411 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
412 this->queue_push_([this, signal]() { this->publish_sensor_(this->temperature_sensor_, signal); });
413 break;
414 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
415 this->queue_push_([this, signal]() { this->publish_sensor_(this->humidity_sensor_, signal); });
416 break;
417 }
418 }
419}
420
422 int64_t time_ms = millis();
423 if (this->last_time_ms_ > time_ms) {
425 }
426 this->last_time_ms_ = time_ms;
427
428 return (time_ms + ((int64_t) this->millis_overflow_counter_ << 32)) * INT64_C(1000000);
429}
430
431void BME680BSECComponent::publish_sensor_(sensor::Sensor *sensor, float value, bool change_only) {
432 if (!sensor || (change_only && sensor->has_state() && sensor->state == value)) {
433 return;
434 }
435 sensor->publish_state(value);
436}
437
438void BME680BSECComponent::publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value) {
439 if (!sensor || (sensor->has_state() && sensor->state == value)) {
440 return;
441 }
442 sensor->publish_state(value);
443}
444
445// Communication function - read
446// First parameter is the "dev_id" member of our "bme680_" object, which is passed-back here as-is
447int8_t BME680BSECComponent::read_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len) {
448 BME680BSECComponent *inst = instances[devid];
449 // Use the I2CDevice::read_bytes method to perform the actual I2C register read
450 return inst->read_bytes(a_register, data, len) ? 0 : -1;
451}
452
453// Communication function - write
454// First parameter is the "dev_id" member of our "bme680_" object, which is passed-back here as-is
455int8_t BME680BSECComponent::write_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len) {
456 BME680BSECComponent *inst = instances[devid];
457 // Use the I2CDevice::write_bytes method to perform the actual I2C register write
458 return inst->write_bytes(a_register, data, len) ? 0 : -1;
459}
460
462 ESP_LOGV(TAG, "Delaying for %" PRIu32 "ms", period);
463 delay(period);
464}
465
466// Fetch the BSEC library state and save it in the bsec_state_data_ member (volatile memory)
467// Used to share the library when using more than one sensor
469 uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
470 this->bsec_status_ = bsec_get_state(0, this->bsec_state_data_, BSEC_MAX_STATE_BLOB_SIZE, this->work_buffer_,
471 sizeof(this->work_buffer_), &num_serialized_state);
472 if (this->bsec_status_ != BSEC_OK) {
473 ESP_LOGW(TAG, "%s: Failed to fetch BSEC library state for snapshot (BSEC Error Code %d)", this->device_id_,
474 this->bsec_status_);
475 return;
476 }
477 this->bsec_state_data_valid_ = true;
478}
479
480// Restores the BSEC library state from a snapshot in memory
481// Used to share the library when using more than one sensor
483 if (!this->bsec_state_data_valid_) {
484 ESP_LOGV(TAG, "%s: BSEC state data NOT valid, aborting restore_state_()", this->device_id_);
485 return;
486 }
487
488 this->bsec_status_ =
489 bsec_set_state(this->bsec_state_data_, BSEC_MAX_STATE_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
490 if (this->bsec_status_ != BSEC_OK) {
491 ESP_LOGW(TAG, "Failed to restore BSEC library state (BSEC Error Code %d)", this->bsec_status_);
492 return;
493 }
494}
495
497 this->bsec_status_ = bsec_init();
498 if (this->bsec_status_ != BSEC_OK) {
499 this->mark_failed();
500 return -1;
501 }
502
503 this->set_config_();
504 if (this->bsec_status_ != BSEC_OK) {
505 this->mark_failed();
506 return -2;
507 }
508
509 this->restore_state_();
510
511 this->update_subscription_();
512 if (this->bsec_status_ != BSEC_OK) {
513 this->mark_failed();
514 return -3;
515 }
516
517 return 0;
518}
519
521 uint32_t hash = fnv1_hash_extend(fnv1_hash("bme680_bsec_state_"), this->device_id_);
522 this->bsec_state_ = global_preferences->make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
523
524 if (!this->bsec_state_.load(&this->bsec_state_data_)) {
525 // No saved BSEC library state available
526 return;
527 }
528
529 ESP_LOGV(TAG, "%s: Loading BSEC library state", this->device_id_);
530 this->bsec_status_ =
531 bsec_set_state(this->bsec_state_data_, BSEC_MAX_STATE_BLOB_SIZE, this->work_buffer_, sizeof(this->work_buffer_));
532 if (this->bsec_status_ != BSEC_OK) {
533 ESP_LOGW(TAG, "%s: Failed to load BSEC library state (BSEC Error Code %d)", this->device_id_, this->bsec_status_);
534 return;
535 }
536 // All OK: set the BSEC state data as valid
537 this->bsec_state_data_valid_ = true;
538 ESP_LOGI(TAG, "%s: Loaded BSEC library state", this->device_id_);
539}
540
541void BME680BSECComponent::save_state_(uint8_t accuracy) {
542 if (accuracy < 3 || (millis() - this->last_state_save_ms_ < this->state_save_interval_ms_)) {
543 return;
544 }
545 if (BME680BSECComponent::instances.size() <= 1) {
546 // When a single device is in use, no snapshot is taken regularly so one is taken now
547 // On multiple devices, a snapshot is taken at every loop, so there is no need to take one here
548 this->snapshot_state_();
549 }
550 if (!this->bsec_state_data_valid_)
551 return;
552
553 ESP_LOGV(TAG, "%s: Saving state", this->device_id_);
554
555 if (!this->bsec_state_.save(&this->bsec_state_data_)) {
556 ESP_LOGW(TAG, "Failed to save state");
557 return;
558 }
559 this->last_state_save_ms_ = millis();
560
561 ESP_LOGI(TAG, "Saved state");
562}
563#endif
564} // namespace esphome::bme680_bsec
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void status_clear_error()
Definition component.h:302
bool status_comm_result(bool ok)
Clears the warning when ok, else sets it with message (default "Communication failed"); returns ok.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
bool has_state() const
std::queue< std::function< void()> > queue_
static std::vector< BME680BSECComponent * > instances
Definition bme680_bsec.h:60
static int8_t read_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len)
static uint8_t work_buffer_[BSEC_MAX_WORKBUFFER_SIZE]
Definition bme680_bsec.h:96
void queue_push_(std::function< void()> &&f)
Definition bme680_bsec.h:94
float calc_sensor_sample_rate_(SampleRate sample_rate)
static int8_t write_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len)
uint8_t bsec_state_data_[BSEC_MAX_STATE_BLOB_SIZE]
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_
static void delay_ms(uint32_t period)
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const
Definition i2c.h:298
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:249
Base-class for all sensors.
Definition sensor.h:47
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:140
void publish_state(const std::string &state)
Definition text_sensor.h:42
ESPHOME_LOG_TAG(TAG, "bme680_bsec.sensor")
constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value)
Extend a FNV-1 hash with an integer (hashes each byte).
Definition helpers.h:928
const void size_t len
Definition hal.h:68
uint16_t size
Definition helpers.cpp:34
ESPPreferences * global_preferences
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:169
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
static void uint32_t
ESPPreferenceObject make_preference(size_t, uint32_t, bool)
Definition preferences.h:24