LPS331AP Configuration

Hello,

I’ve got the LPS331AP Pressure/Altitude Sensor Carrier with Voltage Regulator #2126 and I’m having problems understanding the configuration modes. I’m using the Arduino platform with I2C. The example sketch “seems” to work fine however the noise and drift is to high for my liking.

I want to take 25 measurements per second but I’m uncertain how to set up the baro.

If I go with 25 Hz 1 Hz mode I get this which is unacceptable

I have been doing extensive comparisons with the MS5611 and the BMP180 baro chips.
Here a comparison with the MS5611 sensor

Any guidance for the LPS331AP is appreciated.

Leo

Hello, Leo.

The output data rate is controlled by the ODR2-ODR0 bits in CTRL_REG1. (Note that according to a footnote on page 19 of the datasheet, the default averaging setting is not allowed when using 25Hz/25Hz ODR, so you might have to change the RES_CONF register as well.) Is there something more specific you’re having trouble with?

I’m not sure off the top of my head whether your results look typical for the LPS331AP, so I would have to try to set one up myself to find out. From a quick look at the MS5611 datasheet, it seems like you should be able to get roughly comparable performance from both sensors, though.

Are you graphing the data directly from our example sketch, or are you using a different Arduino program or configuration? If it is a different program, can you tell me more about what it is doing? (Posting the source code might be useful.)

Have you tried graphing the raw pressure data instead of a calculated altitude to see if there’s a similar difference in noise?

- Kevin

Kevin,

as mensioned above I used 25Hz for pressure and 1 Hz for temp thinking the sensor will give 25 individual readings in 1 second.
If I use e.g. 12.5Hz for P readings I would get 12.5 individual readings in 1 second.
Is my thinking correct?

Since I need 25 pressure readings in a second I chose 25Hz / 1Hz and also tried setting the sensor to highest precision:

...
Wire.write(0x10);
Wire.write(0x7A);     // Set the pressure sensor to higher-precision
...
Wire.write(0x20);
Wire.write(0b11000000);     // turns on sensor, output P 25Hz : T 1Hz (0xC0)
...

One shot mod is not an option for my needs, correct?

The reason I’m testing this sensor is because it has simular specs to the MS5611. I’m hoping to have a very good alternativ to the MS5611.

I used your code and modified it for my output needs:

#include <Wire.h>
#include "LPS331.h"

LPS331 ps;

int   Case_Counter = 0;
float baseAltitude = 0;
float pressure     = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  pinMode(13, OUTPUT);                                 // Sets the digital pin as output

  if (!ps.init())
  {
    Serial.println("Failed to autodetect pressure sensor!");
    while (1);
  }

  ps.enableDefault();

  float pressure = ps.readPressureMillibars();
    baseAltitude = ps.pressureToAltitudeMeters(pressure);
}

void loop()
{
  int time = millis();

        pressure    = ps.readPressureMillibars();
  float altitude    = ps.pressureToAltitudeMeters(pressure) - baseAltitude;
  float temperature = ps.readTemperatureC();

  Case_Counter++;                                          // Increase counter by 1
  Serial.print(temperature);  Serial.print("\t");
  Serial.print(pressure);     Serial.print("\t");
  Serial.print(0);            Serial.print("\t");
  Serial.print(altitude);     Serial.print("\t");
  Serial.print(0);            Serial.print("\t");
  Serial.print(0);            Serial.print("\t");
  Serial.print(Case_Counter); Serial.print("\t");
  Serial.print("\r\n");
  if(Case_Counter == 25)                                   // Flash LED every 1 second
  {  
    digitalWrite(13, !digitalRead(13));                    // Toggle LED on/off to signal flight readiness
    Case_Counter = 0;                                      // Reset to 0
  }

  int measured = millis() - time;

  delay(40 - measured);
}

I could make a graph for the pressure readings however the altitude formula is identical in both above graphs.

What configuration seetings would you recommend for my needs?

Hi, Leo.

I notice both your graphs are centered at an altitude of zero, which would mask any scaling error that could account for your results. Are both sensors giving you the same average altitude when you use that formula, and does that altitude make sense?

- Ben

While your Wire.write() calls look appropriate for configuring the settings you want, I don’t see them in your output code (what you have under “I used your code and modified it for my output needs:”). You need to configure the settings in the output program after the call to enableDefault() to avoid them being overridden. Furthermore, our library provides functions that should offer a slightly more convenient way to access the registers in the LPS331AP, so I would recommend adding the following line immediately after enableDefault() to set up the 25Hz/1Hz ODR:

