9static const char *
const TAG =
"bme680_bsec.sensor";
11static const std::string IAQ_ACCURACY_STATES[4] = {
"Stabilizing",
"Uncertain",
"Calibrating",
"Calibrated"};
13std::vector<BME680BSECComponent *>
29 this->
bme680_.intf = BME680_I2C_INTF;
54 const uint8_t config[] = {
55#include "config/generic_33v_300s_28d/bsec_iaq.txt"
60 const uint8_t config[] = {
61#include "config/generic_18v_300s_28d/bsec_iaq.txt"
68 const uint8_t config[] = {
69#include "config/generic_33v_3s_28d/bsec_iaq.txt"
74 const uint8_t config[] = {
75#include "config/generic_18v_3s_28d/bsec_iaq.txt"
87 return sample_rate ==
SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
91 bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
92 int num_virtual_sensors = 0;
95 virtual_sensors[num_virtual_sensors].sensor_id =
98 num_virtual_sensors++;
102 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
104 num_virtual_sensors++;
108 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
110 num_virtual_sensors++;
114 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
116 num_virtual_sensors++;
120 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
122 num_virtual_sensors++;
126 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
128 num_virtual_sensors++;
132 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
134 num_virtual_sensors++;
137 bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
138 uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
140 bsec_update_subscription(virtual_sensors, num_virtual_sensors, sensor_settings, &num_sensor_settings);
141 ESP_LOGV(TAG,
"%s: updating subscription for %d virtual sensors (out=%d sensors)", this->
device_id_.c_str(),
142 num_virtual_sensors, num_sensor_settings);
146 ESP_LOGCONFIG(TAG,
"%s via BSEC:", this->
device_id_.c_str());
148 bsec_version_t version;
149 bsec_get_version(&version);
150 ESP_LOGCONFIG(TAG,
" BSEC Version: %d.%d.%d.%d", version.major, version.minor, version.major_bugfix,
151 version.minor_bugfix);
153 LOG_I2C_DEVICE(
this);
156 ESP_LOGE(TAG,
"Communication failed (BSEC Status: %d, BME680 Status: %d)", this->
bsec_status_,
161 " Temperature Offset: %.2f\n"
163 " Supply Voltage: %sV\n"
165 " State Save Interval: %ims",
202 if (this->
queue_.size()) {
203 auto action = std::move(this->
queue_.front());
215 ESP_LOGV(TAG,
"%s: Performing sensor run", this->
device_id_.c_str());
220 if (BME680BSECComponent::instances.size() > 1) {
228 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC Error Code %d)", this->
bsec_status_);
240 this->
bme680_.power_mode = BME680_FORCED_MODE;
241 uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
244 ESP_LOGW(TAG,
"Failed to set sensor settings (BME680 Error Code %d)", this->
bme680_status_);
250 ESP_LOGW(TAG,
"Failed to set sensor mode (BME680 Error Code %d)", this->
bme680_status_);
254 uint16_t meas_dur = 0;
255 bme680_get_profile_dur(&meas_dur, &this->
bme680_);
260 if (BME680BSECComponent::instances.size() > 1)
263 ESP_LOGV(TAG,
"Queueing read in %ums", meas_dur);
266 ESP_LOGV(TAG,
"Measurement not required");
272 ESP_LOGV(TAG,
"%s: Reading data", this->
device_id_.c_str());
276 while (this->
bme680_.power_mode != BME680_SLEEP_MODE) {
279 ESP_LOGW(TAG,
"Failed to get sensor mode (BME680 Error Code %d)", this->
bme680_status_);
285 ESP_LOGV(TAG,
"Data processing not required");
289 struct bme680_field_data data;
292 if (this->bme680_status_ != BME680_OK) {
293 ESP_LOGW(TAG,
"Failed to get sensor data (BME680 Error Code %d)", this->bme680_status_);
296 if (!(data.status & BME680_NEW_DATA_MSK)) {
297 ESP_LOGD(TAG,
"BME680 did not report new data");
301 bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR];
302 uint8_t num_inputs = 0;
305 inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
306 inputs[num_inputs].signal = data.temperature / 100.0f;
307 inputs[num_inputs].time_stamp = curr_time_ns;
311 inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
313 inputs[num_inputs].time_stamp = curr_time_ns;
317 inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
318 inputs[num_inputs].signal = data.humidity / 1000.0f;
319 inputs[num_inputs].time_stamp = curr_time_ns;
323 inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
324 inputs[num_inputs].signal = data.pressure;
325 inputs[num_inputs].time_stamp = curr_time_ns;
329 if (data.status & BME680_GASM_VALID_MSK) {
330 inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
331 inputs[num_inputs].signal = data.gas_resistance;
332 inputs[num_inputs].time_stamp = curr_time_ns;
335 ESP_LOGD(TAG,
"BME680 did not report gas data");
338 if (num_inputs < 1) {
339 ESP_LOGD(TAG,
"No signal inputs available for BSEC");
354 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC Error Code %d)", this->
bsec_status_);
359 bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
360 uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
361 this->
bsec_status_ = bsec_do_steps(inputs, num_inputs, outputs, &num_outputs);
363 ESP_LOGW(TAG,
"BSEC failed to process signals (BSEC Error Code %d)", this->
bsec_status_);
366 ESP_LOGV(TAG,
"%s: after bsec_do_steps: num_inputs=%d num_outputs=%d", this->
device_id_.c_str(), num_inputs,
373 if (num_outputs < 1) {
374 ESP_LOGD(TAG,
"No signal outputs provided by BSEC");
378 this->
publish_(outputs, num_outputs);
382 ESP_LOGV(TAG,
"%s: Queuing sensor state publish actions", this->
device_id_.c_str());
383 for (uint8_t i = 0; i < num_outputs; i++) {
384 float signal = outputs[i].signal;
385 switch (outputs[i].sensor_id) {
386 case BSEC_OUTPUT_IAQ:
387 case BSEC_OUTPUT_STATIC_IAQ: {
388 uint8_t accuracy = outputs[i].accuracy;
398 case BSEC_OUTPUT_CO2_EQUIVALENT:
401 case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
404 case BSEC_OUTPUT_RAW_PRESSURE:
407 case BSEC_OUTPUT_RAW_GAS:
410 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
413 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
421 int64_t time_ms =
millis();
431 if (!sensor || (change_only && sensor->
has_state() && sensor->
state == value)) {
461 ESP_LOGV(TAG,
"Delaying for %ums", period);
468 uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
472 ESP_LOGW(TAG,
"%s: Failed to fetch BSEC library state for snapshot (BSEC Error Code %d)", this->
device_id_.c_str(),
483 ESP_LOGV(TAG,
"%s: BSEC state data NOT valid, aborting restore_state_()", this->
device_id_.c_str());
490 ESP_LOGW(TAG,
"Failed to restore BSEC library state (BSEC Error Code %d)", this->
bsec_status_);
528 ESP_LOGV(TAG,
"%s: Loading BSEC library state", this->
device_id_.c_str());
532 ESP_LOGW(TAG,
"%s: Failed to load BSEC library state (BSEC Error Code %d)", this->
device_id_.c_str(),
538 ESP_LOGI(TAG,
"%s: Loaded BSEC library state", this->
device_id_.c_str());
545 if (BME680BSECComponent::instances.size() <= 1) {
553 ESP_LOGV(TAG,
"%s: Saving state", this->
device_id_.c_str());
556 ESP_LOGW(TAG,
"Failed to save state");
561 ESP_LOGI(TAG,
"Saved state");
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message=nullptr)
void status_clear_error()
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.
void status_set_error(const char *message=nullptr)
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.
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()