uses BBC micro:bit to measure and display indoor air quality using Bosch BME680 and/or Sensirion SGP30

Dependencies:   microbit

uses Bosch BME680 and/or Sensirion SGP30 sensors to measure indor air quality

sensors should be connected to BBC micro:bit using i2c

commands are received and data is being sent using uBit / nordic radio protocol

display ---

last line always indicates: - first dot: bme680 detected - second dot: sgp30 detected - third dot: sgp 30 setting humidity/temperature - fourth dor: sgp30 measuring - fith dot: bme680 measuring

the detect dots should be in a stable state (not blinking) the measuring dots should be blinking (constant light means: measurement failed)

if only one bme680 is present: - first 3 lines indicate gas resistence (air quality / more dots == worse quality) - fourth line indicates humidity level

if only sgp30 is present: - first two lines indicate SGP30 VOC level - third and fourth line indicate sgp30 CO2 level

if both sensors are present: - first line indicates SGP30 VOC level - second line line indicates sgp30 CO2 level - third line indicates bme680 gas resistence (air quality) - fourth line indicates bme 680 humidity level

buttons - B display state, switches betweeen - full bright - low light - display off

AB reset sgp30 baseline in non volatile storage

data logging -- during measurements the minimum and mximum values for each measured value (temperature, air pressure, humidity,gas resistance, VOC, CO2) are being stored in non volatile storage those (and the last measurement results) are being shown when btn A has been pressed

bme680/bm680.cpp

Committer:
jsa1969
Date:
24 months ago
Revision:
60:6b21ca38ee7c
Parent:
46:2fed2865a0f3

File content as of revision 60:6b21ca38ee7c:

#include "bme680.h"

Bme680::Bme680(I2cCallbacks *callbacks) {
    _dev = new bme680_dev;
    _dev->dev_id = BME680_I2C_ADDR_PRIMARY<<1;
    _i2cCallbacks = callbacks;
}

int Bme680::init() {
    int rslt = soft_reset();
    
    if (rslt!=BME680_OK) return rslt;
    
    rslt = get_regs(BME680_CHIP_ID_ADDR, &(_dev->chip_id), 1);
    
    if (rslt!=BME680_OK) return rslt;
            
    if (_dev->chip_id == BME680_CHIP_ID) {
        rslt = get_calib_data();
    } else {
        rslt = BME680_E_DEV_NOT_FOUND;
    }
            
    return rslt;
}

int Bme680::measure(struct bme680_field_data* data, const int ambTemp, const uint16_t heatr_dur, const uint16_t heatr_temp){
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    _dev->amb_temp = ambTemp;
    _dev->power_mode = BME680_FORCED_MODE;
    
    _dev->tph_sett.os_hum = BME680_OS_1X;
    _dev->tph_sett.os_pres = BME680_OS_16X;
    _dev->tph_sett.os_temp = BME680_OS_2X;
    
    _dev->gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
    _dev->gas_sett.heatr_dur = heatr_dur;
    _dev->gas_sett.heatr_temp = heatr_temp;
    
    uint16_t settings_sel = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;

    uint16_t profile_dur = 0;
    get_profile_dur(&profile_dur);
    
    rslt = set_sensor_settings(settings_sel);
    
    if (rslt == BME680_OK) {
        rslt = set_sensor_mode();
        _i2cCallbacks->delay_ms_strict(profile_dur);
        if (rslt == BME680_OK) {
            rslt = get_sensor_data(data);
        }
        
        if (rslt == BME680_OK) {
            rslt = analyze_sensor_data(data);
        }
    }
        
    return rslt;
}

int Bme680::analyze_sensor_data(struct bme680_field_data *data)
{
    int rslt = BME680_OK;
    uint8_t self_test_failed = 0;

    const int16_t MIN_TEMPERATURE = INT16_C(0);      /* 0 degree Celsius */
    const int16_t MAX_TEMPERATURE = INT16_C(6000);   /* 60 degree Celsius */

    const uint32_t MIN_PRESSURE = UINT32_C(90000);    /* 900 hecto Pascals */
    const uint32_t MAX_PRESSURE = UINT32_C(110000);   /* 1100 hecto Pascals */

    const uint32_t MIN_HUMIDITY = UINT32_C(1000);    /* 20% relative humidity */
    const uint32_t MAX_HUMIDITY = UINT32_C(100000);    /* 80% relative humidity*/

    if ((data->temperature < MIN_TEMPERATURE) || (data->temperature > MAX_TEMPERATURE))
        self_test_failed++;

    if ((data->pressure < MIN_PRESSURE) || (data->pressure > MAX_PRESSURE))
        self_test_failed++;

    if ((data->humidity < MIN_HUMIDITY) || (data->humidity > MAX_HUMIDITY))
        self_test_failed++;

    if (!(data->status & BME680_GASM_VALID_MSK))
            self_test_failed++;

    if (self_test_failed)
        rslt = BME680_W_SELF_TEST_FAILED;

    return rslt;
}

