Gas sensor resistance

This commit is contained in:
William Clark 2023-11-05 02:58:23 +00:00
parent 9a0b274711
commit d5d504d1ad
4 changed files with 389 additions and 126 deletions

199
README.md
View File

@ -1,35 +1,38 @@
# bme680 # bme680
Example implementation, just lacking gas for now. Example implementation, just lacking proven SPI connectivity for now.
``` ```
linux_i2c_init i2c_init
linux_i2c_read: D0 (1) [61] i2c_read: D0 (1) [61]
linux_i2c_write: E0 (1) [B6] i2c_write: E0 [B6]
linux_i2c_read: E9 (2) [5B,66] i2c_read: E9 (2) [5B, 66]
linux_i2c_read: 8A (2) [97,67] i2c_read: 8A (2) [97, 67]
linux_i2c_read: 8C (1) [3] i2c_read: 8C (1) [03]
linux_i2c_read: 8E (2) [C0,88] i2c_read: 8E (2) [C0, 88]
linux_i2c_read: 90 (2) [D4,D7] i2c_read: 90 (2) [D4, D7]
linux_i2c_read: 92 (1) [58] i2c_read: 92 (1) [58]
linux_i2c_read: 94 (2) [DC,25] i2c_read: 94 (2) [DC, 25]
linux_i2c_read: 96 (2) [36,FF] i2c_read: 96 (2) [36, FF]
linux_i2c_read: 99 (1) [1E] i2c_read: 99 (1) [1E]
linux_i2c_read: 98 (1) [18] i2c_read: 98 (1) [18]
linux_i2c_read: 9C (1) [FC] i2c_read: 9C (1) [FC]
linux_i2c_read: 9E (2) [73,F2] i2c_read: 9E (2) [73, F2]
linux_i2c_read: A0 (1) [1E] i2c_read: A0 (1) [1E]
linux_i2c_read: E2 (2) [FA,31] i2c_read: E2 (2) [FA, 31]
linux_i2c_read: E1 (2) [3E,FA] i2c_read: E1 (2) [3E, FA]
linux_i2c_read: E4 (1) [0] i2c_read: E4 (1) [00]
linux_i2c_read: E5 (1) [2D] i2c_read: E5 (1) [2D]
linux_i2c_read: E6 (1) [14] i2c_read: E6 (1) [14]
linux_i2c_read: E7 (1) [78] i2c_read: E7 (1) [78]
linux_i2c_read: E8 (1) [9C] i2c_read: E8 (1) [9C]
linux_i2c_read: ED (1) [D0] i2c_read: ED (1) [D0]
linux_i2c_read: EB (2) [85,E9] i2c_read: EB (2) [85, E9]
linux_i2c_read: EE (1) [12] i2c_read: EE (1) [12]
i2c_read: 04 (1) [13]
i2c_read: 02 (1) [16]
i2c_read: 00 (1) [2E]
par_t1: 26203 par_t1: 26203
par_t2: 26519 par_t2: 26519
par_t3: 3 par_t3: 3
@ -50,50 +53,104 @@ par_h4: 45
par_h5: 20 par_h5: 20
par_h6: 120 par_h6: 120
par_h7: -100 par_h7: -100
linux_i2c_write: 74 (1) [B4] par_g1: 208
linux_i2c_write: 72 (1) [4] par_g2: 59781
linux_i2c_write: 75 (1) [18] par_g3: 18
linux_i2c_write: 74 (1) [B5] range_switching_error: 19
linux_i2c_read: 1D (1) [20] res_heat_range: 1
linux_i2c_read: 1D (1) [20] res_heat_val: 46
linux_i2c_read: 1D (1) [20] i2c_write: 74 [B4]
linux_i2c_read: 1D (1) [20] i2c_write: 72 [04]
linux_i2c_read: 1D (1) [20] i2c_write: 75 [1C]
linux_i2c_read: 1D (1) [20] i2c_write: 6D [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 63 [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 59 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 6C [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 62 [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 58 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 6B [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 61 [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 57 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 6A [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 60 [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 56 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 69 [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 5F [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 55 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 68 [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 5E [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 54 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 67 [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 5D [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 53 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 66 [BC]
linux_i2c_read: 1D (1) [20] i2c_write: 5C [E7]
linux_i2c_read: 1D (1) [20] i2c_write: 52 [28]
linux_i2c_read: 1D (1) [20] i2c_write: 65 [BC]
linux_i2c_read: 1D (1) [80] i2c_write: 5B [E7]
linux_i2c_read: 22 (3) [75,68,0] i2c_write: 51 [28]
linux_i2c_read: 1F (3) [55,63,70] i2c_write: 64 [BC]
linux_i2c_read: 25 (2) [62,9E] 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 float mode
tfine: 99793.535399 tfine: 98700.755011
temp: 19.490925 degC temp: 19.277491 degC
press: 96211.887865 Pa press: 96363.204250 Pa
humidity: 67.809989 % RH humidity: 68.749455 % RH
linux_i2c_deinit 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 Low air pressure due to storm not bad sensor

206
bme680.c
View File

@ -6,13 +6,14 @@
#include "bme680.h" #include "bme680.h"
#include "registers.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_temp_comp(bme680_t *bme680);
static void calc_press_comp(bme680_t *bme680); static void calc_press_comp(bme680_t *bme680);
static void calc_hum_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) { static int write_dev(bme680_t *bme680, uint8_t reg, uint8_t value) {
uint8_t tmp; 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); return bme680->dev.write(REG_STATUS, status_byte);
} }
/********************************************************************/
static int read_dev(bme680_t *bme680, uint8_t reg, uint8_t *dst, uint32_t size) { static int read_dev(bme680_t *bme680, uint8_t reg, uint8_t *dst, uint32_t size) {
uint8_t tmp; 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); return bme680->dev.read(reg, dst, size);
} }
/********************************************************************/
int bme680_init(bme680_t *bme680, uint8_t mode) { int bme680_init(bme680_t *bme680, uint8_t mode) {
uint8_t car; uint8_t car;
uint8_t id_reg; uint8_t id_reg;
int i;
bme680->mode = mode; bme680->mode = mode;
@ -71,10 +74,17 @@ int bme680_init(bme680_t *bme680, uint8_t mode) {
return 1; 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; return 0;
} }
/* for completion */ /********************************************************************/
int bme680_deinit(bme680_t *bme680) { int bme680_deinit(bme680_t *bme680) {
if (bme680->dev.deinit) { if (bme680->dev.deinit) {
bme680->dev.deinit(); bme680->dev.deinit();
@ -83,21 +93,23 @@ int bme680_deinit(bme680_t *bme680) {
return 0; return 0;
} }
/********************************************************************/
int bme680_reset(bme680_t *bme680) { int bme680_reset(bme680_t *bme680) {
uint8_t reg = BME680_IS_SPI(bme680->mode) ? 0x60 : REG_RESET; uint8_t reg = BME680_IS_SPI(bme680->mode) ? 0x60 : REG_RESET;
uint8_t magic = 0xB6; uint8_t magic = 0xB6;
int ret; int ret;
ret = write_dev(bme680, reg, magic); ret = write_dev(bme680, reg, magic);
usleep(5000); /* sleep for 5 ms */ usleep(2000); /* sleep for 5 ms */
return ret; return ret;
} }
/********************************************************************/
/* configure device */
int bme680_configure(bme680_t *bme680) {
/* configure device and trigger forced mode reading on device */ uint8_t meas, hum, filter, ctrl_gas1, ctrl_gas0, err;
int bme680_start(bme680_t *bme680) { int i;
uint8_t meas, hum, filter, err;
meas = hum = filter = err = 0; meas = hum = filter = err = 0;
/* ctrl_meas. the last 0 is ticked on to enable forced mode, /* 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; hum = bme680->cfg.osrs_h;
filter = bme680->cfg.filter << 2; 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_MEAS, meas);
err |= write_dev(bme680, REG_CTRL_HUM, hum); err |= write_dev(bme680, REG_CTRL_HUM, hum);
err |= write_dev(bme680, REG_CONFIG, filter); err |= write_dev(bme680, REG_CONFIG, filter);
// TODO: gas stuff if (!BME680_GAS_ENABLED(bme680->mode)) {
goto SKIP_GAS;
}
/* Now, re-send `meas' but LSb set to 1 to enable a forced conversion */ /* write out all 10 setpoints */
meas |= 1; /* 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); err |= write_dev(bme680, REG_CTRL_MEAS, meas);
return err; return err;
} }
/********************************************************************/
/* blocks until the device claims all scheduled conversions are done. /* blocks until the device claims all scheduled conversions are done.
* don't call out of order. * don't call out of order.
*/ */
#define BME680_MAX_POLL_ATTEMPTS 100
int bme680_poll(bme680_t *bme680) { int bme680_poll(bme680_t *bme680) {
uint8_t meas_status = 0; 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 err = 0;
int attempts = 0;
do { do {
usleep(2000); usleep(5000); /* 5 ms */
err |= read_dev(bme680, REG_EAS_STATUS, &meas_status, 1); 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) { } while ((gas_measuring || any_measuring) && !err);
err = 2;
}
return err; return err;
} }
/********************************************************************/
/* assume start'd and poll'd */ /* assume start'd and poll'd */
int bme680_read(bme680_t *bme680) { int bme680_read(bme680_t *bme680) {
@ -164,23 +208,38 @@ int bme680_read(bme680_t *bme680) {
* otherwise, it depends on the oversample settings. * otherwise, it depends on the oversample settings.
* note: humidity is not IIR filtered, and always 16-bit. * note: humidity is not IIR filtered, and always 16-bit.
* IIR filter on (any level) -> 20-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) { if (bme680->cfg.filter == BME680_IIR_COEFF_0) {
bme680->adc.temp >>= (bme680->cfg.osrs_t - 1); bme680->adc.temp >>= (bme680->cfg.osrs_t - 1);
bme680->adc.press >>= (bme680->cfg.osrs_p - 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 ..*/ /* read/convert in order ..*/
calc_temp_comp(bme680); calc_temp_comp(bme680);
calc_press_comp(bme680); calc_press_comp(bme680);
calc_hum_comp(bme680); calc_hum_comp(bme680);
if (BME680_GAS_ENABLED(bme680->mode)) {
calc_gas_res(bme680);
}
return err; 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] = { static double const_array1[16] = {
1, 1, 1, 1, 1, 0.99, 1, 0.992, 1, 1, 1, 1, 1, 1, 0.99, 1, 0.992, 1,
1, 0.998, 0.995, 1, 0.99, 1, 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; bme680->fcomp.temp = temp_comp;
} }
/********************************************************************/
static void calc_temp_comp_2 (bme680_t *bme680) { static void calc_temp_comp_2 (bme680_t *bme680) {
int32_t var1, var2, var3, temp_comp; 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; bme680->icomp.temp = temp_comp;
} }
/********************************************************************/
static void calc_temp_comp (bme680_t *bme680) { static void calc_temp_comp (bme680_t *bme680) {
if (BME680_IS_FLOAT(bme680->mode)) { if (BME680_IS_FLOAT(bme680->mode)) {
calc_temp_comp_1(bme680); calc_temp_comp_1(bme680);
@ -264,6 +325,7 @@ static void calc_press_comp_1 (bme680_t *bme680) {
bme680->fcomp.press = press_comp; bme680->fcomp.press = press_comp;
} }
/********************************************************************/
static void calc_press_comp_2 (bme680_t *bme680 ) { static void calc_press_comp_2 (bme680_t *bme680 ) {
int32_t var1, var2, var3, press_comp; 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; bme680->icomp.press = press_comp;
} }
/********************************************************************/
static void calc_press_comp (bme680_t *bme680) { static void calc_press_comp (bme680_t *bme680) {
if (BME680_IS_FLOAT(bme680->mode)) { if (BME680_IS_FLOAT(bme680->mode)) {
calc_press_comp_1(bme680); calc_press_comp_1(bme680);
@ -317,6 +380,7 @@ static void calc_hum_comp_1 (bme680_t *bme680) {
bme680->fcomp.hum = hum_comp; bme680->fcomp.hum = hum_comp;
} }
/********************************************************************/
static void calc_hum_comp_2 (bme680_t *bme680) { static void calc_hum_comp_2 (bme680_t *bme680) {
int32_t var1, var2, var3, var4, var5, var6, temp_scaled, hum_comp; 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; bme680->icomp.hum = hum_comp;
} }
/********************************************************************/
static void calc_hum_comp (bme680_t *bme680) { static void calc_hum_comp (bme680_t *bme680) {
if (BME680_IS_FLOAT(bme680->mode)) { if (BME680_IS_FLOAT(bme680->mode)) {
calc_hum_comp_1(bme680); calc_hum_comp_1(bme680);
@ -346,14 +411,12 @@ static void calc_hum_comp (bme680_t *bme680) {
} }
/********************************************************************/ /********************************************************************/
int bme680_calibrate(bme680_t *bme680) { int bme680_calibrate(bme680_t *bme680) {
uint8_t buffer[3] = {0, 0 ,0}; uint8_t buffer[3] = {0, 0 ,0};
int err = 0; int err = 0;
// start with temp params /* temperature */
err |= read_dev(bme680, 0xE9, buffer, 2); err |= read_dev(bme680, 0xE9, buffer, 2);
bme680->cal.par_t1 = (buffer[1] << 8) | buffer[0]; 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); err |= read_dev(bme680, 0x8C, buffer, 1);
bme680->cal.par_t3 = buffer[0]; bme680->cal.par_t3 = buffer[0];
// pressure
/* pressure */
err |= read_dev(bme680, 0x8E, buffer, 2); err |= read_dev(bme680, 0x8E, buffer, 2);
bme680->cal.par_p1 = (buffer[1] << 8) | buffer[0]; 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); err |= read_dev(bme680, 0x99, buffer, 1);
bme680->cal.par_p6 = buffer[0]; 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]; bme680->cal.par_p7 = buffer[0];
err |= read_dev(bme680, 0x9C, buffer, 1); err |= read_dev(bme680, 0x9C, buffer, 1);
@ -395,8 +458,8 @@ int bme680_calibrate(bme680_t *bme680) {
err |= read_dev(bme680, 0xA0, buffer, 1); err |= read_dev(bme680, 0xA0, buffer, 1);
bme680->cal.par_p10 = buffer[0]; bme680->cal.par_p10 = buffer[0];
// humidity
/* humidity */
err |= read_dev(bme680, 0xE2, buffer, 2); err |= read_dev(bme680, 0xE2, buffer, 2);
bme680->cal.par_h1 = (buffer[1] << 4) | (buffer[0] & 0xF); 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); err |= read_dev(bme680, 0xE8, buffer, 1);
bme680->cal.par_h7 = buffer[0]; bme680->cal.par_h7 = buffer[0];
// gas
/* gas */
err |= read_dev(bme680, 0xED, buffer, 1); err |= read_dev(bme680, 0xED, buffer, 1);
bme680->cal.par_g1 = buffer[0]; 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]; bme680->cal.par_g2 = (buffer[1] << 8) | buffer[0];
err |= read_dev(bme680, 0xEE, buffer, 1); 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; 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) { void bme680_print_calibration (bme680_t *bme680) {
printf("par_t1: %d\n", bme680->cal.par_t1); printf("par_t1: %d\n", bme680->cal.par_t1);
printf("par_t2: %d\n", bme680->cal.par_t2); 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_h5: %d\n", bme680->cal.par_h5);
printf("par_h6: %d\n", bme680->cal.par_h6); printf("par_h6: %d\n", bme680->cal.par_h6);
printf("par_h7: %d\n", bme680->cal.par_h7); 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);
}
}

View File

@ -5,6 +5,10 @@
#define BME680_IS_SPI(m) ((m & 1) == 1) #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 */ /* connection modes */
#define BME680_SPI 1 #define BME680_SPI 1
@ -14,6 +18,9 @@
#define BME680_MODE_INT 2 #define BME680_MODE_INT 2
#define BME680_MODE_FLOAT 0 #define BME680_MODE_FLOAT 0
/* to enable gas conversion OR this into mode byte */
#define BME680_ENABLE_GAS 4
/* config values */ /* config values */
#define BME680_OVERSAMPLE_X1 0b001 #define BME680_OVERSAMPLE_X1 0b001
#define BME680_OVERSAMPLE_X2 0b010 #define BME680_OVERSAMPLE_X2 0b010
@ -31,6 +38,12 @@
#define BME680_IIR_COEFF_63 0b110 #define BME680_IIR_COEFF_63 0b110
#define BME680_IIR_COEFF_127 0b111 #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 */ /* user supplied spi/i2c functions */
struct bme680_dev { struct bme680_dev {
@ -67,22 +80,25 @@ struct bme680_cal {
uint8_t par_h6; uint8_t par_h6;
int8_t par_h7; int8_t par_h7;
/* gas maybe */ /* heater + gas */
uint16_t par_g1; uint16_t par_g1;
uint16_t par_g2; uint16_t par_g2;
uint16_t par_g3; uint16_t par_g3;
uint16_t range_switching_error; uint16_t range_switching_error;
uint8_t res_heat_range;
int8_t res_heat_val;
}; };
struct bme680_config { struct bme680_config {
uint8_t osrs_t; uint8_t osrs_t;
uint8_t osrs_p; uint8_t osrs_p;
uint8_t osrs_h; uint8_t osrs_h;
uint8_t filter; uint8_t filter;
uint8_t heater_setpoint[10]; uint8_t setpoint;
uint8_t heater_exposure_ms[10]; uint8_t idac_heat[10];
uint8_t heater_exposure_scalar[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 { struct bme680_comp_float {
@ -90,6 +106,7 @@ struct bme680_comp_float {
double temp; double temp;
double press; double press;
double hum; double hum;
double gas_res;
}; };
struct bme680_comp_int { struct bme680_comp_int {
@ -97,13 +114,16 @@ struct bme680_comp_int {
int32_t temp; int32_t temp;
int32_t press; int32_t press;
int32_t hum; int32_t hum;
int32_t gas_res;
}; };
struct bme680_adc { struct bme680_adc {
uint32_t temp; uint32_t temp;
uint32_t press; uint32_t press;
uint32_t hum; uint32_t hum;
uint16_t gas; uint32_t gas;
uint32_t gas_range;
}; };
struct bme680 { struct bme680 {
@ -124,10 +144,12 @@ int bme680_init(bme680_t *bme680, uint8_t mode);
int bme680_deinit(bme680_t *bme680); int bme680_deinit(bme680_t *bme680);
int bme680_reset(bme680_t *bme680); int bme680_reset(bme680_t *bme680);
int bme680_calibrate(bme680_t *bme680); int bme680_calibrate(bme680_t *bme680);
int bme680_configure(bme680_t *bme680);
int bme680_start(bme680_t *bme680); int bme680_start(bme680_t *bme680);
int bme680_poll(bme680_t *bme680); int bme680_poll(bme680_t *bme680);
int bme680_read(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); void bme680_print_calibration(bme680_t *bme680);

66
main.c
View File

@ -9,6 +9,9 @@
#define DEVICE "/dev/i2c-1" #define DEVICE "/dev/i2c-1"
#define ADDRESS 0x77 #define ADDRESS 0x77
#define AMBIENT_TEMP_GUESS 25.0
#define HEATER_TARGET 320.0
int i2c_dev_fd; int i2c_dev_fd;
int linux_i2c_init (void); 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_write (uint8_t reg, uint8_t value);
int linux_i2c_deinit (void); int linux_i2c_deinit (void);
int main() { int main(void) {
bme680_t bme680; bme680_t bme680;
uint8_t mode; uint8_t mode;
int i;
/* 1. Assign functions for interacting with the device */ /* 1. Assign functions for interacting with the device */
bme680.dev.init = linux_i2c_init; bme680.dev.init = linux_i2c_init;
@ -28,7 +32,7 @@ int main() {
bme680.dev.deinit = linux_i2c_deinit; bme680.dev.deinit = linux_i2c_deinit;
/* 2. set the device mode */ /* 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; */ /* BME680_MODE_INT | BME680_SPI; */
@ -57,41 +61,81 @@ int main() {
bme680.cfg.osrs_h = BME680_OVERSAMPLE_X8; bme680.cfg.osrs_h = BME680_OVERSAMPLE_X8;
bme680.cfg.filter = BME680_IIR_COEFF_127; bme680.cfg.filter = BME680_IIR_COEFF_127;
/* 7. write config to device and set off conversion */ /* configuring gas sensor. */
if (bme680_start(&bme680) != 0) { /* NB: mode |= BME680_ENABLE_GAS; */
fprintf(stderr, "bme680_start()\n"); /* 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); bme680_deinit(&bme680);
exit(EXIT_FAILURE); 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) { if (bme680_poll(&bme680) != 0) {
fprintf(stderr, "bme680_poll()\n"); fprintf(stderr, "bme680_poll()\n");
bme680_deinit(&bme680); bme680_deinit(&bme680);
exit(EXIT_FAILURE); 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) { if (bme680_read(&bme680) != 0) {
fprintf(stderr, "bme680_read()\n"); fprintf(stderr, "bme680_read()\n");
bme680_deinit(&bme680); bme680_deinit(&bme680);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* 10. use data ! */ /* 11. use data ! */
if (BME680_IS_FLOAT(bme680.mode)) { if (BME680_IS_FLOAT(bme680.mode)) {
puts("float mode"); puts("float mode");
printf("tfine: %f\n", bme680.fcomp.tfine); printf("tfine: %f\n", bme680.fcomp.tfine);
printf("temp: %f degC\n", bme680.fcomp.temp); printf("temp: %f degC\n", bme680.fcomp.temp);
printf("press: %f Pa\n", bme680.fcomp.press); printf("press: %f Pa\n", bme680.fcomp.press);
printf("humidity: %f %% RH\n", bme680.fcomp.hum); 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 { } else {
puts("integer mode"); puts("integer mode");
printf("tfine: %d\n", bme680.icomp.tfine); 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("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); bme680_deinit(&bme680);