/* * Copyright (C) 2017 Robert Bosch. All Rights Reserved. * * Disclaimer * * Common: * Bosch Sensortec products are developed for the consumer goods industry. They may only be used * within the parameters of the respective valid product data sheet. Bosch Sensortec products are * provided with the express understanding that there is no warranty of fitness for a particular purpose. * They are not fit for use in life-sustaining, safety or security sensitive systems or any system or device * that may lead to bodily harm or property damage if the system or device malfunctions. In addition, * Bosch Sensortec products are not fit for use in products which interact with motor vehicle systems. * The resale and/or use of products are at the purchasers own risk and his own responsibility. The * examination of fitness for the intended use is the sole responsibility of the Purchaser. * * The purchaser shall indemnify Bosch Sensortec from all third party claims, including any claims for * incidental, or consequential damages, arising from any product use not covered by the parameters of * the respective valid product data sheet or not approved by Bosch Sensortec and reimburse Bosch * Sensortec for all costs in connection with such claims. * * The purchaser must monitor the market for the purchased products, particularly with regard to * product safety and inform Bosch Sensortec without delay of all security relevant incidents. * * Engineering Samples are marked with an asterisk (*) or (e). Samples may vary from the valid * technical specifications of the product series. They are therefore not intended or fit for resale to third * parties or for use in end products. Their sole purpose is internal client testing. The testing of an * engineering sample may in no way replace the testing of a product series. Bosch Sensortec * assumes no liability for the use of engineering samples. By accepting the engineering samples, the * Purchaser agrees to indemnify Bosch Sensortec from all claims arising from the use of engineering * samples. * * Special: * This software module (hereinafter called "Software") and any information on application-sheets * (hereinafter called "Information") is provided free of charge for the sole purpose to support your * application work. The Software and Information is subject to the following terms and conditions: * * The Software is specifically designed for the exclusive use for Bosch Sensortec products by * personnel who have special experience and training. Do not use this Software if you do not have the * proper experience or training. * * This Software package is provided `` as is `` and without any expressed or implied warranties, * including without limitation, the implied warranties of merchantability and fitness for a particular * purpose. * * Bosch Sensortec and their representatives and agents deny any liability for the functional impairment * of this Software in terms of fitness, performance and safety. Bosch Sensortec and their * representatives and agents shall not be liable for any direct or indirect damages or injury, except as * otherwise stipulated in mandatory applicable law. * * The Information provided is believed to be accurate and reliable. Bosch Sensortec assumes no * responsibility for the consequences of use of such Information nor for any infringement of patents or * other rights of third parties which may result from its use. No license is granted by implication or * otherwise under any patent or patent rights of Bosch. Specifications mentioned in the Information are * subject to change without notice. * * It is not allowed to deliver the source code of the Software to any third party without permission of * Bosch Sensortec. * */ /*! * @file bsec_integration.c * * @brief * Private part of the example for using of BSEC library. */ /*! * @addtogroup bsec_examples BSEC Examples * @brief BSEC usage examples * @{*/ /**********************************************************************************************************************/ /* header files */ /**********************************************************************************************************************/ #include #include #include #include "bsec_integration.h" #include "bsec_interface.h" /**********************************************************************************************************************/ /* local macro definitions */ /**********************************************************************************************************************/ #define NUM_USED_OUTPUTS 10 /**********************************************************************************************************************/ /* global variable declarations */ /**********************************************************************************************************************/ /* Global sensor APIs data structure */ static struct bme680_dev bme680_g; /* Global temperature offset to be subtracted */ static float bme680_temperature_offset_g = 0.0f; /**********************************************************************************************************************/ /* functions */ /**********************************************************************************************************************/ /*! * @brief Virtual sensor subscription * Please call this function before processing of data using bsec_do_steps function * * @param[in] sample_rate mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP) * * @return subscription result, zero when successful */ bsec_library_return_t bme680_bsec_update_subscription(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; bsec_library_return_t status = BSEC_OK; /* note: Virtual sensors as desired to be added here */ 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; requested_virtual_sensors[7].sensor_id = BSEC_OUTPUT_STATIC_IAQ; requested_virtual_sensors[7].sample_rate = sample_rate; requested_virtual_sensors[8].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT; requested_virtual_sensors[8].sample_rate = sample_rate; requested_virtual_sensors[9].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT; requested_virtual_sensors[9].sample_rate = sample_rate; /* Call bsec_update_subscription() to enable/disable the requested virtual sensors */ status = bsec_update_subscription(requested_virtual_sensors, n_requested_virtual_sensors, required_sensor_settings, &n_required_sensor_settings); return status; } /*! * @brief Trigger the measurement based on sensor settings * * @param[in] sensor_settings settings of the BME680 sensor adopted by sensor control function * @param[in] sleep pointer to the system specific sleep function * * @return none */ void bme680_bsec_trigger_measurement(bsec_bme_settings_t *sensor_settings, sleep_fct sleep, struct bme680_dev *dev) { uint16_t meas_period; uint8_t set_required_settings; int8_t bme680_status = BME680_OK; /* Check if a forced-mode measurement should be triggered now */ if (sensor_settings->trigger_measurement) { /* Set sensor configuration */ dev->tph_sett.os_hum = sensor_settings->humidity_oversampling; dev->tph_sett.os_pres = sensor_settings->pressure_oversampling; dev->tph_sett.os_temp = sensor_settings->temperature_oversampling; dev->gas_sett.run_gas = sensor_settings->run_gas; dev->gas_sett.heatr_temp = sensor_settings->heater_temperature; /* degree Celsius */ dev->gas_sett.heatr_dur = sensor_settings->heating_duration; /* milliseconds */ /* Select the power mode */ /* Must be set before writing the sensor configuration */ dev->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_status = bme680_set_sensor_settings(set_required_settings, dev); /* Set power mode as forced mode and trigger forced mode measurement */ bme680_status = bme680_set_sensor_mode(dev); /* Get the total measurement duration so as to sleep or wait till the measurement is complete */ bme680_get_profile_dur(&meas_period, dev); /* Delay till the measurement is ready. Timestamp resolution in ms */ sleep((uint32_t)meas_period); } /* Call the API to get current operation mode of the sensor */ bme680_status = bme680_get_sensor_mode(dev); /* 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 (dev->power_mode == BME680_FORCED_MODE) { /* sleep for 5 ms */ sleep(5); bme680_status = bme680_get_sensor_mode(dev); } } /*! * @brief Read the data from registers and populate the inputs structure to be passed to do_steps function * * @param[in] time_stamp_trigger settings of the sensor returned from sensor control function * @param[in] inputs input structure containing the information on sensors to be passed to do_steps * @param[in] num_bsec_inputs number of inputs to be passed to do_steps * @param[in] bsec_process_data process data variable returned from sensor_control * * @return none */ void bme680_bsec_read_data(int64_t time_stamp_trigger, bsec_input_t *inputs, uint8_t *num_bsec_inputs, int32_t bsec_process_data, struct bme680_dev *dev) { static struct bme680_field_data data; int8_t bme680_status = BME680_OK; /* We only have to read data if the previous call the bsec_sensor_control() actually asked for it */ if (bsec_process_data) { bme680_status = bme680_get_sensor_data(&data, dev); if (data.status & BME680_NEW_DATA_MSK) { /* Pressure to be processed by BSEC */ if (bsec_process_data & BSEC_PROCESS_PRESSURE) { /* Place presssure sample into input struct */ 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 to be processed by BSEC */ if (bsec_process_data & BSEC_PROCESS_TEMPERATURE) { /* Place temperature sample into input struct */ inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_TEMPERATURE; #ifdef BME680_FLOAT_POINT_COMPENSATION inputs[*num_bsec_inputs].signal = data.temperature; #else inputs[*num_bsec_inputs].signal = data.temperature / 100.0f; #endif 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 to be processed by BSEC */ if (bsec_process_data & BSEC_PROCESS_HUMIDITY) { /* Place humidity sample into input struct */ inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HUMIDITY; #ifdef BME680_FLOAT_POINT_COMPENSATION inputs[*num_bsec_inputs].signal = data.humidity; #else inputs[*num_bsec_inputs].signal = data.humidity / 1000.0f; #endif inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger; (*num_bsec_inputs)++; } /* Gas to be processed by BSEC */ if (bsec_process_data & BSEC_PROCESS_GAS) { /* Check whether gas_valid flag is set */ if(data.status & BME680_GASM_VALID_MSK) { /* Place sample into input struct */ 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)++; } } } } } /*! * @brief This function is written to process the sensor data for the requested virtual sensors * * @param[in] bsec_inputs input structure containing the information on sensors to be passed to do_steps * @param[in] num_bsec_inputs number of inputs to be passed to do_steps * @param[in] output_ready pointer to the function processing obtained BSEC outputs * * @return none */ void bme680_bsec_process_data(bsec_input_t *bsec_inputs, uint8_t num_bsec_inputs, output_ready_fct output_ready) { /* Output buffer set to the maximum virtual sensor outputs supported */ bsec_output_t bsec_outputs[BSEC_NUMBER_OUTPUTS]; uint8_t num_bsec_outputs = 0; uint8_t index = 0; bsec_library_return_t bsec_status = BSEC_OK; 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; float static_iaq = 0.0f; uint8_t static_iaq_accuracy = 0; float co2_equivalent = 0.0f; uint8_t co2_accuracy = 0; float breath_voc_equivalent = 0.0f; uint8_t breath_voc_accuracy = 0; float comp_gas_value = 0.0f; uint8_t comp_gas_accuracy = 0; float gas_percentage = 0.0f; uint8_t gas_percentage_acccuracy = 0; /* 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; /* Perform processing of the data by BSEC Note: * The number of outputs you get depends on what you asked for during bsec_update_subscription(). This is handled under bme680_bsec_update_subscription() function in this example file. * The number of actual outputs that are returned is written to num_bsec_outputs. */ bsec_status = 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_STATIC_IAQ: static_iaq = bsec_outputs[index].signal; static_iaq_accuracy = bsec_outputs[index].accuracy; break; case BSEC_OUTPUT_CO2_EQUIVALENT: co2_equivalent = bsec_outputs[index].signal; co2_accuracy = bsec_outputs[index].accuracy; break; case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: breath_voc_equivalent = bsec_outputs[index].signal; breath_voc_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; case BSEC_OUTPUT_COMPENSATED_GAS: comp_gas_value = bsec_outputs[index].signal; comp_gas_accuracy = bsec_outputs[index].accuracy; break; case BSEC_OUTPUT_GAS_PERCENTAGE: gas_percentage = bsec_outputs[index].signal; gas_percentage_acccuracy = bsec_outputs[index].accuracy; break; default: continue; } /* Assume that all the returned timestamps are the same */ timestamp = bsec_outputs[index].time_stamp; } /* Pass the extracted outputs to the user provided output_ready() function. */ output_ready(timestamp, iaq, iaq_accuracy, temp, humidity, raw_pressure, raw_temp, raw_humidity, raw_gas, bsec_status, static_iaq, co2_equivalent, breath_voc_equivalent); } } /*! @}*/