int Bme680::soft_reset() {
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    uint8_t reg_addr = BME680_SOFT_RESET_ADDR;
    uint8_t soft_rst_cmd = BME680_SOFT_RESET_CMD;    
    rslt = set_regs(&reg_addr, &soft_rst_cmd, 1);
    
    if (rslt==BME680_OK) {
        _i2cCallbacks->delay_ms_relaxed(BME680_RESET_PERIOD);
    }
    
    return rslt;
}

int Bme680::get_regs(uint8_t reg_addr, uint8_t *reg_data, uint16_t len)
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    _dev->com_rslt = _i2cCallbacks->read(_dev->dev_id, reg_addr, reg_data, len);
    if (_dev->com_rslt != 0)
        rslt = BME680_E_COM_FAIL;
    return rslt;
}


int Bme680::set_regs(const uint8_t *reg_addr, const uint8_t *reg_data, uint8_t len)
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    /* Length of the temporary buffer is 2*(length of register)*/
    uint8_t tmp_buff[BME680_TMP_BUFFER_LENGTH] = { 0 };
    uint16_t index;
    
    if ((len > 0) && (len < BME680_TMP_BUFFER_LENGTH / 2)) {
        /* Interleave the 2 arrays */
        for (index = 0; index < len; index++) {
            tmp_buff[(2 * index)] = reg_addr[index];
            tmp_buff[(2 * index) + 1] = reg_data[index];
        }
        /* Write the interleaved array */
        _dev->com_rslt = _i2cCallbacks->write(_dev->dev_id, tmp_buff[0], &tmp_buff[1], (2 * len) - 1);
        if (_dev->com_rslt != 0) {
            rslt = BME680_E_COM_FAIL;
        }
    } else {
        rslt = BME680_E_INVALID_LENGTH;
    }
    
    return rslt;
}

int Bme680::null_ptr_check()
{
    if (_dev == NULL || _i2cCallbacks == NULL) {
        /* Device structure pointer is not valid */
        return BME680_E_NULL_PTR;
    } else {
        /* Device structure is fine */
        return BME680_OK;
    }
}

