Reading/updating I2C BMP085 without delay()

Hi,

I am working on a quadcopter originally from http://aeroquad.com/ which is using a Arduino Mega board.

It is a great platform for quads, but now I've run into a frustrating problem. I wanna add a BMP085 which is a I2C barometer for altitude hold control/height sensoring. I have managed to get this: http://interactive-matter.org/2009/12/arduino-barometric-pressure-sensor-bmp085/ and this: BMP085 with Arduino (tested & working) | mitat.tuu.fi to work great as stand alone read code. I have already managed to get an analog sensor to regulate height fine but only to a maximum distance above ground of 1.5m due to use of SHARP IR sensor. Now I need to evolve.

The values I get are expected in the range around 1004.00-1006.00hPa, and expected temperature too.

The problem is that I cannot use the delay() function which delays and stops the whole flight program for 26ms (read time from sensor) because then it gets out of control and really wild.

Therefore I have made a change so that the code that writes to the "start measure sensor" registers are called at the end of a time interval state code and in the beginning of that code when it comes the second time to the loop it is ready to be read.

The problem is that the way the code works the values read in are in the range of 600.00hPa the way I implemented this and does not correspond as good to the real pressure as the original code. As soon as I comment in the delay in the read function again the sensor gives out relevant and correct data again.

The question is why?

I'll try to put in relevant bits of code:

//in 26ms loop for the highest resolution of BMP085
byte updateFlag = 0;

if (updateFlag == 1) { //prevent measurement before conversion started
bmp085_read_temperature_and_pressure(&temperature,&pressure);

actualPressure = pressure;
//do something useful with the read in value
}

updateCommandBMP085(); //this function I added to make "write_register" to start reading the values and then wait 26ms
updateFlag = 1;

this is the original code for starting measurement:

long bmp085_read_up() {
write_register(0xf4,0x34+(oversampling_setting<<6)); //this needs to be done in state loop
delay(pressure_waittime[oversampling_setting]); //this needs to be replaced by the loop time

unsigned char msb, lsb, xlsb;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(0xf6); // register to read
Wire.endTransmission();

Wire.requestFrom(I2C_ADDRESS, 3); // read a byte
while(!Wire.available()) {
// waiting
}
msb = Wire.receive();
while(!Wire.available()) {
// waiting
}
lsb |= Wire.receive();
while(!Wire.available()) {
// waiting
}
xlsb |= Wire.receive();
return (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >>(8-oversampling_setting);
}

and what I did to get rid of the delay() was:

void updateCommandBMP085 () {
   write_register(0xf4,0x2e);   //start temperature reading                   
   write_register(0xf4,0x34+(oversampling_setting<<6)); //start pressure reading
   };

And all this should work exactly as the original test code for BMP085 and Arduino, but just by commenting in/out the "delay(pressure_waittime[oversampling_setting]);" line, I get two totally different readings, when using delay() the reading is correct and the quad goes wild, when commented out and the "write_register" is called from the state loop, the values read in from the sensor is off.

My question is: what am I missing? What is the difference here between using the delay() and calling the write register from a loop instead?

Thanks for any answer!

The comments indicate that it takes 26mS to get an accurate reading. The delay allows that time to elapse.

To get rid of the delay statement, you'll need to turn the sensor on, and then return. Create a new function to read the sensor. Use millis() to read the time that the sensor was turned on. Use millis() in each pass through loop, and call the function to get the results when 26 or more milliseconds has elapsed.

Thanks for the answer!

Well, the strange thing is, that is basically what's done, I just didn't show it proper I think if I get you totally. Yes, the 26ms is the conversion time internally in the sensor.

The thing I didn't tell was this (sorry for mixing with pseudo):

if (currentTime > (barSensTime + BARSENSLOOPTIME)) { //checks if 26ms passed (the define is 26, currentTime a counter of millis())

if updateflag == 1 {//prevents first reading to be 0
read finished result;
}

updateCommand; //start measuring with sensor
updateflag =1;

barSensTime = currentTime; //update time last in loop
    }

Now I think this is the missing part I didn't tell, but this didn't work as expected and that's what was puzzling me to pieces! This should first time command sensor to start read - wait 26ms with millis() - second cycle read out the result
But off-value is read.

However, I found a nice shortcut. Some guys working on a bigger quadcopter project made a nice library for the BMP085 with classes that is much easier to call from the same type of time statement loop and it works flawlessly.

Can be found here:
https://code.google.com/p/arducopter/source/browse/trunk/libraries/#libraries/APM_BMP085

And the calls are just like mine, but what happens when the calls are made is different, I read a little in the actual .cpp in the library and fits this time loop code better:

example of usage:

/*
  Example of APM_BMP085 (absolute pressure sensor) library.
  Code by Jordi Muñoz and Jose Julio. DIYDrones.com
*/

#include <Wire.h>
#include <APM_BMP085.h> // ArduPilot Mega BMP085 Library

unsigned long timer;

void setup()
{  
  APM_BMP085.Init();   // APM ADC initialization
  Serial.begin(57600);
  Serial.println("ArduPilot Mega BMP085 library test");
  delay(1000);
  timer = millis();
}

void loop()
{
  int ch;
  float tmp_float;
  float Altitude;
  
  if((millis()- timer) > 50)
    {
    timer=millis();
    APM_BMP085.Read();
    Serial.print("Pressure:");
    Serial.print(APM_BMP085.Press);
    Serial.print(" Temperature:");
    Serial.print(APM_BMP085.Temp/10.0);
    Serial.print(" Altitude:");
    tmp_float = (APM_BMP085.Press/101325.0);
    tmp_float = pow(tmp_float,0.190295);
    Altitude = 44330*(1.0-tmp_float);
    Serial.print(Altitude);
    Serial.println();
    }
}

This is why I love arduino and whatnot. A library was made and I was happy it worked flawlessly. Will look much nicer in my user code.

Hope this will help others too.

Just one thought - BMP085 has EOC signal (it is brought out on SparkFun's breakout board). EOC is End Of Conversion - when low (0) conversion is in progress, when high (1) it is done.

So instead waiting You can attach EOC to interrupt and set it to fire on rising edge - I think it should work.

I found out that the library was not updated for several years and it is outdated, so in case if someone in needs to read BMP085 without delay / blocking try mine:

Comments welcome