diff --git a/docs/images/mpl3115a2.jpeg b/docs/images/mpl3115a2.jpeg new file mode 100644 index 00000000..735baf17 Binary files /dev/null and b/docs/images/mpl3115a2.jpeg differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d6548391..42ab252b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -43,6 +43,7 @@ add_executable (lol-example lol-example.cxx) add_executable (nrf_ble_broadcast-example ble_broadcast.cxx) add_executable (tsl2561-example tsl2561.cxx) add_executable (htu21d-example htu21d.cxx) +add_executable (mpl3115a2-example mpl3115a2.cxx) include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -77,6 +78,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/joystick12) include_directories (${PROJECT_SOURCE_DIR}/src/lol) include_directories (${PROJECT_SOURCE_DIR}/src/tsl2561) include_directories (${PROJECT_SOURCE_DIR}/src/htu21d) +include_directories (${PROJECT_SOURCE_DIR}/src/mpl3115a2) target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -123,3 +125,4 @@ target_link_libraries (lol-example lol ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (nrf_ble_broadcast-example nrf24l01 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (tsl2561-example tsl2561 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (htu21d-example htu21d ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (mpl3115a2-example mpl3115a2 ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/mpl3115a2.cxx b/examples/mpl3115a2.cxx new file mode 100644 index 00000000..f9499d11 --- /dev/null +++ b/examples/mpl3115a2.cxx @@ -0,0 +1,83 @@ +/* + * Author: William Penner + * Copyright (c) 2014 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 "mpl3115a2.h" + +volatile int doWork = 0; + +upm::MPL3115A2 *sensor = NULL; + +void +sig_handler(int signo) +{ + if (signo == SIGINT) { + printf("\nCtrl-C received.\n"); + doWork = 1; + } +} + +int +main(int argc, char **argv) +{ + // Register signal handler + signal(SIGINT, sig_handler); + + //! [Interesting] + float pressure = 0.0; + float temperature = 0.0; + float altitude = 0.0; + float sealevel = 0.0; + + sensor = new upm::MPL3115A2(0, MPL3115A2_I2C_ADDRESS); + + sensor->testSensor(); + + while (!doWork) { + temperature = sensor->getTemperature(true); + pressure = sensor->getPressure(false); + altitude = sensor->getAltitude(); + sealevel = sensor->getSealevelPressure(); + + std::cout << "pressure value = " << + pressure << + ", altitude value = " << + altitude << + ", sealevel value = " << + sealevel << + ", temperature = " << + temperature << std::endl; + usleep (500000); + } + //! [Interesting] + + std::cout << "exiting application" << std::endl; + + delete sensor; + + return 0; +} diff --git a/src/mpl3115a2/CMakeLists.txt b/src/mpl3115a2/CMakeLists.txt new file mode 100644 index 00000000..586cf21d --- /dev/null +++ b/src/mpl3115a2/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "mpl3115a2") +set (libdescription "libupm Pressure/Temperature Sensor") +set (module_src ${libname}.cpp) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/mpl3115a2/jsupm_mpl3115a2.i b/src/mpl3115a2/jsupm_mpl3115a2.i new file mode 100644 index 00000000..318c8a3f --- /dev/null +++ b/src/mpl3115a2/jsupm_mpl3115a2.i @@ -0,0 +1,8 @@ +%module jsupm_mpl3115a2 +%include "../upm.i" + +%{ + #include "mpl3115a2.h" +%} + +%include "mpl3115a2.h" diff --git a/src/mpl3115a2/mpl3115a2.cpp b/src/mpl3115a2/mpl3115a2.cpp new file mode 100644 index 00000000..28bf89e5 --- /dev/null +++ b/src/mpl3115a2/mpl3115a2.cpp @@ -0,0 +1,326 @@ +/* + * Author: William Penner + * Copyright (c) 2014 Intel Corporation. + * + * This application code supports the mpl3115a2 digital barometric pressure + * and temperature sensor from Freescale. The datasheet is available + * from their website: + * http://cache.freescale.com/files/sensors/doc/data_sheet/MPL3115A2.pdf + * + * 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 "mpl3115a2.h" + +using namespace upm; + +MPL3115A2::MPL3115A2 (int bus, int devAddr, uint8_t mode) { + int id; + + m_name = MPL3115A2_NAME; + + m_controlAddr = devAddr; + m_bus = bus; + + m_i2ControlCtx = mraa_i2c_init(m_bus); + + mraa_result_t ret = mraa_i2c_address(m_i2ControlCtx, m_controlAddr); + if (ret != MRAA_SUCCESS) { + fprintf(stderr, "Error accessing i2c bus\n"); + } + + setOversampling(mode); + + id = i2cReadReg_8(MPL3115A2_WHO_AM_I); + if (id != MPL3115A2_DEVICE_ID) { + fprintf(stdout, "Incorrect device id - read: 0x%02x\n", id); + } +} + +MPL3115A2::~MPL3115A2() { + mraa_i2c_stop(m_i2ControlCtx); +} + +/* + * Function to test the device and verify that is appears operational + * Typically functioning sensors will return "noisy" values and would + * be expected to change a bit. This fuction will check for this + * variation. + */ + +int +MPL3115A2::testSensor(void) +{ + int i, iTries; + int iError = 0; + float pressure, temperature; + float fPMin, fPMax, fTMin, fTMax; + + fprintf(stdout, "Executing Sensor Test.\n" ); + + pressure = getPressure(true); + temperature = getTemperature(false); + fPMin = fPMax = pressure; + fTMin = fTMax = temperature; + + iTries = 20; + do { + sampleData(); + pressure = getPressure(true); + temperature = getTemperature(false); + if (pressure < fPMin) fPMin = pressure; + if (pressure > fPMax) fPMax = pressure; + if (temperature < fTMin) fTMin = temperature; + if (temperature > fTMax) fTMax = temperature; + } + while(fPMin == fPMax && fTMin == fTMax && --iTries); + + if (fPMin == fPMax && fTMin == fTMax) { + fprintf(stdout, " Warning - sensor values not changing.\n" ); + return -1; + } + + fprintf(stdout, " Test complete.\n"); + + return 0; +} + +/* + * Function to dump out the i2c register block to the screen + */ + +void +MPL3115A2::dumpSensor(void) +{ + int i, j, ival; + + fprintf(stdout, "Dumping i2c block from %s\n", MPL3115A2_NAME); + for (i=0; i < 256; i+=16) { + fprintf(stdout, " %02x: ", i); + for (j=i; j < i+16; j++) { + fprintf(stdout, "%02x ", i2cReadReg_8(j)); + } + fprintf(stdout, "\n"); + } +} + +/* + * Function used to soft RESET the MPL3115A2 device to ensure + * it is in a known state. This function can be used to reset + * the min/max temperature and pressure values. + */ + +int +MPL3115A2::resetSensor(void) +{ + fprintf(stdout, "Resetting MPL3115A2 device\n" ); + i2cWriteReg(MPL3115A2_CTRL_REG1, MPL3115A2_CTRL_RESET); + usleep(50000); + i2cWriteReg(MPL3115A2_CTRL_REG1, MPL3115A2_CTRL_RESET | + MPL3115A2_SETOVERSAMPLE(m_oversampling)); + + return 0; +} + +int +MPL3115A2::sampleData(void) +{ + int val; + mraa_result_t ret; + int tries = 15; + uint32_t us_delay; + + // trigger measurement + ret = i2cWriteReg(MPL3115A2_CTRL_REG1, + MPL3115A2_CTRL_OST | MPL3115A2_SETOVERSAMPLE(m_oversampling)); + if (MRAA_SUCCESS != ret) { + fprintf(stdout, "Write to trigger measurement failed\n"); + return -1; + } + + // Calculate and delay the appopriate time for the measurement + us_delay = ((1 << m_oversampling) * 4 + 2) * 1000; + usleep(us_delay); + + // Loop waiting for the ready bit to become active + while (tries-- > 0) { + val = i2cReadReg_8(MPL3115A2_CTRL_REG1); + if (val < 0) { + fprintf(stdout,"Error reading CTRL_REG1\n"); + return -1; + } + + /* wait for data ready, i.e. OST cleared */ + if (!(val & MPL3115A2_CTRL_OST)) + break; + usleep(20000); + } + if (tries < 0) { + std::cout << "Device timeout during measurement" << std::endl; + return -1; + } + + return 0; +} + +int32_t +MPL3115A2::getPressureReg(int reg) { + return ((i2cReadReg_16(reg) << 8)|(uint32_t)i2cReadReg_8(reg+2))*100/64; +} + +int32_t +MPL3115A2::getTempReg(int reg) { + return (int32_t)((int16_t)i2cReadReg_16(reg)) * 1000 / 256; +} + +float +MPL3115A2::getPressure(int bSampleData) { + int ret; + + // Trigger request to make a measurement + if (bSampleData) { + ret = sampleData(); + if (ret < 0) { + fprintf(stdout, "Error sampling pressure\n"); + return -1; + } + } + m_iPressure = getPressureReg(MPL3115A2_OUT_PRESS); + + return (float)m_iPressure / 100; +} + +float +MPL3115A2::getTemperature(int bSampleData) { + int ret; + + // Trigger request to make a measurement + if (bSampleData) { + ret = sampleData(); + if (ret < 0) { + fprintf(stdout, "Error sampling temperature\n"); + return -1; + } + } + m_iTemperature = getTempReg(MPL3115A2_OUT_TEMP); + + return (float)m_iTemperature / 1000; +} + +float +MPL3115A2::getSealevelPressure(float altitudeMeters) { + float fPressure = (float)m_iPressure / 100.0; + return fPressure / pow(1.0-altitudeMeters/44330, 5.255); +} + +float +MPL3115A2::getAltitude (float sealevelPressure) { + float fPressure = (float)m_iPressure / 100.0; + return 44330 * (1.0 - pow(fPressure /sealevelPressure,0.1903)); +} + +void +MPL3115A2::setOversampling(uint8_t oversampling) +{ + if (oversampling > MPL3115A2_MAXOVERSAMPLE) + oversampling = MPL3115A2_MAXOVERSAMPLE; + m_oversampling = oversampling; +} + +uint8_t +MPL3115A2::getOversampling(void) +{ + return m_oversampling; +} + +float +MPL3115A2::getTemperatureMax(void) +{ + return (float)getTempReg(MPL3115A2_T_MAX) / 1000; +} + +float +MPL3115A2::getTemperatureMin(void) +{ + return (float)getTempReg(MPL3115A2_T_MIN) / 1000; +} + +float +MPL3115A2::getPressureMax(void) +{ + return (float)getPressureReg(MPL3115A2_P_MAX) / 1000; +} + +float +MPL3115A2::getPressureMin(void) +{ + return (float)getPressureReg(MPL3115A2_P_MIN) / 1000; +} + +float +MPL3115A2::convertTempCtoF(float fTemp) +{ + return(fTemp * 9 / 5 + 32); +} + +/* + * This is set for 15degC (Pa = 0.0002961 in Hg) + */ +float +MPL3115A2::convertPaToinHg(float fPressure) +{ + return(fPressure * 0.0002961); +} + +/* + * Functions to read and write data to the i2c device + */ + +mraa_result_t +MPL3115A2::i2cWriteReg (uint8_t reg, uint8_t value) { + mraa_result_t error = MRAA_SUCCESS; + + uint8_t data[2] = { reg, value }; + mraa_i2c_address (m_i2ControlCtx, m_controlAddr); + error = mraa_i2c_write (m_i2ControlCtx, data, 2); + + return error; +} + +uint16_t +MPL3115A2::i2cReadReg_16 (int reg) { + uint16_t data; + + mraa_i2c_address(m_i2ControlCtx, m_controlAddr); + data = (uint16_t)mraa_i2c_read_byte_data(m_i2ControlCtx, reg) << 8; + data |= (uint16_t)mraa_i2c_read_byte_data(m_i2ControlCtx, reg+1); + + return data; +} + +uint8_t +MPL3115A2::i2cReadReg_8 (int reg) { + mraa_i2c_address(m_i2ControlCtx, m_controlAddr); + return mraa_i2c_read_byte_data(m_i2ControlCtx, reg); +} + diff --git a/src/mpl3115a2/mpl3115a2.h b/src/mpl3115a2/mpl3115a2.h new file mode 100644 index 00000000..bd448f6d --- /dev/null +++ b/src/mpl3115a2/mpl3115a2.h @@ -0,0 +1,247 @@ +/* + * Author: William Penner + * Copyright (c) 2014 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 + +#define MPL3115A2_NAME "mpl3115a2" + +#define MPL3115A2_I2C_ADDRESS 0x60 +#define MPL3115A2_DEVICE_ID 0xc4 + +#define MPL3115A2_STATUS 0x00 +#define MPL3115A2_OUT_PRESS 0x01 /* MSB first, 20 bit */ +#define MPL3115A2_OUT_TEMP 0x04 /* MSB first, 12 bit */ +#define MPL3115A2_WHO_AM_I 0x0c +#define MPL3115A2_PT_DATA_CFG 0x13 +#define MPL3115A2_P_MIN 0x1C +#define MPL3115A2_T_MIN 0x1F +#define MPL3115A2_P_MAX 0x21 +#define MPL3115A2_T_MAX 0x24 +#define MPL3115A2_CTRL_REG1 0x26 + +// CTRL_REG1 +#define MPL3115A2_CTRL_SBYB 0x01 /* Standby (not) */ +#define MPL3115A2_CTRL_OST 0x02 /* One-shot trigger */ +#define MPL3115A2_CTRL_RESET 0x04 /* RESET device */ +#define MPL3115A2_CTRL_ALT_MODE 0x80 /* Altitude mode */ + +#define MPL3115A2_SETOVERSAMPLE(a) ((a & 7) << 3) +#define MPL3115A2_GETOVERSAMPLE(a) ((a >> 3) & 7) +#define MPL3115A2_MAXOVERSAMPLE 7 + +namespace upm { + +/** + * @brief MPL3115A2 atmospheric pressure sensor library + * @defgroup mpl3115 libupm-mpl3115 + */ + +/** + * @brief C++ API for MPL3115A2 chip (Atmospheric Pressure Sensor) + * + * Freescale [MPL3115A2] + * (http://cache.freescale.com/files/sensors/doc/data_sheet/MPL3115A2.pdf) + * is a high precision, ultra-low power consumption pressure sensor. It has a + * range of between 50 and 110 kPa. + * + * @ingroup mpl3115a2 i2c + * @snippet mpl3115a2.cxx Interesting + * @image html mpl3115a2.jpeg + */ +class MPL3115A2 { + public: + /** + * Instantiates an MPL3115A2 object + * + * @param bus number of used bus + * @param devAddr address of used i2c device + * @param mode MPL3115A2 oversampling (6 = 64x) + */ + MPL3115A2(int bus, int devAddr=MPL3115A2_I2C_ADDRESS, uint8_t mode=6); + + /** + * MPL3115A2 object destructor, basicaly it close i2c connection. + */ + ~MPL3115A2(); + + /** + * Test the sensor and try to determine if operating by looking + * for small variations in the value + */ + int testSensor(void); + + /** + * Perform a soft RESET of the MPL3115A2 device to ensure + * it is in a known state. This function can be used to reset + * the min/max temperature and pressure values. + */ + int resetSensor(void); + + /** + * Dump out the i2c register block to stdout + */ + void dumpSensor(void); + + /** + * Initiate a temp/pressure mesasurement and wait for function + * to complete. The temp and pressure registers can be read + * after this call. + */ + int sampleData(void); + + /** + * Read a pressure value from the mpl3115a2 [Pa * 100] + * + * @param reg base address of pressure register + */ + int32_t getPressureReg(int reg); + + /** + * Read a temperature value from the mpl3115a2 [degC * 1000] + * + * @param reg base address of temperature register + */ + int32_t getTempReg(int reg); + + /** + * Read the current pressure value from the mpl3115a2 [Pa] + * + * @param bSampleData Set non-zero to sample reading + */ + float getPressure(int bSampleData = true); + + /** + * Read the current temperature value from the mpl3115a2 [degC] + * + * @param bSampleData Set non-zero to sample reading + */ + float getTemperature(int bSampleData = true); + + /** + * Read the current pressure and using a known altitude calculate + * the sea level pressure value [Pa] + * This function should be preceeded by the sampleData() call + * + * @param altitudeMeters Altitude in meters + */ + float getSealevelPressure(float altitudeMeters = 0.0); + + /** + * Read the current pressure and using a known sea level pressure + * calculate the altitude value [m] + * This function should be preceeded by the sampleData() call + * + * @param sealevelPressure Current sea level pressure + */ + float getAltitude (float sealevelPressure = 101325.0); + + /** + * Set the oversampling setting (ranges from 0 to 7). The + * value represents 2^n samples (ranging from 1 to 128). The + * time to calculate the sample is approximately (2^n * 4 + 2) mS + * + * @param oversampling New oversampling value + */ + void setOversampling(uint8_t oversampling); + + /** + * Returns the current oversampling value + */ + uint8_t getOversampling(void); + + /** + * Read the maximum measured temperature [degC] + */ + float getTemperatureMax(void); + + /** + * Read the minimum measured temperature [degC] + */ + float getTemperatureMin(void); + + /** + * Read the maximum measured pressure [Pa] + */ + float getPressureMax (void); + + /** + * Read the minimum measured pressure [Pa] + */ + float getPressureMin (void); + + /** + * Convert temperature from degC*1000 to degF*1000 + * + * @param iTemp Temperature in degC + */ + float convertTempCtoF(float fTemp); + + /** + * Convert pressure from Pa*100 to inHg*10000 + * This is set for 15degC (Pa = 0.0002961 in Hg) + * TODO: Change function to add temp calibration + * + * @param iPressure Pressure in Pa + */ + float convertPaToinHg(float fPressure); + + /** + * Write to one byte to i2c register + * + * @param reg address of a register + * @param value byte to be written + */ + mraa_result_t i2cWriteReg (uint8_t reg, uint8_t value); + + /** + * Read two bytes from i2c registers + * + * @param reg address of a register + */ + uint16_t i2cReadReg_16 (int reg); + + /** + * Read one byte register + * + * @param reg address of a register + */ + uint8_t i2cReadReg_8 (int reg); + + private: + std::string m_name; + + int m_controlAddr; + int m_bus; + mraa_i2c_context m_i2ControlCtx; + + uint8_t m_oversampling; + int32_t m_iPressure; + int32_t m_iTemperature; +}; + +} + diff --git a/src/mpl3115a2/pyupm_mpl3115a2.i b/src/mpl3115a2/pyupm_mpl3115a2.i new file mode 100644 index 00000000..0a3a35aa --- /dev/null +++ b/src/mpl3115a2/pyupm_mpl3115a2.i @@ -0,0 +1,13 @@ +%module pyupm_mpl3115a2 +%include "../upm.i" + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "mpl3115a2_doc.i" +#endif + +%include "mpl3115a2.h" +%{ + #include "mpl3115a2.h" +%}