9static const char *
const TAG =
"bme680_bsec.sensor";
11static const std::string IAQ_ACCURACY_STATES[4] = {
"Stabilizing",
"Uncertain",
"Calibrating",
"Calibrated"};
13std::vector<BME680BSECComponent *>
18 ESP_LOGCONFIG(TAG,
"Setting up BME680(%s) via BSEC...", this->
device_id_.c_str());
31 this->
bme680_.intf = BME680_I2C_INTF;
56 const uint8_t config[] = {
57#include "config/generic_33v_300s_28d/bsec_iaq.txt"
62 const uint8_t config[] = {
63#include "config/generic_18v_300s_28d/bsec_iaq.txt"
70 const uint8_t config[] = {
71#include "config/generic_33v_3s_28d/bsec_iaq.txt"
76 const uint8_t config[] = {
77#include "config/generic_18v_3s_28d/bsec_iaq.txt"
89 return sample_rate ==
SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
93 bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
94 int num_virtual_sensors = 0;
97 virtual_sensors[num_virtual_sensors].sensor_id =
100 num_virtual_sensors++;
104 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
106 num_virtual_sensors++;
110 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
112 num_virtual_sensors++;
116 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
118 num_virtual_sensors++;
122 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
124 num_virtual_sensors++;
128 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
130 num_virtual_sensors++;
134 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
136 num_virtual_sensors++;
139 bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
140 uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
142 bsec_update_subscription(virtual_sensors, num_virtual_sensors, sensor_settings, &num_sensor_settings);
143 ESP_LOGV(TAG,
"%s: updating subscription for %d virtual sensors (out=%d sensors)", this->
device_id_.c_str(),
144 num_virtual_sensors, num_sensor_settings);
148 ESP_LOGCONFIG(TAG,
"%s via BSEC:", this->
device_id_.c_str());
150 bsec_version_t version;
151 bsec_get_version(&version);
152 ESP_LOGCONFIG(TAG,
" BSEC Version: %d.%d.%d.%d", version.major, version.minor, version.major_bugfix,
153 version.minor_bugfix);
155 LOG_I2C_DEVICE(
this);
158 ESP_LOGE(TAG,
"Communication failed (BSEC Status: %d, BME680 Status: %d)", this->
bsec_status_,
165 ESP_LOGCONFIG(TAG,
" Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->
sample_rate_));
200 if (this->
queue_.size()) {
201 auto action = std::move(this->
queue_.front());
213 ESP_LOGV(TAG,
"%s: Performing sensor run", this->
device_id_.c_str());
218 if (BME680BSECComponent::instances.size() > 1) {
226 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC Error Code %d)", this->
bsec_status_);
238 this->
bme680_.power_mode = BME680_FORCED_MODE;
239 uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
242 ESP_LOGW(TAG,
"Failed to set sensor settings (BME680 Error Code %d)", this->
bme680_status_);
248 ESP_LOGW(TAG,
"Failed to set sensor mode (BME680 Error Code %d)", this->
bme680_status_);
252 uint16_t meas_dur = 0;
253 bme680_get_profile_dur(&meas_dur, &this->
bme680_);
258 if (BME680BSECComponent::instances.size() > 1)
261 ESP_LOGV(TAG,
"Queueing read in %ums", meas_dur);
264 ESP_LOGV(TAG,
"Measurement not required");
270 ESP_LOGV(TAG,
"%s: Reading data", this->
device_id_.c_str());
274 while (this->
bme680_.power_mode != BME680_SLEEP_MODE) {
277 ESP_LOGW(TAG,
"Failed to get sensor mode (BME680 Error Code %d)", this->
bme680_status_);
283 ESP_LOGV(TAG,
"Data processing not required");
287 struct bme680_field_data data;
290 if (this->bme680_status_ != BME680_OK) {
291 ESP_LOGW(TAG,
"Failed to get sensor data (BME680 Error Code %d)", this->bme680_status_);
294 if (!(data.status & BME680_NEW_DATA_MSK)) {
295 ESP_LOGD(TAG,
"BME680 did not report new data");
299 bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR];
300 uint8_t num_inputs = 0;
303 inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
304 inputs[num_inputs].signal = data.temperature / 100.0f;
305 inputs[num_inputs].time_stamp = curr_time_ns;
309 inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
311 inputs[num_inputs].time_stamp = curr_time_ns;
315 inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
316 inputs[num_inputs].signal = data.humidity / 1000.0f;
317 inputs[num_inputs].time_stamp = curr_time_ns;
321 inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
322 inputs[num_inputs].signal = data.pressure;
323 inputs[num_inputs].time_stamp = curr_time_ns;
327 if (data.status & BME680_GASM_VALID_MSK) {
328 inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
329 inputs[num_inputs].signal = data.gas_resistance;
330 inputs[num_inputs].time_stamp = curr_time_ns;
333 ESP_LOGD(TAG,
"BME680 did not report gas data");
336 if (num_inputs < 1) {
337 ESP_LOGD(TAG,
"No signal inputs available for BSEC");
352 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC Error Code %d)", this->
bsec_status_);
357 bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
358 uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
359 this->
bsec_status_ = bsec_do_steps(inputs, num_inputs, outputs, &num_outputs);
361 ESP_LOGW(TAG,
"BSEC failed to process signals (BSEC Error Code %d)", this->
bsec_status_);
364 ESP_LOGV(TAG,
"%s: after bsec_do_steps: num_inputs=%d num_outputs=%d", this->
device_id_.c_str(), num_inputs,
371 if (num_outputs < 1) {
372 ESP_LOGD(TAG,
"No signal outputs provided by BSEC");
376 this->
publish_(outputs, num_outputs);
380 ESP_LOGV(TAG,
"%s: Queuing sensor state publish actions", this->
device_id_.c_str());
381 for (uint8_t i = 0; i < num_outputs; i++) {
382 float signal = outputs[i].signal;
383 switch (outputs[i].sensor_id) {
384 case BSEC_OUTPUT_IAQ:
385 case BSEC_OUTPUT_STATIC_IAQ: {
386 uint8_t accuracy = outputs[i].accuracy;
396 case BSEC_OUTPUT_CO2_EQUIVALENT:
399 case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
402 case BSEC_OUTPUT_RAW_PRESSURE:
405 case BSEC_OUTPUT_RAW_GAS:
408 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
411 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
419 int64_t time_ms =
millis();
429 if (!sensor || (change_only && sensor->
has_state() && sensor->
state == value)) {
459 ESP_LOGV(TAG,
"Delaying for %ums", period);
466 uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
470 ESP_LOGW(TAG,
"%s: Failed to fetch BSEC library state for snapshot (BSEC Error Code %d)", this->
device_id_.c_str(),
481 ESP_LOGV(TAG,
"%s: BSEC state data NOT valid, aborting restore_state_()", this->
device_id_.c_str());
488 ESP_LOGW(TAG,
"Failed to restore BSEC library state (BSEC Error Code %d)", this->
bsec_status_);
526 ESP_LOGV(TAG,
"%s: Loading BSEC library state", this->
device_id_.c_str());
530 ESP_LOGW(TAG,
"%s: Failed to load BSEC library state (BSEC Error Code %d)", this->
device_id_.c_str(),
536 ESP_LOGI(TAG,
"%s: Loaded BSEC library state", this->
device_id_.c_str());
543 if (BME680BSECComponent::instances.size() <= 1) {
551 ESP_LOGV(TAG,
"%s: Saving state", this->
device_id_.c_str());
554 ESP_LOGW(TAG,
"Failed to save state");
559 ESP_LOGI(TAG,
"Saved state");
virtual void mark_failed()
Mark this component as failed.
void status_clear_error()
void status_set_warning(const char *message="unspecified")
void status_set_error(const char *message="unspecified")
void status_clear_warning()
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
ESPPreferenceObject bsec_state_
bsec_library_return_t bsec_status_
uint32_t state_save_interval_ms_
SampleRate pressure_sample_rate_
uint32_t millis_overflow_counter_
float temperature_offset_
sensor::Sensor * iaq_accuracy_sensor_
std::queue< std::function< void()> > queue_
static std::vector< BME680BSECComponent * > instances
sensor::Sensor * humidity_sensor_
SampleRate temperature_sample_rate_
static int8_t read_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len)
bool bsec_state_data_valid_
sensor::Sensor * breath_voc_equivalent_sensor_
SupplyVoltage supply_voltage_
void save_state_(uint8_t accuracy)
sensor::Sensor * iaq_sensor_
static uint8_t work_buffer_[BSEC_MAX_WORKBUFFER_SIZE]
void queue_push_(std::function< void()> &&f)
float calc_sensor_sample_rate_(SampleRate sample_rate)
uint32_t last_state_save_ms_
sensor::Sensor * co2_equivalent_sensor_
void dump_config() override
static int8_t write_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len)
float get_setup_priority() const override
sensor::Sensor * pressure_sensor_
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_
SampleRate humidity_sample_rate_
bsec_bme_settings_t bme680_settings_
sensor::Sensor * temperature_sensor_
sensor::Sensor * gas_resistance_sensor_
void update_subscription_()
struct bme680_dev bme680_
static void delay_ms(uint32_t period)
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len, bool stop=true)
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Base-class for all sensors.
void publish_state(float state)
Publish a new state to the front-end.
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet.
float state
This member variable stores the last state that has passed through all filters.
void publish_state(const std::string &state)
const float DATA
For components that import data from directly connected sensors like DHT.
Providing packet encoding functions for exchanging data with a remote host.
uint32_t fnv1_hash(const std::string &str)
Calculate a FNV-1 hash of str.
ESPPreferences * global_preferences
void IRAM_ATTR HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()