From 6241e20dda9cf52e03272c61e2496d2e9c3e49c0 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Wed, 4 May 2016 12:47:39 -0600 Subject: [PATCH] bmp280, bme280: Initial implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This library adds support for both the BMP280 and BME280 sesnors from Bosch. The BME is virtually identical to the BMP280 aside from some slight register changes and adding support for a humidity 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 μA @1Hz allow the implementation in battery driven devices such as mobile phones, GPS modules or watches. 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 These drivers support both I2C and SPI operation. When using SPI on an Edison with the arduino breakout board, you must specify a CS of -1 to the constructor, and connect the chip's CS pin to arduino pin 10. Signed-off-by: Jon Trulson Signed-off-by: Noel Eck --- examples/c++/CMakeLists.txt | 2 + examples/c++/bme280.cxx | 78 ++++ examples/c++/bmp280.cxx | 77 ++++ examples/java/BME280_Example.java | 69 ++++ examples/java/BMP280_Example.java | 65 ++++ examples/java/CMakeLists.txt | 2 + examples/javascript/bme280.js | 71 ++++ examples/javascript/bmp280.js | 67 ++++ examples/python/bme280.py | 60 ++++ examples/python/bmp280.py | 58 +++ src/bmp280/CMakeLists.txt | 5 + src/bmp280/bme280.cxx | 174 +++++++++ src/bmp280/bme280.hpp | 215 +++++++++++ src/bmp280/bmp280.cxx | 576 ++++++++++++++++++++++++++++++ src/bmp280/bmp280.hpp | 490 +++++++++++++++++++++++++ src/bmp280/javaupm_bmp280.i | 28 ++ src/bmp280/jsupm_bmp280.i | 13 + src/bmp280/pyupm_bmp280.i | 23 ++ 18 files changed, 2073 insertions(+) create mode 100644 examples/c++/bme280.cxx create mode 100644 examples/c++/bmp280.cxx create mode 100644 examples/java/BME280_Example.java create mode 100644 examples/java/BMP280_Example.java create mode 100644 examples/javascript/bme280.js create mode 100644 examples/javascript/bmp280.js create mode 100644 examples/python/bme280.py create mode 100644 examples/python/bmp280.py create mode 100644 src/bmp280/CMakeLists.txt create mode 100644 src/bmp280/bme280.cxx create mode 100644 src/bmp280/bme280.hpp create mode 100644 src/bmp280/bmp280.cxx create mode 100644 src/bmp280/bmp280.hpp create mode 100644 src/bmp280/javaupm_bmp280.i create mode 100644 src/bmp280/jsupm_bmp280.i create mode 100644 src/bmp280/pyupm_bmp280.i diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 61c3f74f..71236723 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -266,6 +266,7 @@ endif() add_example (vcap) add_example (ds2413) add_example (ds18b20) +add_example (bmp280) # These are special cases where you specify example binary, source file and module(s) include_directories (${PROJECT_SOURCE_DIR}/src) @@ -291,3 +292,4 @@ add_custom_example (co2-sensor-example co2-sensor.cxx "t6713") add_custom_example (adc-example adc-sensor.cxx "ads1x15") add_custom_example (light-sensor-example light-sensor.cxx "si1132;max44009") add_custom_example (light-controller-example light-controller.cxx "lp8860;ds1808lc;hlg150h") +add_custom_example (bme280-example bme280.cxx bmp280) diff --git a/examples/c++/bme280.cxx b/examples/c++/bme280.cxx new file mode 100644 index 00000000..12df7a9a --- /dev/null +++ b/examples/c++/bme280.cxx @@ -0,0 +1,78 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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 "bme280.hpp" + +using namespace std; +using namespace upm; + +int 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 + upm::BME280 *sensor = new upm::BME280(); + + // For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: + // BME280(0, -1, 10); + + while (shouldRun) + { + // update our values from the sensor + sensor->update(); + + // we show both C and F for temperature + cout << "Compensation Temperature: " << sensor->getTemperature() + << " C / " << sensor->getTemperature(true) << " F" + << endl; + cout << "Pressure: " << sensor->getPressure() << " Pa" << endl; + cout << "Computed Altitude: " << sensor->getAltitude() << " m" << endl; + cout << "Humidity: " << sensor->getHumidity() << " %RH" << endl; + + cout << endl; + + sleep(1); + } +//! [Interesting] + + cout << "Exiting..." << endl; + + delete sensor; + + return 0; +} diff --git a/examples/c++/bmp280.cxx b/examples/c++/bmp280.cxx new file mode 100644 index 00000000..101159e3 --- /dev/null +++ b/examples/c++/bmp280.cxx @@ -0,0 +1,77 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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.hpp" + +using namespace std; +using namespace upm; + +int 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 + upm::BMP280 *sensor = new upm::BMP280(); + + // For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: + // BMP280(0, -1, 10); + + while (shouldRun) + { + // update our values from the sensor + sensor->update(); + + // we show both C and F for temperature + cout << "Compensation Temperature: " << sensor->getTemperature() + << " C / " << sensor->getTemperature(true) << " F" + << endl; + cout << "Pressure: " << sensor->getPressure() << " Pa" << endl; + cout << "Computed Altitude: " << sensor->getAltitude() << " m" << endl; + + cout << endl; + + sleep(1); + } +//! [Interesting] + + cout << "Exiting..." << endl; + + delete sensor; + + return 0; +} diff --git a/examples/java/BME280_Example.java b/examples/java/BME280_Example.java new file mode 100644 index 00000000..0a627e5a --- /dev/null +++ b/examples/java/BME280_Example.java @@ -0,0 +1,69 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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. + */ + +import upm_bmp280.BME280; + +public class BME280_Example +{ + public static void main(String[] args) throws InterruptedException + { +// ! [Interesting] + + // Instantiate a BME280 instance using default i2c bus and address + BME280 sensor = new BME280(); + + // For SPI, bus 0, you would pass -1 as the address, and a + // valid pin for CS: + // BME280(0, -1, 10); + + while (true) + { + // update our values from the sensor + sensor.update(); + + System.out.println("Compensation Temperature: " + + sensor.getTemperature() + + " C / " + + sensor.getTemperature(true) + + " F"); + + System.out.println("Pressure: " + + sensor.getPressure() + + " Pa"); + + System.out.println("Computed Altitude: " + + sensor.getAltitude() + + " m"); + + System.out.println("Humidity: " + + sensor.getHumidity() + + " %RH"); + + System.out.println(); + Thread.sleep(1000); + } + +// ! [Interesting] + } +} diff --git a/examples/java/BMP280_Example.java b/examples/java/BMP280_Example.java new file mode 100644 index 00000000..6bd600b8 --- /dev/null +++ b/examples/java/BMP280_Example.java @@ -0,0 +1,65 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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. + */ + +import upm_bmp280.BMP280; + +public class BMP280_Example +{ + public static void main(String[] args) throws InterruptedException + { +// ! [Interesting] + + // Instantiate a BMP280 instance using default i2c bus and address + BMP280 sensor = new BMP280(); + + // For SPI, bus 0, you would pass -1 as the address, and a + // valid pin for CS: + // BMP280(0, -1, 10); + + while (true) + { + // update our values from the sensor + sensor.update(); + + System.out.println("Compensation Temperature: " + + sensor.getTemperature() + + " C / " + + sensor.getTemperature(true) + + " F"); + + System.out.println("Pressure: " + + sensor.getPressure() + + " Pa"); + + System.out.println("Computed Altitude: " + + sensor.getAltitude() + + " m"); + + System.out.println(); + Thread.sleep(1000); + } + +// ! [Interesting] + } +} diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index 8afe7acc..d1cb1025 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -126,6 +126,7 @@ if (BACNET_FOUND) add_example(E50HX_Example e50hx) endif() add_example(VCAP_Example vcap) +add_example(BMP280_Example bmp280) add_example_with_path(Jhd1313m1_lcdSample lcd i2clcd) add_example_with_path(Jhd1313m1Sample lcd i2clcd) @@ -133,4 +134,5 @@ add_example_with_path(Lcm1602_i2cSample lcd i2clcd) add_example_with_path(Lcm1602_parallelSample lcd i2clcd) add_example_with_path(SSD1308_oledSample lcd i2clcd) add_example_with_path(SSD1327_oledSample lcd i2clcd) +add_example_with_path(BME280_Example bmp280 bmp280) diff --git a/examples/javascript/bme280.js b/examples/javascript/bme280.js new file mode 100644 index 00000000..a183c3f2 --- /dev/null +++ b/examples/javascript/bme280.js @@ -0,0 +1,71 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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. + */ + +var sensorObj = require('jsupm_bmp280'); + +// Instantiate a BME280 instance using default i2c bus and address +var sensor = new sensorObj.BME280(); + +// For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: +// BME280(0, -1, 10); + +setInterval(function() +{ + // update our values from the sensor + sensor.update(); + + console.log("Compensation Temperature: " + + sensor.getTemperature() + + " C / " + + sensor.getTemperature(true) + + " F"); + + console.log("Pressure: " + + sensor.getPressure() + + " Pa"); + + console.log("Computed Altitude: " + + sensor.getAltitude() + + " m"); + + console.log("Humidity: " + + sensor.getHumidity() + + " %RH"); + + console.log(); + +}, 1000); + +// exit on ^C +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log("Exiting."); + process.exit(0); +}); diff --git a/examples/javascript/bmp280.js b/examples/javascript/bmp280.js new file mode 100644 index 00000000..57a9a702 --- /dev/null +++ b/examples/javascript/bmp280.js @@ -0,0 +1,67 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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. + */ + +var sensorObj = require('jsupm_bmp280'); + +// Instantiate a BMP280 instance using default i2c bus and address +var sensor = new sensorObj.BMP280(); + +// For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: +// BMP280(0, -1, 10); + +setInterval(function() +{ + // update our values from the sensor + sensor.update(); + + console.log("Compensation Temperature: " + + sensor.getTemperature() + + " C / " + + sensor.getTemperature(true) + + " F"); + + console.log("Pressure: " + + sensor.getPressure() + + " Pa"); + + console.log("Computed Altitude: " + + sensor.getAltitude() + + " m"); + + console.log(); + +}, 1000); + +// exit on ^C +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log("Exiting."); + process.exit(0); +}); diff --git a/examples/python/bme280.py b/examples/python/bme280.py new file mode 100644 index 00000000..160a95a4 --- /dev/null +++ b/examples/python/bme280.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2016 Intel Corporation. +# +# 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. + +import time, sys, signal, atexit +import pyupm_bmp280 as sensorObj + +# Instantiate a BME280 instance using default i2c bus and address +sensor = sensorObj.BME280() + +# For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: +# BME280(0, -1, 10); + +## Exit handlers ## +# This function stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# This function lets you run code on exit +def exitHandler(): + print "Exiting" + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + +while (1): + sensor.update() + + print "Compensation Temperature:", sensor.getTemperature(), "C /", + print sensor.getTemperature(True), "F" + + print "Pressure: ", sensor.getPressure(), "Pa" + + print "Computed Altitude:", sensor.getAltitude(), "m" + + print "Humidity:", sensor.getHumidity(), "%RH" + + print + time.sleep(1) diff --git a/examples/python/bmp280.py b/examples/python/bmp280.py new file mode 100644 index 00000000..899e0a8e --- /dev/null +++ b/examples/python/bmp280.py @@ -0,0 +1,58 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2016 Intel Corporation. +# +# 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. + +import time, sys, signal, atexit +import pyupm_bmp280 as sensorObj + +# Instantiate a BMP280 instance using default i2c bus and address +sensor = sensorObj.BMP280() + +# For SPI, bus 0, you would pass -1 as the address, and a valid pin for CS: +# BMP280(0, -1, 10); + +## Exit handlers ## +# This function stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# This function lets you run code on exit +def exitHandler(): + print "Exiting" + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + +while (1): + sensor.update() + + print "Compensation Temperature:", sensor.getTemperature(), "C /", + print sensor.getTemperature(True), "F" + + print "Pressure: ", sensor.getPressure(), "Pa" + + print "Computed Altitude:", sensor.getAltitude(), "m" + + print + time.sleep(1) diff --git a/src/bmp280/CMakeLists.txt b/src/bmp280/CMakeLists.txt new file mode 100644 index 00000000..a82dadd8 --- /dev/null +++ b/src/bmp280/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "bmp280") +set (libdescription "Bosch bmp280 Pressure sensor and bme280 humidity sensor") +set (module_src ${libname}.cxx bme280.cxx) +set (module_h ${libname}.hpp bme280.hpp) +upm_module_init() diff --git a/src/bmp280/bme280.cxx b/src/bmp280/bme280.cxx new file mode 100644 index 00000000..e78b8afa --- /dev/null +++ b/src/bmp280/bme280.cxx @@ -0,0 +1,174 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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 +#include +#include + +#include "bme280.hpp" + +using namespace upm; +using namespace std; + +BME280::BME280(int bus, int addr, int cs, uint8_t theChipID) : + BMP280(bus, addr, cs, theChipID) +{ + + 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; +} + +void BME280::setOversampleRateHumidity(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); +} + + +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 new file mode 100644 index 00000000..e6d84e54 --- /dev/null +++ b/src/bmp280/bme280.hpp @@ -0,0 +1,215 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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 "upm/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 BME280 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. + * + * 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 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. + * + * @return The relative humidity in percent.. + */ + 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); + + /** + * 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); + + // Interface support + const char *getModuleName() + { + return "BME280"; + }; + + 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.cxx b/src/bmp280/bmp280.cxx new file mode 100644 index 00000000..4e599756 --- /dev/null +++ b/src/bmp280/bmp280.cxx @@ -0,0 +1,576 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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 +#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 celcius and back + +static float f2c(float f) +{ + return ((f - 32.0) / (9.0 / 5.0)); +} + +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) +{ + + 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); +} + +BMP280::~BMP280() +{ + if (m_i2c) + delete m_i2c; + + if (m_spi) + delete m_spi; + + if (m_gpioCS) + delete m_gpioCS; +} + +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; +} + +float BMP280::getAltitude(float sealLevelhPA) +{ + // adapted from the NOAA pdf: pressureAltitude.pdf + return 44307.69 * (1.0 - pow((m_pressure/100.0) / sealLevelhPA, 0.190284)); +} + +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); +} + +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); +} + +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; +} + +uint8_t BMP280::getChipID() +{ + + return readReg(REG_CHIPID); +} + +void BMP280::reset() +{ + writeReg(REG_RESET, BMP280_RESET_BYTE); + sleep(1); +} + + +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 BMP280::getPressure() +{ + return m_pressure; +} + +void BMP280::setFilter(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); +} + +void BMP280::setTimerStandby(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); +} + +void BMP280::setMeasureMode(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; +} + +void BMP280::setOversampleRatePressure(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); +} + +void BMP280::setOversampleRateTemperature(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); +} + +uint8_t BMP280::getStatus() +{ + return readReg(REG_STATUS); +} + +void BMP280::setUsageMode(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"); + } +} + +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.hpp b/src/bmp280/bmp280.hpp new file mode 100644 index 00000000..447ac610 --- /dev/null +++ b/src/bmp280/bmp280.hpp @@ -0,0 +1,490 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * 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 "upm/iPressureSensor.hpp" +#include "upm/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 BMP280 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 μA @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 + */ + 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. + * + * 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 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. + * + * @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. + * + * @param fahrenheit true to return data in Fahrenheit, false for + * Celicus. Celcius is the default. + * @return The temperature in degrees Celcius or Fahrenheit. + */ + 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(); + + /** + * 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); + + /** + * 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 contructor 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); + + /** + * 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); + + /** + * 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); + + /** + * 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); + + /** + * 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); + + /** + * 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); + + + // Interface support + const char *getModuleName() + { + return "BMP280"; + }; + + int getTemperatureCelcius() + { + 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/javaupm_bmp280.i b/src/bmp280/javaupm_bmp280.i new file mode 100644 index 00000000..f91403f2 --- /dev/null +++ b/src/bmp280/javaupm_bmp280.i @@ -0,0 +1,28 @@ +%module javaupm_bmp280 +%include "../upm.i" +%include "cpointer.i" +%include "typemaps.i" +%include "arrays_java.i"; +%include "../java_buffer.i" + +%include "bmp280.hpp" +%{ + #include "bmp280.hpp" +%} + +%include "bme280.hpp" +%{ + #include "bme280.hpp" +%} + + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_bmp280"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/bmp280/jsupm_bmp280.i b/src/bmp280/jsupm_bmp280.i new file mode 100644 index 00000000..1d7d75fb --- /dev/null +++ b/src/bmp280/jsupm_bmp280.i @@ -0,0 +1,13 @@ +%module jsupm_bmp280 +%include "../upm.i" +%include "cpointer.i" + +%include "bmp280.hpp" +%{ + #include "bmp280.hpp" +%} + +%include "bme280.hpp" +%{ + #include "bme280.hpp" +%} diff --git a/src/bmp280/pyupm_bmp280.i b/src/bmp280/pyupm_bmp280.i new file mode 100644 index 00000000..d6d1f7b2 --- /dev/null +++ b/src/bmp280/pyupm_bmp280.i @@ -0,0 +1,23 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_bmp280 +%include "../upm.i" +%include "cpointer.i" + +%include "stdint.i" + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "bmp280_doc.i" +#endif + +%include "bmp280.hpp" +%{ + #include "bmp280.hpp" +%} + +%include "bme280.hpp" +%{ + #include "bme280.hpp" +%}