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"};
16 ESP_LOGCONFIG(TAG,
"Setting up BME680 via BSEC...");
17 BME680BSECComponent::instance =
this;
26 this->
bme680_.intf = BME680_I2C_INTF;
39 const uint8_t bsec_config[] = {
40 #include "config/generic_33v_300s_28d/bsec_iaq.txt" 44 const uint8_t bsec_config[] = {
45 #include "config/generic_33v_3s_28d/bsec_iaq.txt" 59 uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
60 this->
bsec_status_ = bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, work_buffer,
sizeof(work_buffer));
67 return sample_rate ==
SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
71 bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
72 int num_virtual_sensors = 0;
75 virtual_sensors[num_virtual_sensors].sensor_id =
78 num_virtual_sensors++;
82 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
84 num_virtual_sensors++;
88 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
90 num_virtual_sensors++;
94 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
96 num_virtual_sensors++;
100 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
102 num_virtual_sensors++;
106 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
108 num_virtual_sensors++;
112 virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
114 num_virtual_sensors++;
117 bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
118 uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
120 bsec_update_subscription(virtual_sensors, num_virtual_sensors, sensor_settings, &num_sensor_settings);
124 ESP_LOGCONFIG(TAG,
"BME680 via BSEC:");
127 bsec_get_version(&version);
128 ESP_LOGCONFIG(TAG,
" BSEC Version: %d.%d.%d.%d", version.major, version.minor, version.major_bugfix,
129 version.minor_bugfix);
131 LOG_I2C_DEVICE(
this);
134 ESP_LOGE(TAG,
"Communication failed (BSEC Status: %d, BME680 Status: %d)", this->
bsec_status_,
140 ESP_LOGCONFIG(TAG,
" Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->
sample_rate_));
162 if (this->bsec_status_ < BSEC_OK || this->
bme680_status_ < BME680_OK) {
175 if (this->
queue_.size()) {
176 auto action = std::move(this->
queue_.front());
188 ESP_LOGV(TAG,
"Performing sensor run");
190 bsec_bme_settings_t bme680_settings;
191 this->
bsec_status_ = bsec_sensor_control(curr_time_ns, &bme680_settings);
193 ESP_LOGW(TAG,
"Failed to fetch sensor control settings (BSEC Error Code %d)", this->
bsec_status_);
196 this->next_call_ns_ = bme680_settings.next_call;
198 if (bme680_settings.trigger_measurement) {
199 this->
bme680_.tph_sett.os_temp = bme680_settings.temperature_oversampling;
200 this->
bme680_.tph_sett.os_pres = bme680_settings.pressure_oversampling;
201 this->
bme680_.tph_sett.os_hum = bme680_settings.humidity_oversampling;
202 this->
bme680_.gas_sett.run_gas = bme680_settings.run_gas;
203 this->
bme680_.gas_sett.heatr_temp = bme680_settings.heater_temperature;
204 this->
bme680_.gas_sett.heatr_dur = bme680_settings.heating_duration;
205 this->
bme680_.power_mode = BME680_FORCED_MODE;
206 uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
209 ESP_LOGW(TAG,
"Failed to set sensor settings (BME680 Error Code %d)", this->
bme680_status_);
215 ESP_LOGW(TAG,
"Failed to set sensor mode (BME680 Error Code %d)", this->
bme680_status_);
219 uint16_t meas_dur = 0;
220 bme680_get_profile_dur(&meas_dur, &this->
bme680_);
221 ESP_LOGV(TAG,
"Queueing read in %ums", meas_dur);
223 [
this, curr_time_ns, bme680_settings]() { this->
read_(curr_time_ns, bme680_settings); });
225 ESP_LOGV(TAG,
"Measurement not required");
226 this->
read_(curr_time_ns, bme680_settings);
231 ESP_LOGV(TAG,
"Reading data");
233 if (bme680_settings.trigger_measurement) {
234 while (this->
bme680_.power_mode != BME680_SLEEP_MODE) {
237 ESP_LOGW(TAG,
"Failed to get sensor mode (BME680 Error Code %d)", this->
bme680_status_);
242 if (!bme680_settings.process_data) {
243 ESP_LOGV(TAG,
"Data processing not required");
247 struct bme680_field_data data;
250 if (this->bme680_status_ != BME680_OK) {
251 ESP_LOGW(TAG,
"Failed to get sensor data (BME680 Error Code %d)", this->bme680_status_);
254 if (!(data.status & BME680_NEW_DATA_MSK)) {
255 ESP_LOGD(TAG,
"BME680 did not report new data");
259 bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR];
260 uint8_t num_inputs = 0;
262 if (bme680_settings.process_data & BSEC_PROCESS_TEMPERATURE) {
263 inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
264 inputs[num_inputs].signal = data.temperature / 100.0f;
265 inputs[num_inputs].time_stamp = trigger_time_ns;
269 inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
271 inputs[num_inputs].time_stamp = trigger_time_ns;
274 if (bme680_settings.process_data & BSEC_PROCESS_HUMIDITY) {
275 inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
276 inputs[num_inputs].signal = data.humidity / 1000.0f;
277 inputs[num_inputs].time_stamp = trigger_time_ns;
280 if (bme680_settings.process_data & BSEC_PROCESS_PRESSURE) {
281 inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
282 inputs[num_inputs].signal = data.pressure;
283 inputs[num_inputs].time_stamp = trigger_time_ns;
286 if (bme680_settings.process_data & BSEC_PROCESS_GAS) {
287 if (data.status & BME680_GASM_VALID_MSK) {
288 inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
289 inputs[num_inputs].signal = data.gas_resistance;
290 inputs[num_inputs].time_stamp = trigger_time_ns;
293 ESP_LOGD(TAG,
"BME680 did not report gas data");
296 if (num_inputs < 1) {
297 ESP_LOGD(TAG,
"No signal inputs available for BSEC");
301 bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
302 uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
303 this->
bsec_status_ = bsec_do_steps(inputs, num_inputs, outputs, &num_outputs);
305 ESP_LOGW(TAG,
"BSEC failed to process signals (BSEC Error Code %d)", this->
bsec_status_);
308 if (num_outputs < 1) {
309 ESP_LOGD(TAG,
"No signal outputs provided by BSEC");
313 this->
publish_(outputs, num_outputs);
317 ESP_LOGV(TAG,
"Queuing sensor state publish actions");
318 for (uint8_t i = 0; i < num_outputs; i++) {
319 float signal = outputs[i].signal;
320 switch (outputs[i].sensor_id) {
321 case BSEC_OUTPUT_IAQ:
322 case BSEC_OUTPUT_STATIC_IAQ: {
323 uint8_t accuracy = outputs[i].accuracy;
333 case BSEC_OUTPUT_CO2_EQUIVALENT:
336 case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
339 case BSEC_OUTPUT_RAW_PRESSURE:
342 case BSEC_OUTPUT_RAW_GAS:
345 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
348 case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
356 int64_t time_ms =
millis();
366 if (!sensor || (change_only && sensor->
has_state() && sensor->
state == value)) {
380 return BME680BSECComponent::instance->
read_bytes(a_register, data, len) ? 0 : -1;
384 return BME680BSECComponent::instance->
write_bytes(a_register, data, len) ? 0 : -1;
388 ESP_LOGV(TAG,
"Delaying for %ums", period);
396 uint8_t
state[BSEC_MAX_STATE_BLOB_SIZE];
398 ESP_LOGV(TAG,
"Loading state");
399 uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
400 this->
bsec_status_ = bsec_set_state(state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer,
sizeof(work_buffer));
402 ESP_LOGW(TAG,
"Failed to load state (BSEC Error Code %d)", this->
bsec_status_);
404 ESP_LOGI(TAG,
"Loaded state");
413 ESP_LOGV(TAG,
"Saving state");
415 uint8_t
state[BSEC_MAX_STATE_BLOB_SIZE];
416 uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
417 uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
420 bsec_get_state(0, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state);
422 ESP_LOGW(TAG,
"Failed fetch state for save (BSEC Error Code %d)", this->
bsec_status_);
427 ESP_LOGW(TAG,
"Failed to save state");
432 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_
static int8_t write_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len)
bsec_library_return_t bsec_status_
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.
static BME680BSECComponent * instance
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
uint32_t millis_overflow_counter_
void publish_state(const std::string &state)
uint32_t last_state_save_ms_
sensor::Sensor * co2_equivalent_sensor_
uint32_t IRAM_ATTR HOT millis()
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 set_config_(const uint8_t *config)
static int8_t read_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len)
ESPPreferences * global_preferences
void status_clear_warning()
uint32_t state_save_interval_ms_
void publish_state(float state)
Publish a new state to the front-end.
sensor::Sensor * gas_resistance_sensor_
void dump_config() override
void status_set_warning()
sensor::Sensor * temperature_sensor_
void status_clear_error()
SampleRate pressure_sample_rate_
void read_(int64_t trigger_time_ns, bsec_bme_settings_t bme680_settings)
std::string to_string(int value)
void update_subscription_()
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
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_
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
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_
SampleRate temperature_sample_rate_
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)