diff --git a/bme680.c b/bme680.c index dd73b01..742d54a 100644 --- a/bme680.c +++ b/bme680.c @@ -1,17 +1,68 @@ +#define _DEFAULT_SOURCE + +#include +#include + #include "bme680.h" #include "registers.h" +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); -int bme680_init(bme680_t *bme680) { +static int write_dev(bme680_t *bme680, uint8_t reg, uint8_t *src, uint32_t size) { + uint8_t tmp; + + if (BME680_IS_SPI(bme680->mode)) { + if ((tmp = REG_SPI_PAGE(reg)) != bme680->spi_page) { + (void) write_spi_page(bme680, tmp); + bme680->spi_page = tmp; + } + } + + return bme680->dev.write(reg, src, size); +} + +static int write_spi_page(bme680_t *bme680, uint8_t page_value) { + uint8_t status_byte = (!!page_value) << 4; + + return bme680->dev.write(REG_STATUS, &status_byte, 1); +} + +static int read_dev(bme680_t *bme680, uint8_t reg, uint8_t *dst, uint32_t size) { + + uint8_t tmp; + + /* Might have to change the SPI page for this reg if using SPI */ + if (BME680_IS_SPI(bme680->mode)) { + if ((tmp = REG_SPI_PAGE(reg)) != bme680->spi_page) { + (void) write_spi_page(bme680, tmp); + bme680->spi_page = tmp; + } + } + + /* some registers, like id and reset, have diff addr in i2c/spi */ + return bme680->dev.read(reg, dst, size); +} + +int bme680_init(bme680_t *bme680, uint8_t mode) { uint8_t car; + uint8_t id_reg; + + bme680->mode = mode; if (bme680->dev.init() != 0) { return 1; } - if (bme680->dev.read(REG_ID, &car, 1) != 0) { + /* id reg depends on spi or i2c */ + id_reg = BME680_IS_SPI(mode) ? 0x50 : REG_ID; + + if (read_dev(bme680, id_reg, &car, 1) != 0) { return 1; } @@ -22,6 +73,112 @@ int bme680_init(bme680_t *bme680) { return 0; } +/* for completion */ +int bme680_deinit(bme680_t *bme680) { + if (bme680->dev.deinit) { + bme680->dev.deinit(); + } + + 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, 1); + usleep(5000); /* sleep for 5 ms */ + return ret; +} + + +/* configure device and trigger forced mode reading on device */ +int bme680_start(bme680_t *bme680) { + + uint8_t meas, hum, filter, err; + meas = hum = filter = err = 0; + + /* ctrl_meas. the last 0 is ticked on to enable forced mode, + * but the config has to be written fist. strange behaviour. + */ + meas = (bme680->cfg.osrs_t) << 5 | (bme680->cfg.osrs_p) << 2; + hum = bme680->cfg.osrs_h; + filter = (bme680->cfg.filter) << 2; + + err |= write_dev(bme680, REG_CTRL_MEAS, &meas, 1); + err |= write_dev(bme680, REG_CTRL_HUM, &hum, 1); + err |= write_dev(bme680, REG_CONFIG, &filter, 1); + + // 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, 1); + + 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; + int err = 0; + int attempts = 0; + + do { + usleep(2000); + err |= read_dev(bme680, REG_EAS_STATUS, &meas_status, 1); + } while (((meas_status >> 5) & 1) && attempts++ < BME680_MAX_POLL_ATTEMPTS && !err); + + if (attempts == BME680_MAX_POLL_ATTEMPTS) { + err = 1; + } + + return err; +} + +/* assume start'd and poll'd */ +int bme680_read(bme680_t *bme680) { + + /* begin by reading ADCs */ + uint8_t buffer[3] = {0, 0 ,0}; + int err = 0; + + err |= read_dev(bme680, 0x22, buffer, 3); + bme680->adc.temp = (buffer[0] << 12) | (buffer[1] << 4) | (buffer[2] >> 4); + + err |= read_dev(bme680, 0x1F, buffer, 3); + bme680->adc.press = (buffer[0] << 12) | (buffer[1] << 4) | (buffer[2] >> 4); + + err |= read_dev(bme680, 0x25, buffer, 2); + bme680->adc.hum = (buffer[0] << 8) | buffer[1]; + + /* adc readings are only 20-bit when the IIR filter is enabled. + * 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);? + * */ + 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/convert in order ..*/ + calc_temp_comp(bme680); + calc_press_comp(bme680); + calc_hum_comp(bme680); + + return err; +} + +/********************************************************************/ static double const_array1[16] = { 1, 1, 1, 1, 1, 0.99, 1, 0.992, 1, @@ -47,7 +204,8 @@ static int const_array2_int[16] = { 2000000, 1000000, 500000, 250000, 125000 }; - +/********************************************************************/ +/********************************************************************/ static void calc_temp_comp_1 (bme680_t *bme680) { double var1, var2, temp_comp; @@ -74,6 +232,16 @@ 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); + } else { + calc_temp_comp_2(bme680); + } +} + +/********************************************************************/ +/********************************************************************/ static void calc_press_comp_1 (bme680_t *bme680) { double var1, var2, var3, press_comp; @@ -95,7 +263,6 @@ 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; @@ -121,9 +288,18 @@ static void calc_press_comp_2 (bme680_t *bme680 ) { (int32_t)(press_comp >> 8) * (int32_t)bme680->cal.par_p10) >> 17; press_comp = (int32_t)(press_comp) + ((var1 + var2 + var3 + ((int32_t)bme680->cal.par_p7 << 7)) >> 4); bme680->icomp.press = press_comp; - } +static void calc_press_comp (bme680_t *bme680) { + if (BME680_IS_FLOAT(bme680->mode)) { + calc_press_comp_1(bme680); + } else { + calc_press_comp_2(bme680); + } +} + +/********************************************************************/ +/********************************************************************/ static void calc_hum_comp_1 (bme680_t *bme680) { double var1, var2, var3, var4, hum_comp, temp_comp; @@ -140,7 +316,6 @@ 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; @@ -161,6 +336,15 @@ 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); + } else { + calc_hum_comp_2(bme680); + } +} + +/********************************************************************/ int bme680_calibrate(bme680_t *bme680) { @@ -169,79 +353,79 @@ int bme680_calibrate(bme680_t *bme680) { // start with temp params - err |= bme680->dev.read(0xE9, buffer, 2); + err |= read_dev(bme680, 0xE9, buffer, 2); bme680->cal.par_t1 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0x8A, buffer, 2); + err |= read_dev(bme680, 0x8A, buffer, 2); bme680->cal.par_t2 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0x8C, buffer, 1); + err |= read_dev(bme680, 0x8C, buffer, 1); bme680->cal.par_t3 = buffer[0]; // pressure - err |= bme680->dev.read(0x8E, buffer, 2); + err |= read_dev(bme680, 0x8E, buffer, 2); bme680->cal.par_p1 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0x90, buffer, 2); + err |= read_dev(bme680, 0x90, buffer, 2); bme680->cal.par_p2 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0x92, buffer, 1); + err |= read_dev(bme680, 0x92, buffer, 1); bme680->cal.par_p3 = buffer[0]; - err |= bme680->dev.read(0x94, buffer, 2); + err |= read_dev(bme680, 0x94, buffer, 2); bme680->cal.par_p4 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0x96, buffer, 2); + err |= read_dev(bme680, 0x96, buffer, 2); bme680->cal.par_p5 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0x99, buffer, 1); + err |= read_dev(bme680, 0x99, buffer, 1); bme680->cal.par_p6 = buffer[0]; - err |= bme680->dev.read(0x98, buffer, 1); // strange order + err |= read_dev(bme680, 0x98, buffer, 1); // strange order bme680->cal.par_p7 = buffer[0]; - err |= bme680->dev.read(0x9C, buffer, 1); + err |= read_dev(bme680, 0x9C, buffer, 1); bme680->cal.par_p8 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0x9E, buffer, 2); + err |= read_dev(bme680, 0x9E, buffer, 2); bme680->cal.par_p9 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0xA0, buffer, 1); + err |= read_dev(bme680, 0xA0, buffer, 1); bme680->cal.par_p10 = buffer[0]; // humidity - err |= bme680->dev.read(0xE2, buffer, 2); + err |= read_dev(bme680, 0xE2, buffer, 2); bme680->cal.par_h1 = (buffer[1] << 4) | (buffer[0] & 0xF); - err |= bme680->dev.read(0xE1, buffer, 2); + err |= read_dev(bme680, 0xE1, buffer, 2); bme680->cal.par_h2 = (buffer[0] << 4) | ((buffer[1] >> 4) & 0xF); - err |= bme680->dev.read(0xE4, buffer, 1); + err |= read_dev(bme680, 0xE4, buffer, 1); bme680->cal.par_h3 = buffer[0]; - err |= bme680->dev.read(0xE5, buffer, 1); + err |= read_dev(bme680, 0xE5, buffer, 1); bme680->cal.par_h4 = buffer[0]; - err |= bme680->dev.read(0xE6, buffer, 1); + err |= read_dev(bme680, 0xE6, buffer, 1); bme680->cal.par_h5 = buffer[0]; - err |= bme680->dev.read(0xE7, buffer, 1); + err |= read_dev(bme680, 0xE7, buffer, 1); bme680->cal.par_h6 = buffer[0]; - err |= bme680->dev.read(0xE8, buffer, 1); + err |= read_dev(bme680, 0xE8, buffer, 1); bme680->cal.par_h7 = buffer[0]; // gas - err |= bme680->dev.read(0xED, buffer, 1); + err |= read_dev(bme680, 0xED, buffer, 1); bme680->cal.par_g1 = buffer[0]; - err |= bme680->dev.read(0xEB, buffer, 2); + err |= read_dev(bme680, 0xEB, buffer, 2); bme680->cal.par_g2 = (buffer[1] << 8) | buffer[0]; - err |= bme680->dev.read(0xEE, buffer, 1); + err |= read_dev(bme680, 0xEE, buffer, 1); bme680->cal.par_g2 = buffer[0]; // todo more @@ -251,28 +435,30 @@ int bme680_calibrate(bme680_t *bme680) { return err; } -/* -void print_calibration ( bme680_calibration *cal ) { - printf("par_t1: %d\n", cal->par_t1); - printf("par_t2: %d\n", cal->par_t2); - printf("par_t3: %d\n", cal->par_t3); - printf("par_p1: %d\n", cal->par_p1); - printf("par_p2: %d\n", cal->par_p2); - printf("par_p3: %d\n", cal->par_p3); - printf("par_p4: %d\n", cal->par_p4); - printf("par_p5: %d\n", cal->par_p5); - printf("par_p6: %d\n", cal->par_p6); - printf("par_p7: %d\n", cal->par_p7); - printf("par_p8: %d\n", cal->par_p8); - printf("par_p9: %d\n", cal->par_p9); - printf("par_p10: %d\n", cal->par_p10); - printf("par_h1: %d\n", cal->par_h1); - printf("par_h2: %d\n", cal->par_h2); - printf("par_h3: %d\n", cal->par_h3); - printf("par_h4: %d\n", cal->par_h4); - printf("par_h5: %d\n", cal->par_h5); - printf("par_h6: %d\n", cal->par_h6); - printf("par_h7: %d\n", cal->par_h7); -} */ + +/********************************************************************/ + +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); + printf("par_t3: %d\n", bme680->cal.par_t3); + printf("par_p1: %d\n", bme680->cal.par_p1); + printf("par_p2: %d\n", bme680->cal.par_p2); + printf("par_p3: %d\n", bme680->cal.par_p3); + printf("par_p4: %d\n", bme680->cal.par_p4); + printf("par_p5: %d\n", bme680->cal.par_p5); + printf("par_p6: %d\n", bme680->cal.par_p6); + printf("par_p7: %d\n", bme680->cal.par_p7); + printf("par_p8: %d\n", bme680->cal.par_p8); + printf("par_p9: %d\n", bme680->cal.par_p9); + printf("par_p10: %d\n", bme680->cal.par_p10); + printf("par_h1: %d\n", bme680->cal.par_h1); + printf("par_h2: %d\n", bme680->cal.par_h2); + printf("par_h3: %d\n", bme680->cal.par_h3); + printf("par_h4: %d\n", bme680->cal.par_h4); + 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); +} diff --git a/bme680.h b/bme680.h index a89081a..50ff679 100644 --- a/bme680.h +++ b/bme680.h @@ -3,6 +3,9 @@ #include +#define BME680_IS_SPI(m) ((m & 1) == 1) +#define BME680_IS_FLOAT(m) (((m >> 1) & 1) == 0) + /* connection modes */ #define BME680_SPI 1 #define BME680_I2C 0 @@ -79,7 +82,7 @@ struct bme680_config { uint8_t osrs_t; uint8_t osrs_p; uint8_t osrs_h; - uint8_t fir; + uint8_t filter; uint8_t heater_setpoint[10]; uint8_t heater_exposure_ms[10]; uint8_t heater_exposure_scalar[10]; @@ -114,15 +117,21 @@ struct bme680 { struct bme680_config cfg; struct bme680_adc adc; uint8_t mode; + uint8_t spi_page; }; typedef struct bme680 bme680_t; -int bme680_init(bme680_t *bme680); +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_start(bme680_t *bme680); +int bme680_poll(bme680_t *bme680); +int bme680_read(bme680_t *bme680); - +void bme680_print_calibration(bme680_t *bme680); #endif diff --git a/main.c b/main.c index 83c8f0c..85ad7b1 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,3 @@ -/* usleep() */ -#define _DEFAULT_SOURCE #include #include @@ -21,39 +19,90 @@ int linux_i2c_deinit (void); int main(){ bme680_t bme680; + uint8_t mode; /* 1. Assign functions for interacting with the device */ - bme680.dev.init = linux_i2c_init; - bme680.dev.read = linux_i2c_read; - bme680.dev.write = linux_i2c_write; + bme680.dev.init = linux_i2c_init; + bme680.dev.read = linux_i2c_read; + bme680.dev.write = linux_i2c_write; bme680.dev.deinit = linux_i2c_deinit; bme680.dev.xfer = NULL; /* 2. set the device mode */ - bme680.mode = BME680_MODE_FLOAT | BME680_I2C; - /* BME680_MODE_INT | BME680_SPI; */ + mode = BME680_MODE_FLOAT | BME680_I2C; + /* BME680_MODE_INT | BME680_SPI; */ /* 3. initialise device, and check its id */ - if (bme680_init(&bme680) != 0) { + if (bme680_init(&bme680, mode) != 0) { fprintf(stderr, "bme680_init()\n"); exit(EXIT_FAILURE); } - /* 4. read calibration parameters from the device and store in memory */ + /* 4. reset */ + bme680_reset(&bme680); + + /* 5. read calibration parameters from the device and store in memory */ if (bme680_calibrate(&bme680) != 0) { fprintf(stderr, "bme680_calibrate()\n"); - bme680.dev.deinit(); + bme680_deinit(&bme680); exit(EXIT_FAILURE); } - /* 5. set up device config */ + /* debug */ + bme680_print_calibration(&bme680); + + + /* 6. set up device config */ bme680.cfg.osrs_t = BME680_OVERSAMPLE_16X; - bme680.cfg.osrs_p = BME680_OVERSAMPLE_8X; - bme680.cfg.osrs_h = BME680_OVERSAMPLE_4X; + bme680.cfg.osrs_p = BME680_OVERSAMPLE_16X; + bme680.cfg.osrs_h = BME680_OVERSAMPLE_8X; + bme680.cfg.filter = BME680_IIR_COEFF_63; + /* 7. write config to device and set off conversion */ + if (bme680_start(&bme680) != 0) { + fprintf(stderr, "bme680_start()\n"); + bme680_deinit(&bme680); + exit(EXIT_FAILURE); + } + /* 8. 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 */ + if (bme680_read(&bme680) != 0) { + fprintf(stderr, "bme680_read()\n"); + bme680_deinit(&bme680); + exit(EXIT_FAILURE); + } + + printf("adc_temp: %d\n", bme680.adc.temp); + printf("adc_press: %d\n", bme680.adc.press); + printf("adc_hum: %d\n", bme680.adc.hum); + + /* 10. 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); + } else { + puts("integer mode"); + printf("tfine: %d\n", bme680.icomp.tfine); + printf("temp: %d\n", bme680.icomp.temp); + printf("press: %d Pa\n", bme680.icomp.press); + printf("humidity: %d %% RH\n", bme680.icomp.hum); + } + + bme680_deinit(&bme680); return 0; } @@ -72,24 +121,50 @@ int linux_i2c_init (void) { } int linux_i2c_read (uint8_t reg, uint8_t *dst, uint32_t size) { - puts("linux_i2c_read"); - if (i2c_read_reg(i2c_dev_fd, reg, (uint8_t)size, dst) != 0) { - printf("panic read: %d %p %d\n", reg, dst, size); - return 1; + + int i; + int ret; + + printf("linux_i2c_read: %X (%d) [", reg, size); + + ret = i2c_read_reg(i2c_dev_fd, reg, (uint8_t)size, dst); + if (ret != 0) { + return -1; } + + for(i=0; i