diff --git a/dev/bme280/README.bme280 b/dev/bme280/README.bme280 new file mode 100644 index 000000000..7ce35cb1d --- /dev/null +++ b/dev/bme280/README.bme280 @@ -0,0 +1,26 @@ + +Contiki implementation for Bosch Sensortec BME280 sensor. +BME280 is compact and fast and includes temp, RH, and pressure. +Chip uses I2C or SPI. This implementation and follow Contiki +device API and I2C. + +For better performance, less chip warm up and less I2C transactions +burst read is recommended. Here all T/RH/P is read in one single +I2C read and kept the measurements have the same time. + +The burst read is stored in struct bme280_mea which accessible from +the Contiki. Also note that the full chip resolution is available. +The variables are overscaled to give the app full controlover resolution. + +See bme280.h + +The pressure can be calculated with 32 or 64. The define BME280_64BIT +controls this. Typically in your project-conf.h + +Implemented according to datasheet Rev 1.1. + +Limitations: +Implementation implements Weather Mode which uses forced one-shot +mode no oversampling nor filters and reads all T/RH/P in one read. + + diff --git a/dev/bme280/bme280-sensor.c b/dev/bme280/bme280-sensor.c new file mode 100644 index 000000000..0a15cfaa4 --- /dev/null +++ b/dev/bme280/bme280-sensor.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, Copyright Robert Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * + * Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com + * Created : 2016-09-14 + */ + +#include +#include "contiki.h" +#include "lib/sensors.h" +#include "dev/bme280/bme280.h" +#include "dev/bme280/bme280-sensor.h" + +const struct sensors_sensor bme280_sensor; + +static int +value(int type) +{ + + /* Read all measurements with one burst read */ + bme280_read(BME280_MODE_WEATHER); + + /* Return a la Contiki API */ + switch(type) { + + case BME280_SENSOR_TEMP: + return bme280_mea.t_overscale100 / 100; + + case BME280_SENSOR_HUMIDITY: + return bme280_mea.h_overscale1024>>10; + + case BME280_SENSOR_PRESSURE: + /* Scale down w. 10 not to overslow the signed int */ +#ifdef BME280_64BIT + return bme280_mea.p_overscale256/(256*10); +#else + return bme280_mea.p/10; +#endif + } + return 0; +} + +static int +status(int type) +{ + return 0; +} + +static int +configure(int type, int c) +{ + bme280_init(BME280_MODE_WEATHER); +} +SENSORS_SENSOR(bme280_sensor, "bme280", value, configure, status); diff --git a/dev/bme280/bme280-sensor.h b/dev/bme280/bme280-sensor.h new file mode 100644 index 000000000..92dfae0d1 --- /dev/null +++ b/dev/bme280/bme280-sensor.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Copyright Robert Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * + * Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com + * Created : 2016-09-14 + */ + +#ifndef BME280_SENSOR_H_ +#define BME280_SENSOR_H_ + +#include "lib/sensors.h" +#include "lib/sensors.h" +#include "bme280.h" + +extern const struct sensors_sensor bme280_sensor; + +#define BME280_SENSOR_TEMP 0 +#define BME280_SENSOR_HUMIDITY 1 +#define BME280_SENSOR_PRESSURE 2 + +#endif /* BME280_SENSOR_H_ */ diff --git a/dev/bme280/bme280.c b/dev/bme280/bme280.c new file mode 100644 index 000000000..7377acaf6 --- /dev/null +++ b/dev/bme280/bme280.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2015, Copyright Robert Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * + * Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com + * Created : 2016-09-14 + */ + +/** + * \file + * Basic functions for Bosch BME280 based on datasheel Rev 1.1 + */ + +#include "contiki.h" +#include +#include +#include +#include +#include +#include "bme280.h" +#include "dev/bme280-arch.h" + +static struct { + unsigned short dig_t1; + signed short dig_t2; + signed short dig_t3; + unsigned short dig_p1; + signed short dig_p2; + signed short dig_p3; + signed short dig_p4; + signed short dig_p5; + signed short dig_p6; + signed short dig_p7; + signed short dig_p8; + signed short dig_p9; + unsigned char dig_h1; + signed short dig_h2; + unsigned char dig_h3; + signed short dig_h4; + signed short dig_h5; + signed char dig_h6; + int32_t t_fine; + uint8_t mode; +} bm; + +int32_t +bme280_t_overscale100(int32_t ut) +{ + int32_t v1, v2, t; + + v1 = ((((ut >> 3) - ((int32_t)bm.dig_t1 << 1))) * + ((int32_t)bm.dig_t2)) >> 11; + + v2 = (((((ut >> 4) - ((int32_t)bm.dig_t1)) * ((ut >> 4) - + ((int32_t)bm.dig_t1))) >> 12) * ((int32_t)bm.dig_t3)) >> 14; + + bm.t_fine = v1 + v2; + t = (bm.t_fine * 5 + 128) >> 8; + return t; +} +#ifdef BME280_32BIT +static uint32_t +bme280_p(int32_t up) +{ + int32_t v1, v2; + uint32_t p; + + v1 = (((int32_t)bm.t_fine) >> 1) - (int32_t)64000; + v2 = (((v1 >> 2) * (v1 >> 2)) >> 11) * ((int32_t)bm.dig_p6); + v2 = v2 + ((v1 * ((int32_t)bm.dig_p5)) << 1); + v2 = (v2 >> 2) + (((int32_t)bm.dig_p4) << 16); + + v1 = (((bm.dig_p3 * (((v1 >> 2) * (v1 >> 2)) >> 13)) >> 3) + + ((((int32_t)bm.dig_p2) * v1) >> 1)) >> 18; + + v1 = ((((32768 + v1)) * ((int32_t)bm.dig_p1)) >> 15); + + if(v1 == 0) { + return 0; + } + + p = (((uint32_t)(((int32_t)1048576) - up) - (v2 >> 12))) * 3125; + + if(p < 0x80000000) { + p = (p << 1) / ((uint32_t)v1); + } else { + p = (p / (uint32_t)v1) * 2; + } + + v1 = (((int32_t)bm.dig_p9) * ((int32_t)(((p >> 3) * (p >> 3)) >> 13))) >> 12; + v2 = (((int32_t)(p >> 2)) * ((int32_t)bm.dig_p8)) >> 13; + p = (uint32_t)((int32_t)p + ((v1 + v2 + bm.dig_p7) >> 4)); + return p; +} +#else + +static uint32_t +bme280_p_overscale256(int32_t up) +{ + int64_t v1, v2, p; + + v1 = ((int64_t)bm.t_fine) - 128000; + v2 = v1 * v1 * (int64_t)bm.dig_p6; + v2 = v2 + ((v1 * (int64_t)bm.dig_p5) << 17); + v2 = v2 + (((int64_t)bm.dig_p4) << 35); + v1 = ((v1 * v1 * (int64_t)bm.dig_p3) >> 8) + ((v1 * (int64_t)bm.dig_p2) << 12); + v1 = (((((int64_t)1) << 47) + v1)) * ((int64_t)bm.dig_p1) >> 33; + + if(v1 == 0) { + return 0; + } + + p = 1048576 - up; + p = (((p << 31) - v2) * 3125) / v1; + v1 = (((int64_t)bm.dig_p9) * (p >> 13) * (p >> 13)) >> 25; + v2 = (((int64_t)bm.dig_p8) * p) >> 19; + p = (((p + v1 + v2) >> 8) + (((int64_t)bm.dig_p7) << 4)); + return (uint32_t)p; +} +#endif + +static uint32_t +bme280_h_overscale1024(int32_t uh) +{ + int32_t v1; + v1 = (bm.t_fine - ((int32_t)76800)); + v1 = (((((uh << 14) - (((int32_t)bm.dig_h4) << 20) - (((int32_t)bm.dig_h5) * v1)) + ((int32_t)16384)) >> 15) + * (((((((v1 * ((int32_t)bm.dig_h6)) >> 10) * (((v1 * ((int32_t)bm.dig_h3)) >> 11) + ((int32_t)32768))) + >> 10) + ((int32_t)2097152)) * ((int32_t)bm.dig_h2) + 8192) >> 14)); + v1 = (v1 - (((((v1 >> 15) * (v1 >> 15)) >> 7) * ((int32_t)bm.dig_h1)) >> 4)); + v1 = (v1 < 0 ? 0 : v1); + v1 = (v1 > 419430400 ? 419430400 : v1); + return (uint32_t)(v1 >> 12); +} +void +bme280_init(uint8_t mode) +{ + uint8_t buf[26]; + + /* Do not mess with other chips */ + i2c_read_mem(BME280_ADDR, 0xD0, buf, 1); + if(buf[0] != BME280_CHIP_ID) { + return; + } + + i2c_write_mem(BME280_ADDR, BME280_CNTL_RESET, 0xB6); + clock_delay_msec(BME280_MAX_WAIT); + + memset(buf, 0, sizeof(buf)); + + /* Burst read of all calibration part 1 */ + i2c_read_mem(BME280_ADDR, BME280_DIG_T1_ADDR, buf, sizeof(buf)); + bm.dig_t1 = ((uint16_t)buf[1] << 8) | (uint16_t)buf[0]; + bm.dig_t2 = ((int16_t)buf[3] << 8) | (uint16_t)buf[2]; + bm.dig_t3 = ((int16_t)buf[5] << 8) | (uint16_t)buf[4]; + bm.dig_p1 = ((uint16_t)buf[7] << 8) | (uint16_t)buf[6]; + bm.dig_p2 = ((int16_t)buf[9] << 8) | (uint16_t)buf[8]; + bm.dig_p3 = ((int16_t)buf[11] << 8) | (uint16_t)buf[10]; + bm.dig_p4 = ((int16_t)buf[13] << 8) | (uint16_t)buf[12]; + bm.dig_p5 = ((int16_t)buf[15] << 8) | (uint16_t)buf[14]; + bm.dig_p6 = ((int16_t)buf[17] << 8) | (uint16_t)buf[16]; + bm.dig_p7 = ((int16_t)buf[19] << 8) | (uint16_t)buf[18]; + bm.dig_p8 = ((int16_t)buf[21] << 8) | (uint16_t)buf[20]; + bm.dig_p9 = ((int16_t)buf[23] << 8) | (uint16_t)buf[22]; + /* A0 not used */ + bm.dig_h1 = (unsigned char)buf[25]; + + /* Burst read of all calibration part 2 */ + i2c_read_mem(BME280_ADDR, BME280_DIG_H2_ADDR, buf, 8); + bm.dig_h2 = ((int16_t)buf[1] << 8) | (uint16_t)buf[0]; + bm.dig_h3 = (unsigned char)buf[2]; + bm.dig_h4 = ((int16_t)buf[3] << 4) | (((uint16_t)buf[4]) & 0xF); + bm.dig_h5 = ((int16_t)buf[6] << 4) | (((uint16_t)buf[5]) & 0xF); + bm.dig_h6 = (unsigned char)buf[7]; + + bm.mode = mode; +} +void +bme280_read(uint8_t mode) +{ + int32_t ut, uh, up; + uint8_t buf[8], sleep; + uint16_t i; + memset(buf, 0, sizeof(buf)); + + /* Are we initilized and in the right mode? */ + if(mode == BME280_MODE_NONE || mode != bm.mode) { + return; + } + + ut = uh = up = 0; + + /* Weather mode. See sectiom 3.5 Datasheet */ + if(mode == BME280_MODE_WEATHER) { + /* Humidity oversampling *1 */ + i2c_write_mem(BME280_ADDR, BME280_CNTL_HUM, 0x01); + + /* 00100111 0x27 oversampling *1 for t and p plus normal mode */ + /* 0.5 ms -- no filter -- no SPI */ + i2c_write_mem(BME280_ADDR, BME280_CONTROL, 0x00); + + /* 00100110 0x26 oversampling *1 for t and p plus forced mode */ + /* Trigger measurement needed for every time in forced mode */ + i2c_write_mem(BME280_ADDR, BME280_CNTL_MEAS, 0x26); + /* Wait to get into sleep mode == measurement done */ + for(i = 0; i < BME280_MAX_WAIT; i++) { + i2c_read_mem(BME280_ADDR, BME280_CNTL_MEAS, &sleep, 1); + sleep = sleep& 0x03; + if(sleep== 0) { + break; + } else { + clock_delay_msec(1); + } + } + if(i == BME280_MAX_WAIT) { + return; /* error wait*/ + } + } else { /* if(mode == BME280_MODE_WEATHER) */ + return; /* error mode*/ + } + + /* Burst read of all measurements */ + i2c_read_mem(BME280_ADDR, BME280_PRESS, buf, 8); + ut = (uint32_t)(buf[3]) << 12 | (uint32_t)(buf[4]) << 4 | (uint32_t)buf[5] >> 4; + up = (uint32_t)(buf[0]) << 12 | (uint32_t)(buf[1]) << 4 | (uint32_t)buf[2] >> 4; + uh = (uint32_t)(buf[6]) << 8 | (uint32_t)buf[7]; + + bme280_mea.t_overscale100 = bme280_t_overscale100(ut); + bme280_mea.h_overscale1024 = bme280_h_overscale1024(uh); +#ifdef BME280_64BIT + bme280_mea.p_overscale256 = bme280_p_overscale256(up); +#else + bme280_mea.p = bme280_p(up); +#endif + +#if TEST + printf("T_BME280=%5.2f", (double)bme280_mea.t_overscale100 / 100.); + printf(" RH_BME280=%5.2f ", (double)bme280_mea.h_overscale1024 / 1024.); +#ifdef BME280_64BIT + printf(" P_BME280=%5.2f\n", (double)bme280_mea.p_overscale256 / 256.); +#else + printf(" P_BME280=%5.2f\n", (double)bme280_mea.p); +#endif +#endif +} diff --git a/dev/bme280/bme280.h b/dev/bme280/bme280.h new file mode 100644 index 000000000..9486738c3 --- /dev/null +++ b/dev/bme280/bme280.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, Copyright Robert Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * + * Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com + * + * Created : 2016-09-14 + */ + +/** + * \file + * Definitions for the Bosch BME280 based on datasheel Rev 1.1 + */ + +#ifndef BME280_H +#define BME280_H + +#ifndef BME280_32BIT +#define BME280_64BIT +#endif + +void bme280_init(uint8_t mode); +void bme280_read(uint8_t mode); + +#define BME280_ADDR (0x77 << 1) /* Alternative 0x76 */ + +/* Diffrent BOSCH chip id's */ +#define BMP085_CHIP_ID 0x55 /* And also BMP180 */ +#define BMP280_CHIP_ID 0x58 +#define BME280_CHIP_ID 0x60 + +/* Address map */ +#define BME280_DIG_T1_ADDR 0x88 +#define BME280_DIG_T2_ADDR 0x8A +#define BME280_DIG_T3_ADDR 0x8C +#define BME280_DIG_P1_ADDR 0x8E +#define BME280_DIG_P2_ADDR 0x90 +#define BME280_DIG_P3_ADDR 0x92 +#define BME280_DIG_P4_ADDR 0x94 +#define BME280_DIG_P5_ADDR 0x96 +#define BME280_DIG_P6_ADDR 0x98 +#define BME280_DIG_P7_ADDR 0x9A +#define BME280_DIG_P8_ADDR 0x9C +#define BME280_DIG_P9_ADDR 0x9E +#define BME280_DIG_H1_ADDR 0xA1 +#define BMP_CHIP_ID_ADDR 0xD0 +#define BME280_CNTL_RESET 0xE0 +#define BME280_DIG_H2_ADDR 0xE1 +#define BME280_DIG_H3_ADDR 0xE3 +#define BME280_DIG_H4_ADDR 0xE4 +#define BME280_DIG_H5_ADDR 0xE5 +#define BME280_DIG_H6_ADDR 0xE7 +#define BME280_CNTL_HUM 0xF2 +#define BME280_STATUS 0xF3 +#define BME280_CNTL_MEAS 0xF4 +#define BME280_CONTROL 0xF5 +#define BME280_PRESS 0xF7 + +/* Function modes outlined in datasheet */ +#define BME280_MODE_NONE 0 +#define BME280_MODE_WEATHER 1 +#define BME280_MODE_HUMIDITY 2 +#define BME280_MODE_INDOOR_NAVIGATION 3 +#define BME280_MODE_GAMING 4 + +#define BME280_MAX_WAIT 300 /* ms. Forced mode max wait */ +#define BME280_STARTUP_TIME 2 /* ms */ + +struct { + int32_t t_overscale100; + uint32_t h_overscale1024; +#ifdef BME280_64BIT + uint32_t p_overscale256; +#else + uint32_t p; +#endif +} bme280_mea; + +#endif /* BME280_H */