#include "bsec.h" #include "bsec_serialized_configurations_selectivity.h" #include "BMEManager.h" #include "bme68x_defs.h" static bsec_version_t bsec_version; static BsecOutput bsec_output; static bsec_library_return_t bsec_status; static bsec_bme_settings_t bsec_bme_settings; static bsec_step step; // BME688 related variables static int8_t bme68x_status; static struct bme68x_dev *bme68x; static struct bme68x_conf bme68x_conf; static struct bme68x_heatr_conf bme68x_heatr_conf; static struct bme68x_data _bme68x_data[3]; // accessing bme68x data registers static uint8_t op_mode; // operating mode of sensor static bool check_meas_idx; // check measurement index to track the order static uint8_t last_meas_index; // last measurement index received from sensor // 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. static uint32_t timer_overflow_counter; static uint32_t timer_last_value; static float temp_offset; int64_t timer_count; // Temporary Use variable void initialize_bsec_library() { timer_overflow_counter = 0; timer_last_value = 0; step = CONTROL_STEP; bme68x_status = BME68X_OK; bsec_status = BSEC_OK; temp_offset = 5.0f; memset(&bsec_version, 0, sizeof(bsec_version)); memset(&bsec_bme_settings, 0, sizeof(bsec_bme_settings)); memset(&bsec_output, 0, sizeof(bsec_output)); bsec_virtual_sensor_t sensor_list[] = { BSEC_OUTPUT_RAW_TEMPERATURE, BSEC_OUTPUT_RAW_PRESSURE, BSEC_OUTPUT_RAW_HUMIDITY, BSEC_OUTPUT_RAW_GAS, BSEC_OUTPUT_RAW_GAS_INDEX, BSEC_OUTPUT_IAQ, BSEC_OUTPUT_STATIC_IAQ, BSEC_OUTPUT_CO2_EQUIVALENT, BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, BSEC_OUTPUT_STABILIZATION_STATUS, BSEC_OUTPUT_RUN_IN_STATUS, BSEC_OUTPUT_GAS_ESTIMATE_1, BSEC_OUTPUT_GAS_ESTIMATE_2, BSEC_OUTPUT_GAS_ESTIMATE_3, BSEC_OUTPUT_GAS_ESTIMATE_4 }; bsec_status = bsec_init(); if(bsec_status < BSEC_OK) { NRF_LOG_INFO("BSEC Library Init Failed"); } else { bsec_status = bsec_get_version(&bsec_version); if(bsec_status == BSEC_OK) { NRF_LOG_INFO("#### BSEC Version = %d.%d.%d.%d", bsec_version.major, bsec_version.minor, bsec_version.major_bugfix, bsec_version.minor_bugfix); } else { NRF_LOG_INFO("Unable to get BSEC Version "); } } /* Set configuration to selectivity mode and pass configuration */ set_config(bsec_config_selectivity); /* Set sample rate for all virtual sensor data we need from BSEC library */ update_subscription(sensor_list, ARRAY_LEN(sensor_list), BSEC_SAMPLE_RATE_HIGH_PERFORMANCE); //update_subscription(sensor_list, ARRAY_LEN(sensor_list), BSEC_SAMPLE_RATE_LP); bme68x = (struct bme68x_dev *)BME688_get_dev_structure(); } /** * @brief Function to set the configuration of the algorithm from memory * * @details This function will set configuration of algorithm during initialization * by reading the array stored in memory * * @param[in] state Configuration to be set */ bool set_config(const uint8_t *state) { uint8_t workBuffer[BSEC_MAX_PROPERTY_BLOB_SIZE]; bsec_status = bsec_set_configuration(state, BSEC_MAX_PROPERTY_BLOB_SIZE, workBuffer, BSEC_MAX_PROPERTY_BLOB_SIZE); NRF_LOG_INFO("set_config returned: %d", bsec_status); if (bsec_status < BSEC_OK) return false; memset(&bsec_bme_settings, 0, sizeof(bsec_bme_settings)); step = CONTROL_STEP; return true; } /** * @brief Function that sets the desired sensors and the sample rates (by subscription) * * @details To instruct BSEC which of the processed output signals are requested at which * sample rates. * * @param[in] sensor_list array of requested virtual sensor configurations for the library * @param[in] num_of_sensors Number of virtual sensors * @param[in] sample_rate Sample rate to be set for each virtual sensors */ bool update_subscription(bsec_virtual_sensor_t sensor_list[], uint8_t num_of_sensors, float sample_rate) { bsec_sensor_configuration_t requested_virtual_sensors[BSEC_NUMBER_OUTPUTS]; bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR]; uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR; for (uint8_t i = 0; i < num_of_sensors; i++) { requested_virtual_sensors[i].sensor_id = sensor_list[i]; requested_virtual_sensors[i].sample_rate = sample_rate; } bsec_status = bsec_update_subscription(requested_virtual_sensors, num_of_sensors, sensor_settings, &n_required_sensor_settings); NRF_LOG_INFO("bsec_update_subscription returned: %d", bsec_status); if (bsec_status < BSEC_OK) return false; #if 0 bme68x_status = bme68x_init(&bme68x); if (bme68x_status < BME68X_OK) return false; #endif return true; } /** * @brief Process BME sensor data * * @details Process raw data output of BME688 sensor * * @param[in] curr_time_ns timestamp of sample in nanoseconds * @param[in] data datastrucure containing raw sensor data */ bool process_bme_data(int64_t _curr_time_ns, const struct bme68x_data *data) { //NRF_LOG_INFO("#### process_bme_data called"); bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temp, Pres, Hum & Gas uint8_t num_of_inputs = 0; if (CHECK_BSEC_INPUT(bsec_bme_settings.process_data, BSEC_INPUT_HEATSOURCE)) { //NRF_LOG_INFO("BSEC_INPUT_HEATSOURCE"); inputs[num_of_inputs].sensor_id = BSEC_INPUT_HEATSOURCE; inputs[num_of_inputs].signal = temp_offset; inputs[num_of_inputs].time_stamp = _curr_time_ns; //NRF_LOG_INFO("timestamp = %llu", inputs[num_of_inputs].time_stamp); num_of_inputs++; } if (CHECK_BSEC_INPUT(bsec_bme_settings.process_data, BSEC_INPUT_TEMPERATURE)) { //NRF_LOG_INFO("BSEC_INPUT_TEMPERATURE %d", data->temperature); //NRF_LOG_INFO("BSEC_INPUT_TEMPERATURE"); inputs[num_of_inputs].sensor_id = BSEC_INPUT_TEMPERATURE; //inputs[num_of_inputs].signal = (data->temperature)/100; inputs[num_of_inputs].signal = (data->temperature); inputs[num_of_inputs].time_stamp = _curr_time_ns; //NRF_LOG_INFO("timestamp = %llu", inputs[num_of_inputs].time_stamp); num_of_inputs++; } if (CHECK_BSEC_INPUT(bsec_bme_settings.process_data, BSEC_INPUT_HUMIDITY)) { //NRF_LOG_INFO("BSEC_INPUT_HUMIDITY %d", data->humidity); //NRF_LOG_INFO("BSEC_INPUT_HUMIDITY "); inputs[num_of_inputs].sensor_id = BSEC_INPUT_HUMIDITY; //inputs[num_of_inputs].signal = (data->humidity) / 1000; inputs[num_of_inputs].signal = (data->humidity); inputs[num_of_inputs].time_stamp = _curr_time_ns; //NRF_LOG_INFO("timestamp = %llu", inputs[num_of_inputs].time_stamp); num_of_inputs++; } if (CHECK_BSEC_INPUT(bsec_bme_settings.process_data, BSEC_INPUT_PRESSURE)) { //NRF_LOG_INFO("BSEC_INPUT_PRESSURE %d", data->pressure); //NRF_LOG_INFO("BSEC_INPUT_PRESSURE"); inputs[num_of_inputs].sensor_id = BSEC_INPUT_PRESSURE; inputs[num_of_inputs].signal = data->pressure; inputs[num_of_inputs].time_stamp = _curr_time_ns; //NRF_LOG_INFO("timestamp = %llu", inputs[num_of_inputs].time_stamp); num_of_inputs++; } if (CHECK_BSEC_INPUT(bsec_bme_settings.process_data, BSEC_INPUT_GASRESISTOR)) { //NRF_LOG_INFO("BSEC_INPUT_GASRESISTOR %d", data->gas_resistance); //NRF_LOG_INFO("BSEC_INPUT_GASRESISTOR"); inputs[num_of_inputs].sensor_id = BSEC_INPUT_GASRESISTOR; inputs[num_of_inputs].signal = data->gas_resistance; inputs[num_of_inputs].time_stamp = _curr_time_ns; //NRF_LOG_INFO("timestamp = %llu", inputs[num_of_inputs].time_stamp); num_of_inputs++; } if (CHECK_BSEC_INPUT(bsec_bme_settings.process_data, BSEC_INPUT_PROFILE_PART)) { //NRF_LOG_INFO("BSEC_INPUT_PROFILE_PART %d", data->gas_index); //NRF_LOG_INFO("BSEC_INPUT_PROFILE_PART"); inputs[num_of_inputs].sensor_id = BSEC_INPUT_PROFILE_PART; inputs[num_of_inputs].signal = (op_mode == BME68X_FORCED_MODE) ? 0 : data->gas_index; //NRF_LOG_INFO("gas_index = %d", inputs[num_of_inputs].signal); inputs[num_of_inputs].time_stamp = _curr_time_ns; //NRF_LOG_INFO("timestamp = %llu", inputs[num_of_inputs].time_stamp); num_of_inputs++; } //NRF_LOG_INFO("#### num_of_inputs = %d", num_of_inputs); if (num_of_inputs > 0) { NRF_LOG_INFO("timestamp bsec = %llu", inputs[num_of_inputs-1].time_stamp); NRF_LOG_INFO("timestamp ms = %llu", _curr_time_ns); bsec_output.len = BSEC_NUMBER_OUTPUTS; //bsec_output.len = 4; memset(bsec_output.outputs, 0, sizeof(bsec_output.outputs)); bsec_status = bsec_do_steps(inputs, num_of_inputs, bsec_output.outputs, &bsec_output.len); //bsec_status = bsec_do_steps(inputs, num_of_inputs-1, bsec_output.outputs, &bsec_output.len); NRF_LOG_INFO("#### bsec_do_steps returned %d", bsec_status); if (bsec_status < BSEC_OK) //if (bsec_status != BSEC_OK) { NRF_LOG_INFO("#### bsec_do_steps returned %d", bsec_status); return false; } } //print_bsec_processed_data(data, &bsec_output); print_bsec_processed_data(data); return true; } /** * @brief Read BME688 sensor data and process it * * @details Callback function from user to read data from BME688 sensor using Forced/Parallel mode * This data from sensor is supplied to BSEC library for further processing * * @param[in] sensor_list array of requested virtual sensor configurations for the library * @param[in] num_of_sensors Number of virtual sensors * @param[in] sample_rate Sample rate to be set for each virtual sensors */ #define MS_TO_NS 1000000 bool read_sensor_and_process_data(void) { uint8_t n_fields = 0; int64_t curr_time_ns = get_time_ms() * MS_TO_NS; op_mode = bsec_bme_settings.op_mode; if (curr_time_ns >= bsec_bme_settings.next_call) { bsec_status = bsec_sensor_control(curr_time_ns, &bsec_bme_settings); //NRF_LOG_INFO("#### bsec_sensor_control returned: %d, RTC COUNTER = %llu, current_time = %llu, next_call: %llu", // bsec_status, timer_count, curr_time_ns, (bsec_bme_settings.next_call / MS_TO_NS)); //NRF_LOG_INFO("##################################################"); //NRF_LOG_INFO("RTC COUNTER = %llu, current_time = %llu", timer_count, curr_time_ns); //NRF_LOG_INFO("#### next_call: %llu", bsec_bme_settings.next_call); //NRF_LOG_INFO("#### process_data: %d", bsec_bme_settings.process_data); //NRF_LOG_INFO("#### heater_temperature: %d", bsec_bme_settings.heater_temperature); //NRF_LOG_INFO("#### heater_duration: %d", bsec_bme_settings.heater_duration); //for(int i = 0; i < 10; i++) //{ // NRF_LOG_INFO("heater_temperature_profile[%d] = %d", i, bsec_bme_settings.heater_temperature_profile[i]); // NRF_LOG_INFO("heater_duration_profile[%d] = %d", i, bsec_bme_settings.heater_duration_profile[i]); //} //NRF_LOG_INFO("#### heater_profile_len: %d", bsec_bme_settings.heater_profile_len); //NRF_LOG_INFO("#### run_gas: %d", bsec_bme_settings.run_gas); //NRF_LOG_INFO("#### pressure_oversampling: %d", bsec_bme_settings.pressure_oversampling); //NRF_LOG_INFO("#### temperature_oversampling: %d", bsec_bme_settings.temperature_oversampling); //NRF_LOG_INFO("#### humidity_oversampling: %d", bsec_bme_settings.humidity_oversampling); //NRF_LOG_INFO("#### trigger_measurement: %d", bsec_bme_settings.trigger_measurement); //NRF_LOG_INFO("#### op_mode: %d", bsec_bme_settings.op_mode); if (bsec_status < BSEC_OK) return false; //NRF_LOG_INFO("op_mode: %d", bsec_bme_settings.op_mode); switch(bsec_bme_settings.op_mode) { case BME68X_FORCED_MODE: set_bme68x_forced_mode(); break; case BME68X_PARALLEL_MODE: if(op_mode != bsec_bme_settings.op_mode) { set_bme68x_parallel_mode(); } break; case BME68X_SLEEP_MODE: if(op_mode != bsec_bme_settings.op_mode) { set_bme68x_sleep_mode(); } break; } //NRF_LOG_INFO("status = %d", bme68x_status); if (bme68x_status < BME68X_OK) return false; if(bsec_bme_settings.trigger_measurement && bsec_bme_settings.op_mode != BME68X_SLEEP_MODE) { //NRF_LOG_INFO("##### Going to get data"); bme68x_status = bme68x_get_data(op_mode, _bme68x_data, &n_fields, bme68x); if(bme68x_status != BME68X_OK) return false; 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(!process_bme_data(curr_time_ns, &_bme68x_data[i])) { return false; } } } } } return true; } /** * @brief Function to calculate an int64_t timestamp in milliseconds */ int64_t get_time_ms(void) { int64_t time_ms; time_ms = (int64_t)app_timer_cnt_get(); timer_count = time_ms; time_ms /= 32.768; if (timer_last_value > time_ms) { // An overflow occured timer_last_value = time_ms; timer_overflow_counter++; } timer_last_value = time_ms; return time_ms + (timer_overflow_counter * 0x7D000); } /** * @brief Set the BME68X sensor configuration to forced mode */ void set_bme68x_forced_mode(void) { NRF_LOG_INFO("####Setting FORCED Mode"); /* 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 set_bme68x_sleep_mode(void) { NRF_LOG_INFO("#### Setting Sleep Mode"); bme68x_status = bme68x_set_op_mode(BME68X_SLEEP_MODE, bme68x); NRF_LOG_INFO("SLeep Mode Status = %d", bme68x_status); if (bme68x_status < BME68X_OK) return; op_mode = BME68X_SLEEP_MODE; } /** * @brief Set the BME68X sensor configuration to parallel mode */ void set_bme68x_parallel_mode(void) { NRF_LOG_INFO("#### Setting Parallel Mode"); uint8_t n_fields = 0; /* 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); NRF_LOG_INFO("#### set conf status = %d", bme68x_status); 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; NRF_LOG_INFO("Enable Status = %d %d", 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) / 1000); 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); NRF_LOG_INFO("#### heater conf status = %d", bme68x_status); if (bme68x_status < BME68X_OK) return; bme68x_status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, bme68x); NRF_LOG_INFO("#### op mode set status = %d", bme68x_status); if (bme68x_status < BME68X_OK) return; else if(bme68x_status == BME68X_OK) NRF_LOG_INFO("##### PArallel mode set"); /* Enable measurement index check to track the first valid sample after operation mode change */ check_meas_idx = true; op_mode = BME68X_PARALLEL_MODE; } //void print_bsec_processed_data(const struct bme68x_data *input, const BsecOutput *outputs) void print_bsec_processed_data(const struct bme68x_data *input) { NRF_LOG_INFO("BSEC outputs:"); NRF_LOG_INFO("timestamp = %llu", (bsec_output.outputs[0].time_stamp)); if (!bsec_output.len) return; for (uint8_t i = 0; i < bsec_output.len; i++) { const bsec_output_t *output = &bsec_output.outputs[i]; switch (output->sensor_id) { case BSEC_OUTPUT_IAQ: //NRF_LOG_INFO("IAQ = %d", output->signal); NRF_LOG_INFO("IAQ = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); NRF_LOG_INFO("IAQ accuracy = %d", (int)output->accuracy); break; case BSEC_OUTPUT_CO2_EQUIVALENT: NRF_LOG_INFO("Co2 Equivalent = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: NRF_LOG_INFO("Breathe VOC Equivalent = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_RAW_TEMPERATURE: //NRF_LOG_INFO("Temperature = %d", output->signal); NRF_LOG_INFO("Temperature = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_RAW_PRESSURE: //NRF_LOG_INFO("Pressure = %d", output->signal); NRF_LOG_INFO("Pressure = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_RAW_HUMIDITY: //NRF_LOG_INFO("Humidity = %d", output->signal); NRF_LOG_INFO("Humidity = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_RAW_GAS: //NRF_LOG_INFO("Gas resistance = %d", output->signal); NRF_LOG_INFO("Gas resistance = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_RAW_GAS_INDEX: //NRF_LOG_INFO("Gas index = %d", output->signal); NRF_LOG_INFO("Gas index = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_STABILIZATION_STATUS: //NRF_LOG_INFO("Stabilization status = %d", output->signal); NRF_LOG_INFO("Stabilization status = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_RUN_IN_STATUS: //NRF_LOG_INFO("Run in status = %d", output->signal); NRF_LOG_INFO("Run in status = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: //NRF_LOG_INFO("Compensated temperature = %d", output->signal); NRF_LOG_INFO("Compensated temperature = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: //NRF_LOG_INFO("Compensated humidity = %d", output->signal); NRF_LOG_INFO("Compensated humidity = " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(output->signal)); break; case BSEC_OUTPUT_GAS_ESTIMATE_1: case BSEC_OUTPUT_GAS_ESTIMATE_2: case BSEC_OUTPUT_GAS_ESTIMATE_3: case BSEC_OUTPUT_GAS_ESTIMATE_4: NRF_LOG_INFO("Gas Estimate %d = " NRF_LOG_FLOAT_MARKER, (int)(output->sensor_id + 1 - BSEC_OUTPUT_GAS_ESTIMATE_1), NRF_LOG_FLOAT(output->signal)); NRF_LOG_INFO("Gas Estimate %d accuracy = %d", (int)(output->sensor_id + 1 - BSEC_OUTPUT_GAS_ESTIMATE_1), output->accuracy); break; default: break; } } }