Faster Readings With BMP180 Sensor On Arduino

Thread Starter

Jblack

Joined Feb 24, 2016
39
I have a BMP180 barometric pressure sensor on an I2C breakout board connected to an Arduino. My goal is to use it for a model rocket altimeter. For that I need fast readings, ideally about 50 per second, or 20 mS between readings. With the example code below the fastest I'm able to get is about 20 readings per second, about 50 mS. Is there any way that the readings can be taken at a faster rate or is this the max for this sensor?

Code:
/* SFE_BMP180 altitude example sketch

This sketch shows how to use the Bosch BMP180 pressure sensor
as an altimiter.
https://www.sparkfun.com/products/11824

Like most pressure sensors, the BMP180 measures absolute pressure.
Since absolute pressure varies with altitude, you can use the pressure
to determine your altitude.

Because pressure also varies with weather, you must first take a pressure
reading at a known baseline altitude. Then you can measure variations
from that pressure

Hardware connections:

- (GND) to GND
+ (VDD) to 3.3V

(WARNING: do not connect + to 5V or the sensor will be damaged!)

You will also need to connect the I2C pins (SCL and SDA) to your
Arduino. The pins are different on different Arduinos:

Any Arduino pins labeled:  SDA  SCL
Uno, Redboard, Pro:        A4   A5
Mega2560, Due:             20   21
Leonardo:                   2    3

Leave the IO (VDDIO) pin unconnected. This pin is for connecting
the BMP180 to systems with lower logic levels such as 1.8V

Have fun! -Your friends at SparkFun.

The SFE_BMP180 library uses floating-point equations developed by the
Weather Station Data Logger project: http://wmrx00.sourceforge.net/

Our example code uses the "beerware" license. You can do anything
you like with this code. No really, anything. If you find it useful,
buy me a beer someday.

V10 Mike Grusin, SparkFun Electronics 10/24/2013
V1.1.2 Updates for Arduino 1.6.4 5/2015
*/

// Your sketch must #include this library, and the Wire library.
// (Wire is a standard library included with Arduino.):

#include <SFE_BMP180.h>
#include <Wire.h>

// You will need to create an SFE_BMP180 object, here called "pressure":

SFE_BMP180 pressure;

double baseline; // baseline pressure

void setup()
{
  Serial.begin(9600);
  Serial.println("REBOOT");

  // Initialize the sensor (it is important to get calibration values stored on the device).

  if (pressure.begin())
    Serial.println("BMP180 init success");
  else
  {
    // Oops, something went wrong, this is usually a connection problem,
    // see the comments at the top of this sketch for the proper connections.

    Serial.println("BMP180 init fail (disconnected?)\n\n");
    while(1); // Pause forever.
  }

  // Get the baseline pressure:
 
  baseline = getPressure();
 
  Serial.print("baseline pressure: ");
  Serial.print(baseline);
  Serial.println(" mb"); 
}

void loop()
{
  double a,P;
 
  // Get a new pressure reading:

  P = getPressure();

  // Show the relative altitude difference between
  // the new reading and the baseline reading:

  a = pressure.altitude(P,baseline);
 
  Serial.print("relative altitude: ");
  if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
  Serial.print(a,1);
  Serial.print(" meters, ");
  if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
  Serial.print(a*3.28084,0);
  Serial.print(" feet  ");
  Serial.println(millis());
  delay(0);
}


double getPressure()
{
  char status;
  double T,P,p0,a;

  // You must first get a temperature measurement to perform a pressure reading.
 
  // Start a temperature measurement:
  // If request is successful, the number of ms to wait is returned.
  // If request is unsuccessful, 0 is returned.

  status = pressure.startTemperature();
  if (status != 0)
  {
    // Wait for the measurement to complete:

    delay(status);

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Use '&T' to provide the address of T to the function.
    // Function returns 1 if successful, 0 if failure.

    status = pressure.getTemperature(T);
    if (status != 0)
    {
      // Start a pressure measurement:
      // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      // If request is successful, the number of ms to wait is returned.
      // If request is unsuccessful, 0 is returned.

      status = pressure.startPressure(3);
      if (status != 0)
      {
        // Wait for the measurement to complete:
        delay(status);

        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable P.
        // Use '&P' to provide the address of P.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.

        status = pressure.getPressure(P,T);
        if (status != 0)
        {
          return(P);
        }
        else Serial.println("error retrieving pressure measurement\n");
      }
      else Serial.println("error starting pressure measurement\n");
    }
    else Serial.println("error retrieving temperature measurement\n");
  }
  else Serial.println("error starting temperature measurement\n");
}
 

