07-19-2022 10:25 AM
Hello
I'm working with the BME688 and using the parallel mode. Is it normal, that after each measurement the device take 2 Minutes to rest? Could time between two measurements a bit shorter? In my point of view, the bsec calculate 5 times for each value during the measurement. Should I take the last value or the average (see picture humidity, x-scale time[s])?
During this record I had compared the CO2 values, which were between 2425 and 2150ppm, and an alternative CO2 Sensor, which values were between 650 and 900. Why do I have such a difference between these values. Is this connected to the values which I load from the eeprom?
Additonal I would like to say, that the temperature and humidity works almost correct.
/* * BME688.c * * Created on: Aug 25, 2021 * Author: Ruo */ #include <ApplicationData.h> #include <string.h> #include <time.h> #include <stdio.h> #include <stdbool.h> #include "eeprom.h" #include <BME688_driver.h> #include "bsec_datatypes_hidden.h" #define BMETimestamp 1200000 //every 20 Minutes uint32_t LastSaveBMEState = 0; bool SaveState = false; float COtwo = 500; uint8_t n_fields; static uint32_t tick; static int64_t currTimeNs = 0; enum BsecStep _step; struct BsecOutput BME688_Output; struct bme68x_dev _bme68x; struct bme68x_conf _bme68x_conf; struct bme68x_heatr_conf _bme68x_heatr_conf; struct bme68x_data _bme68x_data[3]; // accessing bme68x data registers int8_t _bme68x_status; // Placeholder for the BME68x driver's error codes #define BME68X_VALID_DATA UINT8_C(0xB0) #define CHECK_BSEC_INPUT(x, shift) (x & (1 << (shift-1))) #define ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) struct BsecOutput { bsec_output_t outputs[BSEC_NUMBER_OUTPUTS]; uint8_t len; }BsecOutput; typedef void (*BsecCallback)(const struct bme68x_data*, const struct BsecOutput*); bsec_version_t _bsec_version; // Stores the version of the BSEC algorithm bsec_bme_settings_t _bsec_bme_settings; bsec_library_return_t _bsec_status; // struct BsecOutput _output; BsecCallback _proc; uint8_t _op_mode; // operating mode of sensor uint8_t prev_gas_idx; bool check_meas_idx; // check measurement index to track the order uint8_t last_meas_index; // last measurement index received from sensor float _temp_offset; // Global variables to help create a millisecond timestamp that doesn't overflow every 51 days. // If it overflows, it will have a negative value. Something that should never happen. uint32_t _timer_overflow_counter; uint32_t _timer_last_value; extern float analogFilterValue(float filteredValue, float value, float filterDelta); enum BsecStep { CONTROL_STEP, MEASUREMENT_STEP, LAST_STEP }; bsec_virtual_sensor_t sensorList[] = { BSEC_OUTPUT_RAW_TEMPERATURE, // ID = 6 BSEC_OUTPUT_RAW_PRESSURE, // ID = 7 BSEC_OUTPUT_RAW_HUMIDITY, // ID = 8 BSEC_OUTPUT_RAW_GAS, // ID = 9 BSEC_OUTPUT_RAW_GAS_INDEX, // ID = 26 BSEC_OUTPUT_IAQ, // ID = 1 //BSEC_OUTPUT_STATIC_IAQ, /* ID = !< Unscaled indoor-air-quality estimate */ BSEC_OUTPUT_CO2_EQUIVALENT, /* ID = 3 !< co2 equivalent estimate [ppm] */ BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, /* ID = 4 !< breath VOC concentration estimate [ppm] */ BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, // ID = 14 BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, // ID = 15 // BSEC_OUTPUT_STABILIZATION_STATUS, // ID = 12 //BSEC_OUTPUT_GAS_ESTIMATE_1, // ID = 22 //BSEC_OUTPUT_GAS_ESTIMATE_2, // ID = 23 //BSEC_OUTPUT_GAS_ESTIMATE_3, // ID = 24 //BSEC_OUTPUT_GAS_ESTIMATE_4 // ID = 25 }; typedef struct structBME688_t { I2C_HandleTypeDef *phi2c; //!< handle to I2C Device } structBME688_t; structBME688_t BME688_MCU; bool loadState(void) { uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE]; if (eepromReadPage((unsigned char*) &bsecState, EEPROM_DEVICE_BME_DATA_ADDRESS, sizeof(bsecState)) != false) return false; if(bsecState[0] != 0xff && bsecState[0] != 0) { if (!setState(bsecState)) return false; } else // Erase the EEPROM with zeroes { if (!setState(bsec_fistState)) { for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) { bsecState[i] = 0; } eepromWritePage((unsigned char*) &bsecState, EEPROM_DEVICE_BME_DATA_ADDRESS, sizeof(bsecState)); return false; } } return true; } bool saveState(void) { uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE]; if (!getState(bsecState)) return false; eepromWritePage((unsigned char*) &bsecState, EEPROM_DEVICE_BME_DATA_ADDRESS, sizeof(bsecState)); return true; } #define ALength 5 float array[ALength]; /*! * */ bool BME688_Init(I2C_HandleTypeDef *hi2c, uint8_t Slave_Address) { BME688_MCU.phi2c = hi2c; //Data for the BME Driver & Communication _bme68x.intf_ptr = (void*)(intptr_t) Slave_Address; _bme68x.intf = BME68X_I2C_INTF; _bme68x.amb_temp = 25; _bme68x.delay_us = delay_us; _bme68x.read = bme68x_i2c_read; _bme68x.write = bme68x_i2c_write; _timer_overflow_counter = 0; _timer_last_value = 0; _step = CONTROL_STEP; _bme68x_status = BME68X_OK; _bsec_status = BSEC_OK; _temp_offset = 5.0; _bme68x_status = bsec_init(); // Bsec Library initialization _step = CONTROL_STEP; last_meas_index = 0; _bme68x_status |= setConfig(bsec_config_selectivity); // BME688 vordefinierte Config wird geladen _bme68x_status |= loadState(); _bme68x_status |= updateSubscription(BSEC_SAMPLE_RATE_HIGH_PERFORMANCE); // BME688 samplerate definition _bme68x_status |= bme68x_init(&_bme68x); // BME688 initialization if (_bsec_status < BSEC_OK) return false; //BME688 setting definition _bsec_bme_settings.op_mode = BME68X_PARALLEL_MODE; _bsec_bme_settings.humidity_oversampling = BME68X_OS_2X; _bsec_bme_settings.pressure_oversampling = BME68X_OS_16X; _bsec_bme_settings.temperature_oversampling = BME68X_OS_2X; _bsec_bme_settings.run_gas = BME68X_ENABLE; uint16_t temp_prof[10] = { 250, 100, 100, 100, 100, 100, 100, 250, 250, 250 }; // Heater temperature in degree Celsius uint16_t mul_prof[10] = { 2, 0, 10, 15, 2, 2, 2, 2, 2, 2 }; // Multiplier to the shared heater duration for(uint8_t i=0; i<(ARRAY_LEN(temp_prof));i++) _bsec_bme_settings.heater_temperature_profile[i]= temp_prof[i]; for(uint8_t j=0; j<(ARRAY_LEN(mul_prof));j++) _bsec_bme_settings.heater_duration_profile[j] = mul_prof[j]; _bsec_bme_settings.heater_profile_len = 10; setBme68xConfigParallel(); for(uint8_t i=0; i<ALength; i++) { array[i] = 500; } return true; } float C02Value = 0; uint8_t Co2counter = 0; float CO2_Summe = 0; float smallestValue = 500; /*! * @brief Die Werte des Sensors werden abgerufen und zugewiesen. * * @return 1 = OK / 0 = nOK */ bool BME688_Handle(void) // kann im spaeteren Verlauf komplett durch run ersetzt werden. Benenung run soll genauer sein. { if(run()) // Command from BME688 { if (!_output.len) return false; for (uint8_t i = 0; i < _output.len; i++) { const bsec_output_t DataOutput = _output.outputs[i]; switch(DataOutput.sensor_id) { case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: // BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE = 14 | RAW_TEMPERATURE = 6 Pab.PabIn.s.SensorData.BME_temperature = DataOutput.signal; break; case BSEC_OUTPUT_RAW_PRESSURE: //BSEC_OUTPUT_RAW_PRESSURE = 7 Pab.PabIn.s.SensorData.BME_pressure = analogFilterValue(Pab.PabIn.s.SensorData.BME_pressure, (float) DataOutput.signal, 25) - 30; //-30 offset zum anderen Sensor break; case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: // BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY = 15 | RAW_Hum = 8 Pab.PabIn.s.SensorData.BME_humidity = DataOutput.signal; break; case BSEC_OUTPUT_CO2_EQUIVALENT: // BSEC_OUTPUT_CO2_EQUIVALENT = 3 if(DataOutput.accuracy > 0) { array[Co2counter] = DataOutput.signal; Co2counter++; if(Co2counter>= ALength) { Co2counter =0; C02Value = array[0]; for(uint8_t i=0; i<ALength; i++) { if(C02Value>array[i]) C02Value = array[i]; } } Pab.PabIn.s.SensorData.BME_CO2 = C02Value; //smallestValue } break; case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: //BSEC_OUTPUT_BREATH_VOC_EQUIVALENT = 4 if(DataOutput.accuracy > 0) Pab.PabIn.s.SensorData.BME_VOC = DataOutput.signal; break; case BSEC_OUTPUT_IAQ: //1 if(DataOutput.accuracy>=2) { Pab.PabIn.s.SensorData.BME_IAQ = DataOutput.signal; SaveState = true; } break; } } if(HAL_GetTick() - LastSaveBMEState > BMETimestamp && SaveState == true) { LastSaveBMEState = HAL_GetTick(); saveState(); } _output.len = 0; } else { return false; } return true; } /** * @brief Function that sets the desired sensors and the sample rates (by subscription) */ bool updateSubscription(float sampleRate) { uint8_t nSensors = ARRAY_LEN(sensorList); bsec_sensor_configuration_t virtualSensors[BSEC_NUMBER_OUTPUTS], sensorSettings[BSEC_MAX_PHYSICAL_SENSOR]; uint8_t nSensorSettings = BSEC_MAX_PHYSICAL_SENSOR; for (uint8_t i = 0; i < nSensors; i++) { virtualSensors[i].sensor_id = sensorList[i]; virtualSensors[i].sample_rate = sampleRate; } _bsec_status = bsec_update_subscription(virtualSensors, nSensors, sensorSettings, &nSensorSettings); if (_bsec_status < BSEC_OK) return false; return true; } /*! * @brief Die BME library (BSEC) kontrolliert den Sensor. Hier werden die Werte aus dem Sensor gelesen. Die Library berechnet die zusätzlichen Werte. * * @return 1 = OK / 0 = nOK */ bool run(void) { n_fields = 0; tick = HAL_GetTick(); currTimeNs = 1000000 * (int64_t)tick; _op_mode = _bsec_bme_settings.op_mode; if (currTimeNs >= _bsec_bme_settings.next_call) { _bsec_status = bsec_sensor_control(currTimeNs, &_bsec_bme_settings); if (_bsec_status < BSEC_OK) return false; switch(_bsec_bme_settings.op_mode) { case BME68X_FORCED_MODE: setBme68xConfigForced(); break; case BME68X_PARALLEL_MODE: if (_op_mode != _bsec_bme_settings.op_mode) { setBme68xConfigParallel(); } break; case BME68X_SLEEP_MODE: if (_op_mode != _bsec_bme_settings.op_mode) { setBme68xConfigSleep(); } break; } if (_bme68x_status < BME68X_OK) return false; _bme68x_status = bme68x_get_data(_op_mode, _bme68x_data, &n_fields, &_bme68x); for(uint8_t i = 0; i < n_fields; i++) { if (_bme68x_data[i].status & BME68X_GASM_VALID_MSK) { /* Measurement index check to track the first valid sample after operation mode change */ if(check_meas_idx == true ) { /* After changing the operation mode, Measurement index expected to be zero * however with considering the data miss case as well, condition shall be checked less * than last received measurement index */ if(last_meas_index == 0 || _bme68x_data[i].meas_index == 0 || _bme68x_data[i].meas_index < last_meas_index) { check_meas_idx = false; } else { continue; // Skip the invalid data samples or data from last duty cycle scan } } last_meas_index = _bme68x_data[i].meas_index; if(!processData(currTimeNs, &_bme68x_data[i])) { return false; } } } } return true; } /** * @brief Function to get the state of the algorithm to save to non-volatile memory */ bool getState(uint8_t *state) { uint8_t workBuffer[BSEC_MAX_STATE_BLOB_SIZE]; uint32_t n_serialized_state = BSEC_MAX_STATE_BLOB_SIZE; _bsec_status = bsec_get_state(0, state, BSEC_MAX_STATE_BLOB_SIZE, workBuffer, BSEC_MAX_STATE_BLOB_SIZE, &n_serialized_state); if (_bsec_status < BSEC_OK) return false; return true; } /** * @brief Function to set the state of the algorithm from non-volatile memory */ bool setState(uint8_t *state) { uint8_t workBuffer[BSEC_MAX_STATE_BLOB_SIZE]; _bsec_status = bsec_set_state(state, BSEC_MAX_STATE_BLOB_SIZE, workBuffer, BSEC_MAX_STATE_BLOB_SIZE); if (_bsec_status < BSEC_OK) return false; memset(&_bsec_bme_settings, 0, sizeof(_bsec_bme_settings)); _step = CONTROL_STEP; return true; } /** * @brief Function to set the configuration of the algorithm from memory * Error through the */ bool setConfig(const uint8_t *state) { uint8_t workBuffer[BSEC_MAX_WORKBUFFER_SIZE]; _bsec_status = bsec_set_configuration(state, BSEC_MAX_PROPERTY_BLOB_SIZE, workBuffer, BSEC_MAX_PROPERTY_BLOB_SIZE); if (_bsec_status < BSEC_OK) return false; memset(&_bsec_bme_settings, 0, sizeof(_bsec_bme_settings)); _step = CONTROL_STEP; return true; } /* Private functions */ /** * @brief Read data from the BME68X and process it */ bool processData(int64_t currTimeNs, struct bme68x_data *data) { bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temp, Pres, Hum & Gas uint8_t nInputs = 0; if (CHECK_BSEC_INPUT(_bsec_bme_settings.process_data, BSEC_INPUT_HEATSOURCE)) { inputs[nInputs].sensor_id = BSEC_INPUT_HEATSOURCE; inputs[nInputs].signal = _temp_offset; inputs[nInputs].time_stamp = currTimeNs; nInputs++; } if (CHECK_BSEC_INPUT(_bsec_bme_settings.process_data, BSEC_INPUT_TEMPERATURE)) { inputs[nInputs].sensor_id = BSEC_INPUT_TEMPERATURE; inputs[nInputs].signal = data->temperature; inputs[nInputs].time_stamp = currTimeNs; nInputs++; } if (CHECK_BSEC_INPUT(_bsec_bme_settings.process_data, BSEC_INPUT_HUMIDITY)) { inputs[nInputs].sensor_id = BSEC_INPUT_HUMIDITY; inputs[nInputs].signal = data->humidity; inputs[nInputs].time_stamp = currTimeNs; nInputs++; } if (CHECK_BSEC_INPUT(_bsec_bme_settings.process_data, BSEC_INPUT_PRESSURE)) { inputs[nInputs].sensor_id = BSEC_INPUT_PRESSURE; inputs[nInputs].signal = data->pressure; inputs[nInputs].time_stamp = currTimeNs; nInputs++; } if (CHECK_BSEC_INPUT(_bsec_bme_settings.process_data, BSEC_INPUT_GASRESISTOR)) { inputs[nInputs].sensor_id = BSEC_INPUT_GASRESISTOR; inputs[nInputs].signal = data->gas_resistance; inputs[nInputs].time_stamp = currTimeNs; nInputs++; } if (CHECK_BSEC_INPUT(_bsec_bme_settings.process_data, BSEC_INPUT_PROFILE_PART)) { inputs[nInputs].sensor_id = BSEC_INPUT_PROFILE_PART; inputs[nInputs].signal = (_op_mode == BME68X_FORCED_MODE) ? 0 : data->gas_index; inputs[nInputs].time_stamp = currTimeNs; nInputs++; } if (nInputs > 0) { _output.len = BSEC_NUMBER_OUTPUTS; memset(_output.outputs, 0, sizeof(_output.outputs)); _bsec_status = bsec_do_steps(inputs, nInputs, _output.outputs, &_output.len); if (_bsec_status < BSEC_OK) return false; } if (_proc != NULL) _proc(data, &_output); return true; } /** * @brief Set the BME68X sensor configuration to forced mode */ void setBme68xConfigForced(void) { /* Set the filter, odr, temperature, pressure and humidity settings */ _bme68x_conf.filter = BME68X_FILTER_OFF; _bme68x_conf.odr = BME68X_ODR_NONE; _bme68x_conf.os_hum = _bsec_bme_settings.humidity_oversampling; _bme68x_conf.os_pres = _bsec_bme_settings.pressure_oversampling; _bme68x_conf.os_temp = _bsec_bme_settings.temperature_oversampling; _bme68x_status = bme68x_set_conf(&_bme68x_conf, &_bme68x); if (_bme68x_status < BME68X_OK) return; /* Set the gas sensor settings and link the heating profile */ _bme68x_heatr_conf.enable = _bsec_bme_settings.run_gas; _bme68x_heatr_conf.shared_heatr_dur = 0; _bme68x_heatr_conf.heatr_temp = _bsec_bme_settings.heater_temperature; _bme68x_heatr_conf.heatr_dur = _bsec_bme_settings.heater_duration; _bme68x_heatr_conf.heatr_temp_prof = NULL; _bme68x_heatr_conf.heatr_dur_prof = NULL; _bme68x_heatr_conf.profile_len = 0; /* Select the power mode */ _bme68x_status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &_bme68x_heatr_conf, &_bme68x); if (_bme68x_status < BME68X_OK) return; _bme68x_status = bme68x_set_op_mode(BME68X_FORCED_MODE, &_bme68x); if (_bme68x_status < BME68X_OK) return; _op_mode = BME68X_FORCED_MODE; } /** * @brief Set the BME68X sensor configuration to sleep mode */ void setBme68xConfigSleep(void) { _bme68x_status = bme68x_set_op_mode(BME68X_SLEEP_MODE, &_bme68x); if (_bme68x_status < BME68X_OK) return; _op_mode = BME68X_SLEEP_MODE; } /** * @brief Set the BME68X sensor configuration to parallel mode */ void setBme68xConfigParallel(void) { /* Set the filter, odr, temperature, pressure and humidity settings */ _bme68x_conf.filter = BME68X_FILTER_OFF; _bme68x_conf.odr = BME68X_ODR_NONE; _bme68x_conf.os_hum = _bsec_bme_settings.humidity_oversampling; _bme68x_conf.os_pres = _bsec_bme_settings.pressure_oversampling; _bme68x_conf.os_temp = _bsec_bme_settings.temperature_oversampling; _bme68x_status = bme68x_set_conf(&_bme68x_conf, &_bme68x); if (_bme68x_status < BME68X_OK) return; /* Set the gas sensor settings and link the heating profile */ _bme68x_heatr_conf.enable = _bsec_bme_settings.run_gas; _bme68x_heatr_conf.shared_heatr_dur = GAS_WAIT_SHARED - (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &_bme68x_conf, &_bme68x) / INT64_C(1000)); _bme68x_heatr_conf.heatr_temp = 0; _bme68x_heatr_conf.heatr_dur = 0; _bme68x_heatr_conf.heatr_temp_prof = _bsec_bme_settings.heater_temperature_profile; _bme68x_heatr_conf.heatr_dur_prof = _bsec_bme_settings.heater_duration_profile; _bme68x_heatr_conf.profile_len = _bsec_bme_settings.heater_profile_len; /* Select the power mode */ _bme68x_status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &_bme68x_heatr_conf, &_bme68x); if (_bme68x_status < BME68X_OK) return; _bme68x_status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &_bme68x); if (_bme68x_status < BME68X_OK) return; /* Enable measurement index check to track the first valid sample after operation mode change */ check_meas_idx = true; _op_mode = BME68X_PARALLEL_MODE; } /** * @brief Function to calculate an int64_t timestamp in milliseconds */ int64_t getTimeMs(void) { int64_t timeMs = HAL_GetTick(); if (_timer_last_value > timeMs) { // An overflow occured _timer_last_value = timeMs; _timer_overflow_counter++; } return (timeMs + (_timer_overflow_counter * 0xFFFFFFFF)); } int64_t getTimeUs(void) { int64_t timeUs = (HAL_GetTick()/1000); if (_timer_last_value > timeUs) { // An overflow occured _timer_last_value = timeUs; _timer_overflow_counter++; } return (timeUs + (_timer_overflow_counter * 0xFFFFFFFF)); } void delay_ms(uint32_t period) { HAL_Delay(period); } void delay_us(uint32_t period, void *intf_ptr) { (void) intf_ptr; HAL_Delay(period/1000); } int8_t bme68x_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr) { intptr_t devID = (intptr_t)intf_ptr; HAL_I2C_Mem_Write(BME688_MCU.phi2c,devID,reg_addr,I2C_MEMADD_SIZE_8BIT,(uint8_t *) reg_data, length, 2000); return 0; } int8_t bme68x_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr) { intptr_t devID = (intptr_t)intf_ptr; HAL_I2C_Mem_Read(BME688_MCU.phi2c,devID,reg_addr,I2C_MEMADD_SIZE_8BIT, reg_data, length, 2000); return 0; }
Best regards
08-24-2022 10:42 AM
Hi,
thank you. Do you know how much time does it need to get the accuracy of 3?
Best regards
08-30-2022 01:30 AM
Hi,
It totally depends on your environments.
It might take 20~30 mins to several hours.
Thank you.