04-20-2020 10:14 PM - edited 04-20-2020 10:15 PM
I have the attached code. I am attemping to run at 1hz. I see good data several times, and then the bsec never triggers again. I am using an Stm32F767 with the normal M7 Bsec binary.
Any ideas on what may be going on?
This is my output:
Up!
5
16000000 :: IAQ: 25.000000, IAQ Accuracy 0, Temp: 25.520000, Humidity: 35.554005
44
5
1017000000 :: IAQ: 25.000000, IAQ Accuracy 0, Temp: 26.065571, Humidity: 35.213631
45
5
2018000000 :: IAQ: 25.000000, IAQ Accuracy 0, Temp: 26.775574, Humidity: 33.929062
45
5
3019000000 :: IAQ: 25.000000, IAQ Accuracy 0, Temp: 27.085571, Humidity: 33.259838
45
5
4020000000 :: IAQ: 25.000000, IAQ Accuracy 0, Temp: 27.275574, Humidity: 32.777760
4339
0
4294
This is my code:
#include <stm32f7xx_hal.h>
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "bme680.h"
#include "I2c1.h"
#include "bsec_interface.h"
#include "bsec_datatypes.h"
#include "BsecDemo.h"
#include "Ticks.h"
static void BME680_delay(uint32_t period);
static int8_t BME680_I2C_bus_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len);
static int8_t BME680_I2C_bus_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len);
static void BsecDemoTask(void* args);
#define NUM_USED_OUTPUTS 7
static struct bme680_dev bme680_g;
static float bme680_temperature_offset_g = 0.0f;
static bsec_library_return_t bme680_bsec_update_subscription(const float sample_rate)
{
bsec_sensor_configuration_t requested_virtual_sensors[NUM_USED_OUTPUTS];
uint8_t n_requested_virtual_sensors = NUM_USED_OUTPUTS;
bsec_sensor_configuration_t required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
requested_virtual_sensors[0].sensor_id = BSEC_OUTPUT_IAQ;
requested_virtual_sensors[0].sample_rate = sample_rate;
requested_virtual_sensors[1].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
requested_virtual_sensors[1].sample_rate = sample_rate;
requested_virtual_sensors[2].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
requested_virtual_sensors[2].sample_rate = sample_rate;
requested_virtual_sensors[3].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
requested_virtual_sensors[3].sample_rate = sample_rate;
requested_virtual_sensors[4].sensor_id = BSEC_OUTPUT_RAW_GAS;
requested_virtual_sensors[4].sample_rate = sample_rate;
requested_virtual_sensors[5].sensor_id = BSEC_OUTPUT_RAW_TEMPERATURE;
requested_virtual_sensors[5].sample_rate = sample_rate;
requested_virtual_sensors[6].sensor_id = BSEC_OUTPUT_RAW_HUMIDITY;
requested_virtual_sensors[6].sample_rate = sample_rate;
// Enable/disable the requested virtual sensors
return bsec_update_subscription(requested_virtual_sensors,
n_requested_virtual_sensors,
required_sensor_settings,
&n_required_sensor_settings);
}
bool BsecDemo_Init(float sample_rate)
{
int32_t status;
I2c1_Init();
bme680_g.dev_id = (0x77 << 1);
bme680_g.intf = BME680_I2C_INTF;
bme680_g.write = BME680_I2C_bus_write;
bme680_g.read = BME680_I2C_bus_read;
bme680_g.delay_ms = BME680_delay;
// Initialize BME680 API
status = bme680_init(&bme680_g);
if (status != BME680_OK)
{
printf("bme680 init fail\r\n");
return false;
}
// Initialize BSEC library
status = bsec_init();
if (status != BSEC_OK)
{
printf("bsec init fail\r\n");
return false;
}
// Call to the function which sets the library with subscription information
status = bme680_bsec_update_subscription(sample_rate);
if (status != BSEC_OK)
{
printf("update sub init fail %ld\r\n", status);
return false;
}
xTaskCreate(BsecDemoTask, "BSECDEMO", 2048, NULL, 0, NULL);
return true;
}
static void bme680_bsec_trigger_measurement(bsec_bme_settings_t *sensor_settings)
{
uint16_t meas_period;
uint8_t set_required_settings;
// Check if a forced-mode measurement should be triggered now
if (sensor_settings->trigger_measurement)
{
// Set sensor configuration
bme680_g.tph_sett.os_hum = sensor_settings->humidity_oversampling;
bme680_g.tph_sett.os_pres = sensor_settings->pressure_oversampling;
bme680_g.tph_sett.os_temp = sensor_settings->temperature_oversampling;
bme680_g.gas_sett.run_gas = sensor_settings->run_gas;
bme680_g.gas_sett.heatr_temp = sensor_settings->heater_temperature; // degree Celsius
bme680_g.gas_sett.heatr_dur = sensor_settings->heating_duration; // milliseconds
// Select the power mode
// Must be set before writing the sensor configuration
bme680_g.power_mode = BME680_FORCED_MODE;
// Set the required sensor settings needed
set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
// Set the desired sensor configuration
bme680_set_sensor_settings(set_required_settings, &bme680_g);
// Set power mode as forced mode and trigger forced mode measurement
bme680_set_sensor_mode(&bme680_g);
// Get the total measurement duration so as to sleep or wait till the measurement is complete
bme680_get_profile_dur(&meas_period, &bme680_g);
// Delay till the measurement is ready. Timestamp resolution in ms
vTaskDelay(meas_period);
}
// Call the API to get current operation mode of the sensor
bme680_get_sensor_mode(&bme680_g);
// When the measurement is completed and data is ready for reading, the sensor must be in BME680_SLEEP_MODE.
// Read operation mode to check whether measurement is completely done and wait until the sensor is no more
// in BME680_FORCED_MODE.
while (bme680_g.power_mode == BME680_FORCED_MODE)
{
// sleep for 5 ms
vTaskDelay(5);
bme680_get_sensor_mode(&bme680_g);
}
}
static void bme680_bsec_read_data(int64_t time_stamp_trigger, bsec_input_t *inputs, uint8_t *num_bsec_inputs, int32_t bsec_process_data)
{
static struct bme680_field_data data;
// We only have to read data if the previous call the bsec_sensor_control() actually asked for it
if (bsec_process_data)
{
bme680_get_sensor_data(&data, &bme680_g);
if (data.status & BME680_NEW_DATA_MSK)
{
// Pressure
if (bsec_process_data & BSEC_PROCESS_PRESSURE)
{
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_PRESSURE;
inputs[*num_bsec_inputs].signal = data.pressure;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
// Temperature
if (bsec_process_data & BSEC_PROCESS_TEMPERATURE)
{
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
inputs[*num_bsec_inputs].signal = data.temperature / 100.0f;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
// Also add optional heatsource input which will be subtracted from the temperature reading to
// compensate for device-specific self-heating (supported in BSEC IAQ solution)
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[*num_bsec_inputs].signal = bme680_temperature_offset_g;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
// Humidity
if (bsec_process_data & BSEC_PROCESS_HUMIDITY)
{
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
inputs[*num_bsec_inputs].signal = data.humidity / 1000.0f;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
// Gas
if (bsec_process_data & BSEC_PROCESS_GAS)
{
// Check whether gas_valid flag is set
if(data.status & BME680_GASM_VALID_MSK)
{
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
inputs[*num_bsec_inputs].signal = data.gas_resistance;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
}
}
}
}
static void bme680_bsec_process_data(bsec_input_t *bsec_inputs, uint8_t num_bsec_inputs)
{
bsec_output_t bsec_outputs[BSEC_NUMBER_OUTPUTS];
uint8_t num_bsec_outputs = 0;
uint8_t index = 0;
int64_t timestamp = 0;
float iaq = 0.0f;
uint8_t iaq_accuracy = 0;
float temp = 0.0f;
float raw_temp = 0.0f;
float raw_pressure = 0.0f;
float humidity = 0.0f;
float raw_humidity = 0.0f;
float raw_gas = 0.0f;
// Check if something should be processed by BSEC
if (num_bsec_inputs > 0)
{
// Set number of outputs to the size of the allocated buffer
// BSEC_NUMBER_OUTPUTS to be defined
num_bsec_outputs = BSEC_NUMBER_OUTPUTS;
// Do Bsec
bsec_do_steps(bsec_inputs, num_bsec_inputs, bsec_outputs, &num_bsec_outputs);
// Iterate through the outputs and extract the relevant ones.
for (index = 0; index < num_bsec_outputs; index++)
{
switch (bsec_outputs[index].sensor_id)
{
case BSEC_OUTPUT_IAQ:
iaq = bsec_outputs[index].signal;
iaq_accuracy = bsec_outputs[index].accuracy;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
temp = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_PRESSURE:
raw_pressure = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
humidity = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_GAS:
raw_gas = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_TEMPERATURE:
raw_temp = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_HUMIDITY:
raw_humidity = bsec_outputs[index].signal;
break;
default:
continue;
}
// Assume that all the returned timestamps are the same
timestamp = bsec_outputs[index].time_stamp;
}
printf("%lli :: IAQ: %f, IAQ Accuracy %d, Temp: %f, Humidity: %f\r\n",
timestamp,
iaq,
iaq_accuracy,
temp,
humidity);
}
}
static void BsecDemoTask(void* args)
{
int64_t time_stamp = 0;
bsec_input_t bsec_inputs[BSEC_MAX_PHYSICAL_SENSOR];
uint8_t num_bsec_inputs = 0;
bsec_bme_settings_t sensor_settings;
int64_t time_stamp_interval_ms = 0;
while (true)
{
// get the timestamp in nanoseconds before calling bsec_sensor_control()
time_stamp = Ticks_Now() * 1000 * 1000;
// Retrieve sensor settings to be used in this time instant by calling bsec_sensor_control
bsec_sensor_control(time_stamp, &sensor_settings);
// Trigger a measurement if necessary
bme680_bsec_trigger_measurement(&sensor_settings);
// Read data from last measurement
num_bsec_inputs = 0;
bme680_bsec_read_data(time_stamp, bsec_inputs, &num_bsec_inputs, sensor_settings.process_data);
printf("%d\r\n", num_bsec_inputs);
// Time to invoke BSEC to perform the actual processing
bme680_bsec_process_data(bsec_inputs, num_bsec_inputs);
// Compute how long we can sleep until we need to call bsec_sensor_control() next
// Time_stamp is converted from microseconds to nanoseconds first and then the difference to milliseconds
time_stamp_interval_ms = (sensor_settings.next_call - Ticks_Now() * 1000 * 1000) / 1000000;
printf("%lld\r\n", time_stamp_interval_ms);
if (time_stamp_interval_ms > 0)
{
vTaskDelay(time_stamp_interval_ms);
}
}
}
static int8_t BME680_I2C_bus_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len)
{
if (!I2c1_Write(dev_id, reg_addr, data, len))
{
return -1;
}
return 0;
}
static int8_t BME680_I2C_bus_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len)
{
if (!I2c1_Read(dev_id, reg_addr, data, len))
{
return -1;
}
return 0;
}
static void BME680_delay(uint32_t period)
{
// Inline delay
// Don't let the scheduler take it!
HAL_Delay(period);
}
04-27-2020 06:57 PM
I tried to reply to this before, but I think the Bosch forum software is having an issue or my post is stuck in moderation.
Regardless, the issue is that in my code, I take the timestamp of the sample when the bme680 is done sampling. This is 2mS off of the next_call time which the bsec wants. After a while, this results in the bsec returning the timing error. My code is doing nothing but attending to the bsec, but obviousl it's impossible form e ot reall have data taken exactly to the nanosecond that the bsec wants.
I think the bsec is far too senstive to this, especially if the mcu is going other things in addition to the bsec sampling. If I fake it and just tell the bsec that the sample timestamp is the previous next_call value everything seems to work fine.
04-28-2020 04:45 PM
Thanks for the update. Could you share a short log including the expected "next_call" values and actual timestamps passed to bsec_sensor_control()?
The BME680 requires accurate timings to perform optimally, but we understand real-time limitation of most systems and do not expect nanosecond accuracy in BSEC. Actually, a jitter of max. 6.25% is allowed, and BSEC_W_SC_CALL_TIMING_VIOLATION is a warning flag indicating that the error in the timestamp received is greater than that limit.
04-29-2020 05:59 PM
04-30-2020 09:26 AM
Please use one of the supported operating mode in bsec_datatypes.h, for example BSEC_SAMPLE_RATE_LP with samples every 3 seconds. There is no error/warning in this log, if your software in longer getting stuck I believe the issue is not related to BSEC but to the integration/platform requirements.