Gas sensor resistance
This commit is contained in:
parent
9a0b274711
commit
d5d504d1ad
199
README.md
199
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
|
||||
|
206
bme680.c
206
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
|
||||
if (!BME680_GAS_ENABLED(bme680->mode)) {
|
||||
goto SKIP_GAS;
|
||||
}
|
||||
|
||||
/* Now, re-send `meas' but LSb set to 1 to enable a forced conversion */
|
||||
meas |= 1;
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
34
bme680.h
34
bme680.h
@ -5,6 +5,10 @@
|
||||
|
||||
#define BME680_IS_SPI(m) ((m & 1) == 1)
|
||||
#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);
|
||||
|
||||
|
66
main.c
66
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);
|
||||
|
Loading…
Reference in New Issue
Block a user