shteii01

Joined Feb 19, 2010
4,644
1. danny is right. this thing is not going to be talking to your pc when it is in the air, the serial prints are nice when it is connected to pc so that you, user/designer, have a clue what is going on. But. Once you send it into the air, the serial prints are useless because the board IS NOT connected to pc. Also the baud rate of 9600 is very old and "slow". They used baud rate of 9600 back in 1990? 1991? I know for a fact we were doing 28 or 32k baud rate in 1994-95. So. SPEED IT UP! It is 2016 for goodness sake!

2. When I used i2c in my senior design project, I ran into a small problem. What I ended up doing is going into arduino i2c library and changing a few things. Unlike you, I did not need to speed up i2c. However, the Arduino i2c library is where they define the speed of i2c. You might want to get into that file and see what the speed is setup to as a default. Do some changes to it. See what works and what does not.
 

Thread Starter

Jblack

Joined Feb 24, 2016
39
Thank you for the suggestions guys! I've rewritten the sketch in order to try to maximize the speed. Instead of printing each reading as it's read, the readings are now stored in an array, then, after a large number of them are stored, they are all printed. This got the speed up to about 30 readings per second. Then I went into the I2C library and changed the clock speed from whatever the default is to 400KHz. This got the speed to about 32 readings per second, obviously it didn't have much effect at all.
Here's my new code, I'm still not up to the speed I would like to be, but it's better than before.
Code:
// This is the example for the customized SFE_BMP180B library
// The result of the new library is only a few mS faster readings
// This sketch attempts to maximize the speed of readings
//The with everything - 3350 mS per 100 readings or 33.5 mS per reading
//The with only FT recorded - 3272 mS per 100 or 32.7 mS per reading
// using array and measuring then printing - 3233 mS per 100 or 32.3 mS per reading
/* SFE_BMP180 altitude example sketch

This sketch shows how to use the Bosch BMP180 pressure sensor
as an altimiter.
https://www.sparkfun.com/products/11824

Like most pressure sensors, the BMP180 measures absolute pressure.
Since absolute pressure varies with altitude, you can use the pressure
to determine your altitude.

Because pressure also varies with weather, you must first take a pressure
reading at a known baseline altitude. Then you can measure variations
from that pressure

Hardware connections:

- (GND) to GND
+ (VDD) to 3.3V

(WARNING: do not connect + to 5V or the sensor will be damaged!)

You will also need to connect the I2C pins (SCL and SDA) to your
Arduino. The pins are different on different Arduinos:

Any Arduino pins labeled:  SDA  SCL
Uno, Redboard, Pro:        A4   A5
Mega2560, Due:             20   21
Leonardo:                   2    3

Leave the IO (VDDIO) pin unconnected. This pin is for connecting
the BMP180 to systems with lower logic levels such as 1.8V

Have fun! -Your friends at SparkFun.

The SFE_BMP180 library uses floating-point equations developed by the
Weather Station Data Logger project: http://wmrx00.sourceforge.net/

Our example code uses the "beerware" license. You can do anything
you like with this code. No really, anything. If you find it useful,
buy me a beer someday.

V10 Mike Grusin, SparkFun Electronics 10/24/2013
V1.1.2 Updates for Arduino 1.6.4 5/2015
*/

// Your sketch must #include this library, and the Wire library.
// (Wire is a standard library included with Arduino.):

#include <SFE_BMP180B.h>
#include <Wire.h>

// You will need to create an SFE_BMP180 object, here called "pressure":

SFE_BMP180B pressure;

double baseline; // baseline pressure
long initial;
int val[600];