I will try to see what kind of results I get from an LPS331AP myself. Could you try comparing the raw pressure data and un-zeroed altitude data from the two sensors to make sure it’s not a conversion problem?

Could you also post the full code of the program you’re using to read from the MS5611, including a copy of or link to any libraries you’re using?

EDIT: If your MS5611 program outputs the same format as the LPS331AP program you posted above, a log of some of its output would be useful as well.

- Kevin

@Ben:
There is no scaling error. Both graphs are done with the same code. The altitude is being calculated using the same formula and the sensor is stationary. What you are seeing is more or less the sensor noise in meters

@Kevin:
I didn’t want to confuse you with my “Wire.write(0x20); Wire.write(0b11000000);” declaration. I did make the changes as you suggested through out my tests with the same results.

Let’s break it down to a simple test mainly because I only have one sensor and now need to eliminate the possibily of having received a defective sensor.

Here the sligthly modified code that I’d like someone to run for 30 seconds.

SerialMetric

#include <Wire.h>
#include "LPS331.h"

LPS331 ps;

int   Case_Counter = 0;
float baseAltitude = 0;
float pressure     = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  pinMode(13, OUTPUT);                                 // Sets the digital pin as output

  if (!ps.init())
  {
    Serial.println("Failed to autodetect pressure sensor!");
    while(1);
  }

  ps.enableDefault();

  float pressure = ps.readPressureMillibars();
    baseAltitude = ps.pressureToAltitudeMeters(pressure);
}

void loop()
{
  int time = millis();

        pressure    = ps.readPressureMillibars();
  float altitude    = ps.pressureToAltitudeMeters(pressure) - baseAltitude;
  float temperature = ps.readTemperatureC();

  Case_Counter++;                                          // Increase counter by 1
  Serial.print(temperature);  Serial.print("\t");
  Serial.print(pressure);     Serial.print("\t");
  Serial.print(0);            Serial.print("\t");
  Serial.print(altitude);     Serial.print("\t");
  Serial.print(0);            Serial.print("\t");
  Serial.print(0);            Serial.print("\t");
  Serial.print(Case_Counter); Serial.print("\t");
  Serial.print("\r\n");
  if(Case_Counter == 25)                                   // Flash LED every 1 second
  {  
    digitalWrite(13, !digitalRead(13));                    // Toggle LED on/off to signal flight readiness
    Case_Counter = 0;                                      // Reset to 0
  }

  int measured = millis() - time;
  delay(40 - measured);
}

LPS331.ccp

#include "LPS331.h"
#include <Wire.h>

// Defines ///////////////////////////////////////////////////////////

// The Arduino two-wire interface uses a 7-bit number for the address,
// and sets the last bit correctly based on reads and writes
#define LPS331AP_ADDRESS_SA0_LOW  0b1011100
#define LPS331AP_ADDRESS_SA0_HIGH 0b1011101

// Constructors //////////////////////////////////////////////////////

LPS331::LPS331(void)
{
  // Pololu board pulls SA0 high, so default assumption is that it is
  // high
  address = LPS331AP_ADDRESS_SA0_HIGH;
}

// Public Methods ////////////////////////////////////////////////////

// sets or detects slave address; returns bool indicating success
bool LPS331::init(byte sa0)
{
  switch(sa0)
  {
    case LPS331_SA0_LOW:
      address = LPS331AP_ADDRESS_SA0_LOW;
      return testWhoAmI();

    case LPS331_SA0_HIGH:
      address = LPS331AP_ADDRESS_SA0_HIGH;
      return testWhoAmI();

    default:
      return autoDetectAddress();
  }
}

// turns on sensor and enables continuous output
void LPS331::enableDefault(void)
{
//  writeReg(LPS331_CTRL_REG1, 0b11100000); // 12,5 12,5
  writeReg(LPS331_CTRL_REG1, 0b11000000); // 25 1
//    writeReg(LPS331_CTRL_REG1, 0b11010000); // 7 7
}