int Bme680::get_calib_data()
{
    int rslt;
    uint8_t coeff_array[BME680_COEFF_SIZE] = { 0 };
    uint8_t temp_var = 0; /* Temporary variable */

    /* Check for null pointer in the device structure*/
    rslt = null_ptr_check();
    if (rslt == BME680_OK) {
        rslt = get_regs(BME680_COEFF_ADDR1, coeff_array, BME680_COEFF_ADDR1_LEN);
        /* Append the second half in the same array */
        if (rslt == BME680_OK)
            rslt = get_regs(BME680_COEFF_ADDR2, &coeff_array[BME680_COEFF_ADDR1_LEN]
            , BME680_COEFF_ADDR2_LEN);

        /* Temperature related coefficients */
        _dev->calib.par_t1 = (uint16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_T1_MSB_REG],
            coeff_array[BME680_T1_LSB_REG]));
        _dev->calib.par_t2 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_T2_MSB_REG],
            coeff_array[BME680_T2_LSB_REG]));
        _dev->calib.par_t3 = (int8_t) (coeff_array[BME680_T3_REG]);

        /* Pressure related coefficients */
        _dev->calib.par_p1 = (uint16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P1_MSB_REG],
            coeff_array[BME680_P1_LSB_REG]));
        _dev->calib.par_p2 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P2_MSB_REG],
            coeff_array[BME680_P2_LSB_REG]));
        _dev->calib.par_p3 = (int8_t) coeff_array[BME680_P3_REG];
        _dev->calib.par_p4 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P4_MSB_REG],
            coeff_array[BME680_P4_LSB_REG]));
        _dev->calib.par_p5 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P5_MSB_REG],
            coeff_array[BME680_P5_LSB_REG]));
        _dev->calib.par_p6 = (int8_t) (coeff_array[BME680_P6_REG]);
        _dev->calib.par_p7 = (int8_t) (coeff_array[BME680_P7_REG]);
        _dev->calib.par_p8 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P8_MSB_REG],
            coeff_array[BME680_P8_LSB_REG]));
        _dev->calib.par_p9 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_P9_MSB_REG],
            coeff_array[BME680_P9_LSB_REG]));
        _dev->calib.par_p10 = (uint8_t) (coeff_array[BME680_P10_REG]);

        /* Humidity related coefficients */
        _dev->calib.par_h1 = (uint16_t) (((uint16_t) coeff_array[BME680_H1_MSB_REG] << BME680_HUM_REG_SHIFT_VAL)
            | (coeff_array[BME680_H1_LSB_REG] & BME680_BIT_H1_DATA_MSK));
        _dev->calib.par_h2 = (uint16_t) (((uint16_t) coeff_array[BME680_H2_MSB_REG] << BME680_HUM_REG_SHIFT_VAL)
            | ((coeff_array[BME680_H2_LSB_REG]) >> BME680_HUM_REG_SHIFT_VAL));
        _dev->calib.par_h3 = (int8_t) coeff_array[BME680_H3_REG];
        _dev->calib.par_h4 = (int8_t) coeff_array[BME680_H4_REG];
        _dev->calib.par_h5 = (int8_t) coeff_array[BME680_H5_REG];
        _dev->calib.par_h6 = (uint8_t) coeff_array[BME680_H6_REG];
        _dev->calib.par_h7 = (int8_t) coeff_array[BME680_H7_REG];

        /* Gas heater related coefficients */
        _dev->calib.par_gh1 = (int8_t) coeff_array[BME680_GH1_REG];
        _dev->calib.par_gh2 = (int16_t) (BME680_CONCAT_BYTES(coeff_array[BME680_GH2_MSB_REG],
            coeff_array[BME680_GH2_LSB_REG]));
        _dev->calib.par_gh3 = (int8_t) coeff_array[BME680_GH3_REG];

        /* Other coefficients */
        if (rslt == BME680_OK) {
            rslt = get_regs(BME680_ADDR_RES_HEAT_RANGE_ADDR, &temp_var, 1);

            _dev->calib.res_heat_range = ((temp_var & BME680_RHRANGE_MSK) / 16);
            if (rslt == BME680_OK) {
                rslt = get_regs(BME680_ADDR_RES_HEAT_VAL_ADDR, &temp_var, 1);

                _dev->calib.res_heat_val = (int8_t) temp_var;
                if (rslt == BME680_OK)
                    rslt = get_regs(BME680_ADDR_RANGE_SW_ERR_ADDR, &temp_var, 1);
            }
        }
        _dev->calib.range_sw_err = ((int8_t) temp_var & (int8_t) BME680_RSERROR_MSK) / 16;
    }

    return rslt;
}

void Bme680::set_profile_dur(uint16_t duration)
{
    uint32_t tph_dur; /* Calculate in us */
    uint32_t meas_cycles;
    uint8_t os_to_meas_cycles[6] = {0, 1, 2, 4, 8, 16};

    meas_cycles = os_to_meas_cycles[_dev->tph_sett.os_temp];
    meas_cycles += os_to_meas_cycles[_dev->tph_sett.os_pres];
    meas_cycles += os_to_meas_cycles[_dev->tph_sett.os_hum];

    /* TPH measurement duration */
    tph_dur = meas_cycles * UINT32_C(1963);
    tph_dur += UINT32_C(477 * 4); /* TPH switching duration */
    tph_dur += UINT32_C(477 * 5); /* Gas measurement duration */
    tph_dur += UINT32_C(500); /* Get it to the closest whole number.*/
    tph_dur /= UINT32_C(1000); /* Convert to ms */

    tph_dur += UINT32_C(1); /* Wake up duration of 1ms */
    /* The remaining time should be used for heating */
    _dev->gas_sett.heatr_dur = duration - (uint16_t) tph_dur;
}

