Go Down

Topic: Barometer / Altimeter MS5611 (Read 13088 times) previous topic - next topic

freto

Feb 26, 2012, 08:01 am Last Edit: Feb 26, 2012, 09:04 am by freto Reason: 1
I am trying to interface an MS5611 (I bought the breakout from drotek.fr) with I2C, the datasheet is here:
http://www.meas-spec.com/downloads/MS5611-01BA01.pdf

I can read and convert the data, temperature looks accurate however the pressure is completely off, raw data is around 6450000
instead of around 9000000 necessary to calculate real atmospheric pressure around 1000 mbar.

I wrote an Arduino code and verified the math with Excel. I tried a few libraries found on the net. I tried many combination of pullups for I2C which did not make any changes, adjusted I2C speed, and tried a Mega and a Pro Mini 3.3v. Also I have 2 sensors, they give roughly the same result.

Today my BMP058 says 1003 millibar, my real altitude is 53 meter.

Anybody has experienced this with those sensors? Is there a calibration I missed?

Here is the code I am using:
Code: [Select]

// Simplest code to interface the MS5611

#include <Wire.h>

#define ADDRESS 0x77

uint32_t D1 = 0;
uint32_t D2 = 0;

int32_t dT = 0;
int32_t TEMP = 0;
int64_t OFF = 0;
int64_t SENS = 0;
int32_t P = 0;

uint16_t C[7];

void setup() {

  //Mega Arduino disable pullups
  //PORTD |= (1 << 0);
  //PORTD |= (1 << 1);
  // Disable internal pullups, 10Kohms are on the breakout
    PORTC |= (1 << 4);
    PORTC |= (1 << 5);
   
    Wire.begin();
    Serial.begin(9600);
    delay(100);
    initial(ADDRESS);
    Serial.println("temp*100 pressure*100 rawTemp rawPressure");
}

void loop()
{
    D1 = getVal(ADDRESS, 0x48); // Pressure raw
    D2 = getVal(ADDRESS, 0x58);// Temperature raw
 
    dT = (D2 - C[4] * 256);
    TEMP = 2000 + dT * C[5]/8388608;
    OFF  = C[1] * 65536LL + (dT *C[3])/128;
    SENS = C[0] * 32768LL + (C[2] * dT)/256;
    P = (D1 * SENS/2097152 - OFF)/32768;

    Serial.print(TEMP);
    Serial.print(" ");
    Serial.print(P);
    Serial.print(" ");
    Serial.print(D2); //D2 temperature
    Serial.print(" ");
    Serial.println(D1); // D1 pressure

    delay(100);
}

long getVal(int address, byte code)
{
    unsigned long ret = 0;
    Wire.beginTransmission(address);
    Wire.write(code);
    Wire.endTransmission();
    delay(10);
    // start read sequence
    Wire.beginTransmission(address);
    Wire.write((byte) 0x00);
    Wire.endTransmission();
    Wire.beginTransmission(address);
    Wire.requestFrom(address, (int)3);
    if(Wire.available()) {
    //    ret = ((uint32_t)Wire.read() << 16) | ((uint32_t)Wire.read() << 8) | Wire.read();
            ret = Wire.read() * 65536 + Wire.read() * 256 + Wire.read();
    }
    else {
        ret = -1;
    }
    Wire.endTransmission();
    return ret;
}

void initial(uint8_t address)
{
    Serial.println();
    Serial.println("PROM COEFFICIENTS");
   
    Wire.beginTransmission(address);
    Wire.write(0x1E); // reset
    Wire.endTransmission();
    delay(10);

    for (int i=0; i<7; i++) {
        Wire.beginTransmission(address);
        Wire.write(0xA2 + (i * 2));
        Wire.endTransmission();

        Wire.beginTransmission(address);
        Wire.requestFrom(address, (uint8_t) 6);
        delay(1);
        if(Wire.available()) {
            C[i] = Wire.read() *256 + Wire.read();
        }
        else {
            Serial.println("Error reading PROM 1"); // error reading the PROM or communicating with the device
        }
        Serial.println(C[i]);
    }
    Serial.println();
}

MarkT

Code: [Select]

    if(Wire.available()) {
    //    ret = ((uint32_t)Wire.read() << 16) | ((uint32_t)Wire.read() << 8) | Wire.read();
            ret = Wire.read() * 65536 + Wire.read() * 256 + Wire.read();
    }
    else {
        ret = -1;
    }

The commented out line seems better than the one you are using which isn't calculating values as long...
You could try
Code: [Select]

            ret = Wire.read() * 65536L + Wire.read() * 256L + Wire.read();

But that's not guaranteed to work for another reason:  the order of evaluation of subexpressions is not guaranteed, so that you can't be sure which byte gets used where, so you should be doing something like:
Code: [Select]

    ret = Wire.read () ;
    ret = (ret << 8) | Wire.read () ;
    ret = (ret << 8) | Wire.read () ;

