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
... View more