void Bme680::get_profile_dur(uint16_t *duration)
{
    uint32_t tph_dur; /* Calculate in us */
    uint32_t meas_cycles;
    uint8_t os_to_meas_cycles[6] = {0, 1, 2, 4, 8, 16};

    meas_cycles = os_to_meas_cycles[_dev->tph_sett.os_temp];
    meas_cycles += os_to_meas_cycles[_dev->tph_sett.os_pres];
    meas_cycles += os_to_meas_cycles[_dev->tph_sett.os_hum];

    /* TPH measurement duration */
    tph_dur = meas_cycles * UINT32_C(1963);
    tph_dur += UINT32_C(477 * 4); /* TPH switching duration */
    tph_dur += UINT32_C(477 * 5); /* Gas measurement duration */
    tph_dur += UINT32_C(500); /* Get it to the closest whole number.*/
    tph_dur /= UINT32_C(1000); /* Convert to ms */

    tph_dur += UINT32_C(1); /* Wake up duration of 1ms */

    *duration = (uint16_t) tph_dur;

    /* Get the gas duration only when the run gas is enabled */
    if (_dev->gas_sett.run_gas) {
        /* The remaining time should be used for heating */
        *duration += _dev->gas_sett.heatr_dur;
    }
}

int Bme680::get_sensor_data(struct bme680_field_data *data)
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    /* Reading the sensor data in forced mode only */
    rslt = read_field_data(data);
    if (rslt == BME680_OK) {
        if (data->status & BME680_NEW_DATA_MSK)
            _dev->new_fields = 1;
        else
            _dev->new_fields = 0;
    }

    return rslt;
}

int Bme680::read_field_data(struct bme680_field_data *data){
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    uint8_t buff[BME680_FIELD_LENGTH] = { 0 };
    uint8_t gas_range;
    uint32_t adc_temp;
    uint32_t adc_pres;
    uint16_t adc_hum;
    uint16_t adc_gas_res;
    uint8_t tries = 10;
    
    do {
        if (rslt == BME680_OK) {
            rslt = get_regs(((uint8_t) (BME680_FIELD0_ADDR)), buff, (uint16_t) BME680_FIELD_LENGTH);

            data->status = buff[0] & BME680_NEW_DATA_MSK;
            data->gas_index = buff[0] & BME680_GAS_INDEX_MSK;
            data->meas_index = buff[1];

            /* read the raw data from the sensor */
            adc_pres = (uint32_t) (((uint32_t) buff[2] * 4096) | ((uint32_t) buff[3] * 16)
                | ((uint32_t) buff[4] / 16));
            adc_temp = (uint32_t) (((uint32_t) buff[5] * 4096) | ((uint32_t) buff[6] * 16)
                | ((uint32_t) buff[7] / 16));
            adc_hum = (uint16_t) (((uint32_t) buff[8] * 256) | (uint32_t) buff[9]);
            adc_gas_res = (uint16_t) ((uint32_t) buff[13] * 4 | (((uint32_t) buff[14]) / 64));
            gas_range = buff[14] & BME680_GAS_RANGE_MSK;

            data->status |= buff[14] & BME680_GASM_VALID_MSK;
            data->status |= buff[14] & BME680_HEAT_STAB_MSK;

            if (data->status & BME680_NEW_DATA_MSK) {
                data->temperature = calc_temperature(adc_temp);
                data->pressure = calc_pressure(adc_pres);
                data->humidity = calc_humidity(adc_hum);
                data->gas_resistance = calc_gas_resistance(adc_gas_res, gas_range);
                break;
            }
            /* Delay to poll the data */
            _i2cCallbacks->delay_ms_relaxed(BME680_POLL_PERIOD_MS);
        }
        tries--;
    } while (tries);

    if (!tries){
        rslt = BME680_W_NO_NEW_DATA;
    }
    
    return rslt;
}

int Bme680::get_sensor_mode(struct bme680_dev *dev)
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    uint8_t mode;

    rslt = get_regs(BME680_CONF_T_P_MODE_ADDR, &mode, 1);
    /* Masking the other register bit info*/
    _dev->power_mode = mode & BME680_MODE_MSK;

    return rslt;
}

