Air quality sensor
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
esp-airsensor/components/bme680/src/bsec2.c

404 lines
14 KiB

#include <string.h>
#include "bsec2.h"
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include <esp_log.h>
#include <inttypes.h>
static const char *TAG = "bsec";
/* Private funcs - prototypes */
/**
* @brief Reads the data from the BME68x sensor and process it
* @param currTimeNs: Current time in ns
* @return true if there are new outputs. false otherwise
*/
static bool bsec2_processData(struct bsec2 *self, int64_t currTimeNs, const struct bme68x_data *data);
/**
* @brief Set the BME68x sensor configuration to forced mode
*/
static void bsec2_setBme68xConfigForced(struct bsec2 *self);
/**
* @brief Set the BME68x sensor configuration to parallel mode
*/
static void bsec2_setBme68xConfigParallel(struct bsec2 *self);
/* Public funcs impl */
int bsec2_init(struct bsec2 *self, struct bme68x_dev *dev) {
self->ovfCounter = 0;
self->lastMillis = 0;
self->status = BSEC_OK;
self->extTempOffset = 0.0f;
self->opMode = BME68X_SLEEP_MODE;
self->newDataCallback = NULL;
self->sensor = dev;
memset(&self->version, 0, sizeof(self->version));
memset(&self->bmeConf, 0, sizeof(self->bmeConf));
memset(&self->outputs, 0, sizeof(self->outputs));
// The sensor must be inited and OK by the time this func is called!
// Begin
self->status = bsec_init();
if (self->status != BSEC_OK) {
return -1;
}
self->status = bsec_get_version(&self->version);
if (self->status != BSEC_OK) {
return -2;
}
memset(&self->bmeConf, 0, sizeof(self->bmeConf));
memset(&self->outputs, 0, sizeof(self->outputs));
return 0;
}
bool bsec2_updateSubscription(struct bsec2 *self, const bsecSensor *sensorList, uint8_t nSensors, float sampleRate) {
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;
}
/* Subscribe to library virtual sensors outputs */
self->status = bsec_update_subscription(virtualSensors, nSensors, sensorSettings, &nSensorSettings);
if (self->status != BSEC_OK) {
return false;
}
return true;
}
bool bsec2_run(struct bsec2 *self) {
int64_t ms = bsec2_getTimeMs(self);
int64_t currTimeNs = ms * INT64_C(1000000);
//ESP_LOGD(TAG, "%"PRIi64" ms = %"PRIi64" ns, nextcall %"PRIi64, ms, currTimeNs, self->bmeConf.next_call);
uint8_t lastOpMode = self->opMode;
// set new opmode to what bsec wants
self->opMode = self->bmeConf.op_mode;
if (currTimeNs >= self->bmeConf.next_call) {
ESP_LOGD(TAG, "bsec runs");
/* Provides the information about the current sensor configuration that is
necessary to fulfill the input requirements, eg: operation mode, timestamp
at which the sensor data shall be fetched etc */
self->status = bsec_sensor_control(currTimeNs, &self->bmeConf);
if (self->status != BSEC_OK) {
return false;
}
switch (self->bmeConf.op_mode) {
case BME68X_FORCED_MODE:
bsec2_setBme68xConfigForced(self);
break;
case BME68X_PARALLEL_MODE:
if (self->opMode != self->bmeConf.op_mode) {
bsec2_setBme68xConfigParallel(self);
}
break;
case BME68X_SLEEP_MODE:
if (self->opMode != self->bmeConf.op_mode) {
self->sensor_status = bme68x_set_op_mode(BME68X_SLEEP_MODE, self->sensor);
self->opMode = BME68X_SLEEP_MODE;
}
break;
}
if (self->sensor_status < BME68X_OK) {
bsec2_errormsg("bsec2 sensor error");
return false;
}
struct bme68x_data sensorData[3];
struct bme68x_data *pdata;
if (self->bmeConf.trigger_measurement && self->bmeConf.op_mode != BME68X_SLEEP_MODE) {
uint8_t nfields;
self->sensor_status = bme68x_get_data(self->opMode, sensorData, &nfields, self->sensor);
if (self->sensor_status == BME68X_OK && nfields > 0) {
if (lastOpMode == BME68X_FORCED_MODE) {
pdata = &sensorData[0];
if (pdata->status & BME68X_GASM_VALID_MSK) {
if (!bsec2_processData(self, currTimeNs, pdata)) {
return false;
}
}
} else {
for (int i = 0; i < nfields; i++) {
pdata = &sensorData[i];
if (pdata->status & BME68X_GASM_VALID_MSK) {
if (!bsec2_processData(self, currTimeNs, pdata)) {
return false;
}
}
}
}
}
}
}
return true;
}
bsecData bsec2_getData(struct bsec2 *self, bsecSensor id) {
bsecData emp =
{0};
for (uint8_t i = 0; i < self->outputs.nOutputs; i++)
if (id == self->outputs.output[i].sensor_id)
return self->outputs.output[i];
return emp;
}
/**
* @brief Function to get the state of the algorithm to save to non-volatile memory
* @param state : Pointer to a memory location, to hold the state
* @return true for success, false otherwise
*/
bool bsec2_getState(struct bsec2 *self, uint8_t *state) {
uint32_t n_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
self->status = bsec_get_state(0, state, BSEC_MAX_STATE_BLOB_SIZE, self->workBuffer, BSEC_MAX_WORKBUFFER_SIZE,
&n_serialized_state);
if (self->status != BSEC_OK) {
bsec2_errormsg("bsec2_getState: error from bsec_get_state");
return false;
}
return true;
}
/**
* @brief Function to set the state of the algorithm from non-volatile memory
* @param state : Pointer to a memory location that contains the state
* @return true for success, false otherwise
*/
bool bsec2_setState(struct bsec2 *self, uint8_t *state) {
self->status = bsec_set_state(state, BSEC_MAX_STATE_BLOB_SIZE, self->workBuffer, BSEC_MAX_WORKBUFFER_SIZE);
if (self->status != BSEC_OK) {
bsec2_errormsg("bsec2_setState: error from bsec_set_state");
return false;
}
memset(&self->bmeConf, 0, sizeof(self->bmeConf));
return true;
}
/**
* @brief Function to retrieve the current library configuration
* @param config : Pointer to a memory location, to hold the serialized config blob, must hold BSEC_MAX_PROPERTY_BLOB_SIZE
* @return true for success, false otherwise
*/
bool bsec2_getConfig(struct bsec2 *self, uint8_t *config) {
uint32_t n_serialized_settings = 0;
self->status = bsec_get_configuration(0, config, BSEC_MAX_PROPERTY_BLOB_SIZE, self->workBuffer, BSEC_MAX_WORKBUFFER_SIZE, &n_serialized_settings);
if (self->status != BSEC_OK) {
bsec2_errormsg("bsec2_getConfig: error from bsec_get_configuration");
return false;
}
return true;
}
/**
* @brief Function to set the configuration of the algorithm from memory
* @param state : Pointer to a memory location that contains the configuration
* @return true for success, false otherwise
*/
bool bsec2_setConfig(struct bsec2 *self, const uint8_t *config) {
self->status = bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, self->workBuffer, BSEC_MAX_WORKBUFFER_SIZE);
if (self->status != BSEC_OK) {
bsec2_errormsg("bsec2_setConfig: error from bsec_set_configuration");
return false;
}
// copied from arduino lib, but why?
memset(&self->bmeConf, 0, sizeof(self->bmeConf));
return true;
}
/**
* @brief Function to calculate an int64_t timestamp in milliseconds
*/
int64_t bsec2_getTimeMs(struct bsec2 *self) {
uint64_t timeMs = bsec2_timestamp_millis();
if (self->lastMillis > timeMs) /* An overflow occurred */
{
self->lastMillis = timeMs;
self->ovfCounter++;
}
self->lastMillis = timeMs;
return (int64_t) timeMs + (self->ovfCounter * INT64_C(0xFFFFFFFF));
}
/* Private funcs impl */
static bool bsec2_processData(struct bsec2 *self, int64_t currTimeNs, const struct bme68x_data *data) {
bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; /* Temp, Pres, Hum & Gas */
uint8_t nInputs = 0;
/* Checks all the required sensor inputs, required for the BSEC library for the requested outputs */
if (BSEC_CHECK_INPUT(self->bmeConf.process_data, BSEC_INPUT_HEATSOURCE)) {
inputs[nInputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[nInputs].signal = self->extTempOffset;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (BSEC_CHECK_INPUT(self->bmeConf.process_data, BSEC_INPUT_TEMPERATURE)) {
#ifdef BME68X_USE_FPU
inputs[nInputs].sensor_id = BSEC_INPUT_TEMPERATURE;
#else
inputs[nInputs].sensor_id = BSEC_INPUT_TEMPERATURE / 100.0f;
#endif
inputs[nInputs].signal = data->temperature;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (BSEC_CHECK_INPUT(self->bmeConf.process_data, BSEC_INPUT_HUMIDITY)) {
#ifdef BME68X_USE_FPU
inputs[nInputs].sensor_id = BSEC_INPUT_HUMIDITY;
#else
inputs[nInputs].sensor_id = BSEC_INPUT_HUMIDITY / 1000.0f;
#endif
inputs[nInputs].signal = data->humidity;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (BSEC_CHECK_INPUT(self->bmeConf.process_data, BSEC_INPUT_PRESSURE)) {
inputs[nInputs].sensor_id = BSEC_INPUT_PRESSURE;
inputs[nInputs].signal = data->pressure;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (BSEC_CHECK_INPUT(self->bmeConf.process_data, BSEC_INPUT_GASRESISTOR) &&
(data->status & BME68X_GASM_VALID_MSK)) {
inputs[nInputs].sensor_id = BSEC_INPUT_GASRESISTOR;
inputs[nInputs].signal = data->gas_resistance;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (BSEC_CHECK_INPUT(self->bmeConf.process_data, BSEC_INPUT_PROFILE_PART) &&
(data->status & BME68X_GASM_VALID_MSK)) {
inputs[nInputs].sensor_id = BSEC_INPUT_PROFILE_PART;
inputs[nInputs].signal = (self->opMode == BME68X_FORCED_MODE) ? 0 : (float) data->gas_index;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (nInputs > 0) {
self->outputs.nOutputs = BSEC_NUMBER_OUTPUTS;
memset(self->outputs.output, 0, sizeof(self->outputs.output));
/* Processing of the input signals and returning of output samples is performed by bsec_do_steps() */
self->status = bsec_do_steps(inputs, nInputs, self->outputs.output, &self->outputs.nOutputs);
if (self->status != BSEC_OK) {
bsec2_errormsg("bsec2_processData: error from bsec_do_steps");
return false;
}
if (self->newDataCallback) {
self->newDataCallback(data, &self->outputs, self);
}
}
return true;
}
static void bsec2_setBme68xConfigForced(struct bsec2 *self) {
struct bme68x_conf conf;
struct bme68x_heatr_conf hconf;
self->sensor_status = bme68x_get_conf(&conf, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigForced: error from bme68x_get_conf");
return;
}
conf.os_hum = self->bmeConf.humidity_oversampling;
conf.os_pres = self->bmeConf.pressure_oversampling;
conf.os_temp = self->bmeConf.temperature_oversampling;
self->sensor_status = bme68x_set_conf(&conf, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigForced: error from bme68x_set_conf");
return;
}
hconf.enable = BME68X_ENABLE;
hconf.heatr_dur = self->bmeConf.heater_duration;
hconf.heatr_temp = self->bmeConf.heater_temperature;
// the other parameters are not needed for forced mode
self->sensor_status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &hconf, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigForced: error from bme68x_set_heatr_conf");
return;
}
self->sensor_status = bme68x_set_op_mode(BME68X_FORCED_MODE, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigForced: error from bme68x_set_op_mode");
return;
}
self->opMode = BME68X_FORCED_MODE;
}
static void bsec2_setBme68xConfigParallel(struct bsec2 *self) {
uint16_t sharedHeaterDur = 0;
struct bme68x_conf conf;
struct bme68x_heatr_conf hconf;
self->sensor_status = bme68x_get_conf(&conf, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigParallel: error from bme68x_get_conf");
return;
}
conf.os_hum = self->bmeConf.humidity_oversampling;
conf.os_pres = self->bmeConf.pressure_oversampling;
conf.os_temp = self->bmeConf.temperature_oversampling;
self->sensor_status = bme68x_set_conf(&conf, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigParallel: error from bme68x_set_conf");
return;
}
sharedHeaterDur = BSEC_TOTAL_HEAT_DUR - (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &conf, self->sensor) / INT64_C(1000));
hconf.enable = BME68X_ENABLE;
hconf.shared_heatr_dur = sharedHeaterDur;
hconf.heatr_dur = self->bmeConf.heater_duration;
hconf.heatr_temp = self->bmeConf.heater_temperature;
hconf.heatr_temp_prof = self->bmeConf.heater_temperature_profile;
hconf.heatr_dur_prof = self->bmeConf.heater_duration_profile;
hconf.profile_len = self->bmeConf.heater_profile_len;
self->sensor_status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &hconf, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigParallel: error from bme68x_set_heatr_conf");
return;
}
self->sensor_status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, self->sensor);
if (BME68X_OK != self->sensor_status) {
bsec2_errormsg("bsec2_setBme68xConfigParallel: error from bme68x_set_op_mode");
return;
}
self->opMode = BME68X_PARALLEL_MODE;
}