7 namespace bme680_bsec {
9 static const char *
const TAG =
"bme680_bsec.sensor";
11 static const std::string IAQ_ACCURACY_STATES[4] = {
"Stabilizing",
"Uncertain",
"Calibrating",
"Calibrated"};
13 std::vector<BME680BSECComponent *>
18 ESP_LOGCONFIG(TAG,
"Setting up BME680(%s) via BSEC...", this->
device_id_.c_str());
20 uint8_t new_idx = BME680BSECComponent::instances.size();
21 BME680BSECComponent::instances.push_back(
this);
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());
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_));
187 if (this->bsec_status_ < BSEC_OK || this->
bme680_status_ < BME680_OK) {
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");
344 if (BME680BSECComponent::instances.size() > 1) {
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,
368 if (BME680BSECComponent::instances.size() > 1)
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)) {
447 return inst->
read_bytes(a_register, data, len) ? 0 : -1;
455 return inst->
write_bytes(a_register, data, len) ? 0 : -1;
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());
529 if (this->bsec_status_ != BSEC_OK) {
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");
text_sensor::TextSensor * iaq_accuracy_text_sensor_
SampleRate humidity_sample_rate_
const float DATA
For components that import data from directly connected sensors like DHT.
sensor::Sensor * breath_voc_equivalent_sensor_
bsec_library_return_t bsec_status_
void status_set_warning(const char *message="unspecified")
void queue_push_(std::function< void()> &&f)
void publish_(const bsec_output_t *outputs, uint8_t num_outputs)
float temperature_offset_
static void delay_ms(uint32_t period)
float calc_sensor_sample_rate_(SampleRate sample_rate)
sensor::Sensor * pressure_sensor_
void save_state_(uint8_t accuracy)
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
uint32_t millis_overflow_counter_
static int8_t read_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len)
void publish_state(const std::string &state)
uint32_t last_state_save_ms_
sensor::Sensor * co2_equivalent_sensor_
uint32_t IRAM_ATTR HOT millis()
uint8_t bsec_state_data_[BSEC_MAX_STATE_BLOB_SIZE]
struct bme680_dev bme680_
sensor::Sensor * humidity_sensor_
ESPPreferenceObject bsec_state_
float state
This member variable stores the last state that has passed through all filters.
void status_set_error(const char *message="unspecified")
bsec_bme_settings_t bme680_settings_
ESPPreferences * global_preferences
void status_clear_warning()
uint32_t state_save_interval_ms_
static std::vector< BME680BSECComponent * > instances
void publish_state(float state)
Publish a new state to the front-end.
sensor::Sensor * gas_resistance_sensor_
void dump_config() override
sensor::Sensor * temperature_sensor_
void status_clear_error()
SampleRate pressure_sample_rate_
bool bsec_state_data_valid_
void update_subscription_()
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
static int8_t write_bytes_wrapper(uint8_t devid, uint8_t a_register, uint8_t *data, uint16_t len)
uint32_t fnv1_hash(const std::string &str)
Calculate a FNV-1 hash of str.
float get_setup_priority() const override
virtual void mark_failed()
Mark this component as failed.
sensor::Sensor * iaq_accuracy_sensor_
Implementation of SPI Controller mode.
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
static uint8_t work_buffer_[BSEC_MAX_WORKBUFFER_SIZE]
Base-class for all sensors.
void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only=false)
std::queue< std::function< void()> > queue_
sensor::Sensor * iaq_sensor_
SupplyVoltage supply_voltage_
SampleRate temperature_sample_rate_
esphome::sensor::Sensor * sensor
void IRAM_ATTR HOT delay(uint32_t ms)
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len, bool stop=true)