int Bme680::set_sensor_mode()
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;

    uint8_t tmp_pow_mode;
    uint8_t pow_mode = 0;
    uint8_t reg_addr = BME680_CONF_T_P_MODE_ADDR;

    /* Call repeatedly until in sleep */
    do {
        rslt = get_regs(BME680_CONF_T_P_MODE_ADDR, &tmp_pow_mode, 1);
        if (rslt == BME680_OK) {
            /* Put to sleep before changing mode */
            pow_mode = (tmp_pow_mode & BME680_MODE_MSK);

            if (pow_mode != BME680_SLEEP_MODE) {
                tmp_pow_mode = tmp_pow_mode & (~BME680_MODE_MSK); /* Set to sleep */
                rslt = set_regs(&reg_addr, &tmp_pow_mode, 1);
                _i2cCallbacks->delay_ms_relaxed(BME680_POLL_PERIOD_MS);
            }
        }
    } while (pow_mode != BME680_SLEEP_MODE);

    /* Already in sleep */
    if (_dev->power_mode != BME680_SLEEP_MODE) {
        tmp_pow_mode = (tmp_pow_mode & ~BME680_MODE_MSK) | (_dev->power_mode & BME680_MODE_MSK);
        if (rslt == BME680_OK) {
            rslt = set_regs(&reg_addr, &tmp_pow_mode, 1);
        }
    }
    
    return rslt;
}

int Bme680::set_gas_config()
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;

    uint8_t reg_addr[2] = {0};
    uint8_t reg_data[2] = {0};

    if (_dev->power_mode == BME680_FORCED_MODE) {
        reg_addr[0] = BME680_RES_HEAT0_ADDR;
        reg_data[0] = calc_heater_res(_dev->gas_sett.heatr_temp);
        reg_addr[1] = BME680_GAS_WAIT0_ADDR;
        reg_data[1] = calc_heater_dur(_dev->gas_sett.heatr_dur);
        _dev->gas_sett.nb_conv = 0;
    } else {
        rslt = BME680_W_DEFINE_PWR_MODE;
    }
    
    if (rslt == BME680_OK) {
        rslt = set_regs(reg_addr, reg_data, 2);
    }
    
    return rslt;
}

uint8_t Bme680::calc_heater_dur(uint16_t dur)
{
    uint8_t factor = 0;
    uint8_t durval;

    if (dur >= 0xfc0) {
        durval = 0xff; /* Max duration*/
    } else {
        while (dur > 0x3F) {
            dur = dur / 4;
            factor += 1;
        }
        durval = (uint8_t) (dur + (factor * 64));
    }

    return durval;
}


uint8_t Bme680::calc_heater_res(uint16_t temp)
{
    uint8_t heatr_res;
    int32_t var1;
    int32_t var2;
    int32_t var3;
    int32_t var4;
    int32_t var5;
    int32_t heatr_res_x100;

    if (temp > 400) {
        temp = 400;
    }
    
    var1 = (((int32_t) _dev->amb_temp * _dev->calib.par_gh3) / 1000) * 256;
    var2 = (_dev->calib.par_gh1 + 784) * (((((_dev->calib.par_gh2 + 154009) * temp * 5) / 100) + 3276800) / 10);
    var3 = var1 + (var2 / 2);
    var4 = (var3 / (_dev->calib.res_heat_range + 4));
    var5 = (131 * _dev->calib.res_heat_val) + 65536;
    heatr_res_x100 = (int32_t) (((var4 / var5) - 250) * 34);
    heatr_res = (uint8_t) ((heatr_res_x100 + 50) / 100);

    return heatr_res;
}

int Bme680::boundary_check(uint8_t *value, uint8_t min, uint8_t max) {
    int rslt = BME680_OK;

    if (value != NULL) {
        // Check if value is below minimum value
        if (*value < min) {
            // Auto correct the invalid value to minimum value
            *value = min;
            _dev->info_msg |= BME680_I_MIN_CORRECTION;
        }
        // Check if value is above maximum value
        if (*value > max) {
            // Auto correct the invalid value to maximum value
            *value = max;
            _dev->info_msg |= BME680_I_MAX_CORRECTION;
        }
    } else {
        rslt = BME680_E_NULL_PTR;
    }

    return rslt;
}

