From d5d504d1ada51ad329236250d1208726a2fc9a7b Mon Sep 17 00:00:00 2001 From: William Clark Date: Sun, 5 Nov 2023 02:58:23 +0000 Subject: [PATCH] Gas sensor resistance --- README.md | 199 ++++++++++++++++++++++++++++++++------------------ bme680.c | 214 ++++++++++++++++++++++++++++++++++++++++++++---------- bme680.h | 36 +++++++-- main.c | 66 ++++++++++++++--- 4 files changed, 389 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index c3f4411..c9f3b9f 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,38 @@ # bme680 -Example implementation, just lacking gas for now. +Example implementation, just lacking proven SPI connectivity for now. ``` -linux_i2c_init -linux_i2c_read: D0 (1) [61] -linux_i2c_write: E0 (1) [B6] -linux_i2c_read: E9 (2) [5B,66] -linux_i2c_read: 8A (2) [97,67] -linux_i2c_read: 8C (1) [3] -linux_i2c_read: 8E (2) [C0,88] -linux_i2c_read: 90 (2) [D4,D7] -linux_i2c_read: 92 (1) [58] -linux_i2c_read: 94 (2) [DC,25] -linux_i2c_read: 96 (2) [36,FF] -linux_i2c_read: 99 (1) [1E] -linux_i2c_read: 98 (1) [18] -linux_i2c_read: 9C (1) [FC] -linux_i2c_read: 9E (2) [73,F2] -linux_i2c_read: A0 (1) [1E] -linux_i2c_read: E2 (2) [FA,31] -linux_i2c_read: E1 (2) [3E,FA] -linux_i2c_read: E4 (1) [0] -linux_i2c_read: E5 (1) [2D] -linux_i2c_read: E6 (1) [14] -linux_i2c_read: E7 (1) [78] -linux_i2c_read: E8 (1) [9C] -linux_i2c_read: ED (1) [D0] -linux_i2c_read: EB (2) [85,E9] -linux_i2c_read: EE (1) [12] +i2c_init +i2c_read: D0 (1) [61] +i2c_write: E0 [B6] +i2c_read: E9 (2) [5B, 66] +i2c_read: 8A (2) [97, 67] +i2c_read: 8C (1) [03] +i2c_read: 8E (2) [C0, 88] +i2c_read: 90 (2) [D4, D7] +i2c_read: 92 (1) [58] +i2c_read: 94 (2) [DC, 25] +i2c_read: 96 (2) [36, FF] +i2c_read: 99 (1) [1E] +i2c_read: 98 (1) [18] +i2c_read: 9C (1) [FC] +i2c_read: 9E (2) [73, F2] +i2c_read: A0 (1) [1E] +i2c_read: E2 (2) [FA, 31] +i2c_read: E1 (2) [3E, FA] +i2c_read: E4 (1) [00] +i2c_read: E5 (1) [2D] +i2c_read: E6 (1) [14] +i2c_read: E7 (1) [78] +i2c_read: E8 (1) [9C] +i2c_read: ED (1) [D0] +i2c_read: EB (2) [85, E9] +i2c_read: EE (1) [12] +i2c_read: 04 (1) [13] +i2c_read: 02 (1) [16] +i2c_read: 00 (1) [2E] par_t1: 26203 par_t2: 26519 par_t3: 3 @@ -50,50 +53,104 @@ par_h4: 45 par_h5: 20 par_h6: 120 par_h7: -100 -linux_i2c_write: 74 (1) [B4] -linux_i2c_write: 72 (1) [4] -linux_i2c_write: 75 (1) [18] -linux_i2c_write: 74 (1) [B5] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [20] -linux_i2c_read: 1D (1) [80] -linux_i2c_read: 22 (3) [75,68,0] -linux_i2c_read: 1F (3) [55,63,70] -linux_i2c_read: 25 (2) [62,9E] +par_g1: 208 +par_g2: 59781 +par_g3: 18 +range_switching_error: 19 +res_heat_range: 1 +res_heat_val: 46 +i2c_write: 74 [B4] +i2c_write: 72 [04] +i2c_write: 75 [1C] +i2c_write: 6D [BC] +i2c_write: 63 [E7] +i2c_write: 59 [28] +i2c_write: 6C [BC] +i2c_write: 62 [E7] +i2c_write: 58 [28] +i2c_write: 6B [BC] +i2c_write: 61 [E7] +i2c_write: 57 [28] +i2c_write: 6A [BC] +i2c_write: 60 [E7] +i2c_write: 56 [28] +i2c_write: 69 [BC] +i2c_write: 5F [E7] +i2c_write: 55 [28] +i2c_write: 68 [BC] +i2c_write: 5E [E7] +i2c_write: 54 [28] +i2c_write: 67 [BC] +i2c_write: 5D [E7] +i2c_write: 53 [28] +i2c_write: 66 [BC] +i2c_write: 5C [E7] +i2c_write: 52 [28] +i2c_write: 65 [BC] +i2c_write: 5B [E7] +i2c_write: 51 [28] +i2c_write: 64 [BC] +i2c_write: 5A [E7] +i2c_write: 50 [28] +i2c_write: 71 [10] +i2c_write: 70 [00] +i2c_write: 74 [B5] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +...many identical lines omitted... +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [20] +i2c_read: 1D (1) [80] +i2c_read: 22 (3) [75, 3D, D0] +i2c_read: 1F (3) [55, 22, 30] +i2c_read: 25 (2) [63, 2E] +i2c_read: 2A (2) [00, 28] +i2c_read: 2B (1) [28] float mode -tfine: 99793.535399 -temp: 19.490925 degC -press: 96211.887865 Pa -humidity: 67.809989 % RH -linux_i2c_deinit +tfine: 98700.755011 +temp: 19.277491 degC +press: 96363.204250 Pa +humidity: 68.749455 % RH +gas resistance: 48633.411307 Ohm +== for heater target=320.0 and ambient temp=25.0 (degC) +i2c_deinit ``` - Low air pressure due to storm not bad sensor diff --git a/bme680.c b/bme680.c index 78fe4a5..023da5d 100644 --- a/bme680.c +++ b/bme680.c @@ -6,13 +6,14 @@ #include "bme680.h" #include "registers.h" -static int write_spi_page(bme680_t *bme680, uint8_t page_value); +static int write_spi_page(bme680_t *bme680, uint8_t page_value); static void calc_temp_comp(bme680_t *bme680); static void calc_press_comp(bme680_t *bme680); static void calc_hum_comp(bme680_t *bme680); +static void calc_gas_res(bme680_t *bme680); - +/********************************************************************/ static int write_dev(bme680_t *bme680, uint8_t reg, uint8_t value) { uint8_t tmp; @@ -32,7 +33,7 @@ static int write_spi_page(bme680_t *bme680, uint8_t page_value) { return bme680->dev.write(REG_STATUS, status_byte); } - +/********************************************************************/ static int read_dev(bme680_t *bme680, uint8_t reg, uint8_t *dst, uint32_t size) { uint8_t tmp; @@ -49,10 +50,12 @@ static int read_dev(bme680_t *bme680, uint8_t reg, uint8_t *dst, uint32_t size) return bme680->dev.read(reg, dst, size); } +/********************************************************************/ int bme680_init(bme680_t *bme680, uint8_t mode) { uint8_t car; uint8_t id_reg; + int i; bme680->mode = mode; @@ -71,10 +74,17 @@ int bme680_init(bme680_t *bme680, uint8_t mode) { return 1; } + /* zero gas sensor arrays */ + for(i=0; i<10; i++) { + bme680->cfg.idac_heat[i] = 0; + bme680->cfg.res_heat[i] = 0; + bme680->cfg.gas_wait[i] = 0; + } + return 0; } -/* for completion */ +/********************************************************************/ int bme680_deinit(bme680_t *bme680) { if (bme680->dev.deinit) { bme680->dev.deinit(); @@ -83,21 +93,23 @@ int bme680_deinit(bme680_t *bme680) { return 0; } +/********************************************************************/ int bme680_reset(bme680_t *bme680) { uint8_t reg = BME680_IS_SPI(bme680->mode) ? 0x60 : REG_RESET; uint8_t magic = 0xB6; int ret; ret = write_dev(bme680, reg, magic); - usleep(5000); /* sleep for 5 ms */ + usleep(2000); /* sleep for 5 ms */ return ret; } +/********************************************************************/ +/* configure device */ +int bme680_configure(bme680_t *bme680) { -/* configure device and trigger forced mode reading on device */ -int bme680_start(bme680_t *bme680) { - - uint8_t meas, hum, filter, err; + uint8_t meas, hum, filter, ctrl_gas1, ctrl_gas0, err; + int i; meas = hum = filter = err = 0; /* ctrl_meas. the last 0 is ticked on to enable forced mode, @@ -107,43 +119,75 @@ int bme680_start(bme680_t *bme680) { hum = bme680->cfg.osrs_h; filter = bme680->cfg.filter << 2; + /* backup of ctrl meas reg because you cannot retrieve it from the device later */ + bme680->cfg.meas = meas; + err |= write_dev(bme680, REG_CTRL_MEAS, meas); err |= write_dev(bme680, REG_CTRL_HUM, hum); err |= write_dev(bme680, REG_CONFIG, filter); - // TODO: gas stuff - - /* Now, re-send `meas' but LSb set to 1 to enable a forced conversion */ - meas |= 1; - err |= write_dev(bme680, REG_CTRL_MEAS, meas); + if (!BME680_GAS_ENABLED(bme680->mode)) { + goto SKIP_GAS; + } + /* write out all 10 setpoints */ + /* those not explicitly set are defaulted to 0 (which has no effect) */ + for(i=0; i<10; i++) { + err |= bme680->dev.write(0x6D - i, bme680->cfg.gas_wait[9 - i]); + err |= bme680->dev.write(0x63 - i, bme680->cfg.res_heat[9 - i]); + err |= bme680->dev.write(0x59 - i, bme680->cfg.idac_heat[9 - i]); + } + + /* `run_gas' bit */ + ctrl_gas1 = bme680->cfg.setpoint | (1 << 4); + ctrl_gas0 = 0; /* := (1 << 3) to turn off current going to heater */ + + err |= bme680->dev.write(REG_CTRL_GAS_1, ctrl_gas1); + err |= bme680->dev.write(REG_CTRL_GAS_0, ctrl_gas0); + + // TODO: check gas_valid_r and heat_stab_r in 0x2B + + + +SKIP_GAS: + return err; +} + +/* To start forced mode, you just have to set the lsb=1 of REG_CTRL_MEAS */ +int bme680_start(bme680_t *bme680) { + + int err = 0; + uint8_t meas; + meas = bme680->cfg.meas | 1; + err |= write_dev(bme680, REG_CTRL_MEAS, meas); return err; } +/********************************************************************/ /* blocks until the device claims all scheduled conversions are done. * don't call out of order. */ - -#define BME680_MAX_POLL_ATTEMPTS 100 int bme680_poll(bme680_t *bme680) { uint8_t meas_status = 0; + uint8_t gas_measuring = 0; // is only 1 during gas measurement + uint8_t any_measuring = 0; int err = 0; - int attempts = 0; + do { - usleep(2000); + usleep(5000); /* 5 ms */ err |= read_dev(bme680, REG_EAS_STATUS, &meas_status, 1); - } while (((meas_status >> 5) & 1) && attempts++ < BME680_MAX_POLL_ATTEMPTS && !err); + gas_measuring = ((meas_status >> 6) & 1) == 1; + any_measuring = ((meas_status >> 5) & 1) == 1; - if (attempts == BME680_MAX_POLL_ATTEMPTS) { - err = 2; - } + } while ((gas_measuring || any_measuring) && !err); return err; } +/********************************************************************/ /* assume start'd and poll'd */ int bme680_read(bme680_t *bme680) { @@ -164,23 +208,38 @@ int bme680_read(bme680_t *bme680) { * otherwise, it depends on the oversample settings. * note: humidity is not IIR filtered, and always 16-bit. * IIR filter on (any level) -> 20-bit - * IIR filter off -> 16 + (osrs_x - 1) bits. adc >> (osrs_p -1);? + * IIR filter off -> 16 + (osrs_x - 1) bits. * */ if (bme680->cfg.filter == BME680_IIR_COEFF_0) { bme680->adc.temp >>= (bme680->cfg.osrs_t - 1); bme680->adc.press >>= (bme680->cfg.osrs_p - 1); } + /* read gas adc ?*/ + if (BME680_GAS_ENABLED(bme680->mode)) { + err |= read_dev(bme680, 0x2A, buffer, 2); + bme680->adc.gas = (buffer[0] << 2) | (buffer[1] >> 6); + + err |= read_dev(bme680, 0x2B, buffer, 1); + bme680->adc.gas_range = buffer[0] & 0xF; + } + /* read/convert in order ..*/ calc_temp_comp(bme680); calc_press_comp(bme680); calc_hum_comp(bme680); + + if (BME680_GAS_ENABLED(bme680->mode)) { + calc_gas_res(bme680); + } return err; } -/********************************************************************/ - +/***********************************************************************/ +/* These arrays are used to compute a sensor heating value `res_heat' */ +/* for a specified heating target, specified in degree C. */ +/***********************************************************************/ static double const_array1[16] = { 1, 1, 1, 1, 1, 0.99, 1, 0.992, 1, 1, 0.998, 0.995, 1, 0.99, 1, 1 @@ -221,6 +280,7 @@ static void calc_temp_comp_1 (bme680_t *bme680) { bme680->fcomp.temp = temp_comp; } +/********************************************************************/ static void calc_temp_comp_2 (bme680_t *bme680) { int32_t var1, var2, var3, temp_comp; @@ -233,6 +293,7 @@ static void calc_temp_comp_2 (bme680_t *bme680) { bme680->icomp.temp = temp_comp; } +/********************************************************************/ static void calc_temp_comp (bme680_t *bme680) { if (BME680_IS_FLOAT(bme680->mode)) { calc_temp_comp_1(bme680); @@ -264,6 +325,7 @@ static void calc_press_comp_1 (bme680_t *bme680) { bme680->fcomp.press = press_comp; } +/********************************************************************/ static void calc_press_comp_2 (bme680_t *bme680 ) { int32_t var1, var2, var3, press_comp; @@ -291,6 +353,7 @@ static void calc_press_comp_2 (bme680_t *bme680 ) { bme680->icomp.press = press_comp; } +/********************************************************************/ static void calc_press_comp (bme680_t *bme680) { if (BME680_IS_FLOAT(bme680->mode)) { calc_press_comp_1(bme680); @@ -317,6 +380,7 @@ static void calc_hum_comp_1 (bme680_t *bme680) { bme680->fcomp.hum = hum_comp; } +/********************************************************************/ static void calc_hum_comp_2 (bme680_t *bme680) { int32_t var1, var2, var3, var4, var5, var6, temp_scaled, hum_comp; @@ -337,6 +401,7 @@ static void calc_hum_comp_2 (bme680_t *bme680) { bme680->icomp.hum = hum_comp; } +/********************************************************************/ static void calc_hum_comp (bme680_t *bme680) { if (BME680_IS_FLOAT(bme680->mode)) { calc_hum_comp_1(bme680); @@ -346,14 +411,12 @@ static void calc_hum_comp (bme680_t *bme680) { } /********************************************************************/ - int bme680_calibrate(bme680_t *bme680) { uint8_t buffer[3] = {0, 0 ,0}; int err = 0; - // start with temp params - + /* temperature */ err |= read_dev(bme680, 0xE9, buffer, 2); bme680->cal.par_t1 = (buffer[1] << 8) | buffer[0]; @@ -363,8 +426,8 @@ int bme680_calibrate(bme680_t *bme680) { err |= read_dev(bme680, 0x8C, buffer, 1); bme680->cal.par_t3 = buffer[0]; - // pressure - + + /* pressure */ err |= read_dev(bme680, 0x8E, buffer, 2); bme680->cal.par_p1 = (buffer[1] << 8) | buffer[0]; @@ -383,7 +446,7 @@ int bme680_calibrate(bme680_t *bme680) { err |= read_dev(bme680, 0x99, buffer, 1); bme680->cal.par_p6 = buffer[0]; - err |= read_dev(bme680, 0x98, buffer, 1); // strange order + err |= read_dev(bme680, 0x98, buffer, 1); bme680->cal.par_p7 = buffer[0]; err |= read_dev(bme680, 0x9C, buffer, 1); @@ -395,8 +458,8 @@ int bme680_calibrate(bme680_t *bme680) { err |= read_dev(bme680, 0xA0, buffer, 1); bme680->cal.par_p10 = buffer[0]; - // humidity - + + /* humidity */ err |= read_dev(bme680, 0xE2, buffer, 2); bme680->cal.par_h1 = (buffer[1] << 4) | (buffer[0] & 0xF); @@ -418,8 +481,8 @@ int bme680_calibrate(bme680_t *bme680) { err |= read_dev(bme680, 0xE8, buffer, 1); bme680->cal.par_h7 = buffer[0]; - // gas - + + /* gas */ err |= read_dev(bme680, 0xED, buffer, 1); bme680->cal.par_g1 = buffer[0]; @@ -427,18 +490,53 @@ int bme680_calibrate(bme680_t *bme680) { bme680->cal.par_g2 = (buffer[1] << 8) | buffer[0]; err |= read_dev(bme680, 0xEE, buffer, 1); - bme680->cal.par_g2 = buffer[0]; + bme680->cal.par_g3 = buffer[0]; - // todo more + err |= read_dev(bme680, 0x04, buffer, 1); + bme680->cal.range_switching_error = buffer[0]; + err |= read_dev(bme680, 0x02, buffer, 1); + bme680->cal.res_heat_range = (buffer[0] >> 4) & 0b11; + err |= read_dev(bme680, 0x00, buffer, 1); + bme680->cal.res_heat_val = buffer[0]; return err; } +/********************************************************************/ +/********************************************************************/ +static void calc_gas_res_1(bme680_t *bme680) { + double var1, gas_res; + var1 = (1340.0 + 5.0 * bme680->cal.range_switching_error) * const_array1[bme680->adc.gas_range]; + gas_res = var1 * const_array2[bme680->adc.gas_range] / (bme680->adc.gas - 512.0 + var1); + bme680->fcomp.gas_res = gas_res; +} /********************************************************************/ +static void calc_gas_res_2(bme680_t *bme680) { + int64_t var1, var2; + int32_t gas_res; + var1 = (int64_t)(((1340 + (5 * (int64_t)bme680->cal.range_switching_error)) * + ((int64_t)const_array1_int[bme680->adc.gas_range])) >> 16); + var2 = (int64_t)(bme680->adc.gas << 15) - (int64_t)(1 << 24) + var1; + gas_res = (int32_t)((((int64_t)(const_array2_int[bme680->adc.gas_range] * + (int64_t)var1) >> 9) + (var2 >> 1)) / var2); + bme680->icomp.gas_res = gas_res; +} + +/********************************************************************/ +static void calc_gas_res(bme680_t *bme680) { + if (BME680_IS_FLOAT(bme680->mode)) { + calc_gas_res_1(bme680); + } else { + calc_gas_res_2(bme680); + } +} + +/********************************************************************/ +/********************************************************************/ void bme680_print_calibration (bme680_t *bme680) { printf("par_t1: %d\n", bme680->cal.par_t1); printf("par_t2: %d\n", bme680->cal.par_t2); @@ -460,6 +558,48 @@ void bme680_print_calibration (bme680_t *bme680) { printf("par_h5: %d\n", bme680->cal.par_h5); printf("par_h6: %d\n", bme680->cal.par_h6); printf("par_h7: %d\n", bme680->cal.par_h7); + printf("par_g1: %d\n", bme680->cal.par_g1); + printf("par_g2: %d\n", bme680->cal.par_g2); + printf("par_g3: %d\n", bme680->cal.par_g3); + printf("range_switching_error: %d\n", bme680->cal.range_switching_error); + printf("res_heat_range: %d\n", bme680->cal.res_heat_range); + printf("res_heat_val: %d\n", bme680->cal.res_heat_val); } +static uint8_t calc_target_1(bme680_t *bme680, double target, double ambient) { + double var1, var2, var3, var4, var5; + uint8_t res_heat; + + var1 = ((double)bme680->cal.par_g1 / 16.0) + 49.0; + var2 = (((double)bme680->cal.par_g2 / 32768.0) * 0.0005) + 0.00235; + var3 = (double)bme680->cal.par_g3 / 1024.0; + var4 = var1 * (1.0 + (var2 * (double)target)); /* */ + var5 = var4 + (var3 * (double)ambient); + res_heat = (uint8_t)(3.4 * ((var5 * (4.0 / (4.0 + (double)bme680->cal.res_heat_range)) * + (1.0 / (1.0 + ((double)bme680->cal.res_heat_val * 0.002)))) - 25)); + return res_heat; +} + +static uint8_t calc_target_2(bme680_t *bme680, double target, double ambient) { + int32_t var1, var2, var3, var4, var5, res_heat_x100; + uint8_t res_heat; + + var1 = (((int32_t)ambient * bme680->cal.par_g3) / 10) << 8; + var2 = (bme680->cal.par_g1 + 784) * (((((bme680->cal.par_g2 + 154009) * target * 5) / + 100) + 3276800) / 10); + var3 = var1 + (var2 >> 1); + var4 = (var3 / (bme680->cal.res_heat_range + 4)); + var5 = (131 * bme680->cal.res_heat_val) + 65536; + res_heat_x100 = (int32_t)(((var4 / var5) - 250) * 34); + res_heat = (uint8_t)((res_heat_x100 + 50) / 100); + return res_heat; +} + +uint8_t bme680_calc_target(bme680_t *bme680, double target, double ambient) { + if (BME680_IS_FLOAT(bme680->mode)) { + return calc_target_1(bme680, target, ambient); + } else { + return calc_target_2(bme680, target, ambient); + } +} diff --git a/bme680.h b/bme680.h index 8e93a09..0ff82db 100644 --- a/bme680.h +++ b/bme680.h @@ -4,7 +4,11 @@ #include #define BME680_IS_SPI(m) ((m & 1) == 1) -#define BME680_IS_FLOAT(m) (((m >> 1) & 1) == 0) +#define BME680_IS_FLOAT(m) (((m >> 1) & 1) == 0) +#define BME680_GAS_ENABLED(m) (((m >> 2) & 1) == 1) + +#define BME680_IDAC(c) (c << 1) +#define BME680_GAS_WAIT(val, scal) ((uint8_t)(((scal & 0b11) << 6) | (val & 0b111111))) /* connection modes */ #define BME680_SPI 1 @@ -14,6 +18,9 @@ #define BME680_MODE_INT 2 #define BME680_MODE_FLOAT 0 +/* to enable gas conversion OR this into mode byte */ +#define BME680_ENABLE_GAS 4 + /* config values */ #define BME680_OVERSAMPLE_X1 0b001 #define BME680_OVERSAMPLE_X2 0b010 @@ -31,6 +38,12 @@ #define BME680_IIR_COEFF_63 0b110 #define BME680_IIR_COEFF_127 0b111 +/* gas related values */ +#define BME680_GAS_WAIT_X1 0b00 +#define BME680_GAS_WAIT_X4 0b01 +#define BME680_GAS_WAIT_X16 0b10 +#define BME680_GAS_WAIT_X64 0b11 + /* user supplied spi/i2c functions */ struct bme680_dev { @@ -67,22 +80,25 @@ struct bme680_cal { uint8_t par_h6; int8_t par_h7; - /* gas maybe */ + /* heater + gas */ uint16_t par_g1; uint16_t par_g2; uint16_t par_g3; uint16_t range_switching_error; + uint8_t res_heat_range; + int8_t res_heat_val; }; - struct bme680_config { uint8_t osrs_t; uint8_t osrs_p; uint8_t osrs_h; uint8_t filter; - uint8_t heater_setpoint[10]; - uint8_t heater_exposure_ms[10]; - uint8_t heater_exposure_scalar[10]; + uint8_t setpoint; + uint8_t idac_heat[10]; + uint8_t res_heat[10]; + uint8_t gas_wait[10]; + uint8_t meas; /* required because you can't read back ctrl regs */ }; struct bme680_comp_float { @@ -90,6 +106,7 @@ struct bme680_comp_float { double temp; double press; double hum; + double gas_res; }; struct bme680_comp_int { @@ -97,13 +114,16 @@ struct bme680_comp_int { int32_t temp; int32_t press; int32_t hum; + int32_t gas_res; }; struct bme680_adc { uint32_t temp; uint32_t press; uint32_t hum; - uint16_t gas; + uint32_t gas; + uint32_t gas_range; + }; struct bme680 { @@ -124,10 +144,12 @@ int bme680_init(bme680_t *bme680, uint8_t mode); int bme680_deinit(bme680_t *bme680); int bme680_reset(bme680_t *bme680); int bme680_calibrate(bme680_t *bme680); +int bme680_configure(bme680_t *bme680); int bme680_start(bme680_t *bme680); int bme680_poll(bme680_t *bme680); int bme680_read(bme680_t *bme680); +uint8_t bme680_calc_target(bme680_t *bme680, double target, double ambient); void bme680_print_calibration(bme680_t *bme680); diff --git a/main.c b/main.c index b008f97..a50ef84 100644 --- a/main.c +++ b/main.c @@ -9,6 +9,9 @@ #define DEVICE "/dev/i2c-1" #define ADDRESS 0x77 +#define AMBIENT_TEMP_GUESS 25.0 +#define HEATER_TARGET 320.0 + int i2c_dev_fd; int linux_i2c_init (void); @@ -16,10 +19,11 @@ int linux_i2c_read (uint8_t reg, uint8_t *dst, uint32_t size); int linux_i2c_write (uint8_t reg, uint8_t value); int linux_i2c_deinit (void); -int main() { +int main(void) { bme680_t bme680; uint8_t mode; + int i; /* 1. Assign functions for interacting with the device */ bme680.dev.init = linux_i2c_init; @@ -28,7 +32,7 @@ int main() { bme680.dev.deinit = linux_i2c_deinit; /* 2. set the device mode */ - mode = BME680_MODE_FLOAT | BME680_I2C; + mode = BME680_MODE_FLOAT | BME680_I2C | BME680_ENABLE_GAS; /* BME680_MODE_INT | BME680_SPI; */ @@ -57,41 +61,81 @@ int main() { bme680.cfg.osrs_h = BME680_OVERSAMPLE_X8; bme680.cfg.filter = BME680_IIR_COEFF_127; - /* 7. write config to device and set off conversion */ - if (bme680_start(&bme680) != 0) { - fprintf(stderr, "bme680_start()\n"); + /* configuring gas sensor. */ + /* NB: mode |= BME680_ENABLE_GAS; */ + /* there are 10 possible heater setpoints */ + /* they can be though of as frames that define one single gas-resistance measurement */ + /* they do not carry over or affect eachother in any way. */ + for(i=0; i<10; i++) { + + /* calculate a resistance target, based on desired temp, and ambient temp */ + bme680.cfg.res_heat[i] = bme680_calc_target(&bme680, HEATER_TARGET, AMBIENT_TEMP_GUESS); + + /* initial heating current for the setpoint. Could be useful in cold places.. */ + /* 7-bit word. Each step/lsb is equiv. to 1/8 mA; so max 16 mA */ + /* this s.p. field is allowed to be left as 0 if no preload is required. */ + bme680.cfg.idac_heat[i] = BME680_IDAC(20); + + /* define the time between the start of heating and start of resistance sensing in this s.p.*/ + /* Bosch datasheet suggests ~30 - 40ms is usually all that is required to get up to temp. */ + /* 60 * 16 = 960 ms wait before sampling resistance starts. */ + /* the first value is 6-bit (0...64) with 1 ms step size. */ + bme680.cfg.gas_wait[i] = BME680_GAS_WAIT(60, BME680_GAS_WAIT_X16); + } + + /* The BME680 does not cycle between setpoints. They have to be manually set. */ + /* After each "run", the setpoint has to be changed. */ + bme680.cfg.setpoint = 0; /* 0 thru 9 */ + + /* 7. write config to device */ + if (bme680_configure(&bme680) != 0) { + fprintf(stderr, "bme680_configure()\n"); bme680_deinit(&bme680); exit(EXIT_FAILURE); } - /* 8. poll the meas_status register until all scheduled conversions are done */ + /* 8. Start forced measurement. After it finishes, it should remember the previous config. */ + if (bme680_start(&bme680) != 0) { + fprintf(stderr, "bme680_configure()\n"); + bme680_deinit(&bme680); + exit(EXIT_FAILURE); + } + + /* 9. poll the meas_status register until all scheduled conversions are done */ if (bme680_poll(&bme680) != 0) { fprintf(stderr, "bme680_poll()\n"); bme680_deinit(&bme680); exit(EXIT_FAILURE); } - /* 9. read the ADC's and perform a conversion */ + /* 10. read the ADC's and perform a conversion */ if (bme680_read(&bme680) != 0) { fprintf(stderr, "bme680_read()\n"); bme680_deinit(&bme680); exit(EXIT_FAILURE); } - /* 10. use data ! */ - + /* 11. use data ! */ if (BME680_IS_FLOAT(bme680.mode)) { puts("float mode"); printf("tfine: %f\n", bme680.fcomp.tfine); printf("temp: %f degC\n", bme680.fcomp.temp); printf("press: %f Pa\n", bme680.fcomp.press); printf("humidity: %f %% RH\n", bme680.fcomp.hum); + if (BME680_GAS_ENABLED(bme680.mode)) { + printf("gas resistance: %f Ohm\n", bme680.fcomp.gas_res); + printf("== for heater target=%.1f and ambient temp=%.1f (degC)\n", HEATER_TARGET, AMBIENT_TEMP_GUESS); + } } else { puts("integer mode"); printf("tfine: %d\n", bme680.icomp.tfine); - printf("temp: %d\n", bme680.icomp.temp); + printf("temp: %d (degC * 100)\n", bme680.icomp.temp); printf("press: %d Pa\n", bme680.icomp.press); - printf("humidity: %d %% RH\n", bme680.icomp.hum); + printf("humidity: %d (%% RH * 1000)\n", bme680.icomp.hum); + if (BME680_GAS_ENABLED(bme680.mode)) { + printf("gas resistance: %d Ohm\n", bme680.icomp.gas_res); + printf("== for heater target=%.1f and ambient temp=%.1f (degC)\n", HEATER_TARGET, AMBIENT_TEMP_GUESS); + } } bme680_deinit(&bme680);