Skip to content
Snippets Groups Projects
Select Git revision
  • 5ddc1877c99702b1d8b37a22c49f582471ca6f85
  • master default protected
  • fix-warnings
  • tvbgone-fixes
  • genofire/ble-follow-py
  • schneider/ble-stability-new-phy-adv
  • schneider/ble-stability
  • msgctl/gfx_rle
  • schneider/ble-stability-new-phy
  • add_menu_vibration
  • plaetzchen/ios-workaround
  • blinkisync-as-preload
  • schneider/max30001-pycardium
  • schneider/max30001-epicaridum
  • schneider/max30001
  • schneider/stream-locks
  • schneider/fundamental-test
  • schneider/ble-buffers
  • schneider/maxim-sdk-update
  • ch3/splashscreen
  • koalo/bhi160-works-but-dirty
  • v1.11
  • v1.10
  • v1.9
  • v1.8
  • v1.7
  • v1.6
  • v1.5
  • v1.4
  • v1.3
  • v1.2
  • v1.1
  • v1.0
  • release-1
  • bootloader-v1
  • v0.0
36 results

bsec_integration.c

Blame
  • Forked from card10 / firmware
    296 commits behind the upstream repository.
    bsec_integration.c 25.20 KiB
    /*
     * 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 <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    #include "bsec_integration.h"
    
    /**********************************************************************************************************************/
    /* local macro definitions */
    /**********************************************************************************************************************/
    
    #define NUM_USED_OUTPUTS 8
    
    /**********************************************************************************************************************/
    /* 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
     */
    static 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;
        
        /* 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       Initialize the BME680 sensor and the BSEC library
     *
     * @param[in]   sample_rate         mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP)
     * @param[in]   temperature_offset  device-specific temperature offset (due to self-heating)
     * @param[in]   bus_write           pointer to the bus writing function
     * @param[in]   bus_read            pointer to the bus reading function
     * @param[in]   sleep               pointer to the system specific sleep function
     * @param[in]   state_load          pointer to the system-specific state load function
     * @param[in]   config_load         pointer to the system-specific config load function
     *
     * @return      zero if successful, negative otherwise
     */
    return_values_init bsec_iot_init(float sample_rate, float temperature_offset, bme680_com_fptr_t bus_write, 
                        bme680_com_fptr_t bus_read, sleep_fct sleep, state_load_fct state_load, config_load_fct config_load)
    {
        return_values_init ret = {BME680_OK, BSEC_OK};
        __attribute__((unused)) bsec_library_return_t bsec_status = BSEC_OK;
        
        uint8_t bsec_data[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
        uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
        int bsec_state_len, bsec_config_len;
        
        /* Fixed I2C configuration */
        bme680_g.dev_id = BME680_I2C_ADDR_PRIMARY;
        bme680_g.intf = BME680_I2C_INTF;
        /* User configurable I2C configuration */
        bme680_g.write = bus_write;
        bme680_g.read = bus_read;
        bme680_g.delay_ms = sleep;
        
        /* Initialize BME680 API */
        ret.bme680_status = bme680_init(&bme680_g);
        if (ret.bme680_status != BME680_OK)
        {
            return ret;
        }
        
        /* Initialize BSEC library */
        ret.bsec_status = bsec_init();
        if (ret.bsec_status != BSEC_OK)
        {
            return ret;
        }
        
        /* Load library config, if available */
        bsec_config_len = config_load(bsec_data, sizeof(bsec_data));
        if (bsec_config_len != 0)
        {       
            ret.bsec_status = bsec_set_configuration(bsec_data, bsec_config_len, work_buffer, sizeof(work_buffer));     
            if (ret.bsec_status != BSEC_OK)
            {
                return ret;
            }
        }
        
        /* Load previous library state, if available */
        bsec_state_len = state_load(bsec_data, sizeof(bsec_data));
        if (bsec_state_len != 0)
        {       
            ret.bsec_status = bsec_set_state(bsec_data, bsec_state_len, work_buffer, sizeof(work_buffer));     
            if (ret.bsec_status != BSEC_OK)
            {
                return ret;
            }
        }
        
        /* Set temperature offset */
        bme680_temperature_offset_g = temperature_offset;
        
        /* Call to the function which sets the library with subscription information */
        ret.bsec_status = bme680_bsec_update_subscription(sample_rate);
        if (ret.bsec_status != BSEC_OK)
        {
            return ret;
        }
        
        return ret;
    }
    
    /*!
     * @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
     */
    static void bme680_bsec_trigger_measurement(bsec_bme_settings_t *sensor_settings, sleep_fct sleep)
    {
        uint16_t meas_period;
        uint8_t set_required_settings;
        __attribute__((unused)) int8_t bme680_status = BME680_OK;
            
        /* Check if a forced-mode measurement should be triggered now */
        if (sensor_settings->trigger_measurement)
        {
            /* Set sensor configuration */
    
            bme680_g.tph_sett.os_hum  = sensor_settings->humidity_oversampling;
            bme680_g.tph_sett.os_pres = sensor_settings->pressure_oversampling;
            bme680_g.tph_sett.os_temp = sensor_settings->temperature_oversampling;
            bme680_g.gas_sett.run_gas = sensor_settings->run_gas;
            bme680_g.gas_sett.heatr_temp = sensor_settings->heater_temperature; /* degree Celsius */
            bme680_g.gas_sett.heatr_dur  = sensor_settings->heating_duration; /* milliseconds */
            
            /* Select the power mode */
            /* Must be set before writing the sensor configuration */
            bme680_g.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, &bme680_g);
                 
            /* Set power mode as forced mode and trigger forced mode measurement */
            bme680_status = bme680_set_sensor_mode(&bme680_g);
            
            /* Get the total measurement duration so as to sleep or wait till the measurement is complete */
            bme680_get_profile_dur(&meas_period, &bme680_g);
            
            /* 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(&bme680_g);  
        /* 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 (bme680_g.power_mode == BME680_FORCED_MODE)
        {
            /* sleep for 5 ms */
            sleep(5);
            bme680_status = bme680_get_sensor_mode(&bme680_g);
        }
    }
    
    /*!
     * @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
     */
    static void bme680_bsec_read_data(int64_t time_stamp_trigger, bsec_input_t *inputs, uint8_t *num_bsec_inputs,
        int32_t bsec_process_data)
    {
        static struct bme680_field_data data;
        __attribute__((unused)) 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, &bme680_g);
    
            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
     */
    static 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;
        __attribute__((unused)) uint8_t static_iaq_accuracy = 0;
        float co2_equivalent = 0.0f;
        __attribute__((unused)) uint8_t co2_accuracy = 0;
        float breath_voc_equivalent = 0.0f;
        __attribute__((unused)) uint8_t breath_voc_accuracy = 0;
        __attribute__((unused)) float comp_gas_value = 0.0f;
        __attribute__((unused)) uint8_t comp_gas_accuracy = 0;
        __attribute__((unused)) float gas_percentage = 0.0f;
        __attribute__((unused)) 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);
        }
    }
    
    /*!
     * @brief       Runs the main (endless) loop that queries sensor settings, applies them, and processes the measured data
     *
     * @param[in]   sleep               pointer to the system specific sleep function
     * @param[in]   get_timestamp_us    pointer to the system specific timestamp derivation function
     * @param[in]   output_ready        pointer to the function processing obtained BSEC outputs
     * @param[in]   state_save          pointer to the system-specific state save function
     * @param[in]   save_intvl          interval at which BSEC state should be saved (in samples)
     *
     * @return      none
     */
    void bsec_iot_loop(sleep_fct sleep, get_timestamp_us_fct get_timestamp_us, output_ready_fct output_ready,
                        state_save_fct state_save, uint32_t save_intvl)
    {
        /* Timestamp variables */
        int64_t time_stamp = 0;
        int64_t time_stamp_interval_ms = 0;
        
        /* Allocate enough memory for up to BSEC_MAX_PHYSICAL_SENSOR physical inputs*/
        bsec_input_t bsec_inputs[BSEC_MAX_PHYSICAL_SENSOR];
        
        /* Number of inputs to BSEC */
        uint8_t num_bsec_inputs = 0;
        
        /* BSEC sensor settings struct */
        bsec_bme_settings_t sensor_settings;
        
        /* Save state variables */
        uint8_t bsec_state[BSEC_MAX_STATE_BLOB_SIZE];
        uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
        uint32_t bsec_state_len = 0;
        uint32_t n_samples = 0;
        
        bsec_library_return_t bsec_status = BSEC_OK;
    
        while (1)
        {
            /* get the timestamp in nanoseconds before calling bsec_sensor_control() */
            time_stamp = get_timestamp_us() * 1000;
            
            /* Retrieve sensor settings to be used in this time instant by calling bsec_sensor_control */
            bsec_sensor_control(time_stamp, &sensor_settings);
            
            /* Trigger a measurement if necessary */
            bme680_bsec_trigger_measurement(&sensor_settings, sleep);
            
            /* Read data from last measurement */
            num_bsec_inputs = 0;
            bme680_bsec_read_data(time_stamp, bsec_inputs, &num_bsec_inputs, sensor_settings.process_data);
            
            /* Time to invoke BSEC to perform the actual processing */
            bme680_bsec_process_data(bsec_inputs, num_bsec_inputs, output_ready);
            
            /* Increment sample counter */
            n_samples++;
            
            /* Retrieve and store state if the passed save_intvl */
            if (n_samples >= save_intvl)
            {
                bsec_status = bsec_get_state(0, bsec_state, sizeof(bsec_state), work_buffer, sizeof(work_buffer), &bsec_state_len);
                if (bsec_status == BSEC_OK)
                {
                    state_save(bsec_state, bsec_state_len);
                }
                n_samples = 0;
            }
            
            
            /* Compute how long we can sleep until we need to call bsec_sensor_control() next */
            /* Time_stamp is converted from microseconds to nanoseconds first and then the difference to milliseconds */
            time_stamp_interval_ms = (sensor_settings.next_call - get_timestamp_us() * 1000) / 1000000;
            if (time_stamp_interval_ms > 0)
            {
                sleep((uint32_t)time_stamp_interval_ms);
            }
        }
    }
    
    /*! @}*/