int Bme680::set_sensor_settings(uint16_t desired_settings)
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    uint8_t reg_addr;
    uint8_t data = 0;
    uint8_t count = 0;
    uint8_t reg_array[BME680_REG_BUFFER_LENGTH] = { 0 };
    uint8_t data_array[BME680_REG_BUFFER_LENGTH] = { 0 };
    uint8_t intended_power_mode = _dev->power_mode; // Save intended power mode

    // Check for null pointer in the device structure
    
    if (desired_settings & BME680_GAS_MEAS_SEL) {
        rslt = set_gas_config();
    }
    
    _dev->power_mode = BME680_SLEEP_MODE;
    if (rslt == BME680_OK) {
        rslt = set_sensor_mode();
    }
    
    // Selecting the filter
    if (desired_settings & BME680_FILTER_SEL) {
        rslt = boundary_check(&(_dev->tph_sett.filter), BME680_FILTER_SIZE_0, BME680_FILTER_SIZE_127);
        reg_addr = BME680_CONF_ODR_FILT_ADDR;

        if (rslt == BME680_OK) {
            rslt = get_regs(reg_addr, &data, 1);
        }
        
        if (desired_settings & BME680_FILTER_SEL) {
            data = BME680_SET_BITS(data, BME680_FILTER, _dev->tph_sett.filter);
        }
        
        reg_array[count] = reg_addr; // Append configuration
        data_array[count] = data;
        count++;
    }

    // Selecting heater control for the sensor
    if (desired_settings & BME680_HCNTRL_SEL) {
        rslt = boundary_check(&(_dev->gas_sett.heatr_ctrl), BME680_ENABLE_HEATER,
            BME680_DISABLE_HEATER);
        reg_addr = BME680_CONF_HEAT_CTRL_ADDR;

        if (rslt == BME680_OK){
            rslt = get_regs(reg_addr, &data, 1);
        }
        
        data = BME680_SET_BITS_POS_0(data, BME680_HCTRL, _dev->gas_sett.heatr_ctrl);

        reg_array[count] = reg_addr; // Append configuration
        data_array[count] = data;
        count++;
    }

    // Selecting heater T,P oversampling for the sensor
    if (desired_settings & (BME680_OST_SEL | BME680_OSP_SEL)) {
        rslt = boundary_check(&(_dev->tph_sett.os_temp), BME680_OS_NONE, BME680_OS_16X);
        reg_addr = BME680_CONF_T_P_MODE_ADDR;

        if (rslt == BME680_OK) {
            rslt = get_regs(reg_addr, &data, 1);
        }
        
        if (desired_settings & BME680_OST_SEL) {
            data = BME680_SET_BITS(data, BME680_OST, _dev->tph_sett.os_temp);
        }
        
        if (desired_settings & BME680_OSP_SEL) {
            data = BME680_SET_BITS(data, BME680_OSP, _dev->tph_sett.os_pres);
        }
        
        reg_array[count] = reg_addr;
        data_array[count] = data;
        count++;
    }

    // Selecting humidity oversampling for the sensor
    if (desired_settings & BME680_OSH_SEL) {
        rslt = boundary_check(&(_dev->tph_sett.os_hum), BME680_OS_NONE, BME680_OS_16X);
        reg_addr = BME680_CONF_OS_H_ADDR;

        if (rslt == BME680_OK) {
            rslt = get_regs(reg_addr, &data, 1);
        }
        
        data = BME680_SET_BITS_POS_0(data, BME680_OSH, _dev->tph_sett.os_hum);

        reg_array[count] = reg_addr; // Append configuration
        data_array[count] = data;
        count++;
    }

    // Selecting the runGas and NB conversion settings for the sensor
    if (desired_settings & (BME680_RUN_GAS_SEL | BME680_NBCONV_SEL)) {
        rslt = boundary_check(&(_dev->gas_sett.run_gas), BME680_RUN_GAS_DISABLE,
            BME680_RUN_GAS_ENABLE);
            
        if (rslt == BME680_OK) {
            // Validate boundary conditions
            rslt = boundary_check(&(_dev->gas_sett.nb_conv), BME680_NBCONV_MIN,
                BME680_NBCONV_MAX);
        }

        reg_addr = BME680_CONF_ODR_RUN_GAS_NBC_ADDR;

        if (rslt == BME680_OK)
            rslt = get_regs(reg_addr, &data, 1);

        if (desired_settings & BME680_RUN_GAS_SEL)
            data = BME680_SET_BITS(data, BME680_RUN_GAS, _dev->gas_sett.run_gas);

        if (desired_settings & BME680_NBCONV_SEL)
            data = BME680_SET_BITS_POS_0(data, BME680_NBCONV, _dev->gas_sett.nb_conv);

        reg_array[count] = reg_addr; // Append configuration
        data_array[count] = data;
        count++;
    }

    if (rslt == BME680_OK)
        rslt = set_regs(reg_array, data_array, count);

    // Restore previous intended power mode
    _dev->power_mode = intended_power_mode;

    return rslt;
}