// writes register
void LPS331::writeReg(byte reg, byte value)
{
  Wire.beginTransmission(address);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

// reads register
byte LPS331::readReg(byte reg)
{
  byte value;

  Wire.beginTransmission(address);
  Wire.write(reg);
  Wire.endTransmission(false); // restart
  Wire.requestFrom(address, (byte)1);
  value = Wire.read();
  Wire.endTransmission();

  return value;
}

// reads pressure in millibars (mbar)/hectopascals (hPa)
float LPS331::readPressureMillibars(void)
{
  return (float)readPressureRaw() / 4096;
}

// reads pressure in inches of mercury (inHg)
float LPS331::readPressureInchesHg(void)
{
  return (float)readPressureRaw() / 138706.5;
}

// reads pressure and returns raw 24-bit sensor output
int32_t LPS331::readPressureRaw(void)
{
  Wire.beginTransmission(address);
  // assert MSB to enable register address auto-increment
  Wire.write(LPS331_PRESS_OUT_XL | (1 << 7));
  Wire.endTransmission();
  Wire.requestFrom(address, (byte)3);

  while (Wire.available() < 3);

  uint8_t pxl = Wire.read();
  uint8_t pl = Wire.read();
  uint8_t ph = Wire.read();

  // combine bytes
  return (int32_t)(int8_t)ph << 16 | (uint16_t)pl << 8 | pxl;
}

// reads temperature in degrees C
float LPS331::readTemperatureC(void)
{
  return 42.5 + (float)readTemperatureRaw() / 480;
}

// reads temperature in degrees F
float LPS331::readTemperatureF(void)
{
  return 108.5 + (float)readTemperatureRaw() / 480 * 1.8;
}

// reads temperature and returns raw 16-bit sensor output
int16_t LPS331::readTemperatureRaw(void)
{
  Wire.beginTransmission(address);
  // assert MSB to enable register address auto-increment
  Wire.write(LPS331_TEMP_OUT_L | (1 << 7));
  Wire.endTransmission();
  Wire.requestFrom(address, (byte)2);

  while (Wire.available() < 2);

  uint8_t tl = Wire.read();
  uint8_t th = Wire.read();

  // combine bytes
  return (int16_t)(th << 8 | tl);
}

// 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
//  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
float LPS331::pressureToAltitudeMeters(float pressure_mbar, float altimeter_setting_mbar)
{
  return (1 - pow(pressure_mbar / altimeter_setting_mbar, 0.190263)) * 44330.8;
}

// converts pressure in inHg to altitude in feet; see notes above
float LPS331::pressureToAltitudeFeet(float pressure_inHg, float altimeter_setting_inHg)
{
  return (1 - pow(pressure_inHg / altimeter_setting_inHg, 0.190263)) * 145442;
}

// Private Methods ///////////////////////////////////////////////////

bool LPS331::autoDetectAddress(void)
{
  // try each possible address and stop if reading WHO_AM_I returns the expected response
  address = LPS331AP_ADDRESS_SA0_LOW;
  if (testWhoAmI()) return true;
  address = LPS331AP_ADDRESS_SA0_HIGH;
  if (testWhoAmI()) return true;

  return false;
}

bool LPS331::testWhoAmI(void)
{
  return (readReg(LPS331_WHO_AM_I) == 0xBB);
}

LPS331.h

#ifndef LPS331_h
#define LPS331_h

#include <Arduino.h> // for byte data type

// SA0 states

#define LPS331_SA0_LOW  0
#define LPS331_SA0_HIGH 1
#define LPS331_SA0_AUTO 2

// register addresses
// Note: Some of the register names in the datasheet are inconsistent
// between Table 14 in section 6 and the register descriptions in
// section 7. Where they differ, the names from section 7 have been
// used here.

#define LPS331_REF_P_XL       0x08
#define LPS331_REF_P_L        0x09
#define LPS331_REF_P_H        0x0A

#define LPS331_WHO_AM_I       0x0F

#define LPS331_RES_CONF       0x10

#define LPS331_CTRL_REG1      0x20
#define LPS331_CTRL_REG2      0x21
#define LPS331_CTRL_REG3      0x22
#define LPS331_INTERRUPT_CFG  0x23
#define LPS331_INT_SOURCE     0x24
#define LPS331_THS_P_L        0x25
#define LPS331_THS_P_H        0x26
#define LPS331_STATUS_REG     0x27

#define LPS331_PRESS_OUT_XL   0x28
#define LPS331_PRESS_OUT_L    0x29
#define LPS331_PRESS_OUT_H    0x2A

#define LPS331_TEMP_OUT_L     0x2B
#define LPS331_TEMP_OUT_H     0x2C

#define LPS331_AMP_CTRL       0x30

#define LPS331_DELTA_PRESS_XL 0x3C
#define LPS331_DELTA_PRESS_L  0x3D
#define LPS331_DELTA_PRESS_H  0x3E

class LPS331
{
  public:
    LPS331(void);

    bool init(byte sa0 = LPS331_SA0_AUTO);

    void enableDefault(void);

    void writeReg(byte reg, byte value);
    byte readReg(byte reg);

    float readPressureMillibars(void);
    float readPressureInchesHg(void);
    int32_t readPressureRaw(void);
    float readTemperatureC(void);
    float readTemperatureF(void);
    int16_t readTemperatureRaw(void);

    static float pressureToAltitudeMeters(float pressure_mbar, float altimeter_setting_mbar = 1013.25);
    static float pressureToAltitudeFeet(float pressure_inHg, float altimeter_setting_inHg = 29.9213);

  private:
    byte address;

    bool autoDetectAddress(void);
    bool testWhoAmI(void);
};

#endif

Please post the results here so that I can run it through my graph program.

Thanks.

Leo

Using the same formula does not ensure that there is no scaling error. For example, if the two sensors output the barometric pressure in different units, you wouldn’t be able to tell from your program output; it would just look like one had a lot more noise than the other. I don’t know if this is the case, but I don’t really feel like going to the datasheets when it should be trivial for you to post the actual altitudes you are computing from the two sensors and confirm that they approximately match and make sense for your location (and this is just a general sanity check you should be doing, anyway).

- Ben

Both sensors are outputing in mbar. Having done a test as I write this I get e.g. 977.xx mbar @ 24.55 Celsius with the MS5611 and 974.xx mbar @ 23.46 Ceslsius with the LPS331AP but that is not the issue. The Problem is that the LPS331AP is very unstable and I’m not having any success in improving the issue.

Here pressure output from the MS5611:

977.31
977.30
977.30
977.31
977.31
977.30
977.30
977.30
977.32
977.31
977.30
977.30
977.31
977.30
977.31
977.30
977.31
977.31
977.30
977.31
977.30
977.30
977.32
977.30
977.30
977.31
977.32
977.31
977.29
977.28
977.31
977.31
977.32
977.30
977.30
977.30
977.31
977.31
977.30
977.31
977.32
977.30
977.30
977.30
977.30
977.30
977.30
977.30
977.31
977.30
977.30
977.30
977.30

and here from the LPS331AP:

974.49
974.47
974.45
974.45
974.59
974.59
974.61
974.54
974.56
974.57
974.50
974.53
974.55
974.54
974.48
974.53
974.53
974.53
974.45
974.51
974.47
974.50
974.51
974.52
974.42
974.47
974.53
974.56
974.56
974.65
974.64
974.63
974.56
974.61
974.61
974.58
974.53
974.57
974.54
974.51
974.56
974.59
974.57
974.52
974.54
974.57

I haven’t worked with either the MS5611 the LPS331AP sensor, but I have worked with a lot of others, for example the SCP-1000 (now obsolete, but offers the same pressure resolution as the LPS331AP). The noise you show for the LPS331AP looks perfectly typical to me and that the MS5611 readings appear to be more stable, suggests that it is already doing some sort of averaging.

It is very easy to implement a digital low pass filter or a moving average to smooth the readings, but that will of course reduce the response time of the system to genuine pressure changes. With the SCP-1000 I found it necessary to average over about 30 seconds of readings to get stable altitude measurements, with 20 cm precision.

Hi, Leo.

I ran your code with two LPS331AP carriers and I’ve attached the results in two separate files.

Unfortunately, I think the results you’re getting might be pretty much what you can expect from this sensor unless there’s a configuration setting we’re missing or a strange bug in our library. I tried graphing my results and they looked pretty similar to yours. I also used Excel to find the standard deviation of both data sets and got about 0.06 mbar and 0.09 mbar; those are higher than the RMS noise of 0.02 mbar the datasheet claims (with RES_CONF = 7A), but it’s within the same general range.

Like Jim said, it’s possible that the MS5611 is just doing more internal averaging. From a quick look at its datasheet, it looks like the MS5611 can use an oversampling ratio as high as 4096, while the LPS331AP only does 512 internal averages of the pressure reading at most. You might be able to get more stable results from the LPS331 with further filtering or averaging (as Jim also mentioned).

- Kevin
lps331_log_2.txt (23.8 KB)
lps331_log_1.txt (23.7 KB)

Kevin, thanks a lot!

I’ll look at the files and comment tomorrow after my analysis is complete.

Well, this went a lot quicker than I expected. I just added a read txt file routine to my analyzer software.

Here the two readouts:

In a nutshell it seems my sensor isn’t damaged however I’m very disappointed with the quality!
According the data sheet I place it between the MS5611 and the BMP180 but this device doesn’t come close.
I can’t find any other option in the data sheet to improve the outcome.

I need a clean raw pressure output with slight averaging before any kind of higher filtering is implemented. The LPS331AP just doesn’t cut it.

Anyways, thanks for your help guys!

Leo

Hi,

this post was an answere for me too, because I had also very bad results even by changing RES to 7A. Now I will buy MS5611 because it looks much more better. Anyway thanks for library ans support.