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" +%}