int Bme680::get_sensor_settings(uint16_t desired_settings)
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
   
    /* starting address of the register array for burst read*/
    uint8_t reg_addr = BME680_CONF_HEAT_CTRL_ADDR;
    uint8_t data_array[BME680_REG_BUFFER_LENGTH] = { 0 };

    
    rslt = get_regs(reg_addr, data_array, BME680_REG_BUFFER_LENGTH);

    if (rslt == BME680_OK) {
        if (desired_settings & BME680_GAS_MEAS_SEL)
            rslt = get_gas_config();

        /* get the T,P,H ,Filter,ODR settings here */
        if (desired_settings & BME680_FILTER_SEL)
            _dev->tph_sett.filter = BME680_GET_BITS(data_array[BME680_REG_FILTER_INDEX],
                BME680_FILTER);

        if (desired_settings & (BME680_OST_SEL | BME680_OSP_SEL)) {
            _dev->tph_sett.os_temp = BME680_GET_BITS(data_array[BME680_REG_TEMP_INDEX], BME680_OST);
            _dev->tph_sett.os_pres = BME680_GET_BITS(data_array[BME680_REG_PRES_INDEX], BME680_OSP);
        }

        if (desired_settings & BME680_OSH_SEL)
            _dev->tph_sett.os_hum = BME680_GET_BITS_POS_0(data_array[BME680_REG_HUM_INDEX],
                BME680_OSH);

        /* get the gas related settings */
        if (desired_settings & BME680_HCNTRL_SEL)
            _dev->gas_sett.heatr_ctrl = BME680_GET_BITS_POS_0(data_array[BME680_REG_HCTRL_INDEX],
                BME680_HCTRL);

        if (desired_settings & (BME680_RUN_GAS_SEL | BME680_NBCONV_SEL)) {
            _dev->gas_sett.nb_conv = BME680_GET_BITS_POS_0(data_array[BME680_REG_NBCONV_INDEX],
                BME680_NBCONV);
            _dev->gas_sett.run_gas = BME680_GET_BITS(data_array[BME680_REG_RUN_GAS_INDEX],
                BME680_RUN_GAS);
        }
    }

    return rslt;
}

int Bme680::get_gas_config()
{
    int rslt = null_ptr_check();
    if (rslt!=BME680_OK) return rslt;
    
    /* starting address of the register array for burst read*/
    uint8_t reg_addr1 = BME680_ADDR_SENS_CONF_START;
    uint8_t reg_addr2 = BME680_ADDR_GAS_CONF_START;
    uint8_t reg_data = 0;

    if (rslt == BME680_OK) {
        rslt = get_regs(reg_addr1, &reg_data, 1);
        if (rslt == BME680_OK) {
            _dev->gas_sett.heatr_temp = reg_data;
            rslt = get_regs(reg_addr2, &reg_data, 1);
            if (rslt == BME680_OK) {
                /* Heating duration register value */
                _dev->gas_sett.heatr_dur = reg_data;
            }
        }
    }
    
    return rslt;
}

int16_t Bme680::calc_temperature(uint32_t temp_adc)
{
    int64_t var1;
    int64_t var2;
    int64_t var3;
    int16_t calc_temp;

    var1 = ((int32_t) temp_adc >> 3) - ((int32_t) _dev->calib.par_t1 << 1);
    var2 = (var1 * (int32_t) _dev->calib.par_t2) >> 11;
    var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
    var3 = ((var3) * ((int32_t) _dev->calib.par_t3 << 4)) >> 14;
    _dev->calib.t_fine = (int32_t) (var2 + var3);
    calc_temp = (int16_t) (((_dev->calib.t_fine * 5) + 128) >> 8);

    return calc_temp;
}

