diff --git a/Makefile b/Makefile index a87a7df..65acd3e 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,10 @@ DEVICE := stm32l152rc DEFS += BME68X_DO_NOT_USE_FPU DEFS += FW_USE_RTOS +LDLIBS := src/BSEC/libalgobsec.a + INCDIRS := \ +src/BSEC \ src/BME68x-Sensor-API \ src/oled-driver/ \ src/oled-driver/include \ diff --git a/src/BME68x-Sensor-API b/src/BME68x-Sensor-API index e104fe5..9f9b030 160000 --- a/src/BME68x-Sensor-API +++ b/src/BME68x-Sensor-API @@ -1 +1 @@ -Subproject commit e104fe56e58dfdf4d827f1344587dba7cf45bb01 +Subproject commit 9f9b030c7236aa308252921973ec2abeb679c1bd diff --git a/src/BSEC/bsec_datatypes.h b/src/BSEC/bsec_datatypes.h new file mode 100644 index 0000000..535ed5d --- /dev/null +++ b/src/BSEC/bsec_datatypes.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2015, 2016, 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_datatypes.h + * + * @brief + * Contains the data types used by BSEC + * + */ + +#ifndef __BSEC_DATATYPES_H__ +#define __BSEC_DATATYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*! + * @addtogroup bsec_interface BSEC C Interface + * @{*/ + +#ifdef __KERNEL__ +#include +#endif +#include +#include + +#define BSEC_MAX_WORKBUFFER_SIZE (2048) /*!< Maximum size (in bytes) of the work buffer */ +#define BSEC_MAX_PHYSICAL_SENSOR (8) /*!< Number of physical sensors that need allocated space before calling bsec_update_subscription() */ +#define BSEC_MAX_PROPERTY_BLOB_SIZE (454) /*!< Maximum size (in bytes) of the data blobs returned by bsec_get_configuration() */ +#define BSEC_MAX_STATE_BLOB_SIZE (139) /*!< Maximum size (in bytes) of the data blobs returned by bsec_get_state()*/ +#define BSEC_SAMPLE_RATE_DISABLED (65535.0f) /*!< Sample rate of a disabled sensor */ +#define BSEC_SAMPLE_RATE_ULP (0.0033333f) /*!< Sample rate in case of Ultra Low Power Mode */ +#define BSEC_SAMPLE_RATE_CONTINUOUS (1.0f) /*!< Sample rate in case of Continuous Mode */ +#define BSEC_SAMPLE_RATE_LP (0.33333f) /*!< Sample rate in case of Low Power Mode */ +#define BSEC_SAMPLE_RATE_ULP_MEASUREMENT_ON_DEMAND (0.0f) /*!< Input value used to trigger an extra measurment (ULP plus) */ + +#define BSEC_PROCESS_PRESSURE (1 << (BSEC_INPUT_PRESSURE-1)) /*!< process_data bitfield constant for pressure @sa bsec_bme_settings_t */ +#define BSEC_PROCESS_TEMPERATURE (1 << (BSEC_INPUT_TEMPERATURE-1)) /*!< process_data bitfield constant for temperature @sa bsec_bme_settings_t */ +#define BSEC_PROCESS_HUMIDITY (1 << (BSEC_INPUT_HUMIDITY-1)) /*!< process_data bitfield constant for humidity @sa bsec_bme_settings_t */ +#define BSEC_PROCESS_GAS (1 << (BSEC_INPUT_GASRESISTOR-1)) /*!< process_data bitfield constant for gas sensor @sa bsec_bme_settings_t */ +#define BSEC_NUMBER_OUTPUTS (14) /*!< Number of outputs, depending on solution */ +#define BSEC_OUTPUT_INCLUDED (1210863) /*!< bitfield that indicates which outputs are included in the solution */ + +/*! + * @brief Enumeration for input (physical) sensors. + * + * Used to populate bsec_input_t::sensor_id. It is also used in bsec_sensor_configuration_t::sensor_id structs + * returned in the parameter required_sensor_settings of bsec_update_subscription(). + * + * @sa bsec_sensor_configuration_t @sa bsec_input_t + */ +typedef enum +{ + /** + * @brief Pressure sensor output of BMExxx [Pa] + */ + BSEC_INPUT_PRESSURE = 1, + + /** + * @brief Humidity sensor output of BMExxx [%] + * + * @note Relative humidity strongly depends on the temperature (it is measured at). It may require a conversion to + * the temperature outside of the device. + * + * @sa bsec_virtual_sensor_t + */ + BSEC_INPUT_HUMIDITY = 2, + + /** + * @brief Temperature sensor output of BMExxx [degrees Celsius] + * + * @note The BME680 is factory trimmed, thus the temperature sensor of the BME680 is very accurate. + * The temperature value is a very local measurement value and can be influenced by external heat sources. + * + * @sa bsec_virtual_sensor_t + */ + BSEC_INPUT_TEMPERATURE = 3, + + /** + * @brief Gas sensor resistance output of BMExxx [Ohm] + * + * The resistance value changes due to varying VOC concentrations (the higher the concentration of reducing VOCs, + * the lower the resistance and vice versa). + */ + BSEC_INPUT_GASRESISTOR = 4, /*!< */ + + /** + * @brief Additional input for device heat compensation + * + * IAQ solution: The value is subtracted from ::BSEC_INPUT_TEMPERATURE to compute + * ::BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE. + * + * ALL solution: Generic heat source 1 + * + * @sa bsec_virtual_sensor_t + */ + BSEC_INPUT_HEATSOURCE = 14, + + /** + * @brief Additional input for device heat compensation 8 + * + * Generic heat source 8 + */ + + + /** + * @brief Additional input that disables baseline tracker + * + * 0 - Normal + * 1 - Event 1 + * 2 - Event 2 + */ + BSEC_INPUT_DISABLE_BASELINE_TRACKER = 23, + +} bsec_physical_sensor_t; + +/*! + * @brief Enumeration for output (virtual) sensors + * + * Used to populate bsec_output_t::sensor_id. It is also used in bsec_sensor_configuration_t::sensor_id structs + * passed in the parameter requested_virtual_sensors of bsec_update_subscription(). + * + * @sa bsec_sensor_configuration_t @sa bsec_output_t + */ +typedef enum +{ + /** + * @brief Indoor-air-quality estimate [0-500] + * + * Indoor-air-quality (IAQ) gives an indication of the relative change in ambient TVOCs detected by BME680. + * + * @note The IAQ scale ranges from 0 (clean air) to 500 (heavily polluted air). During operation, algorithms + * automatically calibrate and adapt themselves to the typical environments where the sensor is operated + * (e.g., home, workplace, inside a car, etc.).This automatic background calibration ensures that users experience + * consistent IAQ performance. The calibration process considers the recent measurement history (typ. up to four + * days) to ensure that IAQ=25 corresponds to typical good air and IAQ=250 indicates typical polluted air. + */ + BSEC_OUTPUT_IAQ = 1, + BSEC_OUTPUT_STATIC_IAQ = 2, /*!< Unscaled indoor-air-quality estimate */ + BSEC_OUTPUT_CO2_EQUIVALENT = 3, /*!< co2 equivalent estimate [ppm] */ + BSEC_OUTPUT_BREATH_VOC_EQUIVALENT = 4, /*!< breath VOC concentration estimate [ppm] */ + + /** + * @brief Temperature sensor signal [degrees Celsius] + * + * Temperature directly measured by BME680 in degree Celsius. + * + * @note This value is cross-influenced by the sensor heating and device specific heating. + */ + BSEC_OUTPUT_RAW_TEMPERATURE = 6, + + /** + * @brief Pressure sensor signal [Pa] + * + * Pressure directly measured by the BME680 in Pa. + */ + BSEC_OUTPUT_RAW_PRESSURE = 7, + + /** + * @brief Relative humidity sensor signal [%] + * + * Relative humidity directly measured by the BME680 in %. + * + * @note This value is cross-influenced by the sensor heating and device specific heating. + */ + BSEC_OUTPUT_RAW_HUMIDITY = 8, + + /** + * @brief Gas sensor signal [Ohm] + * + * Gas resistance measured directly by the BME680 in Ohm.The resistance value changes due to varying VOC + * concentrations (the higher the concentration of reducing VOCs, the lower the resistance and vice versa). + */ + BSEC_OUTPUT_RAW_GAS = 9, + + /** + * @brief Gas sensor stabilization status [boolean] + * + * Indicates initial stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization + * is finished (1). + */ + BSEC_OUTPUT_STABILIZATION_STATUS = 12, + + /** + * @brief Gas sensor run-in status [boolean] + * + * Indicates power-on stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization + * is finished (1). + */ + BSEC_OUTPUT_RUN_IN_STATUS = 13, + + /** + * @brief Sensor heat compensated temperature [degrees Celsius] + * + * Temperature measured by BME680 which is compensated for the influence of sensor (heater) in degree Celsius. + * The self heating introduced by the heater is depending on the sensor operation mode and the sensor supply voltage. + * + * + * @note IAQ solution: In addition, the temperature output can be compensated by an user defined value + * (::BSEC_INPUT_HEATSOURCE in degrees Celsius), which represents the device specific self-heating. + * + * Thus, the value is calculated as follows: + * * IAQ solution: ```BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE = ::BSEC_INPUT_TEMPERATURE - function(sensor operation mode, sensor supply voltage) - ::BSEC_INPUT_HEATSOURCE``` + * * other solutions: ```::BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE = ::BSEC_INPUT_TEMPERATURE - function(sensor operation mode, sensor supply voltage)``` + * + * The self-heating in operation mode BSEC_SAMPLE_RATE_ULP is negligible. + * The self-heating in operation mode BSEC_SAMPLE_RATE_LP is supported for 1.8V by default (no config file required). If the BME680 sensor supply voltage is 3.3V, the IoT_LP_3_3V.config shall be used. + */ + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE = 14, + + /** + * @brief Sensor heat compensated humidity [%] + * + * Relative measured by BME680 which is compensated for the influence of sensor (heater) in %. + * + * It converts the ::BSEC_INPUT_HUMIDITY from temperature ::BSEC_INPUT_TEMPERATURE to temperature + * ::BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE. + * + * @note IAQ solution: If ::BSEC_INPUT_HEATSOURCE is used for device specific temperature compensation, it will be + * effective for ::BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY too. + */ + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY = 15, + + BSEC_OUTPUT_COMPENSATED_GAS = 18, /*!< Reserved internal debug output */ + BSEC_OUTPUT_GAS_PERCENTAGE = 21 /*!< percentage of min and max filtered gas value [%] */ +} bsec_virtual_sensor_t; + +/*! + * @brief Enumeration for function return codes + */ +typedef enum +{ + BSEC_OK = 0, /*!< Function execution successful */ + BSEC_E_DOSTEPS_INVALIDINPUT = -1, /*!< Input (physical) sensor id passed to bsec_do_steps() is not in the valid range or not valid for requested virtual sensor */ + BSEC_E_DOSTEPS_VALUELIMITS = -2, /*!< Value of input (physical) sensor signal passed to bsec_do_steps() is not in the valid range */ + BSEC_E_DOSTEPS_DUPLICATEINPUT = -6, /*!< Duplicate input (physical) sensor ids passed as input to bsec_do_steps() */ + BSEC_I_DOSTEPS_NOOUTPUTSRETURNABLE = 2, /*!< No memory allocated to hold return values from bsec_do_steps(), i.e., n_outputs == 0 */ + BSEC_W_DOSTEPS_EXCESSOUTPUTS = 3, /*!< Not enough memory allocated to hold return values from bsec_do_steps(), i.e., n_outputs < maximum number of requested output (virtual) sensors */ + BSEC_W_DOSTEPS_TSINTRADIFFOUTOFRANGE = 4, /*!< Duplicate timestamps passed to bsec_do_steps() */ + BSEC_E_SU_WRONGDATARATE = -10, /*!< The sample_rate of the requested output (virtual) sensor passed to bsec_update_subscription() is zero */ + BSEC_E_SU_SAMPLERATELIMITS = -12, /*!< The sample_rate of the requested output (virtual) sensor passed to bsec_update_subscription() does not match with the sampling rate allowed for that sensor */ + BSEC_E_SU_DUPLICATEGATE = -13, /*!< Duplicate output (virtual) sensor ids requested through bsec_update_subscription() */ + BSEC_E_SU_INVALIDSAMPLERATE = -14, /*!< The sample_rate of the requested output (virtual) sensor passed to bsec_update_subscription() does not fall within the global minimum and maximum sampling rates */ + BSEC_E_SU_GATECOUNTEXCEEDSARRAY = -15, /*!< Not enough memory allocated to hold returned input (physical) sensor data from bsec_update_subscription(), i.e., n_required_sensor_settings < #BSEC_MAX_PHYSICAL_SENSOR */ + BSEC_E_SU_SAMPLINTVLINTEGERMULT = -16, /*!< The sample_rate of the requested output (virtual) sensor passed to bsec_update_subscription() is not correct */ + BSEC_E_SU_MULTGASSAMPLINTVL = -17, /*!< The sample_rate of the requested output (virtual), which requires the gas sensor, is not equal to the sample_rate that the gas sensor is being operated */ + BSEC_E_SU_HIGHHEATERONDURATION = -18, /*!< The duration of one measurement is longer than the requested sampling interval */ + BSEC_W_SU_UNKNOWNOUTPUTGATE = 10, /*!< Output (virtual) sensor id passed to bsec_update_subscription() is not in the valid range; e.g., n_requested_virtual_sensors > actual number of output (virtual) sensors requested */ + BSEC_W_SU_MODINNOULP = 11, /*!< ULP plus can not be requested in non-ulp mode */ /*MOD_ONLY*/ + BSEC_I_SU_SUBSCRIBEDOUTPUTGATES = 12, /*!< No output (virtual) sensor data were requested via bsec_update_subscription() */ + BSEC_E_PARSE_SECTIONEXCEEDSWORKBUFFER = -32, /*!< n_work_buffer_size passed to bsec_set_[configuration/state]() not sufficient */ + BSEC_E_CONFIG_FAIL = -33, /*!< Configuration failed */ + BSEC_E_CONFIG_VERSIONMISMATCH = -34, /*!< Version encoded in serialized_[settings/state] passed to bsec_set_[configuration/state]() does not match with current version */ + BSEC_E_CONFIG_FEATUREMISMATCH = -35, /*!< Enabled features encoded in serialized_[settings/state] passed to bsec_set_[configuration/state]() does not match with current library implementation */ + BSEC_E_CONFIG_CRCMISMATCH = -36, /*!< serialized_[settings/state] passed to bsec_set_[configuration/state]() is corrupted */ + BSEC_E_CONFIG_EMPTY = -37, /*!< n_serialized_[settings/state] passed to bsec_set_[configuration/state]() is to short to be valid */ + BSEC_E_CONFIG_INSUFFICIENTWORKBUFFER = -38, /*!< Provided work_buffer is not large enough to hold the desired string */ + BSEC_E_CONFIG_INVALIDSTRINGSIZE = -40, /*!< String size encoded in configuration/state strings passed to bsec_set_[configuration/state]() does not match with the actual string size n_serialized_[settings/state] passed to these functions */ + BSEC_E_CONFIG_INSUFFICIENTBUFFER = -41, /*!< String buffer insufficient to hold serialized data from BSEC library */ + BSEC_E_SET_INVALIDCHANNELIDENTIFIER = -100, /*!< Internal error code, size of work buffer in setConfig must be set to BSEC_MAX_WORKBUFFER_SIZE */ + BSEC_E_SET_INVALIDLENGTH = -104, /*!< Internal error code */ + BSEC_W_SC_CALL_TIMING_VIOLATION = 100, /*!< Difference between actual and defined sampling intervals of bsec_sensor_control() greater than allowed */ + BSEC_W_SC_MODEXCEEDULPTIMELIMIT = 101, /*!< ULP plus is not allowed because an ULP measurement just took or will take place */ /*MOD_ONLY*/ + BSEC_W_SC_MODINSUFFICIENTWAITTIME = 102 /*!< ULP plus is not allowed because not sufficient time passed since last ULP plus */ /*MOD_ONLY*/ +} bsec_library_return_t; + +/*! + * @brief Structure containing the version information + * + * Please note that configuration and state strings are coded to a specific version and will not be accepted by other + * versions of BSEC. + * + */ +typedef struct +{ + uint8_t major; /**< @brief Major version */ + uint8_t minor; /**< @brief Minor version */ + uint8_t major_bugfix; /**< @brief Major bug fix version */ + uint8_t minor_bugfix; /**< @brief Minor bug fix version */ +} bsec_version_t; + +/*! + * @brief Structure describing an input sample to the library + * + * Each input sample is provided to BSEC as an element in a struct array of this type. Timestamps must be provided + * in nanosecond resolution. Moreover, duplicate timestamps for subsequent samples are not allowed and will results in + * an error code being returned from bsec_do_steps(). + * + * The meaning unit of the signal field are determined by the bsec_input_t::sensor_id field content. Possible + * bsec_input_t::sensor_id values and and their meaning are described in ::bsec_physical_sensor_t. + * + * @sa bsec_physical_sensor_t + * + */ +typedef struct +{ + /** + * @brief Time stamp in nanosecond resolution [ns] + * + * Timestamps must be provided as non-repeating and increasing values. They can have their 0-points at system start or + * at a defined wall-clock time (e.g., 01-Jan-1970 00:00:00) + */ + int64_t time_stamp; + float signal; /*!< @brief Signal sample in the unit defined for the respective sensor_id @sa bsec_physical_sensor_t */ + uint8_t signal_dimensions; /*!< @brief Signal dimensions (reserved for future use, shall be set to 1) */ + uint8_t sensor_id; /*!< @brief Identifier of physical sensor @sa bsec_physical_sensor_t */ +} bsec_input_t; + +/*! + * @brief Structure describing an output sample of the library + * + * Each output sample is returned from BSEC by populating the element of a struct array of this type. The contents of + * the signal field is defined by the supplied bsec_output_t::sensor_id. Possible output + * bsec_output_t::sensor_id values are defined in ::bsec_virtual_sensor_t. + * + * @sa bsec_virtual_sensor_t + */ +typedef struct +{ + int64_t time_stamp; /*!< @brief Time stamp in nanosecond resolution as provided as input [ns] */ + float signal; /*!< @brief Signal sample in the unit defined for the respective bsec_output_t::sensor_id @sa bsec_virtual_sensor_t */ + uint8_t signal_dimensions; /*!< @brief Signal dimensions (reserved for future use, shall be set to 1) */ + uint8_t sensor_id; /*!< @brief Identifier of virtual sensor @sa bsec_virtual_sensor_t */ + + /** + * @brief Accuracy status 0-3 + * + * Some virtual sensors provide a value in the accuracy field. If this is the case, the meaning of the field is as + * follows: + * + * | Name | Value | Accuracy description | + * |----------------------------|-------|-------------------------------------------------------------------------------------------------------------| + * | UNRELIABLE | 0 | Sensor data is unreliable, the sensor must be calibrated | + * | LOW_ACCURACY | 1 | Low accuracy, sensor should be calibrated | + * | MEDIUM_ACCURACY | 2 | Medium accuracy, sensor calibration may improve performance | + * | HIGH_ACCURACY | 3 | High accuracy | + * + * For example: + * + * - Ambient temperature accuracy is derived from change in the temperature in 1 minute. + * + * | Virtual sensor | Value | Accuracy description | + * |--------------------- |-------|------------------------------------------------------------------------------| + * | Ambient temperature | 0 | The difference in ambient temperature is greater than 4 degree in one minute | + * | | 1 | The difference in ambient temperature is less than 4 degree in one minute | + * | | 2 | The difference in ambient temperature is less than 3 degree in one minute | + * | | 3 | The difference in ambient temperature is less than 2 degree in one minute | + * + * - IAQ accuracy indicator will notify the user when she/he should initiate a calibration process. Calibration is + * performed automatically in the background if the sensor is exposed to clean and polluted air for approximately + * 30 minutes each. + * + * | Virtual sensor | Value | Accuracy description | + * |----------------------------|-------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| + * | IAQ | 0 | Stabilization / run-in ongoing | + * | | 1 | Low accuracy,to reach high accuracy(3),please expose sensor once to good air (e.g. outdoor air) and bad air (e.g. box with exhaled breath) for auto-trimming | + * | | 2 | Medium accuracy: auto-trimming ongoing | + * | | 3 | High accuracy | + */ + uint8_t accuracy; +} bsec_output_t; + +/*! + * @brief Structure describing sample rate of physical/virtual sensors + * + * This structure is used together with bsec_update_subscription() to enable BSEC outputs and to retrieve information + * about the sample rates used for BSEC inputs. + */ +typedef struct +{ + /** + * @brief Sample rate of the virtual or physical sensor in Hertz [Hz] + * + * Only supported sample rates are allowed. + */ + float sample_rate; + + /** + * @brief Identifier of the virtual or physical sensor + * + * The meaning of this field changes depending on whether the structs are as the requested_virtual_sensors argument + * to bsec_update_subscription() or as the required_sensor_settings argument. + * + * | bsec_update_subscription() argument | sensor_id field interpretation | + * |-------------------------------------|--------------------------------| + * | requested_virtual_sensors | ::bsec_virtual_sensor_t | + * | required_sensor_settings | ::bsec_physical_sensor_t | + * + * @sa bsec_physical_sensor_t + * @sa bsec_virtual_sensor_t + */ + uint8_t sensor_id; +} bsec_sensor_configuration_t; + +/*! + * @brief Structure returned by bsec_sensor_control() to configure BMExxx sensor + * + * This structure contains settings that must be used to configure the BMExxx to perform a forced-mode measurement. + * A measurement should only be executed if bsec_bme_settings_t::trigger_measurement is 1. If so, the oversampling + * settings for temperature, humidity, and pressure should be set to the provided settings provided in + * bsec_bme_settings_t::temperature_oversampling, bsec_bme_settings_t::humidity_oversampling, and + * bsec_bme_settings_t::pressure_oversampling, respectively. + * + * In case of bsec_bme_settings_t::run_gas = 1, the gas sensor must be enabled with the provided + * bsec_bme_settings_t::heater_temperature and bsec_bme_settings_t::heating_duration settings. + */ +typedef struct +{ + int64_t next_call; /*!< @brief Time stamp of the next call of the sensor_control*/ + uint32_t process_data; /*!< @brief Bit field describing which data is to be passed to bsec_do_steps() @sa BSEC_PROCESS_* */ + uint16_t heater_temperature; /*!< @brief Heating temperature [degrees Celsius] */ + uint16_t heating_duration; /*!< @brief Heating duration [ms] */ + uint8_t run_gas; /*!< @brief Enable gas measurements [0/1] */ + uint8_t pressure_oversampling; /*!< @brief Pressure oversampling settings [0-5] */ + uint8_t temperature_oversampling; /*!< @brief Temperature oversampling settings [0-5] */ + uint8_t humidity_oversampling; /*!< @brief Humidity oversampling settings [0-5] */ + uint8_t trigger_measurement; /*!< @brief Trigger a forced measurement with these settings now [0/1] */ +} bsec_bme_settings_t; + +/* internal defines and backward compatibility */ +#define BSEC_STRUCT_NAME Bsec /*!< Internal struct name */ + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/BSEC/bsec_interface.h b/src/BSEC/bsec_interface.h new file mode 100644 index 0000000..d6c09a3 --- /dev/null +++ b/src/BSEC/bsec_interface.h @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2015, 2016, 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_interface.h + * + * @brief + * Contains the API for BSEC + * + */ + + +#ifndef __BSEC_INTERFACE_H__ +#define __BSEC_INTERFACE_H__ + +#include "bsec_datatypes.h" + +#ifdef __cplusplus + extern "C" { +#endif + + + /*! @addtogroup bsec_interface BSEC C Interface + * @brief Interfaces of BSEC signal processing library + * + * ### Interface usage + * + * The following provides a short overview on the typical operation sequence for BSEC. + * + * - Initialization of the library + * + * | Steps | Function | + * |---------------------------------------------------------------------|--------------------------| + * | Initialization of library | bsec_init() | + * | Update configuration settings (optional) | bsec_set_configuration() | + * | Restore the state of the library (optional) | bsec_set_state() | + * + * + * - The following function is called to enable output signals and define their sampling rate / operation mode. + * + * | Steps | Function | + * |---------------------------------------------|----------------------------| + * | Enable library outputs with specified mode | bsec_update_subscription() | + * + * + * - This table describes the main processing loop. + * + * | Steps | Function | + * |-------------------------------------------|----------------------------------| + * | Retrieve sensor settings to be used | bsec_sensor_control() | + * | Configure sensor and trigger measurement | See BME680 API and example codes | + * | Read results from sensor | See BME680 API and example codes | + * | Perform signal processing | bsec_do_steps() | + * + * + * - Before shutting down the system, the current state of BSEC can be retrieved and can then be used during + * re-initialization to continue processing. + * + * | Steps | Function | + * |----------------------------------------|-------------------| + * | To retrieve the current library state | bsec_get_state() | + * + * + * + * ### Configuration and state + * + * Values of variables belonging to a BSEC instance are divided into two groups: + * - Values **not updated by processing** of signals belong to the **configuration group**. If available, BSEC can be + * configured before use with a customer specific configuration string. + * - Values **updated during processing** are member of the **state group**. Saving and restoring of the state of BSEC + * is necessary to maintain previously estimated sensor models and baseline information which is important for best + * performance of the gas sensor outputs. + * + * @note BSEC library consists of adaptive algorithms which models the gas sensor which improves its performance over + * the time. These will be lost if library is initialized due to system reset. In order to avoid this situation + * library state shall be stored in non volatile memory so that it can be loaded after system reset. + * + * + * @{ + */ + + +/*! + * @brief Return the version information of BSEC library + * + * @param [out] bsec_version_p pointer to struct which is to be populated with the version information + * + * @return Zero if successful, otherwise an error code + * + * See also: bsec_version_t + * + \code{.c} + // Example // + bsec_version_t version; + bsec_get_version(&version); + printf("BSEC version: %d.%d.%d.%d",version.major, version.minor, version.major_bugfix, version.minor_bugfix); + + \endcode +*/ + +bsec_library_return_t bsec_get_version(bsec_version_t * bsec_version_p); + + +/*! + * @brief Initialize the library + * + * Initialization and reset of BSEC is performed by calling bsec_init(). Calling this function sets up the relation + * among all internal modules, initializes run-time dependent library states and resets the configuration and state + * of all BSEC signal processing modules to defaults. + * + * Before any further use, the library must be initialized. This ensure that all memory and states are in defined + * conditions prior to processing any data. + * + * @return Zero if successful, otherwise an error code + * + \code{.c} + + // Initialize BSEC library before further use + bsec_init(); + + \endcode +*/ + +bsec_library_return_t bsec_init(void); + +/*! + * @brief Subscribe to library virtual sensors outputs + * + * Use bsec_update_subscription() to instruct BSEC which of the processed output signals are requested at which sample rates. + * See ::bsec_virtual_sensor_t for available library outputs. + * + * Based on the requested virtual sensors outputs, BSEC will provide information about the required physical sensor input signals + * (see ::bsec_physical_sensor_t) with corresponding sample rates. This information is purely informational as bsec_sensor_control() + * will ensure the sensor is operated in the required manner. To disable a virtual sensor, set the sample rate to BSEC_SAMPLE_RATE_DISABLED. + * + * The subscription update using bsec_update_subscription() is apart from the signal processing one of the the most + * important functions. It allows to enable the desired library outputs. The function determines which physical input + * sensor signals are required at which sample rate to produce the virtual output sensor signals requested by the user. + * When this function returns with success, the requested outputs are called subscribed. A very important feature is the + * retaining of already subscribed outputs. Further outputs can be requested or disabled both individually and + * group-wise in addition to already subscribed outputs without changing them unless a change of already subscribed + * outputs is requested. + * + * @note The state of the library concerning the subscribed outputs cannot be retained among reboots. + * + * The interface of bsec_update_subscription() requires the usage of arrays of sensor configuration structures. + * Such a structure has the fields sensor identifier and sample rate. These fields have the properties: + * - Output signals of virtual sensors must be requested using unique identifiers (Member of ::bsec_virtual_sensor_t) + * - Different sets of identifiers are available for inputs of physical sensors and outputs of virtual sensors + * - Identifiers are unique values defined by the library, not from external + * - Sample rates must be provided as value of + * - An allowed sample rate for continuously sampled signals + * - 65535.0f (BSEC_SAMPLE_RATE_DISABLED) to turn off outputs and identify disabled inputs + * + * @note The same sensor identifiers are also used within the functions bsec_do_steps(). + * + * The usage principles of bsec_update_subscription() are: + * - Differential updates (i.e., only asking for outputs that the user would like to change) is supported. + * - Invalid requests of outputs are ignored. Also if one of the requested outputs is unavailable, all the requests + * are ignored. At the same time, a warning is returned. + * - To disable BSEC, all outputs shall be turned off. Only enabled (subscribed) outputs have to be disabled while + * already disabled outputs do not have to be disabled explicitly. + * + * @param[in] requested_virtual_sensors Pointer to array of requested virtual sensor (output) configurations for the library + * @param[in] n_requested_virtual_sensors Number of virtual sensor structs pointed by requested_virtual_sensors + * @param[out] required_sensor_settings Pointer to array of required physical sensor configurations for the library + * @param[in,out] n_required_sensor_settings [in] Size of allocated required_sensor_settings array, [out] number of sensor configurations returned + * + * @return Zero when successful, otherwise an error code + * + * @sa bsec_sensor_configuration_t + * @sa bsec_physical_sensor_t + * @sa bsec_virtual_sensor_t + * + \code{.c} + // Example // + + // Change 3 virtual sensors (switch IAQ and raw temperature -> on / pressure -> off) + bsec_sensor_configuration_t requested_virtual_sensors[3]; + uint8_t n_requested_virtual_sensors = 3; + + requested_virtual_sensors[0].sensor_id = BSEC_OUTPUT_IAQ; + requested_virtual_sensors[0].sample_rate = BSEC_SAMPLE_RATE_ULP; + requested_virtual_sensors[1].sensor_id = BSEC_OUTPUT_RAW_TEMPERATURE; + requested_virtual_sensors[1].sample_rate = BSEC_SAMPLE_RATE_ULP; + requested_virtual_sensors[2].sensor_id = BSEC_OUTPUT_RAW_PRESSURE; + requested_virtual_sensors[2].sample_rate = BSEC_SAMPLE_RATE_DISABLED; + + // Allocate a struct for the returned physical sensor settings + bsec_sensor_configuration_t required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR]; + uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR; + + // Call bsec_update_subscription() to enable/disable the requested virtual sensors + bsec_update_subscription(requested_virtual_sensors, n_requested_virtual_sensors, required_sensor_settings, &n_required_sensor_settings); + \endcode + * + */ +bsec_library_return_t bsec_update_subscription(const bsec_sensor_configuration_t * const requested_virtual_sensors, + const uint8_t n_requested_virtual_sensors, bsec_sensor_configuration_t * required_sensor_settings, + uint8_t * n_required_sensor_settings); + + +/*! + * @brief Main signal processing function of BSEC + * + * + * Processing of the input signals and returning of output samples is performed by bsec_do_steps(). + * - The samples of all library inputs must be passed with unique identifiers representing the input signals from + * physical sensors where the order of these inputs can be chosen arbitrary. However, all input have to be provided + * within the same time period as they are read. A sequential provision to the library might result in undefined + * behavior. + * - The samples of all library outputs are returned with unique identifiers corresponding to the output signals of + * virtual sensors where the order of the returned outputs may be arbitrary. + * - The samples of all input as well as output signals of physical as well as virtual sensors use the same + * representation in memory with the following fields: + * - Sensor identifier: + * - For inputs: required to identify the input signal from a physical sensor + * - For output: overwritten by bsec_do_steps() to identify the returned signal from a virtual sensor + * - Time stamp of the sample + * + * Calling bsec_do_steps() requires the samples of the input signals to be provided along with their time stamp when + * they are recorded and only when they are acquired. Repetition of samples with the same time stamp are ignored and + * result in a warning. Repetition of values of samples which are not acquired anew by a sensor result in deviations + * of the computed output signals. Concerning the returned output samples, an important feature is, that a value is + * returned for an output only when a new occurrence has been computed. A sample of an output signal is returned only + * once. + * + * + * @param[in] inputs Array of input data samples. Each array element represents a sample of a different physical sensor. + * @param[in] n_inputs Number of passed input data structs. + * @param[out] outputs Array of output data samples. Each array element represents a sample of a different virtual sensor. + * @param[in,out] n_outputs [in] Number of allocated output structs, [out] number of outputs returned + * + * @return Zero when successful, otherwise an error code + * + + \code{.c} + // Example // + + // Allocate input and output memory + bsec_input_t input[3]; + uint8_t n_input = 3; + bsec_output_t output[2]; + uint8_t n_output=2; + + bsec_library_return_t status; + + // Populate the input structs, assuming the we have timestamp (ts), + // gas sensor resistance (R), temperature (T), and humidity (rH) available + // as input variables + input[0].sensor_id = BSEC_INPUT_GASRESISTOR; + input[0].signal = R; + input[0].time_stamp= ts; + input[1].sensor_id = BSEC_INPUT_TEMPERATURE; + input[1].signal = T; + input[1].time_stamp= ts; + input[2].sensor_id = BSEC_INPUT_HUMIDITY; + input[2].signal = rH; + input[2].time_stamp= ts; + + + // Invoke main processing BSEC function + status = bsec_do_steps( input, n_input, output, &n_output ); + + // Iterate through the BSEC output data, if the call succeeded + if(status == BSEC_OK) + { + for(int i = 0; i < n_output; i++) + { + switch(output[i].sensor_id) + { + case BSEC_OUTPUT_IAQ: + // Retrieve the IAQ results from output[i].signal + // and do something with the data + break; + case BSEC_OUTPUT_AMBIENT_TEMPERATURE: + // Retrieve the ambient temperature results from output[i].signal + // and do something with the data + break; + + } + } + } + + \endcode + */ + +bsec_library_return_t bsec_do_steps(const bsec_input_t * const inputs, const uint8_t n_inputs, bsec_output_t * outputs, uint8_t * n_outputs); + + +/*! + * @brief Reset a particular virtual sensor output + * + * This function allows specific virtual sensor outputs to be reset. The meaning of "reset" depends on the specific + * output. In case of the IAQ output, reset means zeroing the output to the current ambient conditions. + * + * @param[in] sensor_id Virtual sensor to be reset + * + * @return Zero when successful, otherwise an error code + * + * + \code{.c} + // Example // + bsec_reset_output(BSEC_OUTPUT_IAQ); + + \endcode + */ + +bsec_library_return_t bsec_reset_output(uint8_t sensor_id); + + +/*! + * @brief Update algorithm configuration parameters + * + * BSEC uses a default configuration for the modules and common settings. The initial configuration can be customized + * by bsec_set_configuration(). This is an optional step. + * + * @note A work buffer with sufficient size is required and has to be provided by the function caller to decompose + * the serialization and apply it to the library and its modules. Please use #BSEC_MAX_PROPERTY_BLOB_SIZE for allotting + * the required size. + * + * @param[in] serialized_settings Settings serialized to a binary blob + * @param[in] n_serialized_settings Size of the settings blob + * @param[in,out] work_buffer Work buffer used to parse the blob + * @param[in] n_work_buffer_size Length of the work buffer available for parsing the blob + * + * @return Zero when successful, otherwise an error code + * + \code{.c} + // Example // + + // Allocate variables + uint8_t serialized_settings[BSEC_MAX_PROPERTY_BLOB_SIZE]; + uint32_t n_serialized_settings_max = BSEC_MAX_PROPERTY_BLOB_SIZE; + uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE]; + uint32_t n_work_buffer = BSEC_MAX_PROPERTY_BLOB_SIZE; + + // Here we will load a provided config string into serialized_settings + + // Apply the configuration + bsec_set_configuration(serialized_settings, n_serialized_settings_max, work_buffer, n_work_buffer); + + \endcode + */ + +bsec_library_return_t bsec_set_configuration(const uint8_t * const serialized_settings, + const uint32_t n_serialized_settings, uint8_t * work_buffer, + const uint32_t n_work_buffer_size); + + +/*! + * @brief Restore the internal state of the library + * + * BSEC uses a default state for all signal processing modules and the BSEC module. To ensure optimal performance, + * especially of the gas sensor functionality, it is recommended to retrieve the state using bsec_get_state() + * before unloading the library, storing it in some form of non-volatile memory, and setting it using bsec_set_state() + * before resuming further operation of the library. + * + * @note A work buffer with sufficient size is required and has to be provided by the function caller to decompose the + * serialization and apply it to the library and its modules. Please use #BSEC_MAX_PROPERTY_BLOB_SIZE for allotting the + * required size. + * + * @param[in] serialized_state States serialized to a binary blob + * @param[in] n_serialized_state Size of the state blob + * @param[in,out] work_buffer Work buffer used to parse the blob + * @param[in] n_work_buffer_size Length of the work buffer available for parsing the blob + * + * @return Zero when successful, otherwise an error code + * + \code{.c} + // Example // + + // Allocate variables + uint8_t serialized_state[BSEC_MAX_PROPERTY_BLOB_SIZE]; + uint32_t n_serialized_state = BSEC_MAX_PROPERTY_BLOB_SIZE; + uint8_t work_buffer_state[BSEC_MAX_PROPERTY_BLOB_SIZE]; + uint32_t n_work_buffer_size = BSEC_MAX_PROPERTY_BLOB_SIZE; + + // Here we will load a state string from a previous use of BSEC + + // Apply the previous state to the current BSEC session + bsec_set_state(serialized_state, n_serialized_state, work_buffer_state, n_work_buffer_size); + + \endcode +*/ + +bsec_library_return_t bsec_set_state(const uint8_t * const serialized_state, const uint32_t n_serialized_state, + uint8_t * work_buffer, const uint32_t n_work_buffer_size); + + +/*! + * @brief Retrieve the current library configuration + * + * BSEC allows to retrieve the current configuration using bsec_get_configuration(). Returns a binary blob encoding + * the current configuration parameters of the library in a format compatible with bsec_set_configuration(). + * + * @note The function bsec_get_configuration() is required to be used for debugging purposes only. + * @note A work buffer with sufficient size is required and has to be provided by the function caller to decompose the + * serialization and apply it to the library and its modules. Please use #BSEC_MAX_PROPERTY_BLOB_SIZE for allotting the + * required size. + * + * + * @param[in] config_id Identifier for a specific set of configuration settings to be returned; + * shall be zero to retrieve all configuration settings. + * @param[out] serialized_settings Buffer to hold the serialized config blob + * @param[in] n_serialized_settings_max Maximum available size for the serialized settings + * @param[in,out] work_buffer Work buffer used to parse the binary blob + * @param[in] n_work_buffer Length of the work buffer available for parsing the blob + * @param[out] n_serialized_settings Actual size of the returned serialized configuration blob + * + * @return Zero when successful, otherwise an error code + * + \code{.c} + // Example // + + // Allocate variables + uint8_t serialized_settings[BSEC_MAX_PROPERTY_BLOB_SIZE]; + uint32_t n_serialized_settings_max = BSEC_MAX_PROPERTY_BLOB_SIZE; + uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE]; + uint32_t n_work_buffer = BSEC_MAX_PROPERTY_BLOB_SIZE; + uint32_t n_serialized_settings = 0; + + // Configuration of BSEC algorithm is stored in 'serialized_settings' + bsec_get_configuration(0, serialized_settings, n_serialized_settings_max, work_buffer, n_work_buffer, &n_serialized_settings); + + \endcode + */ + +bsec_library_return_t bsec_get_configuration(const uint8_t config_id, uint8_t * serialized_settings, const uint32_t n_serialized_settings_max, + uint8_t * work_buffer, const uint32_t n_work_buffer, uint32_t * n_serialized_settings); + + +/*! + *@brief Retrieve the current internal library state + * + * BSEC allows to retrieve the current states of all signal processing modules and the BSEC module using + * bsec_get_state(). This allows a restart of the processing after a reboot of the system by calling bsec_set_state(). + * + * @note A work buffer with sufficient size is required and has to be provided by the function caller to decompose the + * serialization and apply it to the library and its modules. Please use #BSEC_MAX_STATE_BLOB_SIZE for allotting the + * required size. + * + * + * @param[in] state_set_id Identifier for a specific set of states to be returned; shall be + * zero to retrieve all states. + * @param[out] serialized_state Buffer to hold the serialized config blob + * @param[in] n_serialized_state_max Maximum available size for the serialized states + * @param[in,out] work_buffer Work buffer used to parse the blob + * @param[in] n_work_buffer Length of the work buffer available for parsing the blob + * @param[out] n_serialized_state Actual size of the returned serialized blob + * + * @return Zero when successful, otherwise an error code + * + \code{.c} + // Example // + + // Allocate variables + uint8_t serialized_state[BSEC_MAX_STATE_BLOB_SIZE]; + uint32_t n_serialized_state_max = BSEC_MAX_STATE_BLOB_SIZE; + uint32_t n_serialized_state = BSEC_MAX_STATE_BLOB_SIZE; + uint8_t work_buffer_state[BSEC_MAX_STATE_BLOB_SIZE]; + uint32_t n_work_buffer_size = BSEC_MAX_STATE_BLOB_SIZE; + + // Algorithm state is stored in 'serialized_state' + bsec_get_state(0, serialized_state, n_serialized_state_max, work_buffer_state, n_work_buffer_size, &n_serialized_state); + + \endcode + */ + +bsec_library_return_t bsec_get_state(const uint8_t state_set_id, uint8_t * serialized_state, + const uint32_t n_serialized_state_max, uint8_t * work_buffer, const uint32_t n_work_buffer, + uint32_t * n_serialized_state); + +/*! + * @brief Retrieve BMExxx sensor instructions + * + * The bsec_sensor_control() interface is a key feature of BSEC, as it allows an easy way for the signal processing + * library to control the operation of the BME sensor. This is important since gas sensor behaviour is mainly + * determined by how the integrated heater is configured. To ensure an easy integration of BSEC into any system, + * bsec_sensor_control() will provide the caller with information about the current sensor configuration that is + * necessary to fulfill the input requirements derived from the current outputs requested via + * bsec_update_subscription(). + * + * In practice the use of this function shall be as follows: + * - Call bsec_sensor_control() which returns a bsec_bme_settings_t struct. + * - Based on the information contained in this struct, the sensor is configured and a forced-mode measurement is + * triggered if requested by bsec_sensor_control(). + * - Once this forced-mode measurement is complete, the signals specified in this struct shall be passed to + * bsec_do_steps() to perform the signal processing. + * - After processing, the process should sleep until the bsec_bme_settings_t::next_call timestamp is reached. + * + * + * @param [in] time_stamp Current timestamp in [ns] + * @param[out] sensor_settings Settings to be passed to API to operate sensor at this time instance + * + * @return Zero when successful, otherwise an error code + */ + +bsec_library_return_t bsec_sensor_control(const int64_t time_stamp, bsec_bme_settings_t *sensor_settings); + +/*@}*/ //BSEC Interface + +#ifdef __cplusplus + } +#endif + +#endif /* __BSEC_INTERFACE_H__ */ diff --git a/src/BSEC/libalgobsec.a b/src/BSEC/libalgobsec.a new file mode 100644 index 0000000..51c6242 Binary files /dev/null and b/src/BSEC/libalgobsec.a differ diff --git a/src/bmeSPI.cxx b/src/bmeSPI.cxx index f223f64..6499172 100644 --- a/src/bmeSPI.cxx +++ b/src/bmeSPI.cxx @@ -8,34 +8,51 @@ #include #include "BME68x-Sensor-API/bme68x.h" +#include "BSEC/bsec_interface.h" #include "oled-driver/Renderer.hpp" extern QueueHandle_t spiMutex; extern void waitForSpiFinished(); extern Renderer renderer; +extern void initDisplay(); + constexpr auto MaximumChars = 22 * 4; char buffer[MaximumChars]; -constexpr auto SPI_DEVICE = &hspi2; -uint8_t txBuffer[512]; +constexpr auto SpiPeripherie = &hspi2; +uint8_t txBuffer[512 + 1]; + +constexpr auto temperatureOffset = 7.0f; struct bme68x_dev bmeSensor; -volatile int8_t res; struct bme68x_conf bmeConf; struct bme68x_heatr_conf bmeHeaterConf; struct bme68x_data bmeData[3]; -uint16_t del_period; -uint32_t time_ms = 0; -uint8_t n_fields; -uint16_t sampleCount = 1; +uint32_t delayInUs; +uint8_t numberOfData; -/* Heater temperature in degree Celsius */ -uint16_t temp_prof[10] = {200, 240, 280, 320, 360, 360, 320, 280, 240, 200}; +constexpr auto ProfileLength = 1; -/* Heating duration in milliseconds */ -uint16_t dur_prof[10] = {100, 100, 100, 100, 100, 100, 100, 100, 100, 100}; +// Heater temperature in degree Celsius +uint16_t temperatureProfile[ProfileLength] = {320}; + +// Heating duration in milliseconds +uint16_t durationProfile[ProfileLength] = {150}; + +constexpr uint8_t numberRequestedVirtualSensors = 4; +bsec_sensor_configuration_t requestedVirtualSensors[numberRequestedVirtualSensors]; + +float iaq, rawTemperature, pressure, rawHumidity, gasResistance, stabStatus, runInStatus, + temperature, humidity, staticIaq, co2Equivalent, breathVocEquivalent, compGasValue, + gasPercentage; + +uint8_t iaqAccuracy, staticIaqAccuracy, co2Accuracy, breathVocAccuracy, compGasAccuracy, + gasPercentageAcccuracy; + +// uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE]; +// uint8_t workBuffer[BSEC_MAX_WORKBUFFER_SIZE]; void setChipSelect(bool state) { @@ -49,9 +66,9 @@ BME68X_INTF_RET_TYPE bme68x_spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32 xSemaphoreTake(spiMutex, portMAX_DELAY); setChipSelect(true); - HAL_SPI_Transmit_DMA(SPI_DEVICE, ®_addr, 1); + HAL_SPI_Transmit_DMA(SpiPeripherie, ®_addr, 1); waitForSpiFinished(); - HAL_SPI_Receive_DMA(SPI_DEVICE, reg_data, len); + HAL_SPI_Receive_DMA(SpiPeripherie, reg_data, len); waitForSpiFinished(); setChipSelect(false); @@ -73,7 +90,7 @@ BME68X_INTF_RET_TYPE bme68x_spi_write(uint8_t reg_addr, const uint8_t *reg_data, xSemaphoreTake(spiMutex, portMAX_DELAY); setChipSelect(true); - HAL_SPI_Transmit_DMA(SPI_DEVICE, const_cast(txBuffer), len + 1); + HAL_SPI_Transmit_DMA(SpiPeripherie, const_cast(txBuffer), len + 1); waitForSpiFinished(); setChipSelect(false); @@ -85,7 +102,7 @@ BME68X_INTF_RET_TYPE bme68x_spi_write(uint8_t reg_addr, const uint8_t *reg_data, // Delay function maps void bme68x_delay_us(uint32_t period, void *) { - HAL_Delay(period / 1000); + vTaskDelay(period / 1000); } int8_t bme68x_spi_init(struct bme68x_dev *bme) @@ -94,13 +111,11 @@ int8_t bme68x_spi_init(struct bme68x_dev *bme) if (bme != NULL) { - // printf("SPI Interface\n"); bme->read = bme68x_spi_read; bme->write = bme68x_spi_write; bme->intf = BME68X_SPI_INTF; bme->delay_us = bme68x_delay_us; - bme->intf_ptr = SPI_DEVICE; bme->amb_temp = 25; /* The ambient temperature in deg C is used for defining the heater temperature */ } @@ -112,90 +127,216 @@ int8_t bme68x_spi_init(struct bme68x_dev *bme) return rslt; } -void bme68x_check_rslt(const char *, int8_t) +void bmeSensorInit() { + bme68x_spi_init(&bmeSensor); + bme68x_init(&bmeSensor); + + bme68x_get_conf(&bmeConf, &bmeSensor); + bmeConf.os_hum = BME68X_OS_16X; + bmeConf.os_temp = BME68X_OS_2X; + bmeConf.os_pres = BME68X_OS_1X; + bmeConf.filter = BME68X_FILTER_OFF; + bmeConf.odr = BME68X_ODR_NONE; + bme68x_set_conf(&bmeConf, &bmeSensor); + + bmeHeaterConf.enable = BME68X_ENABLE; + bmeHeaterConf.heatr_temp_prof = temperatureProfile; + bmeHeaterConf.heatr_dur_prof = durationProfile; + bmeHeaterConf.profile_len = ProfileLength; + bme68x_set_heatr_conf(BME68X_SEQUENTIAL_MODE, &bmeHeaterConf, &bmeSensor); + + bme68x_set_op_mode(BME68X_SEQUENTIAL_MODE, &bmeSensor); + + bsec_init(); + + // Change 3 virtual sensors (switch IAQ and raw temperature -> on / pressure -> off + requestedVirtualSensors[0].sensor_id = BSEC_OUTPUT_IAQ; + requestedVirtualSensors[0].sample_rate = BSEC_SAMPLE_RATE_CONTINUOUS; + requestedVirtualSensors[1].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT; + requestedVirtualSensors[1].sample_rate = BSEC_SAMPLE_RATE_CONTINUOUS; + requestedVirtualSensors[2].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE; + requestedVirtualSensors[2].sample_rate = BSEC_SAMPLE_RATE_CONTINUOUS; + requestedVirtualSensors[3].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY; + requestedVirtualSensors[3].sample_rate = BSEC_SAMPLE_RATE_CONTINUOUS; + + // Allocate a struct for the returned physical sensor settings + bsec_sensor_configuration_t requiredSensorSettings[BSEC_MAX_PHYSICAL_SENSOR]; + uint8_t numberRequiredSensorSettings = BSEC_MAX_PHYSICAL_SENSOR; + + // Call bsec_update_subscription() to enable/disable the requested virtual sensors + bsec_update_subscription(requestedVirtualSensors, numberRequestedVirtualSensors, + requiredSensorSettings, &numberRequiredSensorSettings); } void bmeRun() { - res = bme68x_spi_init(&bmeSensor); - bme68x_check_rslt("bme68x_interface_init", res); + delayInUs = bme68x_get_meas_dur(BME68X_SEQUENTIAL_MODE, &bmeConf, &bmeSensor) + + (bmeHeaterConf.heatr_dur_prof[0] * 1000); + vTaskDelay(delayInUs / 1000); - res = bme68x_init(&bmeSensor); - bme68x_check_rslt("bme68x_init", res); - - /* Check if res == BME68X_OK, report or handle if otherwise */ - res = bme68x_get_conf(&bmeConf, &bmeSensor); - bme68x_check_rslt("bme68x_get_conf", res); - - /* Check if res == BME68X_OK, report or handle if otherwise */ - bmeConf.filter = BME68X_FILTER_OFF; - bmeConf.odr = - BME68X_ODR_NONE; /* This parameter defines the sleep duration after each profile */ - bmeConf.os_hum = BME68X_OS_16X; - bmeConf.os_pres = BME68X_OS_1X; - bmeConf.os_temp = BME68X_OS_2X; - res = bme68x_set_conf(&bmeConf, &bmeSensor); - bme68x_check_rslt("bme68x_set_conf", res); - - /* Check if res == BME68X_OK, report or handle if otherwise */ - bmeHeaterConf.enable = BME68X_ENABLE; - bmeHeaterConf.heatr_temp_prof = temp_prof; - bmeHeaterConf.heatr_dur_prof = dur_prof; - bmeHeaterConf.profile_len = 10; - res = bme68x_set_heatr_conf(BME68X_SEQUENTIAL_MODE, &bmeHeaterConf, &bmeSensor); - bme68x_check_rslt("bme68x_set_heatr_conf", res); - - /* Check if res == BME68X_OK, report or handle if otherwise */ - res = bme68x_set_op_mode(BME68X_SEQUENTIAL_MODE, &bmeSensor); - bme68x_check_rslt("bme68x_set_op_mode", res); - - /* Check if res == BME68X_OK, report or handle if otherwise */ - // printf("Sample, TimeStamp(ms), Temperature(deg C), Pressure(Pa), Humidity(%%), Gas " - // "resistance(ohm), Status, Profile index, Measurement index\n"); - while (1) + auto status = bme68x_get_data(BME68X_SEQUENTIAL_MODE, bmeData, &numberOfData, &bmeSensor); + if (status != 0) { - del_period = - bme68x_get_meas_dur(BME68X_SEQUENTIAL_MODE, &bmeConf) + bmeHeaterConf.heatr_dur_prof[0]; - vTaskDelay(del_period); + __asm("bkpt"); + } +} - // time_ms = HAL_GetTick(); +void bsecRun() +{ + /* + auto status = bsec_set_state(state, BSEC_MAX_STATE_BLOB_SIZE, workBuffer, sizeof(workBuffer)); - res = bme68x_get_data(BME68X_SEQUENTIAL_MODE, bmeData, &n_fields, &bmeSensor); - bme68x_check_rslt("bme68x_get_data", res); - - /* Check if res == BME68X_OK, report or handle if otherwise */ - for (uint8_t i = 0; i < n_fields; i++) + if (status == BSEC_OK) + { + for (uint32_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) { - renderer.clearAll(); - - snprintf(buffer, MaximumChars, - "%d°C, %luhPa, %lu%%\n%lukOhm, status: 0x%x\ngas_index: %d\nmeas_index: %d", - bmeData[i].temperature / 100, // - bmeData[i].pressure / 100, // - bmeData[i].humidity / 1000, // - bmeData[i].gas_resistance / 1000, // - bmeData[i].status, // - bmeData[i].gas_index, // - bmeData[i].meas_index); - - renderer.print({0, 0}, buffer); - renderer.render(); - /* - snprintf(buffer, MaximumChars, "sampleCount: %u\n, time_ms: %u\n \ - temperature; %d°C\n \ - pressure: %u\n \ - humidity: %u\n \ - gas_resistance: %u\n \ - // status: 0x%x\n \ - //gas_index: %d\n \ - meas_index: %d\n", - sampleCount, time_ms, (bmeData[i].temperature / 100), bmeData[i].pressure, - (bmeData[i].humidity / 1000), bmeData[i].gas_resistance, bmeData[i].status, - bmeData[i].gas_index, bmeData[i].meas_index); - */ + bsecState[i] = state[i]; } + validBsecState = true; + } + */ + + if (!(bmeData[numberOfData - 1].status & BME68X_NEW_DATA_MSK)) + { + __asm("bkpt"); + return; } - return; + bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; + uint8_t nInputs = 0, nOutputs = 0; + int64_t currentTimeInNs = xTaskGetTickCount() * int64_t(1000) * int64_t(1000); + + inputs[nInputs].sensor_id = BSEC_INPUT_TEMPERATURE; + inputs[nInputs].signal = bmeData[numberOfData - 1].temperature / 100.0f; + inputs[nInputs].time_stamp = currentTimeInNs; + nInputs++; + + inputs[nInputs].sensor_id = BSEC_INPUT_HUMIDITY; + inputs[nInputs].signal = bmeData[numberOfData - 1].humidity / 1000.0f; + inputs[nInputs].time_stamp = currentTimeInNs; + nInputs++; + + inputs[nInputs].sensor_id = BSEC_INPUT_PRESSURE; + inputs[nInputs].signal = bmeData[numberOfData - 1].pressure; + inputs[nInputs].time_stamp = currentTimeInNs; + nInputs++; + + inputs[nInputs].sensor_id = BSEC_INPUT_GASRESISTOR; + inputs[nInputs].signal = bmeData[numberOfData - 1].gas_resistance; + inputs[nInputs].time_stamp = currentTimeInNs; + nInputs++; + + inputs[nInputs].sensor_id = BSEC_INPUT_HEATSOURCE; + inputs[nInputs].signal = temperatureOffset; + inputs[nInputs].time_stamp = currentTimeInNs; + nInputs++; + + nOutputs = BSEC_NUMBER_OUTPUTS; + bsec_output_t outputs[BSEC_NUMBER_OUTPUTS]; + + auto status = bsec_do_steps(inputs, nInputs, outputs, &nOutputs); + if (status != BSEC_OK) + { + return; + } + + // zeroOutputs(); + + if (nOutputs > 0) + { + auto outputTimestamp = outputs[0].time_stamp / 1000000; /* Convert from ns to ms */ + + for (uint8_t i = 0; i < nOutputs; i++) + { + switch (outputs[i].sensor_id) + { + case BSEC_OUTPUT_IAQ: + iaq = outputs[i].signal; + iaqAccuracy = outputs[i].accuracy; + break; + case BSEC_OUTPUT_STATIC_IAQ: + staticIaq = outputs[i].signal; + staticIaqAccuracy = outputs[i].accuracy; + break; + case BSEC_OUTPUT_CO2_EQUIVALENT: + co2Equivalent = outputs[i].signal; + co2Accuracy = outputs[i].accuracy; + break; + case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: + breathVocEquivalent = outputs[i].signal; + breathVocAccuracy = outputs[i].accuracy; + break; + case BSEC_OUTPUT_RAW_TEMPERATURE: + rawTemperature = outputs[i].signal; + break; + case BSEC_OUTPUT_RAW_PRESSURE: + pressure = outputs[i].signal; + break; + case BSEC_OUTPUT_RAW_HUMIDITY: + rawHumidity = outputs[i].signal; + break; + case BSEC_OUTPUT_RAW_GAS: + gasResistance = outputs[i].signal; + break; + case BSEC_OUTPUT_STABILIZATION_STATUS: + stabStatus = outputs[i].signal; + break; + case BSEC_OUTPUT_RUN_IN_STATUS: + runInStatus = outputs[i].signal; + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: + temperature = outputs[i].signal; + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: + humidity = outputs[i].signal; + break; + case BSEC_OUTPUT_COMPENSATED_GAS: + compGasValue = outputs[i].signal; + compGasAccuracy = outputs[i].accuracy; + break; + case BSEC_OUTPUT_GAS_PERCENTAGE: + gasPercentage = outputs[i].signal; + gasPercentageAcccuracy = outputs[i].accuracy; + break; + default: + break; + } + } + } +} + +void printBmeSensorData() +{ + renderer.clearAll(); + + snprintf(buffer, MaximumChars, + "%d°C, %luhPa, %d%%\nIAQ: %d, Accuracy: %d\nCO2: %dppm\n%d, %d, %d - %lukOhm", + static_cast(temperature), // + bmeData[numberOfData - 1].pressure / 100, // + static_cast(humidity), // + static_cast(iaq), // + iaqAccuracy, // + static_cast(co2Equivalent), // + bmeData[numberOfData - 1].status, // + bmeData[numberOfData - 1].gas_index, // + bmeData[numberOfData - 1].gas_wait, // + bmeData[numberOfData - 1].gas_resistance / 1000); + + renderer.print({0, 0}, buffer); + renderer.render(); +} + +//-------------------------------------------------------------------------------------------------- +extern "C" void sensorTask(void *) +{ + initDisplay(); + bmeSensorInit(); + + while (1) + { + bmeRun(); + bsecRun(); + printBmeSensorData(); + } } \ No newline at end of file diff --git a/src/main.cxx b/src/main.cxx index ea4b4c9..e734397 100644 --- a/src/main.cxx +++ b/src/main.cxx @@ -3,13 +3,11 @@ #include "semphr.h" #include "task.h" +// #include "BSEC/bsec_integration.h" #include "SSD1306_SPI.hpp" #include "oled-driver/Display.hpp" #include "oled-driver/Renderer.hpp" -extern TaskHandle_t sensorHandle; -extern void bmeRun(); - // oled display SSD1306_SPI ssdSpiInterface; Display display(ssdSpiInterface); @@ -67,15 +65,4 @@ extern "C" void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *) extern "C" void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *) { notifySpiIsFinished(); -} - -//-------------------------------------------------------------------------------------------------- -extern "C" void sensorTask(void *) -{ - initDisplay(); - - bmeRun(); - while (1) - { - } } \ No newline at end of file