From c4a506f5a3e17779e27800c8f0993894a6672b13 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Fri, 10 Mar 2017 09:57:09 -0700 Subject: [PATCH] bmp280/bme280: C port, C++ wraps C Some private methods (relating to calibration/compensation) are no longer exposed. In addition, the driver auto-detects the chip (BMP280 or BME280) and acts accordingly, rather than requiring the specification of a chip id in the ctor. The getHumidity() method no longer accepts an arguement representing pressure at sea level. A new method is provided to specify this. Signed-off-by: Jon Trulson --- docs/apichanges.md | 5 + examples/c++/bme280.cxx | 2 +- examples/c++/bmp280.cxx | 6 +- examples/c/CMakeLists.txt | 2 + examples/c/bme280.c | 89 +++++ examples/c/bmp280.c | 86 ++++ src/bmp280/CMakeLists.txt | 14 +- src/bmp280/bme280.cxx | 132 +----- src/bmp280/bme280.hpp | 259 ++++-------- src/bmp280/bmp280.c | 778 ++++++++++++++++++++++++++++++++++++ src/bmp280/bmp280.cxx | 513 +++--------------------- src/bmp280/bmp280.h | 346 ++++++++++++++++ src/bmp280/bmp280.hpp | 660 ++++++++++-------------------- src/bmp280/bmp280_regs.h | 290 ++++++++++++++ src/bmp280/javaupm_bmp280.i | 6 +- src/bmp280/jsupm_bmp280.i | 2 + src/bmp280/pyupm_bmp280.i | 2 + 17 files changed, 1977 insertions(+), 1215 deletions(-) create mode 100644 examples/c/bme280.c create mode 100644 examples/c/bmp280.c create mode 100644 src/bmp280/bmp280.c create mode 100644 src/bmp280/bmp280.h create mode 100644 src/bmp280/bmp280_regs.h diff --git a/docs/apichanges.md b/docs/apichanges.md index cbe56bba..21673222 100644 --- a/docs/apichanges.md +++ b/docs/apichanges.md @@ -6,6 +6,11 @@ compatibility between releases: # current master + * **bmp280/bme280** Some private methods are no longer exposed + (such as the calibration and compensation routines). In addition, + the *getHumidity()* method no longer accepts an argument representing + pressure at sea level. A separate method is provided to set this now. + * **bno055** This module no longer uses std::strings to pass around binary data (*read/writeCalibrationData()*). Rather, now *std::vectors* of the appropriate type are used. In addition, methods that previously diff --git a/examples/c++/bme280.cxx b/examples/c++/bme280.cxx index 12df7a9a..23fdfb04 100644 --- a/examples/c++/bme280.cxx +++ b/examples/c++/bme280.cxx @@ -31,7 +31,7 @@ using namespace std; using namespace upm; -int shouldRun = true; +bool shouldRun = true; void sig_handler(int signo) { diff --git a/examples/c++/bmp280.cxx b/examples/c++/bmp280.cxx index 101159e3..334edb95 100644 --- a/examples/c++/bmp280.cxx +++ b/examples/c++/bmp280.cxx @@ -1,6 +1,8 @@ /* * Author: Jon Trulson - * Copyright (c) 2016 Intel Corporation. + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -31,7 +33,7 @@ using namespace std; using namespace upm; -int shouldRun = true; +bool shouldRun = true; void sig_handler(int signo) { diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 459a5a15..ba09e492 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -150,6 +150,7 @@ add_example (wfs) add_example (enc03r) add_example (nunchuck) add_example (bno055) +add_example (bmp280) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) @@ -161,3 +162,4 @@ add_custom_example (button_intr-example-c button_intr.c button) add_custom_example (mcp2515-txrx-example-c mcp2515-txrx.c mcp2515) add_custom_example (le910-example-c le910.c uartat) add_custom_example (speaker_pwm-example-c speaker_pwm.c speaker) +add_custom_example (bme280-example-c bme280.c bmp280) diff --git a/examples/c/bme280.c b/examples/c/bme280.c new file mode 100644 index 00000000..3f3b923f --- /dev/null +++ b/examples/c/bme280.c @@ -0,0 +1,89 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "upm_utilities.h" +#include "bmp280.h" + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); +//! [Interesting] + + // Instantiate a BME280 instance using default i2c bus and + // address. We use the BMP280 driver to do all of our work, since + // the BMP280 and the BME280 are identical except for the fact + // that the BME280 includes a humidity sensor. + bmp280_context sensor = bmp280_init(BME280_DEFAULT_I2C_BUS, + BME280_DEFAULT_ADDR, -1); + + if (!sensor) + { + printf("bmp280_init() failed\n"); + return 1; + } + + // For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: + // bmp280_init(BME280_DEFAULT_SPI_BUS, + // -1, 10) + + while (shouldRun) + { + // update our values from the sensor + if (bmp280_update(sensor)) + { + printf("bmp280_update() failed\n"); + bmp280_close(sensor); + return 1; + } + + printf("Compensation Temperature: %f C\n", + bmp280_get_temperature(sensor)); + printf("Pressure: %f Pa\n", bmp280_get_pressure(sensor)); + printf("Computed Altitude: %f m\n", bmp280_get_altitude(sensor)); + printf("Relative Humidity: %f %%\n\n", bmp280_get_humidity(sensor)); + + upm_delay(1); + } +//! [Interesting] + + printf("Exiting...\n"); + + bmp280_close(sensor); + + return 0; +} diff --git a/examples/c/bmp280.c b/examples/c/bmp280.c new file mode 100644 index 00000000..d15912aa --- /dev/null +++ b/examples/c/bmp280.c @@ -0,0 +1,86 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "upm_utilities.h" +#include "bmp280.h" + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); +//! [Interesting] + + // Instantiate a BMP280 instance using default i2c bus and address + bmp280_context sensor = bmp280_init(BMP280_DEFAULT_I2C_BUS, + BMP280_DEFAULT_ADDR, -1); + + if (!sensor) + { + printf("bmp280_init() failed\n"); + return 1; + } + + // For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: + // bmp280_init(BMP280_DEFAULT_SPI_BUS, + // -1, 10) + + while (shouldRun) + { + // update our values from the sensor + if (bmp280_update(sensor)) + { + printf("bmp280_update() failed\n"); + bmp280_close(sensor); + return 1; + } + + printf("Compensation Temperature: %f C\n", + bmp280_get_temperature(sensor)); + printf("Pressure: %f Pa\n", bmp280_get_pressure(sensor)); + printf("Computed Altitude: %f m\n\n", bmp280_get_altitude(sensor)); + + upm_delay(1); + } + + printf("Exiting...\n"); + + bmp280_close(sensor); + +//! [Interesting] + return 0; +} diff --git a/src/bmp280/CMakeLists.txt b/src/bmp280/CMakeLists.txt index 64fec167..53d3b18f 100644 --- a/src/bmp280/CMakeLists.txt +++ b/src/bmp280/CMakeLists.txt @@ -1,5 +1,9 @@ -set (libname "bmp280") -set (libdescription "Bosch Atmospheric Sensor Library (bmp280 and Bme280)") -set (module_src ${libname}.cxx bme280.cxx) -set (module_hpp ${libname}.hpp bme280.hpp) -upm_module_init(interfaces mraa) +upm_mixed_module_init (NAME bmp280 + DESCRIPTION "Bosch Atmospheric Sensor Library (BMP280 and BME280)" + C_HDR bmp280.h bmp280_regs.h + C_SRC bmp280.c + CPP_HDR bmp280.hpp bme280.hpp + CPP_SRC bmp280.cxx bme280.cxx + CPP_WRAPS_C + REQUIRES mraa interfaces) +target_link_libraries(${libnamec} m) diff --git a/src/bmp280/bme280.cxx b/src/bmp280/bme280.cxx index e78b8afa..17c7a248 100644 --- a/src/bmp280/bme280.cxx +++ b/src/bmp280/bme280.cxx @@ -1,6 +1,8 @@ /* * Author: Jon Trulson - * Copyright (c) 2016 Intel Corporation. + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -34,141 +36,23 @@ using namespace upm; using namespace std; -BME280::BME280(int bus, int addr, int cs, uint8_t theChipID) : - BMP280(bus, addr, cs, theChipID) +BME280::BME280(int bus, int addr, int cs) : + BMP280(bus, addr, cs) { - - m_humidity = 0; - - m_dig_H1 = 0; - m_dig_H2 = 0; - m_dig_H3 = 0; - m_dig_H4 = 0; - m_dig_H5 = 0; - m_dig_H6 = 0; - - // set sleep mode for now - setMeasureMode(MODE_SLEEP); - - // read calibration data - readCalibrationData(); - - // set the default mode to the highest resolution mode - setUsageMode(USAGE_MODE_INDOOR_NAV); } BME280::~BME280() { } -void BME280::update() -{ - // call the base class method first. This will handle the details - // WRT forced mode so that we can always be sure to read valid data - // regardless of the underlying measurement mode. - - BMP280::update(); - - int32_t hum = 0; - - const int dataLen = 2; - uint8_t data[dataLen]; - memset(data, 0, dataLen); - - int rv; - if ((rv = readRegs(REG_HUMIDITY_MSB, data, dataLen)) != dataLen) - { - throw std::runtime_error(std::string(__FUNCTION__) - + ": readRegs() failed, returned " - + std::to_string(rv)); - } - - // 20 bits unsigned stored in a 32bit signed quanty - - hum = ( (data[0] << 8) | data[1] ); - - m_humidity = float(bme280_compensate_H_int32(hum)); - m_humidity /= 1024.0; -} - -void BME280::readCalibrationData() -{ - m_dig_H1 = readReg(REG_CALIB_DIG_H1); - - const int calibLen = 7; - uint8_t calibData[calibLen]; - readRegs(REG_CALIB_DIG_H2_LSB, calibData, calibLen); - - m_dig_H2 = int16_t((calibData[1] << 8) | calibData[0]); - - m_dig_H3 = calibData[2]; - - m_dig_H4 = int16_t( (calibData[3] << 4) | - (calibData[4] & 0x0f) ); - - m_dig_H5 = int16_t( ((calibData[4] & 0xf0) >> 4) | - (calibData[5] << 4) ); - - m_dig_H6 = int8_t(calibData[6]); - -# if 0 - cerr << std::dec - << "H1: " << (int)m_dig_H1 - << " H2: " << (int)m_dig_H2 - << " H3: " << (int)m_dig_H3 - << " H4: " << (int)m_dig_H4 - << " H5: " << (int)m_dig_H5 - << " H6: " << (int)m_dig_H5 - << endl; -# endif // 0 - - // The BMP280 ctor will call it's version of readCalibrationData -} - float BME280::getHumidity() { - return m_humidity; + return bmp280_get_humidity(m_bmp280); } -void BME280::setOversampleRateHumidity(OSRS_H_T rate) +void BME280::setOversampleRateHumidity(BME280_OSRS_H_T rate) { - uint8_t reg = readReg(REG_CTRL_HUM); - - reg &= ~(_CTRL_HUM_OSRS_H_MASK << _CTRL_HUM_OSRS_H_SHIFT); - reg |= (rate << _CTRL_HUM_OSRS_H_SHIFT); - - writeReg(REG_CTRL_HUM, reg); + bmp280_set_oversample_rate_humidity(m_bmp280, rate); } -void BME280::setUsageMode(USAGE_MODE_T mode) -{ - // Here, we just set the default humidity oversample to 1 and then - // call the base method. - - m_humidity = 0; - - // set sleep mode first - setMeasureMode(MODE_SLEEP); - - setOversampleRateHumidity(OSRS_H_OVERSAMPLING_1); - BMP280::setUsageMode(mode); -} - -// This function comes from the BMP180 datasheet, section 4.2.3 - -// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format -// (22 integer and 10 fractional bits). Output value of “47445” -// represents 47445/1024 = 46.333 %RH -uint32_t BME280::bme280_compensate_H_int32(int32_t adc_H) -{ - int32_t v_x1_u32r; - v_x1_u32r = (m_t_fine - ((int32_t)76800)); - v_x1_u32r = (((((adc_H << 14) - (((int32_t)m_dig_H4) << 20) - (((int32_t)m_dig_H5) * v_x1_u32r)) + - ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)m_dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)m_dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)m_dig_H2) + 8192) >> 14)); - v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)m_dig_H1)) >> 4)); - v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); - v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r); - return (int32_t)(v_x1_u32r>>12); -} - diff --git a/src/bmp280/bme280.hpp b/src/bmp280/bme280.hpp index 3414d00c..ce46b29f 100644 --- a/src/bmp280/bme280.hpp +++ b/src/bmp280/bme280.hpp @@ -1,6 +1,8 @@ /* * Author: Jon Trulson - * Copyright (c) 2016 Intel Corporation. + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,192 +26,101 @@ #pragma once #include -#include -#include -#include - #include "interfaces/iHumiditySensor.hpp" #include "bmp280.hpp" -#define BME280_DEFAULT_I2C_BUS 0 -#define BME280_DEFAULT_SPI_BUS 0 -#define BME280_DEFAULT_ADDR 0x77 -#define BME280_DEFAULT_CHIPID 0x60 - namespace upm { - /** - * @library bmp280 - * @sensor bme280 - * @comname Digital Humidity, Pressure, and Temperature Sensor - * @type pressure humidity - * @man adafruit - * @con i2c spi gpio - * @web https://www.adafruit.com/products/2652 - * - * @brief API for the BME280 Digital Humidity, Pressure, and - * Temperature Sensor - * - * The BME280 is as combined digital humidity, pressure and - * temperature sensor based on proven sensing principles. The sensor - * module is housed in an extremely compact metal-lid LGA package - * with a footprint of only 2.5 * 2.5 mm2 with a height of 0.93 - * mm. Its small dimensions and its low power consumption allow the - * implementation in battery driven devices such as handsets, GPS - * modules or watches. The BME280 is register and performance - * compatible to the Bosch Sensortec BMP280 digital pressure sensor - * - * @snippet bme280.cxx Interesting - */ - - class BME280 : public BMP280, public IHumiditySensor { - public: - /** - * BMP280 registers - */ - typedef enum : uint8_t { - // Do not write into reserved bits. - - // We only specify those registers specific to the BME280. The - // rest of them can be found in the BMP280 header file. - - // read-only factory calibration data for humidity - - REG_CALIB_DIG_H1 = 0xa1, - - REG_CALIB_DIG_H2_LSB = 0xe1, - REG_CALIB_DIG_H2_MSB = 0xe2, - - REG_CALIB_DIG_H3 = 0xe3, - - REG_CALIB_DIG_H4_0 = 0xe4, // bits 8 -> 11:4 - REG_CALIB_DIG_H4_1 = 0xe5, // bits 3:0 -> 3:0 - - REG_CALIB_DIG_H5_0 = 0xe5, // bits 7:4 -> 3:0 - REG_CALIB_DIG_H5_1 = 0xe6, // bits 8 -> 11:4 - - REG_CALIB_DIG_H6 = 0xe7, - - REG_CTRL_HUM = 0xf2, - - REG_HUMIDITY_MSB = 0xfd, - REG_HUMIDITY_LSB = 0xfe - } BME280_REGS_T; - - /** - * REG_CTRL_HUM bits - */ - typedef enum { - CTRL_HUM_OSRS_H0 = 0x01, - CTRL_HUM_OSRS_H1 = 0x02, - CTRL_HUM_OSRS_H2 = 0x04, - _CTRL_HUM_OSRS_H_MASK = 3, - _CTRL_HUM_OSRS_H_SHIFT = 0 - - // 0x08-0x80 reserved - } CTRL_HUM_T; - - /** - * CTRL_HUM_OSRS_H values - */ - typedef enum { - OSRS_H_SKIPPED = 0, - OSRS_H_OVERSAMPLING_1 = 1, // x1 - OSRS_H_OVERSAMPLING_2 = 2, // x2 - OSRS_H_OVERSAMPLING_4 = 3, - OSRS_H_OVERSAMPLING_8 = 4, - OSRS_H_OVERSAMPLING_16 = 5 - } OSRS_H_T; - - - /** - * BME280 constructor. + * @library bmp280 + * @sensor bme280 + * @comname Digital Humidity, Pressure, and Temperature Sensor + * @type pressure humidity + * @man adafruit + * @con i2c spi gpio + * @web https://www.adafruit.com/products/2652 * - * This device can support both I2C and SPI. For SPI, set the addr - * to -1, and specify a positive integer representing the Chip - * Select (CS) pin for the cs argument. If you are using a - * hardware CS pin, then you can connect the proper pin to the - * hardware CS pin on your MCU and supply -1 for cs. The default - * operating mode is I2C. + * @brief API for the BME280 Digital Humidity, Pressure, and + * Temperature Sensor * - * @param bus I2C or SPI bus to use. - * @param address The address for this device. -1 for SPI. - * @param cs The gpio pin to use for the SPI Chip Select. -1 for - * I2C or for SPI with a hardware controlled pin. - * @param theChipID The chip ID used for validation - */ - BME280(int bus=BME280_DEFAULT_I2C_BUS, int addr=BME280_DEFAULT_ADDR, - int cs=-1, uint8_t theChipID=BME280_DEFAULT_CHIPID); - - /** - * BME280 Destructor. - */ - virtual ~BME280(); - - /** - * Update the internal stored values from sensor data. - */ - virtual void update(); - - /** - * Return the current measured relative humidity. update() must - * have been called prior to calling this method. + * The BME280 is as combined digital humidity, pressure and + * temperature sensor based on proven sensing principles. The + * sensor module is housed in an extremely compact metal-lid LGA + * package with a footprint of only 2.5 * 2.5 mm2 with a height of + * 0.93 mm. Its small dimensions and its low power consumption + * allow the implementation in battery driven devices such as + * handsets, GPS modules or watches. The BME280 is register and + * performance compatible to the Bosch Sensortec BMP280 digital + * pressure sensor. * - * @return The relative humidity in percent.. + * @snippet bme280.cxx Interesting */ - float getHumidity(); - /** - * Set a general usage mode. This function can be used to - * configure the filters and oversampling for a particular use - * case. These settings are documented in the BMP280 and BME280 - * datasheets. - * - * @param mode One of the USAGE_MODE_T values. - */ - virtual void setUsageMode(USAGE_MODE_T mode); + class BME280 : public BMP280, public IHumiditySensor { + public: - /** - * Set the humidity sensor oversampling parameter. See the data - * sheet for details. This value can be automatically set to a - * suitable value by using one of the predefined modes for - * setUsageMode(). - * - * @param mode One of the OSRS_H_T values. - */ - void setOversampleRateHumidity(OSRS_H_T rate); + /** + * BME280 constructor. + * + * This driver supports both the BMP280 and the BME280. The + * BME280 adds a humidity sensor. The device type is detected + * automatically by querying the chip id register. + * + * This device can support both I2C and SPI. For SPI, set the addr + * to -1, and specify a positive integer representing the Chip + * Select (CS) pin for the cs argument. If you are using a + * hardware CS pin, then you can connect the proper pin to the + * hardware CS pin on your MCU and supply -1 for cs. The default + * operating mode is I2C. + * + * @param bus I2C or SPI bus to use. + * @param address The I2C address for this device. Use -1 for SPI. + * @param cs The gpio pin to use for the SPI Chip Select. Use -1 + * for I2C, or for SPI with a hardware controlled pin. + * @throws std::runtime_error on failure. + */ + BME280(int bus=BME280_DEFAULT_I2C_BUS, int addr=BME280_DEFAULT_ADDR, + int cs=-1); - // Interface support - const char *getModuleName() - { - return "BME280"; + /** + * BME280 Destructor. + */ + virtual ~BME280(); + + /** + * Return the current measured relative humidity. update() + * must have been called prior to calling this method. If the + * device is a bmp280 rather than a bme280, this method will + * always return 0. + * + * @return The relative humidity in percent. + */ + float getHumidity(); + + /** + * Set the humidity sensor oversampling parameter. See the data + * sheet for details. This value can be automatically set to a + * suitable value by using one of the predefined modes for + * setUsageMode(). + * + * @param mode One of the OSRS_H_T values. + */ + void setOversampleRateHumidity(BME280_OSRS_H_T rate); + + // Interface support + const char *getModuleName() + { + return "BME280"; + }; + + int getHumidityRelative() + { + return int(getHumidity()); + }; + + protected: + + private: }; - - int getHumidityRelative() - { - return int(getHumidity()); - }; - - protected: - // relative humidity - float m_humidity; - - // read the calibration data - virtual void readCalibrationData(); - - private: - // calibration data humidity - uint8_t m_dig_H1; - int16_t m_dig_H2; - uint8_t m_dig_H3; - int16_t m_dig_H4; - int16_t m_dig_H5; - int8_t m_dig_H6; - - // Bosch supplied conversion/compensation functions from the - // datasheet. - uint32_t bme280_compensate_H_int32(int32_t adc_H); - }; } diff --git a/src/bmp280/bmp280.c b/src/bmp280/bmp280.c new file mode 100644 index 00000000..1e297dce --- /dev/null +++ b/src/bmp280/bmp280.c @@ -0,0 +1,778 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "bmp280.h" + +// number of bytes of stored calibration data (bmp280) +#define BMP280_CALIBRATION_BYTES (26) +// BME280 has these additional calibration regs +#define BME280_CALIBRATION_BYTES (7) + +// Number of bytes to get bmp280 data +#define BMP280_DATA_LEN (6) +// Number of bytes to get bme280 data +#define BME280_DATA_LEN (2) + +// Uncomment the following to use test data as specified in the +// datasheet, section 3.12. This really only tests the compensation +// algorithm, and only for the bmp280 parts (temperature/pressure). + +// #define BMP280_USE_TEST_DATA + +// SPI CS on and off functions +static void _csOn(const bmp280_context dev) +{ + assert(dev != NULL); + + if (dev->gpio) + mraa_gpio_write(dev->gpio, 0); +} + +static void _csOff(const bmp280_context dev) +{ + assert(dev != NULL); + + if (dev->gpio) + mraa_gpio_write(dev->gpio, 1); +} + +// These functions come from the BMP280 datasheet, section 3.11.3 + +// Returns temperature in DegC, resolution is 0.01 DegC. Output value +// of "5123" equals 51.23 DegC. t_fine carries fine temperature as +// global value +static int32_t _bmp280_compensate_T_int32(const bmp280_context dev, + int32_t adc_T) +{ + assert(dev != NULL); + + int32_t var1, var2, T; + var1 = ((((adc_T>>3) - ((int32_t)dev->dig_T1<<1))) * ((int32_t)dev->dig_T2)) >> 11; + var2 = (((((adc_T>>4) - ((int32_t)dev->dig_T1)) * ((adc_T>>4) - ((int32_t)dev->dig_T1))) >> 12) * + ((int32_t)dev->dig_T3)) >> 14; + dev->t_fine = var1 + var2; + T = (dev->t_fine * 5 + 128) >> 8; + return T; +} + +// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format +// (24 integer bits and 8 fractional bits). Output value of +// “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa +static uint32_t _bmp280_compensate_P_int64(const bmp280_context dev, + int32_t adc_P) +{ + assert(dev != NULL); + + int64_t var1, var2, p; + var1 = ((int64_t)dev->t_fine) - 128000; + var2 = var1 * var1 * (int64_t)dev->dig_P6; + var2 = var2 + ((var1*(int64_t)dev->dig_P5)<<17); + var2 = var2 + (((int64_t)dev->dig_P4)<<35); + var1 = ((var1 * var1 * (int64_t)dev->dig_P3)>>8) + ((var1 * (int64_t)dev->dig_P2)<<12); + var1 = (((((int64_t)1)<<47)+var1))*((int64_t)dev->dig_P1)>>33; + if (var1 == 0) + { + return 0; // avoid exception caused by division by zero + } + p = 1048576-adc_P; + p = (((p<<31)-var2)*3125)/var1; + var1 = (((int64_t)dev->dig_P9) * (p>>13) * (p>>13)) >> 25; + var2 = (((int64_t)dev->dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)dev->dig_P7)<<4); + return (uint32_t)p; +} + +// This function comes from the BME280 datasheet, section 4.2.3 +// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format +// (22 integer and 10 fractional bits). Output value of “47445” +// represents 47445/1024 = 46.333 %RH +// BME280 only +static uint32_t _bme280_compensate_H_int32(const bmp280_context dev, + int32_t adc_H) +{ + assert(dev != NULL); + + int32_t v_x1_u32r; + v_x1_u32r = (dev->t_fine - ((int32_t)76800)); + v_x1_u32r = (((((adc_H << 14) - (((int32_t)dev->dig_H4) << 20) - (((int32_t)dev->dig_H5) * v_x1_u32r)) + + ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dev->dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)dev->dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)dev->dig_H2) + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dev->dig_H1)) >> 4)); + v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); + v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r); + return (int32_t)(v_x1_u32r>>12); +} + +// read the calibration data +static upm_result_t _read_calibration_data(const bmp280_context dev) +{ + assert(dev != NULL); + +#if defined(BMP280_USE_TEST_DATA) + printf("%s: WARNING: Test data is being used\n", __FUNCTIOM__); + + // This data is taken from the datasheet, section 3.12 + dev->dig_T1 = 27504; + dev->dig_T2 = 26435; + dev->dig_T3 = -1000; + + dev->dig_P1 = 36477; + dev->dig_P2 = -10685; + dev->dig_P3 = 3024; + dev->dig_P4 = 2855; + dev->dig_P5 = 140; + dev->dig_P6 = -7; + dev->dig_P7 = 15500; + dev->dig_P8 = -14600; + dev->dig_P9 = 6000; + + // BME280 does not have any test data.... + dev->dig_H1 = 0; + dev->dig_H2 = 0; + dev->dig_H3 = 0; + dev->dig_H4 = 0; + dev->dig_H5 = 0; + dev->dig_H6 = 0; + +#else + + // bmp280... + uint8_t calibData[BMP280_CALIBRATION_BYTES]; + int rv; + if ( (rv = bmp280_read_regs(dev, BMP280_REG_CALIB00, calibData, + BMP280_CALIBRATION_BYTES)) + != BMP280_CALIBRATION_BYTES) + { + printf("%s: bmp280_read_regs(BMP280) failed.", + __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + dev->dig_T1 = (uint16_t)((calibData[1] << 8) | calibData[0]); + dev->dig_T2 = (int16_t)((calibData[3] << 8) | calibData[2]); + dev->dig_T3 = (int16_t)((calibData[5] << 8) | calibData[4]); + + dev->dig_P1 = (uint16_t)((calibData[7] << 8) | calibData[6]); + dev->dig_P2 = (int16_t)((calibData[9] << 8) | calibData[8]); + dev->dig_P3 = (int16_t)((calibData[11] << 8) | calibData[10]); + dev->dig_P4 = (int16_t)((calibData[13] << 8) | calibData[12]); + dev->dig_P5 = (int16_t)((calibData[15] << 8) | calibData[14]); + dev->dig_P6 = (int16_t)((calibData[17] << 8) | calibData[16]); + dev->dig_P7 = (int16_t)((calibData[19] << 8) | calibData[18]); + dev->dig_P8 = (int16_t)((calibData[21] << 8) | calibData[20]); + dev->dig_P9 = (int16_t)((calibData[23] << 8) | calibData[22]); + + // bme280... (humidity calibration data) + if (dev->isBME) + { + dev->dig_H1 = bmp280_read_reg(dev, BME280_REG_CALIB_DIG_H1); + + uint8_t hCalibData[BME280_CALIBRATION_BYTES]; + if ( (rv = bmp280_read_regs(dev, BME280_REG_CALIB_DIG_H2_LSB, + hCalibData, + BME280_CALIBRATION_BYTES)) + != BME280_CALIBRATION_BYTES) + { + printf("%s: bmp280_read_regs(BME280) failed.", + __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + dev->dig_H2 = (int16_t)((hCalibData[1] << 8) | hCalibData[0]); + + dev->dig_H3 = hCalibData[2]; + + dev->dig_H4 = (int16_t)( (hCalibData[3] << 4) | + (hCalibData[4] & 0x0f) ); + + dev->dig_H5 = (int16_t)( ((hCalibData[4] & 0xf0) >> 4) | + (hCalibData[5] << 4) ); + + dev->dig_H6 = (int8_t)(hCalibData[6]); + } +#endif // defined(BMP280_USE_TEST_DATA) + + return UPM_SUCCESS; +} + +bmp280_context bmp280_init(int bus, int addr, int cs) +{ + bmp280_context dev = + (bmp280_context)malloc(sizeof(struct _bmp280_context)); + + if (!dev) + return NULL; + + // zero out context + memset((void *)dev, 0, sizeof(struct _bmp280_context)); + + // make sure MRAA is initialized + if (mraa_init() != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed.\n", __FUNCTION__); + bmp280_close(dev); + return NULL; + } + + if (addr < 0) + dev->isSPI = true; + + if (dev->isSPI) + { + if (!(dev->spi = mraa_spi_init(bus))) + { + printf("%s: mraa_spi_init() failed.\n", __FUNCTION__); + bmp280_close(dev); + return NULL; + } + + // Only create cs context if we are actually using a valid pin. + // A hardware controlled pin should specify cs as -1. + if (cs >= 0) + { + if (!(dev->gpio = mraa_gpio_init(cs))) + { + printf("%s: mraa_gpio_init() failed.\n", __FUNCTION__); + bmp280_close(dev); + return NULL; + } + mraa_gpio_dir(dev->gpio, MRAA_GPIO_OUT); + } + + mraa_spi_mode(dev->spi, MRAA_SPI_MODE0); + if (mraa_spi_frequency(dev->spi, 5000000)) + { + printf("%s: mraa_spi_frequency() failed.\n", __FUNCTION__); + bmp280_close(dev); + return NULL; + } + + // toggle CS on/off so chip switches into SPI mode. For a hw + // CS pin, the first SPI transaction should accomplish this. + _csOn(dev); + upm_delay_ms(10); + _csOff(dev); + } + else + { + // I2C + + if (!(dev->i2c = mraa_i2c_init(bus))) + { + printf("%s: mraa_i2c_init() failed.\n", __FUNCTION__); + bmp280_close(dev); + return NULL; + } + + if (mraa_i2c_address(dev->i2c, addr)) + { + printf("%s: mraa_i2c_address() failed.\n", __FUNCTION__); + bmp280_close(dev); + return NULL; + } + } + + // check the chip id + + uint8_t chipID = bmp280_read_reg(dev, BMP280_REG_CHIPID); + + switch(chipID) + { + case BMP280_CHIPID: // BMP280 + dev->isBME = false; + break; + + case BME280_CHIPID: // BME280 + dev->isBME = true; + break; + + default: // ?? + printf("%s: invalid chip id: %02x. Expected either %02x " + "(bmp280) or %02x (bme280)\n", + __FUNCTION__, chipID, BMP280_CHIPID, BME280_CHIPID); + bmp280_close(dev); + return NULL; + } + + // set sleep mode for now + bmp280_set_measure_mode(dev, BMP280_MODE_SLEEP); + + // read calibration data + if (_read_calibration_data(dev)) + { + printf("%s: _read_calibration_data() failed.", __FUNCTION__); + bmp280_close(dev); + return NULL; + } + + // set the default mode to the highest resolution mode + bmp280_set_usage_mode(dev, BMP280_USAGE_MODE_INDOOR_NAV); + + // set the default sea level pressure in hPA + dev->sea_level_hPA = BMP280_SEA_LEVEL_HPA; + + return dev; +} + +void bmp280_close(bmp280_context dev) +{ + assert(dev != NULL); + + if (dev->i2c) + mraa_i2c_stop(dev->i2c); + + if (dev->spi) + mraa_spi_stop(dev->spi); + + if (dev->gpio) + mraa_gpio_close(dev->gpio); +} + +upm_result_t bmp280_update(const bmp280_context dev) +{ + assert(dev != NULL); + + int32_t temp = 0; + int32_t pres = 0; + + uint8_t bmp_data[BMP280_DATA_LEN]; + memset(bmp_data, 0, BMP280_DATA_LEN); + + // If we are using a forced mode, then we need to manually trigger + // the measurement and wait for it to complete. + + if (dev->mode == BMP280_MODE_FORCED) + { + // bmp280 measure mode will return to sleep after completion... + bmp280_set_measure_mode(dev, BMP280_MODE_FORCED); + + uint8_t stat; + do + { + upm_delay_ms(10); + stat = bmp280_get_status(dev); + } while (stat & BMP280_STATUS_MEASURING); + } + + int rv; + if ((rv = bmp280_read_regs(dev, BMP280_REG_PRESSURE_MSB, + bmp_data, BMP280_DATA_LEN)) + != BMP280_DATA_LEN) + { + printf("%s: bmp280_read_regs() failed.", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // 20 bits unsigned stored in a 32bit signed quantity + +#if defined(BMP280_USE_TEST_DATA) + // taken from datasheet, section 3.12 + temp = 519888; + pres = 415148; +#else + temp = ( (bmp_data[5] >> 4) | (bmp_data[4] << 4) | (bmp_data[3] << 12) ); + pres = ( (bmp_data[2] >> 4) | (bmp_data[1] << 4) | (bmp_data[0] << 12) ); +#endif + + dev->temperature = (float)_bmp280_compensate_T_int32(dev, temp); + dev->temperature /= 100.0; + + dev->pressure = (float)_bmp280_compensate_P_int64(dev, pres); + dev->pressure /= 256.0; + + // BME280? + if (dev->isBME) + { + uint8_t bme_data[BME280_DATA_LEN]; + + memset(bme_data, 0, BME280_DATA_LEN); + + if ((rv = bmp280_read_regs(dev, BME280_REG_HUMIDITY_MSB, + bme_data, BME280_DATA_LEN)) + != BME280_DATA_LEN) + { + printf("%s: bmp280_read_regs(BME280) failed.", + __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + // 20 bits unsigned stored in a 32bit signed quantity + int32_t hum = ( (bme_data[0] << 8) | bme_data[1] ); + + dev->humidity = (float)_bme280_compensate_H_int32(dev, hum); + dev->humidity /= 1024.0; + } + + return UPM_SUCCESS; +} + +void bmp280_set_sea_level_pressure(const bmp280_context dev, + float seaLevelhPA) +{ + assert(dev != NULL); + + dev->sea_level_hPA = seaLevelhPA; +} + +float bmp280_get_altitude(const bmp280_context dev) +{ + assert(dev != NULL); + + // adapted from the US NOAA pdf: pressureAltitude.pdf + return 44307.69 * (1.0 - pow((dev->pressure/100.0) + / dev->sea_level_hPA, 0.190284)); +} + +uint8_t bmp280_read_reg(const bmp280_context dev, uint8_t reg) +{ + assert(dev != NULL); + + if (dev->isSPI) + { + reg |= 0x80; // needed for read + uint8_t pkt[2] = {reg, 0}; + + _csOn(dev); + if (mraa_spi_transfer_buf(dev->spi, pkt, pkt, 2)) + { + _csOff(dev); + printf("%s: mraa_spi_transfer_buf() failed.", + __FUNCTION__); + + return 0xff; + } + _csOff(dev); + + return pkt[1]; + } + else + { + return (uint8_t)mraa_i2c_read_byte_data(dev->i2c, reg); + } +} + +int bmp280_read_regs(const bmp280_context dev, + uint8_t reg, uint8_t *buffer, int len) +{ + assert(dev != NULL); + + if (dev->isSPI) + { + reg |= 0x80; // needed for read + + uint8_t sbuf[len + 1]; + memset((char *)sbuf, 0, len + 1); + sbuf[0] = reg; + + // We need to do it this way for edison - ie: use a single + // transfer rather than breaking it up into two like we used + // to, since we have no control over CS. This means a buffer + // copy is now required, but that's the way it goes. + + _csOn(dev); + if (mraa_spi_transfer_buf(dev->spi, sbuf, sbuf, len + 1)) + { + _csOff(dev); + printf("%s: mraa_spi_transfer_buf() failed.", + __FUNCTION__); + + return 0; + } + _csOff(dev); + + // now copy it into user buffer + for (int i=0; ii2c, reg, buffer, len) != len) + return UPM_ERROR_OPERATION_FAILED; + } + + return len; +} + +upm_result_t bmp280_write_reg(const bmp280_context dev, + uint8_t reg, uint8_t val) +{ + assert(dev != NULL); + + if (dev->isSPI) + { + reg &= 0x7f; // mask off 0x80 for writing + uint8_t pkt[2] = {reg, val}; + + _csOn(dev); + if (mraa_spi_transfer_buf(dev->spi, pkt, NULL, 2)) + { + _csOff(dev); + printf("%s: mraa_spi_transfer_buf() failed.", + __FUNCTION__); + + return UPM_ERROR_OPERATION_FAILED; + } + _csOff(dev); + } + else + { + if (mraa_i2c_write_byte_data(dev->i2c, val, reg)) + { + printf("%s: mraa_i2c_write_byte_data() failed.", + __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + } + + return UPM_SUCCESS; +} + +uint8_t bmp280_get_chip_id(const bmp280_context dev) +{ + assert(dev != NULL); + + return bmp280_read_reg(dev, BMP280_REG_CHIPID); +} + +void bmp280_reset(const bmp280_context dev) +{ + assert(dev != NULL); + + bmp280_write_reg(dev, BMP280_REG_RESET, BMP280_RESET_BYTE); + upm_delay(1); +} + +float bmp280_get_temperature(const bmp280_context dev) +{ + assert(dev != NULL); + + return dev->temperature; +} + +float bmp280_get_pressure(const bmp280_context dev) +{ + assert(dev != NULL); + + return dev->pressure; +} + +// BME280 only +float bmp280_get_humidity(const bmp280_context dev) +{ + assert(dev != NULL); + + if (dev->isBME) + return dev->humidity; + else + return 0.0; +} + +void bmp280_set_filter(const bmp280_context dev, BMP280_FILTER_T filter) +{ + assert(dev != NULL); + + uint8_t reg = bmp280_read_reg(dev, BMP280_REG_CONFIG); + + reg &= ~(_BMP280_CONFIG_FILTER_MASK << _BMP280_CONFIG_FILTER_SHIFT); + reg |= (filter << _BMP280_CONFIG_FILTER_SHIFT); + + bmp280_write_reg(dev, BMP280_REG_CONFIG, reg); +} + +void bmp280_set_timer_standby(const bmp280_context dev, + BMP280_T_SB_T tsb) +{ + assert(dev != NULL); + + uint8_t reg = bmp280_read_reg(dev, BMP280_REG_CONFIG); + + reg &= ~(_BMP280_CONFIG_T_SB_MASK << _BMP280_CONFIG_T_SB_SHIFT); + reg |= (tsb << _BMP280_CONFIG_T_SB_SHIFT); + + bmp280_write_reg(dev, BMP280_REG_CONFIG, reg); +} + +void bmp280_set_measure_mode(const bmp280_context dev, + BMP280_MODES_T mode) +{ + assert(dev != NULL); + + uint8_t reg = bmp280_read_reg(dev, BMP280_REG_CTRL_MEAS); + + reg &= ~(_BMP280_CTRL_MEAS_MODE_MASK << _BMP280_CTRL_MEAS_MODE_SHIFT); + reg |= (mode << _BMP280_CTRL_MEAS_MODE_SHIFT); + + bmp280_write_reg(dev, BMP280_REG_CTRL_MEAS, reg); + dev->mode = mode; +} + +void bmp280_set_oversample_rate_pressure(const bmp280_context dev, + BMP280_OSRS_P_T rate) +{ + assert(dev != NULL); + + uint8_t reg = bmp280_read_reg(dev, BMP280_REG_CTRL_MEAS); + + reg &= ~(_BMP280_CTRL_MEAS_OSRS_P_MASK << _BMP280_CTRL_MEAS_OSRS_P_SHIFT); + reg |= (rate << _BMP280_CTRL_MEAS_OSRS_P_SHIFT); + + bmp280_write_reg(dev, BMP280_REG_CTRL_MEAS, reg); +} + +void bmp280_set_oversample_rate_temperature(const bmp280_context dev, + BMP280_OSRS_T_T rate) +{ + assert(dev != NULL); + + uint8_t reg = bmp280_read_reg(dev, BMP280_REG_CTRL_MEAS); + + reg &= ~(_BMP280_CTRL_MEAS_OSRS_T_MASK << _BMP280_CTRL_MEAS_OSRS_T_SHIFT); + reg |= (rate << _BMP280_CTRL_MEAS_OSRS_T_SHIFT); + + bmp280_write_reg(dev, BMP280_REG_CTRL_MEAS, reg); +} + +// bme280 only +void bmp280_set_oversample_rate_humidity(const bmp280_context dev, + BME280_OSRS_H_T rate) +{ + assert(dev != NULL); + + if (dev->isBME) + { + uint8_t reg = bmp280_read_reg(dev, BME280_REG_CTRL_HUM); + + reg &= ~(_BME280_CTRL_HUM_OSRS_H_MASK << _BME280_CTRL_HUM_OSRS_H_SHIFT); + reg |= (rate << _BME280_CTRL_HUM_OSRS_H_SHIFT); + + bmp280_write_reg(dev, BME280_REG_CTRL_HUM, reg); + } +} + +uint8_t bmp280_get_status(const bmp280_context dev) +{ + assert(dev != NULL); + + return bmp280_read_reg(dev, BMP280_REG_STATUS); +} + +void bmp280_set_usage_mode(const bmp280_context dev, BMP280_USAGE_MODE_T mode) +{ + assert(dev != NULL); + + // set up the regs for the given usage mode. These settings come + // from the recomendations in the BMP280 datasheet, section 3.4 + // Filter Selection. + + dev->temperature = 0; + dev->pressure = 0; + dev->humidity = 0; + + // set sleep mode first + bmp280_set_measure_mode(dev, BMP280_MODE_SLEEP); + + switch (mode) + { + case BMP280_USAGE_MODE_HANDHELD_LOW_POWER: + bmp280_set_oversample_rate_pressure(dev, + BMP280_OSRS_P_OVERSAMPLING_16); + bmp280_set_oversample_rate_temperature(dev, + BMP280_OSRS_T_OVERSAMPLING_2); + bmp280_set_oversample_rate_humidity(dev, + BME280_OSRS_H_OVERSAMPLING_1); + bmp280_set_filter(dev, BMP280_FILTER_4); + bmp280_set_measure_mode(dev, BMP280_MODE_NORMAL); + + break; + + case BMP280_USAGE_MODE_HANDHELD_DYNAMIC: + bmp280_set_oversample_rate_pressure(dev, + BMP280_OSRS_P_OVERSAMPLING_4); + bmp280_set_oversample_rate_temperature(dev, + BMP280_OSRS_T_OVERSAMPLING_1); + bmp280_set_oversample_rate_humidity(dev, + BME280_OSRS_H_OVERSAMPLING_1); + bmp280_set_filter(dev, BMP280_FILTER_16); + bmp280_set_measure_mode(dev, BMP280_MODE_NORMAL); + + break; + + case BMP280_USAGE_MODE_WEATHER_MONITOR: + bmp280_set_oversample_rate_pressure(dev, + BMP280_OSRS_P_OVERSAMPLING_1); + bmp280_set_oversample_rate_temperature(dev, + BMP280_OSRS_T_OVERSAMPLING_1); + bmp280_set_oversample_rate_humidity(dev, + BME280_OSRS_H_OVERSAMPLING_1); + bmp280_set_filter(dev, BMP280_FILTER_OFF); + bmp280_set_measure_mode(dev, BMP280_MODE_FORCED); + + break; + + case BMP280_USAGE_MODE_FLOOR_CHG_DETECT: + bmp280_set_oversample_rate_pressure(dev, + BMP280_OSRS_P_OVERSAMPLING_4); + bmp280_set_oversample_rate_temperature(dev, + BMP280_OSRS_T_OVERSAMPLING_1); + bmp280_set_oversample_rate_humidity(dev, + BME280_OSRS_H_OVERSAMPLING_1); + bmp280_set_filter(dev, BMP280_FILTER_4); + bmp280_set_measure_mode(dev, BMP280_MODE_NORMAL); + + break; + + case BMP280_USAGE_MODE_DROP_DETECT: + bmp280_set_oversample_rate_pressure(dev, + BMP280_OSRS_P_OVERSAMPLING_2); + bmp280_set_oversample_rate_temperature(dev, + BMP280_OSRS_T_OVERSAMPLING_1); + bmp280_set_oversample_rate_humidity(dev, + BME280_OSRS_H_OVERSAMPLING_1); + bmp280_set_filter(dev, BMP280_FILTER_OFF); + bmp280_set_measure_mode(dev, BMP280_MODE_NORMAL); + + break; + + case BMP280_USAGE_MODE_INDOOR_NAV: + bmp280_set_oversample_rate_pressure(dev, + BMP280_OSRS_P_OVERSAMPLING_16); + bmp280_set_oversample_rate_temperature(dev, + BMP280_OSRS_T_OVERSAMPLING_2); + bmp280_set_oversample_rate_humidity(dev, + BME280_OSRS_H_OVERSAMPLING_1); + bmp280_set_filter(dev, BMP280_FILTER_16); + bmp280_set_measure_mode(dev, BMP280_MODE_NORMAL); + + break; + + default: + printf("%s: invalid usage mode specified, mode unchanged.", + __FUNCTION__); + } +} diff --git a/src/bmp280/bmp280.cxx b/src/bmp280/bmp280.cxx index a8bd966d..b9d338fc 100644 --- a/src/bmp280/bmp280.cxx +++ b/src/bmp280/bmp280.cxx @@ -1,6 +1,8 @@ /* * Author: Jon Trulson - * Copyright (c) 2016 Intel Corporation. + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -26,546 +28,125 @@ #include #include #include -#include -#include #include "bmp280.hpp" using namespace upm; using namespace std; -// Uncomment the following to use test data as specified in the -// datasheet, section 3.12. This really only tests the compensation -// algorithm. -// #define BMP280_USE_TEST_DATA - -// conversion from fahrenheit to celsius and back +// conversion from Celsius to Fahrenheit. static float c2f(float c) { return (c * (9.0 / 5.0) + 32.0); } -BMP280::BMP280(int bus, int addr, int cs, uint8_t theChipID) : - m_i2c(0), m_spi(0), m_gpioCS(0) +BMP280::BMP280(int bus, int addr, int cs) : + m_bmp280(bmp280_init(bus, addr, cs)) { - - m_addr = addr; - - m_temperature = 0; - m_pressure = 0; - m_isSPI = false; - - clearData(); - - if (addr < 0) - m_isSPI = true; - - mraa::Result rv; - - if (m_isSPI) - { - m_spi = new mraa::Spi(bus); - - // Only create cs context if we are actually using a valid pin. - // A hardware controlled pin should specify cs as -1. - if (cs >= 0) - { - m_gpioCS = new mraa::Gpio(cs); - m_gpioCS->dir(mraa::DIR_OUT); - } - - m_spi->mode(mraa::SPI_MODE0); - m_spi->frequency(5000000); - - // toggle it on/off so chip switches into SPI mode. For a hw CS - // pin, the first SPI transaction should accomplish this. - csOn(); - usleep(10000); - csOff(); - } - else - { - // I2C - m_i2c = new mraa::I2c(bus); - - if ((rv = m_i2c->address(m_addr)) != mraa::SUCCESS) - { - throw std::runtime_error(string(__FUNCTION__) + - ": I2c.address() failed"); - } - } - - // check the chip id - - uint8_t chipID = readReg(REG_CHIPID); - if (chipID != theChipID) - { - throw std::runtime_error(string(__FUNCTION__) - + ": invalid chip ID. Expected " - + std::to_string(int(theChipID)) - + ", got " - + std::to_string(int(chipID))); - } - - // set sleep mode for now - setMeasureMode(MODE_SLEEP); - - // read calibration data - readCalibrationData(); - - // set the default mode to the highest resolution mode - setUsageMode(USAGE_MODE_INDOOR_NAV); + if (!m_bmp280) + throw std::runtime_error(string(__FUNCTION__) + + ": bmp280_init() failed"); } BMP280::~BMP280() { - if (m_i2c) - delete m_i2c; - - if (m_spi) - delete m_spi; - - if (m_gpioCS) - delete m_gpioCS; + bmp280_close(m_bmp280); } void BMP280::update() { - int32_t temp = 0; - int32_t pres = 0; - - const int dataLen = 6; - uint8_t data[dataLen]; - memset(data, 0, dataLen); - - // If we are using a forced mode, then we need to manually trigger - // the measurement, and wait for it to complete. - - if (m_mode == MODE_FORCED) - { - // bmp280 measure mode will return to sleep after completion... - setMeasureMode(MODE_FORCED); - - uint8_t stat; - do - { - usleep(10000); // 10ms - stat = readReg(REG_STATUS); - } while (stat & STATUS_MEASURING); - } - - int rv; - if ((rv = readRegs(REG_PRESSURE_MSB, data, dataLen)) != dataLen) - { - throw std::runtime_error(std::string(__FUNCTION__) - + ": readRegs() failed, returned " - + std::to_string(rv)); - } - - // 20 bits unsigned stored in a 32bit signed quanty - -#if defined(BMP280_USE_TEST_DATA) - // taken from datasheet, section 3.12 - temp = 519888; - pres = 415148; -#else - temp = ( (data[5] >> 4) | (data[4] << 4) | (data[3] << 12) ); - pres = ( (data[2] >> 4) | (data[1] << 4) | (data[0] << 12) ); -#endif - - m_temperature = float(bmp280_compensate_T_int32(temp)); - m_temperature /= 100.0; - - m_pressure = float(bmp280_compensate_P_int64(pres)); - m_pressure /= 256.0; + if (bmp280_update(m_bmp280)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmp280_update() failed"); } -float BMP280::getAltitude(float sealLevelhPA) +void BMP280::setSeaLevelPreassure(float seaLevelhPA) { - // adapted from the NOAA pdf: pressureAltitude.pdf - return 44307.69 * (1.0 - pow((m_pressure/100.0) / sealLevelhPA, 0.190284)); + bmp280_set_sea_level_pressure(m_bmp280, seaLevelhPA); +} + +float BMP280::getAltitude() +{ + return bmp280_get_altitude(m_bmp280); } uint8_t BMP280::readReg(uint8_t reg) { - if (m_isSPI) - { - reg |= 0x80; // needed for read - uint8_t pkt[2] = {reg, 0}; - - csOn(); - if (m_spi->transfer(pkt, pkt, 2)) - { - csOff(); - throw std::runtime_error(string(__FUNCTION__) - + ": Spi.transfer() failed"); - } - csOff(); - -#if 0 - cerr << "readReg: " << std::hex << "p0: " << (int)pkt[0] << " p1: " - << (int)pkt[1] << endl; -#endif // 0 - - return pkt[1]; - } - else - return m_i2c->readReg(reg); + return bmp280_read_reg(m_bmp280, reg); } int BMP280::readRegs(uint8_t reg, uint8_t *buffer, int len) { - if (m_isSPI) - { - reg |= 0x80; // needed for read - - uint8_t sbuf[len + 1]; - memset((char *)sbuf, 0, len + 1); - sbuf[0] = reg; - - // We need to do it this way for edison - ie: use a single - // transfer rather than breaking it up into two like we used to. - // This means a buffer copy is now required, but that's the way - // it goes. - - csOn(); - if (m_spi->transfer(sbuf, sbuf, len + 1)) - { - csOff(); - throw std::runtime_error(string(__FUNCTION__) - + ": Spi.transfer(buf) failed"); - } - csOff(); - - // now copy it into user buffer - for (int i=0; ireadBytesReg(reg, buffer, len); + return bmp280_read_regs(m_bmp280, reg, buffer, len); } void BMP280::writeReg(uint8_t reg, uint8_t val) { - if (m_isSPI) - { - reg &= 0x7f; // mask off 0x80 for writing - uint8_t pkt[2] = {reg, val}; - - csOn(); - if (m_spi->transfer(pkt, NULL, 2)) - { - csOff(); - throw std::runtime_error(string(__FUNCTION__) - + ": Spi.transfer() failed"); - } - csOff(); - } - else - { - - mraa::Result rv; - if ((rv = m_i2c->writeReg(reg, val)) != mraa::SUCCESS) - { - throw std::runtime_error(std::string(__FUNCTION__) - + ": I2c.writeReg() failed"); - } - } -} - -void BMP280::clearData() -{ - m_t_fine = 0; - - m_dig_T1 = 0; - m_dig_T2 = 0; - m_dig_T3 = 0; - - m_dig_P1 = 0; - m_dig_P2 = 0; - m_dig_P3 = 0; - m_dig_P4 = 0; - m_dig_P5 = 0; - m_dig_P6 = 0; - m_dig_P7 = 0; - m_dig_P8 = 0; - m_dig_P9 = 0; + if (bmp280_write_reg(m_bmp280, reg, val)) + throw std::runtime_error(string(__FUNCTION__) + + ": bmp280_write_reg() failed"); } uint8_t BMP280::getChipID() { - - return readReg(REG_CHIPID); + return bmp280_get_chip_id(m_bmp280); } void BMP280::reset() { - writeReg(REG_RESET, BMP280_RESET_BYTE); - sleep(1); + bmp280_reset(m_bmp280); } -void BMP280::readCalibrationData() -{ -#if defined(BMP280_USE_TEST_DATA) - cerr << "WARNING: Test data is being used" << endl; - // This data is taken from the datasheet, section 3.12 - m_dig_T1 = 27504; - m_dig_T2 = 26435; - m_dig_T3 = -1000; - - m_dig_P1 = 36477; - m_dig_P2 = -10685; - m_dig_P3 = 3024; - m_dig_P4 = 2855; - m_dig_P5 = 140; - m_dig_P6 = -7; - m_dig_P7 = 15500; - m_dig_P8 = -14600; - m_dig_P9 = 6000; - -#else - - uint8_t calibData[CALIBRATION_BYTES]; - readRegs(REG_CALIB00, calibData, CALIBRATION_BYTES); - - m_dig_T1 = uint16_t((calibData[1] << 8) | calibData[0]); - m_dig_T2 = int16_t((calibData[3] << 8) | calibData[2]); - m_dig_T3 = int16_t((calibData[5] << 8) | calibData[4]); - -# if 0 - cerr << std::dec << "T1: " << (int)m_dig_T1 - << " T2: " << (int)m_dig_T2 - << " T3: " << (int)m_dig_T3 - << endl; -# endif // 0 - - m_dig_P1 = uint16_t((calibData[7] << 8) | calibData[6]); - m_dig_P2 = int16_t((calibData[9] << 8) | calibData[8]); - m_dig_P3 = int16_t((calibData[11] << 8) | calibData[10]); - m_dig_P4 = int16_t((calibData[13] << 8) | calibData[12]); - m_dig_P5 = int16_t((calibData[15] << 8) | calibData[14]); - m_dig_P6 = int16_t((calibData[17] << 8) | calibData[16]); - m_dig_P7 = int16_t((calibData[19] << 8) | calibData[18]); - m_dig_P8 = int16_t((calibData[21] << 8) | calibData[20]); - m_dig_P9 = int16_t((calibData[23] << 8) | calibData[22]); - -# if 0 - cerr << std::dec << "P1: " << (int)m_dig_P1 - << " P2: " << (int)m_dig_P2 - << " P3: " << (int)m_dig_P3 - << " P4: " << (int)m_dig_P4 - << " P5: " << (int)m_dig_P5 - << endl; - cerr << std::dec << "P6: " << (int)m_dig_P6 - << " P7: " << (int)m_dig_P7 - << " P8: " << (int)m_dig_P8 - << " P9: " << (int)m_dig_P9 - << endl; -# endif // 0 - - -#endif -} - float BMP280::getTemperature(bool fahrenheit) { - if (fahrenheit) - return c2f(m_temperature); - else - return m_temperature; + float temperature = bmp280_get_temperature(m_bmp280); + + if (fahrenheit) + return c2f(temperature); + else + return temperature; } float BMP280::getPressure() { - return m_pressure; + return bmp280_get_pressure(m_bmp280); } -void BMP280::setFilter(FILTER_T filter) +void BMP280::setFilter(BMP280_FILTER_T filter) { - uint8_t reg = readReg(REG_CONFIG); - - reg &= ~(_CONFIG_FILTER_MASK << _CONFIG_FILTER_SHIFT); - reg |= (filter << _CONFIG_FILTER_SHIFT); - - writeReg(REG_CONFIG, reg); + bmp280_set_filter(m_bmp280, filter); } -void BMP280::setTimerStandby(T_SB_T tsb) +void BMP280::setTimerStandby(BMP280_T_SB_T tsb) { - uint8_t reg = readReg(REG_CONFIG); - - reg &= ~(_CONFIG_T_SB_MASK << _CONFIG_T_SB_SHIFT); - reg |= (tsb << _CONFIG_T_SB_SHIFT); - - writeReg(REG_CONFIG, reg); + bmp280_set_timer_standby(m_bmp280, tsb); } -void BMP280::setMeasureMode(MODES_T mode) +void BMP280::setMeasureMode(BMP280_MODES_T mode) { - uint8_t reg = readReg(REG_CTRL_MEAS); - - reg &= ~(_CTRL_MEAS_MODE_MASK << _CTRL_MEAS_MODE_SHIFT); - reg |= (mode << _CTRL_MEAS_MODE_SHIFT); - - writeReg(REG_CTRL_MEAS, reg); - m_mode = mode; + bmp280_set_measure_mode(m_bmp280, mode); } -void BMP280::setOversampleRatePressure(OSRS_P_T rate) +void BMP280::setOversampleRatePressure(BMP280_OSRS_P_T rate) { - uint8_t reg = readReg(REG_CTRL_MEAS); - - reg &= ~(_CTRL_MEAS_OSRS_P_MASK << _CTRL_MEAS_OSRS_P_SHIFT); - reg |= (rate << _CTRL_MEAS_OSRS_P_SHIFT); - - writeReg(REG_CTRL_MEAS, reg); + bmp280_set_oversample_rate_pressure(m_bmp280, rate); } -void BMP280::setOversampleRateTemperature(OSRS_T_T rate) +void BMP280::setOversampleRateTemperature(BMP280_OSRS_T_T rate) { - uint8_t reg = readReg(REG_CTRL_MEAS); - - reg &= ~(_CTRL_MEAS_OSRS_T_MASK << _CTRL_MEAS_OSRS_T_SHIFT); - reg |= (rate << _CTRL_MEAS_OSRS_T_SHIFT); - - writeReg(REG_CTRL_MEAS, reg); + bmp280_set_oversample_rate_temperature(m_bmp280, rate); } uint8_t BMP280::getStatus() { - return readReg(REG_STATUS); + return bmp280_get_status(m_bmp280); } -void BMP280::setUsageMode(USAGE_MODE_T mode) +void BMP280::setUsageMode(BMP280_USAGE_MODE_T mode) { - // set up the regs for the given usage mode. These settings come - // from the recomendations in the BMP280 datasheet, section 3.4 - // Filter Selection. - - m_temperature = 0; - m_pressure = 0; - - // set sleep mode first - setMeasureMode(MODE_SLEEP); - - switch (mode) - { - case USAGE_MODE_HANDHELD_LOW_POWER: - setOversampleRatePressure(OSRS_P_OVERSAMPLING_16); - setOversampleRateTemperature(OSRS_T_OVERSAMPLING_2); - setFilter(FILTER_4); - setMeasureMode(MODE_NORMAL); - - break; - - case USAGE_MODE_HANDHELD_DYNAMIC: - setOversampleRatePressure(OSRS_P_OVERSAMPLING_4); - setOversampleRateTemperature(OSRS_T_OVERSAMPLING_1); - setFilter(FILTER_16); - setMeasureMode(MODE_NORMAL); - - break; - - case USAGE_MODE_WEATHER_MONITOR: - setOversampleRatePressure(OSRS_P_OVERSAMPLING_1); - setOversampleRateTemperature(OSRS_T_OVERSAMPLING_1); - setFilter(FILTER_OFF); - setMeasureMode(MODE_FORCED); - - break; - - case USAGE_MODE_FLOOR_CHG_DETECT: - setOversampleRatePressure(OSRS_P_OVERSAMPLING_4); - setOversampleRateTemperature(OSRS_T_OVERSAMPLING_1); - setFilter(FILTER_4); - setMeasureMode(MODE_NORMAL); - - break; - - case USAGE_MODE_DROP_DETECT: - setOversampleRatePressure(OSRS_P_OVERSAMPLING_2); - setOversampleRateTemperature(OSRS_T_OVERSAMPLING_1); - setFilter(FILTER_OFF); - setMeasureMode(MODE_NORMAL); - - break; - - case USAGE_MODE_INDOOR_NAV: - setOversampleRatePressure(OSRS_P_OVERSAMPLING_16); - setOversampleRateTemperature(OSRS_T_OVERSAMPLING_2); - setFilter(FILTER_16); - setMeasureMode(MODE_NORMAL); - - break; - - default: - throw std::logic_error(string(__FUNCTION__) - + ": invalid mode specified"); - } + bmp280_set_usage_mode(m_bmp280, mode); } -void BMP280::csOn() -{ - if (m_gpioCS) - m_gpioCS->write(0); -} - -void BMP280::csOff() -{ - if (m_gpioCS) - m_gpioCS->write(1); -} - -// These functions come from the BMP280 datasheet, section 3.11.3 - -// Returns temperature in DegC, resolution is 0.01 DegC. Output value -// of "5123" equals 51.23 DegC. t_fine carries fine temperature as -// global value -int32_t BMP280::bmp280_compensate_T_int32(int32_t adc_T) -{ - int32_t var1, var2, T; - var1 = ((((adc_T>>3) - ((int32_t)m_dig_T1<<1))) * ((int32_t)m_dig_T2)) >> 11; - var2 = (((((adc_T>>4) - ((int32_t)m_dig_T1)) * ((adc_T>>4) - ((int32_t)m_dig_T1))) >> 12) * - ((int32_t)m_dig_T3)) >> 14; - m_t_fine = var1 + var2; - T = (m_t_fine * 5 + 128) >> 8; - return T; -} - -// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format -// (24 integer bits and 8 fractional bits). Output value of -// “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa -uint32_t BMP280::bmp280_compensate_P_int64(int32_t adc_P) -{ - int64_t var1, var2, p; - var1 = ((int64_t)m_t_fine) - 128000; - var2 = var1 * var1 * (int64_t)m_dig_P6; - var2 = var2 + ((var1*(int64_t)m_dig_P5)<<17); - var2 = var2 + (((int64_t)m_dig_P4)<<35); - var1 = ((var1 * var1 * (int64_t)m_dig_P3)>>8) + ((var1 * (int64_t)m_dig_P2)<<12); - var1 = (((((int64_t)1)<<47)+var1))*((int64_t)m_dig_P1)>>33; - if (var1 == 0) - { - return 0; // avoid exception caused by division by zero - } - p = 1048576-adc_P; - p = (((p<<31)-var2)*3125)/var1; - var1 = (((int64_t)m_dig_P9) * (p>>13) * (p>>13)) >> 25; - var2 = (((int64_t)m_dig_P8) * p) >> 19; - p = ((p + var1 + var2) >> 8) + (((int64_t)m_dig_P7)<<4); - return (uint32_t)p; -} diff --git a/src/bmp280/bmp280.h b/src/bmp280/bmp280.h new file mode 100644 index 00000000..e4ae00a6 --- /dev/null +++ b/src/bmp280/bmp280.h @@ -0,0 +1,346 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "bmp280_regs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file bmp280.h + * @library bmp280 + * @brief C API for the bmp280 and bme280 driver + * + * @include bmp280.c + */ + + /** + * Device context + */ + typedef struct _bmp280_context { + mraa_i2c_context i2c; + mraa_gpio_context gpio; + mraa_spi_context spi; + + // are we doing SPI? + bool isSPI; + + // are we using a bme280? + bool isBME; + + // always stored in C + float temperature; + + // pressure in Pa + float pressure; + + // humidity (relative) + float humidity; + + // sea level pressure in hectoPascals (hPa) + float sea_level_hPA; + + // shared calibration data - set in temp conversion, used in + // pressure conversion. + int32_t t_fine; + + // current operating mode. BMP280_MODE_FORCED requires + // special attention in update() + BMP280_MODES_T mode; + + // calibration data temperature + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + + // calibration data pressure + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + + // calibration data humidity (BME280 only) + uint8_t dig_H1; + int16_t dig_H2; + uint8_t dig_H3; + int16_t dig_H4; + int16_t dig_H5; + int8_t dig_H6; + } *bmp280_context; + + /** + * BMP280 initialization. + * + * This driver supports both the BMP280 and the BME280. The + * BME280 adds a humidity sensor. The device type is detected + * automatically by querying the chip id register. + * + * Both I2C and SPI accesses are supported. For SPI, set the addr + * to -1, and specify a positive integer representing the Chip + * Select (CS) pin for the cs argument. If you are using a + * hardware CS pin you cannot control (Intel Edison), then you can + * connect the proper pin to the hardware CS pin on your MCU and + * supply -1 for cs. The default operating mode is I2C. + * + * @param bus I2C or SPI bus to use. + * @param address The I2C address for this device. Use -1 for SPI. + * @param cs The gpio pin to use for the SPI Chip Select. Use -1 + * for I2C, or for SPI with a hardware controlled pin. + * @return Device context, or NULL on error. + */ + bmp280_context bmp280_init(int bus, int addr, int cs); + + /** + * BMP280 close function. + * + * @param dev Device context. + */ + void bmp280_close(bmp280_context dev); + + /** + * Update the internal stored values from sensor data. + * + * @param dev Device context. + * @return UPM result. + */ + upm_result_t bmp280_update(const bmp280_context dev); + + /** + * Return the chip ID. + * + * @param dev Device context. + * @return The chip ID (BMP280_CHIPID). + */ + uint8_t bmp280_get_chip_id(const bmp280_context dev); + + /** + * Reset the sensor, as if by a power-on-reset. + * + * @param dev Device context. + */ + void bmp280_reset(const bmp280_context dev); + + /** + * Return the current measured temperature. Note, this is not + * ambient temperature - this is the temperature used to fine tune + * the pressure measurement. bmp280_update() must have been called prior + * to calling this method. + * + * @param dev Device context. + * @return The temperature in degrees Celsius. + */ + float bmp280_get_temperature(const bmp280_context dev); + + /** + * Return the current measured relative humidity (bme280 only). + * bmp280_update() must have been called prior to calling this + * method. For a bmp280, the returned value will always be 0. + * + * @param dev Device context. + * @return The relative humidity in percent. + */ + float bmp280_get_humidity(const bmp280_context dev); + + /** + * Return the current measured pressure in Pascals (Pa). + * bmp280_update() must have been called prior to calling this + * method. + * + * @param dev Device context. + * @return The pressure in Pascals (Pa). + */ + float bmp280_get_pressure(const bmp280_context dev); + + /** + * Set the default Sea Level Pressure in hectoPascals (hPA). A + * default of 1013.25 (101325 Pa) is set during bmp280_init(). + * This value is used for altitude computation. + * + * @param dev Device context. + * @param seaLevelhPA The pressure at sea level in hectoPascals + * (hPa). + */ + void bmp280_set_sea_level_pressure(const bmp280_context dev, + float seaLevelhPA); + + /** + * Return the current computed altitude in meters. + * bmp280_update() must have been called prior to calling this + * method. + * + * Computing this value requires knowing the pressure at sea + * level. bmp280_init() sets this by default to 1013.25 hPA. Use + * bmp280_set_sea_level_pressure() to change this value. + * + * @param dev Device context. + * @return The computed altitude in meters. + */ + float bmp280_get_altitude(const bmp280_context dev); + + /** + * Set a general usage mode. This function can be used to + * configure the filters and oversampling for a particular use + * case. These setting are documented in the BMP280 datasheet. + * The default mode set in the bmp280_init() function is + * BMP280_USAGE_MODE_INDOOR_NAV, the highest resolution mode. + * + * @param dev Device context. + * @param mode One of the BMP280_USAGE_MODE_T values. + */ + void bmp280_set_usage_mode(const bmp280_context dev, + BMP280_USAGE_MODE_T mode); + + /** + * Set the temperature sensor oversampling parameter. See the + * data sheet for details. This value can be automatically set to + * a suitable value by using one of the predefined modes for + * bmp280_set_usage_mode(). + * + * @param dev Device context. + * @param mode One of the BMP280_OSRS_T_T values. + */ + void bmp280_set_oversample_rate_temperature(const bmp280_context dev, + BMP280_OSRS_T_T rate); + + /** + * Set the pressure sensor oversampling parameter. See the + * data sheet for details. This value can be automatically set to + * a suitable value by using one of the predefined modes for + * bmp280_set_usage_mode(). + * + * @param dev Device context. + * @param mode One of the BMP280_OSRS_P_T values. + */ + void bmp280_set_oversample_rate_pressure(const bmp280_context dev, + BMP280_OSRS_P_T rate); + + /** + * Set the humidity sensor oversampling parameter (BME280 only). + * See the data sheet for details. This value can be + * automatically set to a suitable value by using one of the + * predefined modes for bmp280_set_usage_mode(). + * + * @param dev Device context. + * @param mode One of the BME280_OSRS_H_T values. + */ + void bmp280_set_oversample_rate_humidity(const bmp280_context dev, + BME280_OSRS_H_T rate); + + /** + * Set the timer standby value. When in NORMAL operating mode, + * this timer governs how long the chip will wait before + * performing a measurement. See the data sheet for details. + * + * @param dev Device context. + * @param mode One of the BMP280_T_SB_T values. + */ + void bmp280_set_timer_standby(const bmp280_context dev, + BMP280_T_SB_T tsb); + + /** + * Set the IIR filtering parameter. See the data sheet for + * details. This value can be automatically set to a suitable + * value by using one of the predefined modes for + * bmp280_set_usage_mode(). + * + * @param dev Device context. + * @param mode One of the BMP280_FILTER_T values. + */ + void bmp280_set_filter(const bmp280_context dev, BMP280_FILTER_T filter); + + /** + * Set the default measuring mode. Basic values are forced, + * sleep, and normal. See the data sheet for details. This value + * can be automatically set to a suitable value by using one of + * the predefined modes for bmp280_set_usage_mode(). + * + * @param dev Device context. + * @param mode One of the BMP280_MODES_T values. + */ + void bmp280_set_measure_mode(const bmp280_context dev, + BMP280_MODES_T mode); + + /** + * Return the value of the BMP280_REG_STATUS register. + * + * @param dev Device context. + * @return Contents of the status register. + */ + uint8_t bmp280_get_status(const bmp280_context dev); + + /** + * Read a register. + * + * @param dev Device context. + * @param reg The register to read + * @return The value of the register + */ + uint8_t bmp280_read_reg(const bmp280_context dev, uint8_t reg); + + /** + * Read contiguous registers into a buffer. This is a low level + * function, and should not be used unless you know what you are + * doing. + * + * @param dev Device context. + * @param buffer The buffer to store the results + * @param len The number of registers to read + * @return The number of bytes read, or -1 on error + */ + int bmp280_read_regs(const bmp280_context dev, + uint8_t reg, uint8_t *buffer, int len); + + /** + * Write to a register. This is a low level function, and should + * not be used unless you know what you are doing. + * + * @param dev Device context. + * @param reg The register to write to + * @param val The value to write + * @return UPM result. + */ + upm_result_t bmp280_write_reg(const bmp280_context dev, + uint8_t reg, uint8_t val); + +#ifdef __cplusplus +} +#endif diff --git a/src/bmp280/bmp280.hpp b/src/bmp280/bmp280.hpp index 2eda9fed..e8913ab5 100644 --- a/src/bmp280/bmp280.hpp +++ b/src/bmp280/bmp280.hpp @@ -1,6 +1,8 @@ /* * Author: Jon Trulson - * Copyright (c) 2016 Intel Corporation. + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,467 +26,249 @@ #pragma once #include -#include -#include -#include +#include "bmp280.h" #include "interfaces/iPressureSensor.hpp" #include "interfaces/iTemperatureSensor.hpp" -#define BMP280_DEFAULT_I2C_BUS 0 -#define BMP280_DEFAULT_SPI_BUS 0 -#define BMP280_DEFAULT_ADDR 0x77 -#define BMP280_DEFAULT_CHIPID 0x58 - namespace upm { - /** - * @brief BMP280 Digital Pressure Sensor - * @defgroup bmp280 libupm-bmp280 - * @ingroup i2c spi gpio pressure - */ - - /** - * @library bmp280 - * @sensor bmp280 - * @comname Digital Pressure Sensor - * @type pressure - * @man adafruit - * @con i2c spi gpio - * @web https://www.adafruit.com/products/2651 - * - * @brief API for the BMP280 Digital Pressure Sensor - * - * The BMP280 is an absolute barometric pressure sensor especially - * designed for mobile applications. The sensor module is housed in - * an extremely compact 8-pin metal-lid LGA package with a footprint - * of only 2.0 * 2.5 mm2 and 0.95 mm package height. Its small - * dimensions and its low power consumption of 2.7 uA @1Hz allow the - * implementation in battery driven devices such as mobile phones, - * GPS modules or watches. - * - * As the successor to the widely adopted BMP180, the BMP280 - * delivers high performance in all applications that require - * precise pressure measurement. The BMP280 operates at lower noise, - * supports new filter modes and an SPI interface within a footprint - * 63% smaller than the BMP180. - * - * @snippet bmp280.cxx Interesting - */ - - class BMP280 : public ITemperatureSensor, public IPressureSensor { - public: - // special reset byte - const uint8_t BMP280_RESET_BYTE = 0xb6; - - // number of bytes of stored calibration data - const int CALIBRATION_BYTES = 26; - /** - * BMP280 registers + * @brief BMP280 Digital Pressure Sensor + * @defgroup bmp280 libupm-bmp280 + * @ingroup i2c spi gpio pressure */ - typedef enum : uint8_t { - // Do not write into reserved bits. - - // read-only factory calibration data - REG_CALIB00 = 0x88, - REG_CALIB01 = 0x89, - REG_CALIB02 = 0x8a, - REG_CALIB03 = 0x8b, - REG_CALIB04 = 0x8c, - REG_CALIB05 = 0x8d, - REG_CALIB06 = 0x8e, - REG_CALIB07 = 0x8f, - REG_CALIB08 = 0x90, - REG_CALIB09 = 0x91, - REG_CALIB10 = 0x92, - REG_CALIB11 = 0x93, - REG_CALIB12 = 0x94, - REG_CALIB13 = 0x95, - REG_CALIB14 = 0x96, - REG_CALIB15 = 0x97, - REG_CALIB16 = 0x98, - REG_CALIB17 = 0x99, - REG_CALIB18 = 0x9a, - REG_CALIB19 = 0x9b, - REG_CALIB20 = 0x9c, - REG_CALIB21 = 0x9d, - REG_CALIB22 = 0x9e, - REG_CALIB23 = 0x9f, - REG_CALIB24 = 0xa0, - REG_CALIB25 = 0xa1, - - REG_CHIPID = 0xd0, - REG_RESET = 0xe0, - REG_STATUS = 0xf3, - REG_CTRL_MEAS = 0xf4, - REG_CONFIG = 0xf5, - - REG_PRESSURE_MSB = 0xf7, - REG_PRESSURE_LSB = 0xf8, - REG_PRESSURE_XLSB = 0xf9, - - REG_TEMPERATURE_MSB = 0xfa, - REG_TEMPERATURE_LSB = 0xfb, - REG_TEMPERATURE_XLSB = 0xfc - } BMP280_REGS_T; /** - * REG_CONFIG bits - */ - typedef enum { - CONFIG_SPI3W_EN = 0x01, - - // 0x02 reserved - - CONFIG_FILTER0 = 0x04, - CONFIG_FILTER1 = 0x08, - CONFIG_FILTER2 = 0x10, - _CONFIG_FILTER_MASK = 7, - _CONFIG_FILTER_SHIFT = 2, - - CONFIG_T_SB0 = 0x20, - CONFIG_T_SB1 = 0x40, - CONFIG_T_SB2 = 0x80, - _CONFIG_T_SB_MASK = 7, - _CONFIG_T_SB_SHIFT = 5 - } CONFIG_BITS_T; - - /** - * FILTER values (samples to reach >= 75% of step response) - */ - typedef enum { - FILTER_OFF = 0, // 1 samples - FILTER_2 = 1, // 2 - FILTER_4 = 2, // 5 - FILTER_8 = 3, // 11 - FILTER_16 = 4 // 22 - } FILTER_T; - - /** - * T_SB values (timer standby) - */ - typedef enum { - T_SB_0_5 = 0, // 0.5ms - T_SB_62_5 = 1, // 62.5ms - T_SB_125 = 2, // 125ms - T_SB_250 = 3, - T_SB_500 = 4, - T_SB_1000 = 5, - T_SB_2000 = 6, // bme280 - 10ms - T_SB_4000 = 7 // bme280 - 20ms - } T_SB_T; - - - /** - * REG_CTRL_MEAS bits - */ - typedef enum { - CTRL_MEAS_MODE0 = 0x01, - CTRL_MEAS_MODE1 = 0x02, - _CTRL_MEAS_MODE_MASK = 3, - _CTRL_MEAS_MODE_SHIFT = 0, - - CTRL_MEAS_OSRS_P0 = 0x04, - CTRL_MEAS_OSRS_P1 = 0x08, - CTRL_MEAS_OSRS_P2 = 0x10, - _CTRL_MEAS_OSRS_P_MASK = 7, - _CTRL_MEAS_OSRS_P_SHIFT = 2, - - CTRL_MEAS_OSRS_T0 = 0x04, - CTRL_MEAS_OSRS_T1 = 0x08, - CTRL_MEAS_OSRS_T2 = 0x10, - _CTRL_MEAS_OSRS_T_MASK = 7, - _CTRL_MEAS_OSRS_T_SHIFT = 5 - } CTRL_MEAS_T; - - /** - * CTRL_MEAS_MODE values - */ - typedef enum { - MODE_SLEEP = 0, - MODE_FORCED = 1, - // 2 is also FORCED mode - MODE_NORMAL = 3 - } MODES_T; - - /** - * CTRL_MEAS_OSRS_P values - */ - typedef enum { - OSRS_P_SKIPPED = 0, - OSRS_P_OVERSAMPLING_1 = 1, // x1 - OSRS_P_OVERSAMPLING_2 = 2, // x2 - OSRS_P_OVERSAMPLING_4 = 3, - OSRS_P_OVERSAMPLING_8 = 4, - OSRS_P_OVERSAMPLING_16 = 5 - } OSRS_P_T; - - /** - * CTRL_MEAS_OSRS_T values - */ - typedef enum { - OSRS_T_SKIPPED = 0, - OSRS_T_OVERSAMPLING_1 = 1, // x1 - OSRS_T_OVERSAMPLING_2 = 2, // x2 - OSRS_T_OVERSAMPLING_4 = 3, - OSRS_T_OVERSAMPLING_8 = 4, - OSRS_T_OVERSAMPLING_16 = 5 - } OSRS_T_T; - - /** - * REG_STATUS bits - */ - typedef enum { - STATUS_IM_UPDATE = 0x01, - // 0x02-0x04 reserved - STATUS_MEASURING = 0x08 - // 0x10-0x80 reserved - } STATUS_T; - - /** - * USAGE_MODE values. This is a fake specification to configure - * the various knobs based on their typical use modes, as - * recommended by Bosch. - */ - typedef enum { - USAGE_MODE_HANDHELD_LOW_POWER = 0, - USAGE_MODE_HANDHELD_DYNAMIC = 1, - USAGE_MODE_WEATHER_MONITOR = 2, // lowest power consumption - USAGE_MODE_FLOOR_CHG_DETECT = 3, - USAGE_MODE_DROP_DETECT = 4, - USAGE_MODE_INDOOR_NAV = 5 // highest resolution - } USAGE_MODE_T; - - /** - * BMP280 constructor. + * @library bmp280 + * @sensor bmp280 + * @comname Digital Pressure Sensor + * @type pressure + * @man adafruit + * @con i2c spi gpio + * @web https://www.adafruit.com/products/2651 * - * This device can support both I2C and SPI. For SPI, set the addr - * to -1, and specify a positive integer representing the Chip - * Select (CS) pin for the cs argument. If you are using a - * hardware CS pin, then you can connect the proper pin to the - * hardware CS pin on your MCU and supply -1 for cs. The default - * operating mode is I2C. + * @brief API for the BMP280 Digital Pressure Sensor * - * @param bus I2C or SPI bus to use. - * @param address The address for this device. -1 for SPI. - * @param cs The gpio pin to use for the SPI Chip Select. -1 for - * I2C or for SPI with a hardware controlled pin. - * @param theChipID The chip ID to use for validation - */ - BMP280(int bus=BMP280_DEFAULT_I2C_BUS, int addr=BMP280_DEFAULT_ADDR, - int cs=-1, uint8_t theChipID=BMP280_DEFAULT_CHIPID); - - /** - * BMP280 Destructor. - */ - virtual ~BMP280(); - - /** - * Update the internal stored values from sensor data. - */ - virtual void update(); - - /** - * Return the chip ID. + * The BMP280 is an absolute barometric pressure sensor especially + * designed for mobile applications. The sensor module is housed in + * an extremely compact 8-pin metal-lid LGA package with a footprint + * of only 2.0 * 2.5 mm2 and 0.95 mm package height. Its small + * dimensions and its low power consumption of 2.7 uA @1Hz allow the + * implementation in battery driven devices such as mobile phones, + * GPS modules or watches. * - * @return The chip ID (BMP280_CHIPID). - */ - uint8_t getChipID(); - - /** - * Reset the sensor, as if by a power-on-reset. - */ - void reset(); - - /** - * Return the current measured temperature. Note, this is not - * ambient temperature - this is the temperature used to fine tune - * the pressure measurement. update() must have been called prior - * to calling this method. + * As the successor to the widely adopted BMP180, the BMP280 + * delivers high performance in all applications that require + * precise pressure measurement. The BMP280 operates at lower noise, + * supports new filter modes and an SPI interface within a footprint + * 63% smaller than the BMP180. * - * @param fahrenheit true to return data in Fahrenheit, false for - * Celicus. Celsius is the default. - * @return The temperature in degrees Celsius or Fahrenheit. + * @snippet bmp280.cxx Interesting */ - float getTemperature(bool fahrenheit=false); - /** - * Return the current measured pressure in Pascals (Pa). update() - * must have been called prior to calling this method. - * - * @return The pressure in Pascals (Pa). - */ - float getPressure(); + class BMP280 : public ITemperatureSensor, public IPressureSensor { + public: - /** - * Return the current computed altitude in meters. update() - * must have been called prior to calling this method. - * - * @param seaLevelhPA The pressure at sea level in hectoPascals - * (hPa). The default is 1013.25 hPA, (101325 Pa). - * @return The computed altitude in meters. - */ - float getAltitude(float seaLevelhPA=1013.25); + /** + * BMP280 constructor. + * + * This driver supports both the BMP280 and the BME280. The + * BME280 adds a humidity sensor. The device type is detected + * automatically by querying the chip id register. + * + * This device can support both I2C and SPI. For SPI, set the addr + * to -1, and specify a positive integer representing the Chip + * Select (CS) pin for the cs argument. If you are using a + * hardware CS pin, then you can connect the proper pin to the + * hardware CS pin on your MCU and supply -1 for cs. The default + * operating mode is I2C. + * + * @param bus I2C or SPI bus to use. + * @param address The I2C address for this device. Use -1 for SPI. + * @param cs The gpio pin to use for the SPI Chip Select. Use -1 + * for I2C, or for SPI with a hardware controlled pin. + * @throws std::runtime_error on failure. + */ + BMP280(int bus=BMP280_DEFAULT_I2C_BUS, int addr=BMP280_DEFAULT_ADDR, + int cs=-1); - /** - * Set a general usage mode. This function can be used to - * configure the filters and oversampling for a particular use - * case. These setting are documented in the BMP280 datasheet. - * The default mode set in the constructor is - * USAGE_MODE_INDOOR_NAV, the highest resolution mode. - * - * @param mode One of the USAGE_MODE_T values. - */ - virtual void setUsageMode(USAGE_MODE_T mode); + /** + * BMP280 Destructor. + */ + virtual ~BMP280(); - /** - * Set the temperature sensor oversampling parameter. See the - * data sheet for details. This value can be automatically set to - * a suitable value by using one of the predefined modes for - * setUsageMode(). - * - * @param mode One of the OSRS_T_T values. - */ - void setOversampleRateTemperature(OSRS_T_T rate); + /** + * Update the internal stored values from sensor data. + * + * @throws std::runtime_error on failure. + */ + virtual void update(); - /** - * Set the pressure sensor oversampling parameter. See the - * data sheet for details. This value can be automatically set to - * a suitable value by using one of the predefined modes for - * setUsageMode(). - * - * @param mode One of the OSRS_P_T values. - */ - void setOversampleRatePressure(OSRS_P_T rate); + /** + * Return the chip ID. + * + * @return The chip ID (BMP280_CHIPID). + */ + uint8_t getChipID(); - /** - * Set the timer standby value. When in NORMAL operating mode, - * this timer governs how long the chip will wait before - * performing a measurement. See the data sheet for details. - * - * @param mode One of the T_SB_T values. - */ - void setTimerStandby(T_SB_T tsb); + /** + * Reset the sensor, as if by a power-on-reset. + */ + void reset(); - /** - * Set the IIR filtering parameter. See the data sheet for - * details. This value can be automatically set to a suitable - * value by using one of the predefined modes for setUsageMode(). - * - * @param mode One of the FILTER_T values. - */ - void setFilter(FILTER_T filter); + /** + * Return the current measured temperature. Note, this is not + * ambient temperature - this is the temperature used to fine tune + * the pressure measurement. update() must have been called prior + * to calling this method. + * + * @param fahrenheit true to return data in Fahrenheit, false for + * Celicus. Celsius is the default. + * @return The temperature in degrees Celsius or Fahrenheit. + */ + float getTemperature(bool fahrenheit=false); - /** - * Set the default measuring mode. Basic values are forced, - * sleep, and normal. See the data sheet for details. This value - * can be automatically set to a suitable value by using one of - * the predefined modes for setUsageMode(). - * - * @param mode One of the MODES_T values. - */ - void setMeasureMode(MODES_T mode); + /** + * Return the current measured pressure in Pascals (Pa). update() + * must have been called prior to calling this method. + * + * @return The pressure in Pascals (Pa). + */ + float getPressure(); + + /** + * Set the pressure at sea level in hecto-Pascals (hPA). This + * value is used to compute the altitude based on the + * pressure. At driver initialization time, this value is set + * to 1013.25 hPA. + * + * @param seaLevelhPA The pressure at sea level in hectoPascals + * (hPa). The default is 1013.25 hPA, (101325 Pa). + * @return The computed altitude in meters. + */ + void setSeaLevelPreassure(float seaLevelhPA=1013.25); + + /** + * Return the current computed altitude in meters. update() + * must have been called prior to calling this method. + * + * @return The computed altitude in meters. + */ + float getAltitude(); + + /** + * Set a general usage mode. This function can be used to + * configure the filters and oversampling for a particular use + * case. These setting are documented in the BMP280 datasheet. + * The default mode set in the constructor is + * USAGE_MODE_INDOOR_NAV, the highest resolution mode. + * + * @param mode One of the BMP280_USAGE_MODE_T values. + */ + virtual void setUsageMode(BMP280_USAGE_MODE_T mode); + + /** + * Set the temperature sensor oversampling parameter. See the + * data sheet for details. This value can be automatically set to + * a suitable value by using one of the predefined modes for + * setUsageMode(). + * + * @param mode One of the BMP280_OSRS_T_T values. + */ + void setOversampleRateTemperature(BMP280_OSRS_T_T rate); + + /** + * Set the pressure sensor oversampling parameter. See the + * data sheet for details. This value can be automatically set to + * a suitable value by using one of the predefined modes for + * setUsageMode(). + * + * @param mode One of the BMP280_OSRS_P_T values. + */ + void setOversampleRatePressure(BMP280_OSRS_P_T rate); + + /** + * Set the timer standby value. When in NORMAL operating mode, + * this timer governs how long the chip will wait before + * performing a measurement. See the data sheet for details. + * + * @param mode One of the BMP280_T_SB_T values. + */ + void setTimerStandby(BMP280_T_SB_T tsb); + + /** + * Set the IIR filtering parameter. See the data sheet for + * details. This value can be automatically set to a suitable + * value by using one of the predefined modes for setUsageMode(). + * + * @param mode One of the BMP280_FILTER_T values. + */ + void setFilter(BMP280_FILTER_T filter); + + /** + * Set the default measuring mode. Basic values are forced, + * sleep, and normal. See the data sheet for details. This value + * can be automatically set to a suitable value by using one of + * the predefined modes for setUsageMode(). + * + * @param mode One of the BMP280_MODES_T values. + */ + void setMeasureMode(BMP280_MODES_T mode); - // Interface support - const char *getModuleName() - { - return "BMP280"; + // Interface support + const char *getModuleName() + { + return "BMP280"; + }; + + int getTemperatureCelsius() + { + return int(getTemperature(false)); + }; + + int getPressurePa() + { + return int(getPressure()); + }; + + protected: + bmp280_context m_bmp280; + + /** + * Return the value of the BMP280_REG_STATUS register. + * + * @return Contents of the status register. + */ + uint8_t getStatus(); + + /** + * Read a register. + * + * @param reg The register to read + * @return The value of the register + */ + uint8_t readReg(uint8_t reg); + + /** + * Read contiguous registers into a buffer. + * + * @param buffer The buffer to store the results + * @param len The number of registers to read + * @return The number of bytes read, or -1 on error + */ + int readRegs(uint8_t reg, uint8_t *buffer, int len); + + /** + * Write to a register + * + * @param reg The register to write to + * @param val The value to write + * @throws std::runtime_error on failure. + */ + void writeReg(uint8_t reg, uint8_t val); + + private: }; - - int getTemperatureCelsius() - { - return int(getTemperature(false)); - }; - - int getPressurePa() - { - return int(getPressure()); - }; - - protected: - mraa::I2c *m_i2c; - mraa::Spi *m_spi; - mraa::Gpio *m_gpioCS; - - uint8_t m_addr; - - // always stored in C - float m_temperature; - - // pressure in Pa - float m_pressure; - - // shared calibration data - set in temp conversion, used in - // pressure conversion. - int32_t m_t_fine; - - // current operating mode. MODE_FORCED requires special attention - // in update() - MODES_T m_mode; - - // return the status register - uint8_t getStatus(); - - /** - * Read a register. - * - * @param reg The register to read - * @return The value of the register - */ - uint8_t readReg(uint8_t reg); - - /** - * Read contiguous registers into a buffer. - * - * @param buffer The buffer to store the results - * @param len The number of registers to read - * @return The number of bytes read, or -1 on error - */ - int readRegs(uint8_t reg, uint8_t *buffer, int len); - - /** - * Write to a register - * - * @param reg The register to write to - * @param val The value to write - */ - void writeReg(uint8_t reg, uint8_t val); - - // clear member data... - void clearData(); - - // read the calibration data - virtual void readCalibrationData(); - - // SPI chip select - void csOn(); - void csOff(); - - private: - // are we doing SPI? - bool m_isSPI; - - // calibration data temperature - uint16_t m_dig_T1; - int16_t m_dig_T2; - int16_t m_dig_T3; - - // calibration data pressure - uint16_t m_dig_P1; - int16_t m_dig_P2; - int16_t m_dig_P3; - int16_t m_dig_P4; - int16_t m_dig_P5; - int16_t m_dig_P6; - int16_t m_dig_P7; - int16_t m_dig_P8; - int16_t m_dig_P9; - - // Bosch supplied conversion/compensation functions from the - // datasheet. - int32_t bmp280_compensate_T_int32(int32_t adc_T); - uint32_t bmp280_compensate_P_int64(int32_t adc_P); - }; } diff --git a/src/bmp280/bmp280_regs.h b/src/bmp280/bmp280_regs.h new file mode 100644 index 00000000..cbeba966 --- /dev/null +++ b/src/bmp280/bmp280_regs.h @@ -0,0 +1,290 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016-2017 Intel Corporation. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +// BMP280 +#define BMP280_DEFAULT_I2C_BUS 0 +#define BMP280_DEFAULT_SPI_BUS 0 +#define BMP280_DEFAULT_ADDR 0x77 +#define BMP280_CHIPID 0x58 + +// BME280 +#define BME280_DEFAULT_I2C_BUS 0 +#define BME280_DEFAULT_SPI_BUS 0 +#define BME280_DEFAULT_ADDR 0x77 +#define BME280_CHIPID 0x60 + +// default sea level pressure in hPA +#define BMP280_SEA_LEVEL_HPA (1013.25) + +// special reset byte, same for bme280 +#define BMP280_RESET_BYTE 0xb6 + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * BMP280 registers + */ + typedef enum { + // Do not write into reserved bits. + + // read-only factory calibration data + BMP280_REG_CALIB00 = 0x88, + BMP280_REG_CALIB01 = 0x89, + BMP280_REG_CALIB02 = 0x8a, + BMP280_REG_CALIB03 = 0x8b, + BMP280_REG_CALIB04 = 0x8c, + BMP280_REG_CALIB05 = 0x8d, + BMP280_REG_CALIB06 = 0x8e, + BMP280_REG_CALIB07 = 0x8f, + BMP280_REG_CALIB08 = 0x90, + BMP280_REG_CALIB09 = 0x91, + BMP280_REG_CALIB10 = 0x92, + BMP280_REG_CALIB11 = 0x93, + BMP280_REG_CALIB12 = 0x94, + BMP280_REG_CALIB13 = 0x95, + BMP280_REG_CALIB14 = 0x96, + BMP280_REG_CALIB15 = 0x97, + BMP280_REG_CALIB16 = 0x98, + BMP280_REG_CALIB17 = 0x99, + BMP280_REG_CALIB18 = 0x9a, + BMP280_REG_CALIB19 = 0x9b, + BMP280_REG_CALIB20 = 0x9c, + BMP280_REG_CALIB21 = 0x9d, + BMP280_REG_CALIB22 = 0x9e, + BMP280_REG_CALIB23 = 0x9f, + BMP280_REG_CALIB24 = 0xa0, + BMP280_REG_CALIB25 = 0xa1, + + BMP280_REG_CHIPID = 0xd0, + BMP280_REG_RESET = 0xe0, + BMP280_REG_STATUS = 0xf3, + BMP280_REG_CTRL_MEAS = 0xf4, + BMP280_REG_CONFIG = 0xf5, + + BMP280_REG_PRESSURE_MSB = 0xf7, + BMP280_REG_PRESSURE_LSB = 0xf8, + BMP280_REG_PRESSURE_XLSB = 0xf9, + + BMP280_REG_TEMPERATURE_MSB = 0xfa, + BMP280_REG_TEMPERATURE_LSB = 0xfb, + BMP280_REG_TEMPERATURE_XLSB = 0xfc + } BMP280_REGS_T; + + /** + * BMP280_REG_CONFIG bits + */ + typedef enum { + BMP280_CONFIG_SPI3W_EN = 0x01, + + // 0x02 reserved + + BMP280_CONFIG_FILTER0 = 0x04, + BMP280_CONFIG_FILTER1 = 0x08, + BMP280_CONFIG_FILTER2 = 0x10, + _BMP280_CONFIG_FILTER_MASK = 7, + _BMP280_CONFIG_FILTER_SHIFT = 2, + + BMP280_CONFIG_T_SB0 = 0x20, + BMP280_CONFIG_T_SB1 = 0x40, + BMP280_CONFIG_T_SB2 = 0x80, + _BMP280_CONFIG_T_SB_MASK = 7, + _BMP280_CONFIG_T_SB_SHIFT = 5 + } BMP280_CONFIG_BITS_T; + + /** + * BMP280_FILTER values (samples to reach >= 75% of step response) + */ + typedef enum { + BMP280_FILTER_OFF = 0, // 1 samples + BMP280_FILTER_2 = 1, // 2 + BMP280_FILTER_4 = 2, // 5 + BMP280_FILTER_8 = 3, // 11 + BMP280_FILTER_16 = 4 // 22 + } BMP280_FILTER_T; + + /** + * BMP280_T_SB values (timer standby) + */ + typedef enum { + BMP280_T_SB_0_5 = 0, // 0.5ms + BMP280_T_SB_62_5 = 1, // 62.5ms + BMP280_T_SB_125 = 2, // 125ms + BMP280_T_SB_250 = 3, + BMP280_T_SB_500 = 4, + BMP280_T_SB_1000 = 5, + BMP280_T_SB_2000 = 6, // bme280 - 10ms + BMP280_T_SB_4000 = 7 // bme280 - 20ms + } BMP280_T_SB_T; + + + /** + * BMP280_REG_CTRL_MEAS bits + */ + typedef enum { + BMP280_CTRL_MEAS_MODE0 = 0x01, + BMP280_CTRL_MEAS_MODE1 = 0x02, + _BMP280_CTRL_MEAS_MODE_MASK = 3, + _BMP280_CTRL_MEAS_MODE_SHIFT = 0, + + BMP280_CTRL_MEAS_OSRS_P0 = 0x04, + BMP280_CTRL_MEAS_OSRS_P1 = 0x08, + BMP280_CTRL_MEAS_OSRS_P2 = 0x10, + _BMP280_CTRL_MEAS_OSRS_P_MASK = 7, + _BMP280_CTRL_MEAS_OSRS_P_SHIFT = 2, + + BMP280_CTRL_MEAS_OSRS_T0 = 0x04, + BMP280_CTRL_MEAS_OSRS_T1 = 0x08, + BMP280_CTRL_MEAS_OSRS_T2 = 0x10, + _BMP280_CTRL_MEAS_OSRS_T_MASK = 7, + _BMP280_CTRL_MEAS_OSRS_T_SHIFT = 5 + } BMP280_CTRL_MEAS_T; + + /** + * BMP280_CTRL_MEAS_MODE values + */ + typedef enum { + BMP280_MODE_SLEEP = 0, + BMP280_MODE_FORCED = 1, + // 2 is also FORCED mode + BMP280_MODE_NORMAL = 3 + } BMP280_MODES_T; + + /** + * BMP280_CTRL_MEAS_OSRS_P values + */ + typedef enum { + BMP280_OSRS_P_SKIPPED = 0, + BMP280_OSRS_P_OVERSAMPLING_1 = 1, // x1 + BMP280_OSRS_P_OVERSAMPLING_2 = 2, // x2 + BMP280_OSRS_P_OVERSAMPLING_4 = 3, + BMP280_OSRS_P_OVERSAMPLING_8 = 4, + BMP280_OSRS_P_OVERSAMPLING_16 = 5 + } BMP280_OSRS_P_T; + + /** + * BMP280_CTRL_MEAS_OSRS_T values + */ + typedef enum { + BMP280_OSRS_T_SKIPPED = 0, + BMP280_OSRS_T_OVERSAMPLING_1 = 1, // x1 + BMP280_OSRS_T_OVERSAMPLING_2 = 2, // x2 + BMP280_OSRS_T_OVERSAMPLING_4 = 3, + BMP280_OSRS_T_OVERSAMPLING_8 = 4, + BMP280_OSRS_T_OVERSAMPLING_16 = 5 + } BMP280_OSRS_T_T; + + /** + * BMP280_REG_STATUS bits + */ + typedef enum { + BMP280_STATUS_IM_UPDATE = 0x01, + // 0x02-0x04 reserved + BMP280_STATUS_MEASURING = 0x08 + // 0x10-0x80 reserved + } BMP280_STATUS_T; + + /** + * BMP280_USAGE_MODE values. This is a fake specification to + * configure the various knobs based on their typical use modes, + * as recommended by Bosch. + */ + typedef enum { + BMP280_USAGE_MODE_HANDHELD_LOW_POWER = 0, + BMP280_USAGE_MODE_HANDHELD_DYNAMIC = 1, + BMP280_USAGE_MODE_WEATHER_MONITOR = 2, // lowest power consumption + BMP280_USAGE_MODE_FLOOR_CHG_DETECT = 3, + BMP280_USAGE_MODE_DROP_DETECT = 4, + BMP280_USAGE_MODE_INDOOR_NAV = 5 // highest resolution + } BMP280_USAGE_MODE_T; + + // The following are registers specific to the BME280. The BME280 + // is identical to the BMP280 with the exception of humidity + // sensor support and the corresponding registers needed to + // support it. + + /** + * BME280 registers + */ + typedef enum { + // Do not write into reserved bits. + + // We only specify those registers specific to the BME280. The + // rest of them can be found in the BMP280 header file. + + // read-only factory calibration data for humidity + + BME280_REG_CALIB_DIG_H1 = 0xa1, + + BME280_REG_CALIB_DIG_H2_LSB = 0xe1, + BME280_REG_CALIB_DIG_H2_MSB = 0xe2, + + BME280_REG_CALIB_DIG_H3 = 0xe3, + + BME280_REG_CALIB_DIG_H4_0 = 0xe4, // bits 8 -> 11:4 + BME280_REG_CALIB_DIG_H4_1 = 0xe5, // bits 3:0 -> 3:0 + + BME280_REG_CALIB_DIG_H5_0 = 0xe5, // bits 7:4 -> 3:0 + BME280_REG_CALIB_DIG_H5_1 = 0xe6, // bits 8 -> 11:4 + + BME280_REG_CALIB_DIG_H6 = 0xe7, + + BME280_REG_CTRL_HUM = 0xf2, + + BME280_REG_HUMIDITY_MSB = 0xfd, + BME280_REG_HUMIDITY_LSB = 0xfe + } BME280_REGS_T; + + /** + * BME280_REG_CTRL_HUM bits + */ + typedef enum { + BME280_CTRL_HUM_OSRS_H0 = 0x01, + BME280_CTRL_HUM_OSRS_H1 = 0x02, + BME280_CTRL_HUM_OSRS_H2 = 0x04, + _BME280_CTRL_HUM_OSRS_H_MASK = 3, + _BME280_CTRL_HUM_OSRS_H_SHIFT = 0 + + // 0x08-0x80 reserved + } BME280_CTRL_HUM_T; + + /** + * BME280_CTRL_HUM_OSRS_H values + */ + typedef enum { + BME280_OSRS_H_SKIPPED = 0, + BME280_OSRS_H_OVERSAMPLING_1 = 1, // x1 + BME280_OSRS_H_OVERSAMPLING_2 = 2, // x2 + BME280_OSRS_H_OVERSAMPLING_4 = 3, + BME280_OSRS_H_OVERSAMPLING_8 = 4, + BME280_OSRS_H_OVERSAMPLING_16 = 5 + } BME280_OSRS_H_T; + +#ifdef __cplusplus +} +#endif diff --git a/src/bmp280/javaupm_bmp280.i b/src/bmp280/javaupm_bmp280.i index ca71a0d7..a1922f3e 100644 --- a/src/bmp280/javaupm_bmp280.i +++ b/src/bmp280/javaupm_bmp280.i @@ -5,11 +5,7 @@ %include "../interfaces/javaupm_iHumiditySensor.i" %include "../interfaces/javaupm_iPressureSensor.i" -%include "cpointer.i" -%include "typemaps.i" -%include "arrays_java.i"; -%include "../java_buffer.i" - +%include "bmp280_regs.h" %include "bmp280.hpp" %{ #include "bmp280.hpp" diff --git a/src/bmp280/jsupm_bmp280.i b/src/bmp280/jsupm_bmp280.i index 9720894d..b6a08c30 100644 --- a/src/bmp280/jsupm_bmp280.i +++ b/src/bmp280/jsupm_bmp280.i @@ -6,6 +6,8 @@ %include "iHumiditySensor.hpp" %include "iPressureSensor.hpp" %include "iTemperatureSensor.hpp" + +%include "bmp280_regs.h" %include "bmp280.hpp" %{ #include "bmp280.hpp" diff --git a/src/bmp280/pyupm_bmp280.i b/src/bmp280/pyupm_bmp280.i index 6d73f1d7..193f7ec8 100644 --- a/src/bmp280/pyupm_bmp280.i +++ b/src/bmp280/pyupm_bmp280.i @@ -16,6 +16,8 @@ %include "iHumiditySensor.hpp" %include "iPressureSensor.hpp" %include "iTemperatureSensor.hpp" + +%include "bmp280_regs.h" %include "bmp280.hpp" %{ #include "bmp280.hpp"