uint32_t Bme680::calc_pressure(uint32_t pres_adc)
{
    int32_t var1 = 0;
    int32_t var2 = 0;
    int32_t var3 = 0;
    int32_t pressure_comp = 0;

    var1 = (((int32_t)_dev->calib.t_fine) >> 1) - 64000;
    var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) *
        (int32_t)_dev->calib.par_p6) >> 2;
    var2 = var2 + ((var1 * (int32_t)_dev->calib.par_p5) << 1);
    var2 = (var2 >> 2) + ((int32_t)_dev->calib.par_p4 << 16);
    var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
        ((int32_t)_dev->calib.par_p3 << 5)) >> 3) +
        (((int32_t)_dev->calib.par_p2 * var1) >> 1);
    var1 = var1 >> 18;
    var1 = ((32768 + var1) * (int32_t)_dev->calib.par_p1) >> 15;
    pressure_comp = 1048576 - pres_adc;
    pressure_comp = (int32_t)((pressure_comp - (var2 >> 12)) * ((uint32_t)3125));
    if (pressure_comp >= BME680_MAX_OVERFLOW_VAL)
        pressure_comp = ((pressure_comp / (uint32_t)var1) << 1);
    else
        pressure_comp = ((pressure_comp << 1) / (uint32_t)var1);
    var1 = ((int32_t)_dev->calib.par_p9 * (int32_t)(((pressure_comp >> 3) *
        (pressure_comp >> 3)) >> 13)) >> 12;
    var2 = ((int32_t)(pressure_comp >> 2) *
        (int32_t)_dev->calib.par_p8) >> 13;
    var3 = ((int32_t)(pressure_comp >> 8) * (int32_t)(pressure_comp >> 8) *
        (int32_t)(pressure_comp >> 8) *
        (int32_t)_dev->calib.par_p10) >> 17;

    pressure_comp = (int32_t)(pressure_comp) + ((var1 + var2 + var3 +
        ((int32_t)_dev->calib.par_p7 << 7)) >> 4);

    return (uint32_t)pressure_comp;

}

uint32_t Bme680::calc_humidity(uint16_t hum_adc)
{
    int32_t var1;
    int32_t var2;
    int32_t var3;
    int32_t var4;
    int32_t var5;
    int32_t var6;
    int32_t temp_scaled;
    int32_t calc_hum;

    temp_scaled = (((int32_t) _dev->calib.t_fine * 5) + 128) >> 8;
    var1 = (int32_t) (hum_adc - ((int32_t) ((int32_t) _dev->calib.par_h1 * 16)))
        - (((temp_scaled * (int32_t) _dev->calib.par_h3) / ((int32_t) 100)) >> 1);
    var2 = ((int32_t) _dev->calib.par_h2
        * (((temp_scaled * (int32_t) _dev->calib.par_h4) / ((int32_t) 100))
            + (((temp_scaled * ((temp_scaled * (int32_t) _dev->calib.par_h5) / ((int32_t) 100))) >> 6)
                / ((int32_t) 100)) + (int32_t) (1 << 14))) >> 10;
    var3 = var1 * var2;
    var4 = (int32_t) _dev->calib.par_h6 << 7;
    var4 = ((var4) + ((temp_scaled * (int32_t) _dev->calib.par_h7) / ((int32_t) 100))) >> 4;
    var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
    var6 = (var4 * var5) >> 1;
    calc_hum = (((var3 + var6) >> 10) * ((int32_t) 1000)) >> 12;

    if (calc_hum > 100000) /* Cap at 100%rH */
        calc_hum = 100000;
    else if (calc_hum < 0)
        calc_hum = 0;

    return (uint32_t) calc_hum;
}

uint32_t Bme680::calc_gas_resistance(uint16_t gas_res_adc, uint8_t gas_range)
{
    int64_t var1;
    uint64_t var2;
    int64_t var3;
    uint32_t calc_gas_res;
    /**Look up table 1 for the possible gas range values */
    uint32_t lookupTable1[16] = { UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647),
        UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2130303777),
        UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2143188679), UINT32_C(2136746228),
        UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2147483647) };
    /**Look up table 2 for the possible gas range values */
    uint32_t lookupTable2[16] = { UINT32_C(4096000000), UINT32_C(2048000000), UINT32_C(1024000000), UINT32_C(512000000),
        UINT32_C(255744255), UINT32_C(127110228), UINT32_C(64000000), UINT32_C(32258064), UINT32_C(16016016),
        UINT32_C(8000000), UINT32_C(4000000), UINT32_C(2000000), UINT32_C(1000000), UINT32_C(500000),
        UINT32_C(250000), UINT32_C(125000) };

    var1 = (int64_t) ((1340 + (5 * (int64_t) _dev->calib.range_sw_err)) *
        ((int64_t) lookupTable1[gas_range])) >> 16;
    var2 = (((int64_t) ((int64_t) gas_res_adc << 15) - (int64_t) (16777216)) + var1);
    var3 = (((int64_t) lookupTable2[gas_range] * (int64_t) var1) >> 9);
    calc_gas_res = (uint32_t) ((var3 + ((int64_t) var2 >> 1)) / (int64_t) var2);

    return calc_gas_res;
}