Faster Readings With BMP180 Sensor On Arduino

Discussion in 'Embedded Systems and Microcontrollers' started by Jblack, Dec 14, 2016.

  1. Jblack

    Thread Starter Member

    Feb 24, 2016
    30
    0
    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 (Text):
    1. /* SFE_BMP180 altitude example sketch
    2.  
    3. This sketch shows how to use the Bosch BMP180 pressure sensor
    4. as an altimiter.
    5. https://www.sparkfun.com/products/11824
    6.  
    7. Like most pressure sensors, the BMP180 measures absolute pressure.
    8. Since absolute pressure varies with altitude, you can use the pressure
    9. to determine your altitude.
    10.  
    11. Because pressure also varies with weather, you must first take a pressure
    12. reading at a known baseline altitude. Then you can measure variations
    13. from that pressure
    14.  
    15. Hardware connections:
    16.  
    17. - (GND) to GND
    18. + (VDD) to 3.3V
    19.  
    20. (WARNING: do not connect + to 5V or the sensor will be damaged!)
    21.  
    22. You will also need to connect the I2C pins (SCL and SDA) to your
    23. Arduino. The pins are different on different Arduinos:
    24.  
    25. Any Arduino pins labeled:  SDA  SCL
    26. Uno, Redboard, Pro:        A4   A5
    27. Mega2560, Due:             20   21
    28. Leonardo:                   2    3
    29.  
    30. Leave the IO (VDDIO) pin unconnected. This pin is for connecting
    31. the BMP180 to systems with lower logic levels such as 1.8V
    32.  
    33. Have fun! -Your friends at SparkFun.
    34.  
    35. The SFE_BMP180 library uses floating-point equations developed by the
    36. Weather Station Data Logger project: http://wmrx00.sourceforge.net/
    37.  
    38. Our example code uses the "beerware" license. You can do anything
    39. you like with this code. No really, anything. If you find it useful,
    40. buy me a beer someday.
    41.  
    42. V10 Mike Grusin, SparkFun Electronics 10/24/2013
    43. V1.1.2 Updates for Arduino 1.6.4 5/2015
    44. */
    45.  
    46. // Your sketch must #include this library, and the Wire library.
    47. // (Wire is a standard library included with Arduino.):
    48.  
    49. #include <SFE_BMP180.h>
    50. #include <Wire.h>
    51.  
    52. // You will need to create an SFE_BMP180 object, here called "pressure":
    53.  
    54. SFE_BMP180 pressure;
    55.  
    56. double baseline; // baseline pressure
    57.  
    58. void setup()
    59. {
    60.   Serial.begin(9600);
    61.   Serial.println("REBOOT");
    62.  
    63.   // Initialize the sensor (it is important to get calibration values stored on the device).
    64.  
    65.   if (pressure.begin())
    66.     Serial.println("BMP180 init success");
    67.   else
    68.   {
    69.     // Oops, something went wrong, this is usually a connection problem,
    70.     // see the comments at the top of this sketch for the proper connections.
    71.  
    72.     Serial.println("BMP180 init fail (disconnected?)\n\n");
    73.     while(1); // Pause forever.
    74.   }
    75.  
    76.   // Get the baseline pressure:
    77.  
    78.   baseline = getPressure();
    79.  
    80.   Serial.print("baseline pressure: ");
    81.   Serial.print(baseline);
    82.   Serial.println(" mb");
    83. }
    84.  
    85. void loop()
    86. {
    87.   double a,P;
    88.  
    89.   // Get a new pressure reading:
    90.  
    91.   P = getPressure();
    92.  
    93.   // Show the relative altitude difference between
    94.   // the new reading and the baseline reading:
    95.  
    96.   a = pressure.altitude(P,baseline);
    97.  
    98.   Serial.print("relative altitude: ");
    99.   if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
    100.   Serial.print(a,1);
    101.   Serial.print(" meters, ");
    102.   if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
    103.   Serial.print(a*3.28084,0);
    104.   Serial.print(" feet  ");
    105.   Serial.println(millis());
    106.   delay(0);
    107. }
    108.  
    109.  
    110. double getPressure()
    111. {
    112.   char status;
    113.   double T,P,p0,a;
    114.  
    115.   // You must first get a temperature measurement to perform a pressure reading.
    116.  
    117.   // Start a temperature measurement:
    118.   // If request is successful, the number of ms to wait is returned.
    119.   // If request is unsuccessful, 0 is returned.
    120.  
    121.   status = pressure.startTemperature();
    122.   if (status != 0)
    123.   {
    124.     // Wait for the measurement to complete:
    125.  
    126.     delay(status);
    127.  
    128.     // Retrieve the completed temperature measurement:
    129.     // Note that the measurement is stored in the variable T.
    130.     // Use '&T' to provide the address of T to the function.
    131.     // Function returns 1 if successful, 0 if failure.
    132.  
    133.     status = pressure.getTemperature(T);
    134.     if (status != 0)
    135.     {
    136.       // Start a pressure measurement:
    137.       // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
    138.       // If request is successful, the number of ms to wait is returned.
    139.       // If request is unsuccessful, 0 is returned.
    140.  
    141.       status = pressure.startPressure(3);
    142.       if (status != 0)
    143.       {
    144.         // Wait for the measurement to complete:
    145.         delay(status);
    146.  
    147.         // Retrieve the completed pressure measurement:
    148.         // Note that the measurement is stored in the variable P.
    149.         // Use '&P' to provide the address of P.
    150.         // Note also that the function requires the previous temperature measurement (T).
    151.         // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
    152.         // Function returns 1 if successful, 0 if failure.
    153.  
    154.         status = pressure.getPressure(P,T);
    155.         if (status != 0)
    156.         {
    157.           return(P);
    158.         }
    159.         else Serial.println("error retrieving pressure measurement\n");
    160.       }
    161.       else Serial.println("error starting pressure measurement\n");
    162.     }
    163.     else Serial.println("error retrieving temperature measurement\n");
    164.   }
    165.   else Serial.println("error starting temperature measurement\n");
    166. }
    167.  
    168.  
    169.  
     
  2. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    422
    Get rid of all the serial prints, or to run the serial at high baud rates.
     
  3. shteii01

    AAC Fanatic!

    Feb 19, 2010
    4,667
    745
    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.
     
  4. Jblack

    Thread Starter Member

    Feb 24, 2016
    30
    0
    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 (Text):
    1. // This is the example for the customized SFE_BMP180B library
    2. // The result of the new library is only a few mS faster readings
    3. // This sketch attempts to maximize the speed of readings
    4. //The with everything - 3350 mS per 100 readings or 33.5 mS per reading
    5. //The with only FT recorded - 3272 mS per 100 or 32.7 mS per reading
    6. // using array and measuring then printing - 3233 mS per 100 or 32.3 mS per reading
    7. /* SFE_BMP180 altitude example sketch
    8.  
    9. This sketch shows how to use the Bosch BMP180 pressure sensor
    10. as an altimiter.
    11. https://www.sparkfun.com/products/11824
    12.  
    13. Like most pressure sensors, the BMP180 measures absolute pressure.
    14. Since absolute pressure varies with altitude, you can use the pressure
    15. to determine your altitude.
    16.  
    17. Because pressure also varies with weather, you must first take a pressure
    18. reading at a known baseline altitude. Then you can measure variations
    19. from that pressure
    20.  
    21. Hardware connections:
    22.  
    23. - (GND) to GND
    24. + (VDD) to 3.3V
    25.  
    26. (WARNING: do not connect + to 5V or the sensor will be damaged!)
    27.  
    28. You will also need to connect the I2C pins (SCL and SDA) to your
    29. Arduino. The pins are different on different Arduinos:
    30.  
    31. Any Arduino pins labeled:  SDA  SCL
    32. Uno, Redboard, Pro:        A4   A5
    33. Mega2560, Due:             20   21
    34. Leonardo:                   2    3
    35.  
    36. Leave the IO (VDDIO) pin unconnected. This pin is for connecting
    37. the BMP180 to systems with lower logic levels such as 1.8V
    38.  
    39. Have fun! -Your friends at SparkFun.
    40.  
    41. The SFE_BMP180 library uses floating-point equations developed by the
    42. Weather Station Data Logger project: http://wmrx00.sourceforge.net/
    43.  
    44. Our example code uses the "beerware" license. You can do anything
    45. you like with this code. No really, anything. If you find it useful,
    46. buy me a beer someday.
    47.  
    48. V10 Mike Grusin, SparkFun Electronics 10/24/2013
    49. V1.1.2 Updates for Arduino 1.6.4 5/2015
    50. */
    51.  
    52. // Your sketch must #include this library, and the Wire library.
    53. // (Wire is a standard library included with Arduino.):
    54.  
    55. #include <SFE_BMP180B.h>
    56. #include <Wire.h>
    57.  
    58. // You will need to create an SFE_BMP180 object, here called "pressure":
    59.  
    60. SFE_BMP180B pressure;
    61.  
    62. double baseline; // baseline pressure
    63. long initial;
    64. int val[600];
    65.  
    66. void setup()
    67. {
    68.   Serial.begin(115200);
    69.   Serial.println("REBOOT");
    70.  
    71.   // Initialize the sensor (it is important to get calibration values stored on the device).
    72.  
    73.   if (pressure.begin())
    74.     Serial.println("BMP180 init success");
    75.   else
    76.   {
    77.     // Oops, something went wrong, this is usually a connection problem,
    78.     // see the comments at the top of this sketch for the proper connections.
    79.  
    80.     Serial.println("BMP180 init fail (disconnected?)\n\n");
    81.     while(1); // Pause forever.
    82.   }
    83.  
    84.   // Get the baseline pressure:
    85.  
    86.   baseline = getPressure();
    87.  
    88.   Serial.print("baseline pressure: ");
    89.   Serial.print(baseline);
    90.   Serial.println(" mb");
    91. }
    92.  
    93. void loop()
    94. {
    95.     initial = millis();
    96.  
    97.   for(int i = 0; i  < 600; i++) {
    98.   double a,P;
    99.  
    100.   // Get a new pressure reading:
    101.  
    102.   P = getPressure();
    103.  
    104.   // Show the relative altitude difference between
    105.   // the new reading and the baseline reading:
    106.  
    107.   a = pressure.altitude(P,baseline);
    108.   val[i] = a*3.28084;
    109.  
    110. // val[i] = pressure.altitude(P,baseline);
    111. // Serial.print("relative altitude: ");
    112. // if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
    113. // Serial.print(a,1);
    114. // Serial.print(" meters, ");
    115. // if (a >= 0.0) Serial.print(" "); // add a space for positive numbers
    116. // Serial.print(a*3.28084,0);
    117. // Serial.print(" feet   ");
    118. // Serial.print(i);
    119. //  Serial.print("  ");
    120. // Serial.println(millis() - initial);
    121. }
    122. long time = millis() - initial;
    123. for(byte i = 0; i < 30; i++) {
    124.   for(byte j = 0; j < 20; j++) {
    125.   Serial.print(val[(i*20) + j]);
    126.   Serial.print(" ");
    127. }
    128. Serial.println();
    129. }
    130. Serial.println(time);
    131. delay(10000);
    132. }
    133.  
    134.  
    135. double getPressure()
    136. {
    137.   char status;
    138.   double T,P,p0,a;
    139.  
    140.   // You must first get a temperature measurement to perform a pressure reading.
    141.  
    142.   // Start a temperature measurement:
    143.   // If request is successful, the number of ms to wait is returned.
    144.   // If request is unsuccessful, 0 is returned.
    145.  
    146.   status = pressure.startTemperature();
    147.   if (status != 0)
    148.   {
    149.     // Wait for the measurement to complete:
    150.  
    151.     delay(status);
    152.  
    153.     // Retrieve the completed temperature measurement:
    154.     // Note that the measurement is stored in the variable T.
    155.     // Use '&T' to provide the address of T to the function.
    156.     // Function returns 1 if successful, 0 if failure.
    157.  
    158.     status = pressure.getTemperature(T);
    159.     if (status != 0)
    160.     {
    161.       // Start a pressure measurement:
    162.       // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
    163.       // If request is successful, the number of ms to wait is returned.
    164.       // If request is unsuccessful, 0 is returned.
    165.  
    166.       status = pressure.startPressure(3);
    167.       if (status != 0)
    168.       {
    169.         // Wait for the measurement to complete:
    170.         delay(status);
    171.  
    172.         // Retrieve the completed pressure measurement:
    173.         // Note that the measurement is stored in the variable P.
    174.         // Use '&P' to provide the address of P.
    175.         // Note also that the function requires the previous temperature measurement (T).
    176.         // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
    177.         // Function returns 1 if successful, 0 if failure.
    178.  
    179.         status = pressure.getPressure(P,T);
    180.         if (status != 0)
    181.         {
    182.           return(P);
    183.         }
    184.         else Serial.println("error retrieving pressure measurement\n");
    185.       }
    186.       else Serial.println("error starting pressure measurement\n");
    187.     }
    188.     else Serial.println("error retrieving temperature measurement\n");
    189.   }
    190.   else Serial.println("error starting temperature measurement\n");
    191. }
    192.  
    193.  
    194.  
     
  5. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    422
    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.
     
  6. Jblack

    Thread Starter Member

    Feb 24, 2016
    30
    0
    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.
     
  7. shteii01

    AAC Fanatic!

    Feb 19, 2010
    4,667
    745
    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.
     
  8. Jblack

    Thread Starter Member

    Feb 24, 2016
    30
    0
    That is a brilliant idea, thank you. I have occasional access to one, so hopefully I'll be able to try this out soon.
     
  9. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    422
    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.
     
  10. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    422
    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.
     
  11. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    422
    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.
     
Loading...