void setup()
{
  Serial.begin(115200);
  Serial.println("REBOOT");

  // Initialize the sensor (it is important to get calibration values stored on the device).

  if (pressure.begin())
    Serial.println("BMP180 init success");
  else
  {
    // Oops, something went wrong, this is usually a connection problem,
    // see the comments at the top of this sketch for the proper connections.

    Serial.println("BMP180 init fail (disconnected?)\n\n");
    while(1); // Pause forever.
  }

  // Get the baseline pressure:
 
  baseline = getPressure();
 
  Serial.print("baseline pressure: ");
  Serial.print(baseline);
  Serial.println(" mb"); 
}

void loop()
{
    initial = millis();

  for(int i = 0; i  < 600; i++) {
  double a,P;
 
  // Get a new pressure reading:

  P = getPressure();

  // Show the relative altitude difference between
  // the new reading and the baseline reading:

  a = pressure.altitude(P,baseline);
  val[i] = a*3.28084;
 
// val[i] = pressure.altitude(P,baseline);
// Serial.print("relative altitude: ");
// if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
// Serial.print(a,1);
// Serial.print(" meters, ");
// if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
// Serial.print(a*3.28084,0);
// Serial.print(" feet   ");
// Serial.print(i);
//  Serial.print("  ");
// Serial.println(millis() - initial);
}
long time = millis() - initial;
for(byte i = 0; i < 30; i++) {
  for(byte j = 0; j < 20; j++) {
  Serial.print(val[(i*20) + j]);
  Serial.print(" ");
}
Serial.println();
}
Serial.println(time);
delay(10000);
}


double getPressure()
{
  char status;
  double T,P,p0,a;

  // You must first get a temperature measurement to perform a pressure reading.
 
  // Start a temperature measurement:
  // If request is successful, the number of ms to wait is returned.
  // If request is unsuccessful, 0 is returned.

  status = pressure.startTemperature();
  if (status != 0)
  {
    // Wait for the measurement to complete:

    delay(status);

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Use '&T' to provide the address of T to the function.
    // Function returns 1 if successful, 0 if failure.

    status = pressure.getTemperature(T);
    if (status != 0)
    {
      // Start a pressure measurement:
      // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      // If request is successful, the number of ms to wait is returned.
      // If request is unsuccessful, 0 is returned.

      status = pressure.startPressure(3);
      if (status != 0)
      {
        // Wait for the measurement to complete:
        delay(status);

        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable P.
        // Use '&P' to provide the address of P.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.

        status = pressure.getPressure(P,T);
        if (status != 0)
        {
          return(P);
        }
        else Serial.println("error retrieving pressure measurement\n");
      }
      else Serial.println("error starting pressure measurement\n");
    }
    else Serial.println("error retrieving temperature measurement\n");
  }
  else Serial.println("error starting temperature measurement\n");
}
 

dannyf

Joined Sep 13, 2015
2,197
the readings are now stored in an array, then, after a large number of them are stored, they are all printed
I find that approach puzzling. If I were to do it, I would disable all serial prints, and see how much time the naked routines would take on their own. That provides a benchmark for how fast this whole thing could be.

From there you may figure out the costbof serial prints. And implement them as you see fit, via interrupts for example.

Now, you have both mingled together and you have no way of telling which is causing the slow speed.

With that said, the data sheet does lay out a pretty evaborate and calculationally intensive process so maybe it is that slow. If so, you may want to find a faster algorithm.

But first you have to figure out where the slow down is.
 

Thread Starter

Jblack

Joined Feb 24, 2016
39
I find that approach puzzling. If I were to do it, I would disable all serial prints, and see how much time the naked routines would take on their own. That provides a benchmark for how fast this whole thing could be.

From there you may figure out the costbof serial prints. And implement them as you see fit, via interrupts for example.

Now, you have both mingled together and you have no way of telling which is causing the slow speed.

With that said, the data sheet does lay out a pretty evaborate and calculationally intensive process so maybe it is that slow. If so, you may want to find a faster algorithm.

