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,
"Running setup for '%s'", 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_,
163 " Temperature Offset: %.2f\n"
165 " Supply Voltage: %sV\n"
167 " State Save Interval: %ims",
204 if (this->
queue_.size()) {
205 auto action = std::move(this->
queue_.front());
217 ESP_LOGV(TAG,
"%s: Performing sensor run", this->
device_id_.c_str());
222 if (BME680BSECComponent::instances.size() > 1) {
230 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC Error Code %d)", this->
bsec_status_);
242 this->
bme680_.power_mode = BME680_FORCED_MODE;
243 uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
246 ESP_LOGW(TAG,
"Failed to set sensor settings (BME680 Error Code %d)", this->
bme680_status_);
252 ESP_LOGW(TAG,
"Failed to set sensor mode (BME680 Error Code %d)", this->
bme680_status_);
256 uint16_t meas_dur = 0;
257 bme680_get_profile_dur(&meas_dur, &this->
bme680_);
262 if (BME680BSECComponent::instances.size() > 1)
265 ESP_LOGV(TAG,
"Queueing read in %ums", meas_dur);
268 ESP_LOGV(TAG,
"Measurement not required");
274 ESP_LOGV(TAG,
"%s: Reading data", this->
device_id_.c_str());
278 while (this->
bme680_.power_mode != BME680_SLEEP_MODE) {
281 ESP_LOGW(TAG,
"Failed to get sensor mode (BME680 Error Code %d)", this->
bme680_status_);
287 ESP_LOGV(TAG,
"Data processing not required");
291 struct bme680_field_data data;
294 if (this->bme680_status_ != BME680_OK) {
295 ESP_LOGW(TAG,
"Failed to get sensor data (BME680 Error Code %d)", this->bme680_status_);
298 if (!(data.status & BME680_NEW_DATA_MSK)) {
299 ESP_LOGD(TAG,
"BME680 did not report new data");
303 bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR];
304 uint8_t num_inputs = 0;
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;
313 inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
315 inputs[num_inputs].time_stamp = curr_time_ns;
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;
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;
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;
337 ESP_LOGD(TAG,
"BME680 did not report gas data");
340 if (num_inputs < 1) {
341 ESP_LOGD(TAG,
"No signal inputs available for BSEC");
356 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC Error Code %d)", this->
bsec_status_);
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);
365 ESP_LOGW(TAG,
"BSEC failed to process signals (BSEC Error Code %d)", this->
bsec_status_);
368 ESP_LOGV(TAG,
"%s: after bsec_do_steps: num_inputs=%d num_outputs=%d", this->
device_id_.c_str(), num_inputs,
375 if (num_outputs < 1) {
376 ESP_LOGD(TAG,
"No signal outputs provided by BSEC");
380 this->
publish_(outputs, num_outputs);
384 ESP_LOGV(TAG,
"%s: Queuing sensor state publish actions", this->
device_id_.c_str());
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 case BSEC_OUTPUT_STATIC_IAQ: {
390 uint8_t accuracy = outputs[i].accuracy;
400 case BSEC_OUTPUT_CO2_EQUIVALENT:
403 case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
406 case BSEC_OUTPUT_RAW_PRESSURE:
409 case BSEC_OUTPUT_RAW_GAS:
412 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
415 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
423 int64_t time_ms =
millis();
433 if (!sensor || (change_only && sensor->
has_state() && sensor->
state == value)) {
463 ESP_LOGV(TAG,
"Delaying for %ums", period);
470 uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
474 ESP_LOGW(TAG,
"%s: Failed to fetch BSEC library state for snapshot (BSEC Error Code %d)", this->
device_id_.c_str(),
485 ESP_LOGV(TAG,
"%s: BSEC state data NOT valid, aborting restore_state_()", this->
device_id_.c_str());
492 ESP_LOGW(TAG,
"Failed to restore BSEC library state (BSEC Error Code %d)", this->
bsec_status_);
530 ESP_LOGV(TAG,
"%s: Loading BSEC library state", this->
device_id_.c_str());
534 ESP_LOGW(TAG,
"%s: Failed to load BSEC library state (BSEC Error Code %d)", this->
device_id_.c_str(),
540 ESP_LOGI(TAG,
"%s: Loaded BSEC library state", this->
device_id_.c_str());
547 if (BME680BSECComponent::instances.size() <= 1) {
555 ESP_LOGV(TAG,
"%s: Saving state", this->
device_id_.c_str());
558 ESP_LOGW(TAG,
"Failed to save state");
563 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.
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()