diff --git a/LPS.cpp b/LPS.cpp index 42041a9..2a2ce4e 100644 --- a/LPS.cpp +++ b/LPS.cpp @@ -1,6 +1,11 @@ -#include +#include "LPS.h" #include +/* + * For a rigorously complete implementation of the LPS25HB, see ST's github + * version at https://github.com/stm32duino/LPS25HB + */ + // Defines /////////////////////////////////////////////////////////// // The Arduino two-wire interface uses a 7-bit number for the address, @@ -18,7 +23,7 @@ LPS::LPS(void) { _device = device_auto; - + // Pololu board pulls SA0 high, so default assumption is that it is // high address = SA0_HIGH_ADDRESS; @@ -31,7 +36,7 @@ bool LPS::init(deviceType device, byte sa0) { if (!detectDeviceAndAddress(device, (sa0State)sa0)) return false; - + switch (_device) { case device_25H: @@ -41,7 +46,7 @@ bool LPS::init(deviceType device, byte sa0) translated_regs[-THS_P_H] = LPS25H_THS_P_H; return true; break; - + case device_331AP: translated_regs[-INTERRUPT_CFG] = LPS331AP_INTERRUPT_CFG; translated_regs[-INT_SOURCE] = LPS331AP_INT_SOURCE; @@ -50,9 +55,24 @@ bool LPS::init(deviceType device, byte sa0) return true; break; } + return false; +} + +// turns on sensor and enables 1Hz continuous output +// WARNING, this is absolute, intended for init section. +void LPS::enable1Hz(void) +{ + if (_device == device_25H || _device == device_331AP) + { + // 0x90 = 0b10010000 + // PD = 1 (active mode); ODR = 001b (1 Hz pressure & temperature output data rate) + writeReg(CTRL_REG1, 0x90); + } } -// turns on sensor and enables continuous output + +// turns on sensor and enables continuous output at 12.5 Hz +// WARNING, this is absolute, intended for init section. void LPS::enableDefault(void) { if (_device == device_25H) @@ -69,6 +89,28 @@ void LPS::enableDefault(void) } } +// sets the device to a specific FIFO mode +void LPS::enableFifo(fifoMode mode, bool decimate) { + if (_device == device_25H || _device == device_331AP){ + + // Set the correct FIFO_CTRL (2Eh) + byte fiforeg = readReg(FIFO_CTRL); + // The FIFO mode range is 3 bits wide at [7:5] + fiforeg &= ~(0b111 << 5); + fiforeg |= mode << 5; + writeReg(FIFO_CTRL, fiforeg); + + // enable the FIFO mode in CTRL_REG2 (21h). + byte ctrlreg = readReg(CTRL_REG2); + bitSet(ctrlreg, 6); + // You can also decimate the FIFO in 1Hz mode here if you raise bit 4. + // Be sure to set the ODR to 0b001 in CTRL_REG1:[6:4] if you do. See enable1Hz() above. + if (decimate) bitSet(ctrlreg, 4); + writeReg(CTRL_REG2, ctrlreg); + + } +} + // writes register void LPS::writeReg(int reg, byte value) { @@ -88,7 +130,7 @@ void LPS::writeReg(int reg, byte value) byte LPS::readReg(int reg) { byte value; - + // if dummy register address, look up actual translated address (based on device type) if (reg < 0) { @@ -108,7 +150,7 @@ byte LPS::readReg(int reg) // reads pressure in millibars (mbar)/hectopascals (hPa) float LPS::readPressureMillibars(void) { - return (float)readPressureRaw() / 4096; + return (float)readPressureRaw() / 4096.0; } // reads pressure in inches of mercury (inHg) @@ -139,13 +181,13 @@ int32_t LPS::readPressureRaw(void) // reads temperature in degrees C float LPS::readTemperatureC(void) { - return 42.5 + (float)readTemperatureRaw() / 480; + return 42.5 + (float)readTemperatureRaw() / 480.0; } // reads temperature in degrees F float LPS::readTemperatureF(void) { - return 108.5 + (float)readTemperatureRaw() / 480 * 1.8; + return 108.5 + (float)readTemperatureRaw() / 480.0 * 1.8; } // reads temperature and returns raw 16-bit sensor output @@ -169,20 +211,89 @@ int16_t LPS::readTemperatureRaw(void) // converts pressure in mbar to altitude in meters, using 1976 US // Standard Atmosphere model (note that this formula only applies to a // height of 11 km, or about 36000 ft) -// If altimeter setting (QNH, barometric pressure adjusted to sea +// If altimeter setting (QNH, barometric pressure adjusted to standard sea // level) is given, this function returns an indicated altitude // compensated for actual regional pressure; otherwise, it returns // the pressure altitude above the standard pressure level of 1013.25 -// mbar or 29.9213 inHg +// mbar or 29.921 inHg float LPS::pressureToAltitudeMeters(float pressure_mbar, float altimeter_setting_mbar) { - return (1 - pow(pressure_mbar / altimeter_setting_mbar, 0.190263)) * 44330.8; + return (1.0 - pow(pressure_mbar / altimeter_setting_mbar, 0.190263)) * 44330.8; } // converts pressure in inHg to altitude in feet; see notes above float LPS::pressureToAltitudeFeet(float pressure_inHg, float altimeter_setting_inHg) { - return (1 - pow(pressure_inHg / altimeter_setting_inHg, 0.190263)) * 145442; + return (1.0 - pow(pressure_inHg / altimeter_setting_inHg, 0.190263)) * 145442.0; +} + +// Generic extension of the above two functions. +float LPS::pressureToAltitude(float pressure, float altimeter_setting, bool metric) +{ + const float factor = (metric ? 44330.8 : 145442.0); + return (1.0 - pow(pressure / altimeter_setting, 0.190263)) * factor; +} + +// Pressure (hPa), temp (C) and altitude (m), return sea level barometric pressure +// QFF is returned in millibars unless politely declined, in which case you get inHg +float LPS::altitudeToQFF(float altitude_meters, float tempC, bool please_return_mbars) { + const float factor = (please_return_mbars ? 1.0 : 33.864); + float pressure_mbar = LPS::readPressureMillibars(); + + float qff = pressure_mbar / + pow(((-0.0065 * altitude_meters) / + ( 0.0065 * altitude_meters + 273.15 + tempC)), 5.2559); + + return (qff / factor); +} + +/* + * Use this when you first get your device. Tell it your local info and it will help + * you see what your RPDS_L (0x39) and/or RPDS_H (0x3A) should be. Then you can use the + * above to report accurately your local true sea level barometric pressure (QFF). + */ +int32_t LPS::opcHelper(float my_local_qff_mbar, float my_local_altitude_meters) { + // real programmers would turn on BDU here, first. + int32_t currentRawPressure = LPS::readPressureRaw(); + byte rpds_low, rpds_high; + int16_t returnVal; + + rpds_low = readReg(LPS::RPDS_L); + rpds_high = readReg(LPS::RPDS_H); + + Serial.print("Your local pressure:\t\t"); + Serial.print(my_local_qff_mbar); + Serial.print(" mbar, which is "); + + Serial.print((int32_t)(my_local_qff_mbar * 4096.0)); + Serial.println(" int32_t"); + + Serial.print("Your local altitude:\t\t"); + Serial.print(my_local_altitude_meters); + Serial.println(" m"); + + Serial.println("According to the current device state, your local QFF should be:"); + Serial.println(LPS::altitudeToQFF(my_local_altitude_meters)); + + Serial.print("Sensor raw pressure:\t\t"); + Serial.print(currentRawPressure); + Serial.println(" int32_t"); + + Serial.print("Sensor millibars:\t\t"); + Serial.print((float)currentRawPressure / 4096.0); + Serial.println(" mbar"); + + Serial.print("Current RPDS_L:\t\t"); + Serial.print(rpds_low, BIN); + Serial.println(" binary"); + Serial.print("Current RPDS_H:\t\t"); + Serial.print(rpds_high, BIN); + Serial.println(" binary"); + + returnVal = (currentRawPressure - (int32_t)(my_local_qff_mbar * 4096.0)); + + return returnVal; + } // Private Methods /////////////////////////////////////////////////// @@ -206,7 +317,7 @@ bool LPS::detectDeviceAndAddress(deviceType device, sa0State sa0) bool LPS::detectDevice(deviceType device) { int id = testWhoAmI(address); - + if ((device == device_auto || device == device_25H) && id == LPS25H_WHO_ID) { _device = device_25H; @@ -232,4 +343,4 @@ int LPS::testWhoAmI(byte address) return Wire.read(); else return TEST_REG_NACK; -} \ No newline at end of file +} diff --git a/LPS.h b/LPS.h index 63143cc..c54812d 100644 --- a/LPS.h +++ b/LPS.h @@ -6,9 +6,28 @@ class LPS { public: - enum deviceType { device_331AP, device_25H, device_auto }; + enum deviceType { device_331AP, device_25H, device_25HB = device_25H, device_auto }; enum sa0State { sa0_low, sa0_high, sa0_auto }; + /// Size of the hardware moving average depth, from 2 to 32 samples + enum fifoMeanModeSize { + SAMPLES_1 = 0b00001, // 2 + SAMPLES_2 = 0b00011, // 4 + SAMPLES_3 = 0b00111, // 8 + SAMPLES_4 = 0b01111, // 16 + SAMPLES_5 = 0b11111 // 32 + }; + + enum fifoMode { + BYPASS, + FIFO, + STREAM, + STREAM2FIFO, + BYPASS2STREAM, + FIFOMEAN = 0b110, + BYPASS2FIFO + }; + // register addresses // Note: where register names differ between the register mapping table and // the register descriptions in the datasheets, the names from the register @@ -18,56 +37,56 @@ class LPS REF_P_XL = 0x08, REF_P_L = 0x09, REF_P_H = 0x0A, - + WHO_AM_I = 0x0F, - + RES_CONF = 0x10, - + CTRL_REG1 = 0x20, CTRL_REG2 = 0x21, CTRL_REG3 = 0x22, CTRL_REG4 = 0x23, // 25H - + STATUS_REG = 0x27, - + PRESS_OUT_XL = 0x28, PRESS_OUT_L = 0x29, PRESS_OUT_H = 0x2A, TEMP_OUT_L = 0x2B, TEMP_OUT_H = 0x2C, - + FIFO_CTRL = 0x2E, // 25H FIFO_STATUS = 0x2F, // 25H - + AMP_CTRL = 0x30, // 331AP - + RPDS_L = 0x39, // 25H RPDS_H = 0x3A, // 25H - + DELTA_PRESS_XL = 0x3C, // 331AP DELTA_PRESS_L = 0x3D, // 331AP DELTA_PRESS_H = 0x3E, // 331AP - - + + // dummy addresses for registers in different locations on different devices; // the library translates these based on device type // value with sign flipped is used as index into translated_regs array - + INTERRUPT_CFG = -1, INT_SOURCE = -2, THS_P_L = -3, THS_P_H = -4, // update dummy_reg_count if registers are added here! - - + + // device-specific register addresses - + LPS331AP_INTERRUPT_CFG = 0x23, LPS331AP_INT_SOURCE = 0x24, LPS331AP_THS_P_L = 0x25, LPS331AP_THS_P_H = 0x26, - + LPS25H_INTERRUPT_CFG = 0x24, LPS25H_INT_SOURCE = 0x25, LPS25H_THS_P_L = 0x30, @@ -81,6 +100,9 @@ class LPS byte getAddress(void) { return address; } void enableDefault(void); + void enable1Hz(void); + + void enableFifo(fifoMode mode, bool decimate = false); void writeReg(int reg, byte value); byte readReg(int reg); @@ -94,11 +116,16 @@ class LPS static float pressureToAltitudeMeters(float pressure_mbar, float altimeter_setting_mbar = 1013.25); static float pressureToAltitudeFeet(float pressure_inHg, float altimeter_setting_inHg = 29.9213); + static float pressureToAltitude(float pressure, float altimeter_setting, bool metric = true); + + float altitudeToQFF(float altitude_meters, float tempC = 15.0, bool please_return_mbars = true); + + int32_t opcHelper(float my_local_qff_mbar, float my_local_altitude_meters); private: deviceType _device; // chip type (331AP or 25H) byte address; - + static const int dummy_reg_count = 4; regAddr translated_regs[dummy_reg_count + 1]; // index 0 not used @@ -107,4 +134,4 @@ class LPS int testWhoAmI(byte address); }; -#endif \ No newline at end of file +#endif diff --git a/README.md b/README.md index caea344..7186758 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Arduino library for Pololu LPS pressure sensor boards -Version: 3.0.0
-Release date: 2016-08-17
+Version: 3.1.0
+Release date: 2019-05-11
[![Build Status](https://travis-ci.org/pololu/lps-arduino.svg?branch=master)](https://travis-ci.org/pololu/lps-arduino)
[www.pololu.com](https://www.pololu.com/) @@ -10,19 +10,21 @@ Release date: 2016-08-17
This is a library for an [Arduino-compatible controller](https://www.pololu.com/arduino) that -interfaces with ST LPS25H and LPS331AP pressure sensors on Pololu +interfaces with ST LPS25H/B and LPS331AP pressure sensors on Pololu boards. The library makes it simple to read the raw pressure data from these boards: -* [Pololu LPS25H pressure/altitude sensor carrier](https://www.pololu.com/catalog/product/2724) -* [Pololu LPS331AP pressure/altitude sensor carrier](https://www.pololu.com/catalog/product/2126) -* [AltIMU-10 v3 (L3GD20H, LSM303D, and LSM331AP carrier)](https://www.pololu.com/catalog/product/2469) +* [Pololu LPS25HB pressure/altitude sensor carrier](https://www.pololu.com/catalog/product/2867) +* [Pololu LPS25H pressure/altitude sensor carrier](https://www.pololu.com/catalog/product/2724) (deprecated) +* [Pololu LPS331AP pressure/altitude sensor carrier](https://www.pololu.com/catalog/product/2126) (deprecated) +* [AltIMU-10 v5 (LSM6DS33, LIS3MDL, and LPS25H carrier)](https://www.pololu.com/catalog/product/2739) +* [AltIMU-10 v4 (L3GD20H, LSM303D, and LPS25H carrier)](https://www.pololu.com/catalog/product/2468) (deprecated) +* [AltIMU-10 v3 (L3GD20H, LSM303D, and LSM331AP carrier)](https://www.pololu.com/catalog/product/2469) (deprecated) * [AltIMU-10 (L3GD20, LSM303DLHC, and LSM331AP carrier)](https://www.pololu.com/catalog/product/1269) (discontinued) The library also provides functions to help calculate altitude based on the measured pressure. - ## Getting started ### Hardware @@ -117,6 +119,25 @@ Example output: p: 27.52 inHg a: 2296.42 ft t: 83.17 deg F +### OPCHelper + +This program helps to determine what the RPDS registers should be after you have +soldered it. It helps to have some idea what the values should be instead of +hunting and pecking with trial and error. This may help for you to zero in on a +value much more quickly. + +Example output: + + Pololu LPS OPC Helper + Your local pressure: 1016.40 mbar, which is 4163174 int32_t + Your local altitude: 419.70 m + According to the current device state, your local QFF should be: + + Sensor raw pressure: 4153535 int32_t + Sensor millibars: 1014.05 mbar + Current RPDS_L: 1010001 binary + Current RPDS_H: 0 binary + ## Library reference - `bool init(deviceType device, byte sa0)`
Initializes the @@ -133,7 +154,11 @@ Example output: - `byte getAddress(void)`
Returns the address detected by `init()`. - `void enableDefault(void)`
Turns on the pressure sensor in a - default configuration that gives continous output at 12.5 Hz. + default configuration that gives continuous output at 12.5 Hz. +- `void enable1Hz(void)`
Turns on the pressure sensor in a + default configuration that gives continuous output at 1Hz. +- `void enableFifo(fifoMode mode)`
Enables the hardware FIFO + for the given mode. - `void writeReg(int reg, byte value)`
Writes a pressure sensor register with the given value. Register addresses are defined by the regAddr enumeration type in LPS.h. Example use: @@ -168,10 +193,24 @@ Example output: altimeter_setting_inHg)`
Converts a pressure in inHg to an altitude in feet. See the preceding description of `pressureToAltitudeMeters()` for details. +- `float pressureToAltitude(float pressure, float altimeter_setting, bool metric)`
+ Converts a pressure to an altitude. A generic version of the above two functions. Metric pressure and altimeter are expected to be mbars and + meters. Otherwise inHg and feet, respectively. +- `float altitudeToQFF(float altitude_meters, float tempC, bool return_mbars)`
+ Useful for reporting sea level pressure assuming you know the altitude of your + sensor. Once your device is calibrated, you can use this to report local pressure. +- `int32_t opcHelper(float my_local_qff_mbar, float my_local_altitude_meters)`
+ Useful for calibrating the device so you can assign RPDS registers to correct for + changes to pressure value reporting from the device due to soldering. ## Version history +* 3.1.0 (2019-05-07): Added helper functions and variables + * Enums for FIFO modes + * Enable FIFO function + * Altitude to sea level pressure function + * OPC helper to determine post-solder offsets * 3.0.0 (2016-08-17): Updated library to work with the Arduino Library Manager. * 2.0.0 (2014-06-03): Major rewrite. List of significant changes: * Renamed library to LPS. @@ -179,3 +218,7 @@ Example output: * Library constants converted to enums. * 1.0.1 (2014-01-08): Changed raw output byte combination logic to work properly on 32-bit microcontrollers and be more portable. * 1.0.0 (2013-03-22): Original release of LPS331 library. + + +_For a rigorously complete implementation of the LPS25HB, ST's :octocat: github +version at https://github.com/stm32duino/LPS25HB provides interfaces for almost every function on the chip._ diff --git a/examples/OPCHelper/OPCHelper.ino b/examples/OPCHelper/OPCHelper.ino new file mode 100644 index 0000000..e87c981 --- /dev/null +++ b/examples/OPCHelper/OPCHelper.ino @@ -0,0 +1,61 @@ +#include +#include + +LPS ps; + +/*** + * So, you have a brand new LPS sensor and you have soldered on the pins. + * Now you need to see if it reports true pressure readings. I assume you + * know your true altitude. I assume, also, that you can find an accurate + * local pressure reading from nearby weather stations or extrapolating + * from your weather authority's isoline maps. Using those two "true" values + * and the pressure from the sensor, we should be able to show what you ought + * to use for setting the RPDS registers. + + * Why do you want to set the RPDS registers? Glad you asked. + * If your sensor is not going to move in any vertical direction, say, it's + * stuck on a pole in your backyard, then it is easy to report your local + * "weather" pressure by adjusting those registers. Set those in your init + * section and then your pressure values will show your adjusted sea level + * pressure. + + * If you just want to report your actual air pressure, then just move along + * because this function may not be helpful. But you still ought to calibrate + * your unit to make sure that it's reporting accurately. +***/ + +/* Assign these two values from your current local references */ +static const float pressure = 1016.4; +static const float altitude = 419.7; + +bool delaying(int msdelay){ + delay(msdelay); + return true; +} + +void setup() +{ + Serial.begin(115200); + Wire.begin(); + + if (!ps.init()) + { + Serial.println("Failed to autodetect pressure sensor!"); + while (delaying(60)); + } + + Serial.println("Pololu LPS OPC Helper"); + + delay(100); + ps.enableDefault(); + + delay(100); + +} + +void loop() +{ + float pressure_diff = ps.opcHelper(pressure, altitude); + + delay(90000); +} diff --git a/keywords.txt b/keywords.txt index 96eb300..6afc4e4 100644 --- a/keywords.txt +++ b/keywords.txt @@ -4,6 +4,8 @@ init KEYWORD2 getDeviceType KEYWORD2 getAddress KEYWORD2 enableDefault KEYWORD2 +enable1Hz KEYWORD2 +enableFifo KEYWORD2 writeReg KEYWORD2 readReg KEYWORD2 readPressureMillibars KEYWORD2 @@ -14,9 +16,13 @@ readTemperatureF KEYWORD2 readTemperatureRaw KEYWORD2 pressureToAltitudeMeters KEYWORD2 pressureToAltitudeFeet KEYWORD2 +pressureToAltitude KEYWORD2 +altitudeToQFF KEYWORD2 +opcHelper KEYWORD2 device_331AP LITERAL1 device_25H LITERAL1 +device_25HB LITERAL1 device_auto LITERAL1 sa0_low LITERAL1 sa0_high LITERAL1 @@ -24,6 +30,18 @@ sa0_auto LITERAL1 SA0_LOW LITERAL1 SA0_HIGH LITERAL1 SA0_AUTO LITERAL1 +SAMPLES_1 LITERAL1 +SAMPLES_2 LITERAL1 +SAMPLES_3 LITERAL1 +SAMPLES_4 LITERAL1 +SAMPLES_5 LITERAL1 +BYPASS LITERAL1 +FIFO LITERAL1 +STREAM LITERAL1 +STREAM2FIFO LITERAL1 +BYPASS2STREAM LITERAL1 +FIFOMEAN LITERAL1 +BYPASS2FIFO LITERAL1 REF_P_XL LITERAL1 REF_P_L LITERAL1 REF_P_H LITERAL1 @@ -58,4 +76,4 @@ LPS331AP_THS_P_H LITERAL1 LPS25H_INTERRUPT_CFG LITERAL1 LPS25H_INT_SOURCE LITERAL1 LPS25H_THS_P_L LITERAL1 -LPS25H_THS_P_H LITERAL1 \ No newline at end of file +LPS25H_THS_P_H LITERAL1 diff --git a/library.properties b/library.properties index 3d9e9e0..877264f 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=LPS -version=3.0.0 +version=3.1.0 author=Pololu maintainer=Pololu -sentence=Arduino library for Pololu LPS25H and LPS331AP boards +sentence=Arduino library for Pololu LPS25H/B and LPS331AP boards paragraph=This is a library for an Arduino-compatible controller that interfaces with ST LPS25H and LPS331AP pressure sensors on Pololu boards. category=Sensors url=https://github.com/pololu/lps-arduino -architectures=* \ No newline at end of file +architectures=*