But first you have to figure out where the slow down is.
I thought about doing that, but the problem is I have to have a way to read the measurements, to make sure that they are reasonable, I'm afraid that if I simply run it as fast as possible, the data may be corrupted somehow. Also, if all serial prints are gone, how can I measure the time it takes? I'll try what you suggested, just to see if the serial prints are really taking that long, but first I have to figure out how to measure the time without printing it.
 

shteii01

Joined Feb 19, 2010
4,644
I thought about doing that, but the problem is I have to have a way to read the measurements, to make sure that they are reasonable, I'm afraid that if I simply run it as fast as possible, the data may be corrupted somehow. Also, if all serial prints are gone, how can I measure the time it takes? I'll try what you suggested, just to see if the serial prints are really taking that long, but first I have to figure out how to measure the time without printing it.
You are right that you will not be able to see what is going on. Blinking led would be too slow. Do you have oscilloscope? You can cycle a pin to indicate successful transaction, use oscilloscope to see what pin is doing. Maybe even record the pin cycles.
 

Thread Starter

Jblack

Joined Feb 24, 2016
39
You are right that you will not be able to see what is going on. Blinking led would be too slow. Do you have oscilloscope? You can cycle a pin to indicate successful transaction, use oscilloscope to see what pin is doing. Maybe even record the pin cycles.
That is a brilliant idea, thank you. I have occasional access to one, so hopefully I'll be able to try this out soon.
 

dannyf

Joined Sep 13, 2015
2,197
how can I measure the time it takes?
I typically flip a pin and watch it on a scope.

Alternatively, use a simulator.

I think you should be able to get an INTERFET pressure measurement within a few ms. Plus one ms to read the data, and potentially a floating math routine to estimate elevation, 20ms is doable.
 

dannyf

Joined Sep 13, 2015
2,197
I did some experiment.

the stock routine to get true pressure reading takes 13K instructions to complete, on a pic16f. I would expect similar results on an avr.

So reading 16 words via 400Kbps i2c will take about 1ms. calculating the pressure will take another 1ms @ 16MIPS. Doing nothing else, you should complete one round of reading within 2ms.
 

dannyf

Joined Sep 13, 2015
2,197
turning off all serial prints, and forcing the sensor to perform all the calculations yields a refreshing rate of about 400Hz -> 2.x ms per round, very close to the above estimate.

bmp180_atmega328.PNG
In the above, i was flipping PB3 in the loop() as a way to measure execution timing.
 

koshmarus

Joined Mar 31, 2020
2
I've managed to achieve 38 readings per second or 26ms time for full measurement.
It includes not only BMP180's altitude reading, but also 6 parameters from IMU6050 (GY-521).

I've just decreased accuracy for BMP sensor called "status = pressure.startPressure(0);"
When you type "0" - it is lowest resolution, but faster speed.
When you type "3" - it is slower.
 

djsfantasi

Joined Apr 11, 2010
9,163
The code has a lot of delay() statements. This may limit how many readings you can get.

I don’t know what the lines
status=xxx.Temperature(...)​
in the routine GetPressure() are for. But they all have a delay() after them. Status will have a value between 0 and 255. At the upper end, you could be adding 1/4 of a second to one measurement.

I’d estimate the amount of time spent in those delays. They are going to define some discrete minimum time to take a measurement. Then calculate how many measurements, ignoring everything else, you can do at most in a second. There is a chance that this is your limiting factor.
 

koshmarus

Joined Mar 31, 2020
2
In order to correctly measure the air pressure, BMP180 must have readings on temperature.
It incorporates Temperature in pressure calculation formula.

I guess the setting of accuracy of this sensor changes the delays in sensor library, when it performs measurements.

In my setting all the data are logged onto SD card module, so by now SD card logging is the slowest part in whole process. Maybe flash memory module (32 megabyte) will do the job faster.
 

danadak

Joined Mar 10, 2018
4,057
When you look at library code -

1) Look for lack of use of pointers, that helps compress code.

2) Look for nested f() calls. Each time that happens lots of stack push.
Use inline code to minimize nested calls.

3) Look for float math, see if you can convert to integer math until you absolutely
have to use floats.


Regards, Dana.
 
Top