#include #include "bsec2.h" //#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include #include 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; }