Which forces the order of evaluation (and is also clearer to read, and can be converted into a loop in an obvious fashion:)
Code: [Select]

    ret = 0L ;
    for (byte i = 0 ; i < 3 ; i++)
      ret = (ret << 8) | Wire.read () ;


Also I'd be more defensive and check the value returned by available and rewrite
Code: [Select]

    if(Wire.available()) {

as
Code: [Select]

    if (Wire.available() >= 3) {

[ I won't respond to messages, use the forum please ]

freto

Thank you Mark for the explanation, indeed it makes the code better and safer.

I just found the solution with a microscope, in fact the sensors I bought are 5607 http://www.meas-spec.com/product/MS5607-B.aspx instead of the 5611 advertised http://www.drotek.fr/shop/fr/44-ms5611-pression-barometrique-pcb.html so the equation to derive pressure needed different coefficients.

MarkT

I once got sent £50 worth of magnetometer breakouts when I was ordering £10 of a cheaper accelerometer chips - dutifully sent them back though.  You got a barometer at least.
[ I won't respond to messages, use the forum please ]

sbishop61

Hi,
I have made the changes suggested and it appears to give better values.

However there is some behaviour I don't understand: If you make repeated calls to get the raw temperature you get consistent values. But if you request raw temperature, then request raw pressure and then further requests to get the raw temp the temperature values are different compared to the initial reading by roughly:
912 for the 1st call after the pressure reading;
578 for the 2nd call after the pressure reading;
284 for the 3rd call after the pressure reading.
Which is perhaps roughly 4*256, 2*256, 1*256 in each case.

I have checked this behaviour with Fabio Varesano's library with the same result. (his code seems to generally return a raw temp that is different by 3*256)

The code follows and I have attached an Excel spreadsheet with results.
Any thoughts welcome.
Code: [Select]
/*
Example code for using the MS5611 directly.

Copyright (C) 2011 Stephen Bishop

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

SDA (data line) is on analog input pin 4
SCL (clock line) is on analog input pin 5
*/


//#define DEBUG_V
#define ADDRESS 0x76 //Embedded adventures module CSB is VCC so HIGH i.e. 0x76

#include <Wire.h>

uint32_t D1 = 0;
uint32_t D2 = 0;

void setup() {
  Wire.begin();
  Serial.begin(115200);
  delay(1000);

  // Embedded adventures module CSB is VCC so HIGH
}

void loop() {

  Serial.print(" Direct rawtemp: "); // get raw T
  Serial.print(getVal(ADDRESS, 0x58));
  Serial.print(" Direct rawpress: "); // remove this line to test effect of pressure reading
  Serial.println(getVal(ADDRESS, 0x48)); // remove this line to test effect of pressure reading
  Serial.print(" Direct rawtemp: "); // get raw T
  Serial.print(getVal(ADDRESS, 0x58));
  Serial.print(" Direct rawtemp: "); // get raw T
  Serial.print(getVal(ADDRESS, 0x58));
  Serial.print(" Direct rawtemp: ");
  Serial.println(getVal(ADDRESS, 0x58));
}

uint32_t getVal(int address, byte code)
{
    uint32_t ret = 0;
    byte b = 0;
    Wire.beginTransmission(address);
    Wire.send(code); // changed write to send
   // Serial.print(code, BIN); // for debuging
    //Serial.print("  "); // for debuging
    Wire.endTransmission();
    delay(10);
    // start read sequence
    Wire.beginTransmission(address);
   // Wire.send((byte) 0x00);
    Wire.send(0);
    Wire.endTransmission();
    Wire.beginTransmission(address);
   // Serial.print((uint8_t) 3, BIN); // for debuging
    Wire.requestFrom((uint8_t)address, (uint8_t)3);// requestFrom MS5611
    if (Wire.available() >= 3)
        {
            ret = 0L ;
             b = 0;
     /* // for debuging
      Serial.print(b,DEC);
      Serial.print("  ");
      ret = (ret << 8) | b ;
      b = Wire.receive();
      Serial.print(b,DEC);
      Serial.print("  ");
      ret = (ret << 8) | b ;
      b = Wire.receive();
      Serial.print(b,DEC);
      Serial.print("  ");
      ret = (ret << 8) | b ;
      */
             
         for (byte i = 0 ; i < 3 ; i++)
           {
            b = Wire.receive();
           //  Serial.print(b,DEC); // for debuging
           //  Serial.print("  "); // for debuging
           ret = (ret << 8) | b ;
           }
       }
       else {
         ret = -1;
             }
       Wire.endTransmission();
    //   Serial.println(ret); // for debuging
       return ret;
}






julio79

sorry for my intrusion...

but in the sketch   C[7]  ... what values ??are?

_Leo_


sorry for my intrusion...

but in the sketch   C[7]  ... what values ??are?


If you are refering to sbishop61's code, it is incomplete.
Leo

Project "ALTDuino" - A homemade altimeter for model rockets.
http://www.altduino.de

julio79

I solved it ... thanks

could you help me to http://arduino.cc/forum/index.php/topic,113475.0.html

Go Up