Go Down

Topic: Due I2C returns bad values; Uno and Mega are correct (Read 116 times) previous topic - next topic

cdcgg3

I recently bought a Due to collect and log atmospheric data from I2C sensors.  While I was waiting for the Due, I started developing on an Uno then loaded the code on the Due and I got bad results.  Puzzled, I loaded the same code onto a Mega and got the expected results.  To study this anomaly I went back to some simple sample code from the sensor vendor, connected the sensor, and loaded the code - same thing. Summary thus far: with the exact same sensor and code the Uno and Mega produce expected values while the Due reports bad data.

I'm not well versed in the I2C protocol or in the byte-level programming/addressing used for this sensor.  I don't know if it's a problem with the I2C communication or with how the different processor architectures handle datatypes/operations/etc....?  Any help would be greatly appreciated.

Here are the details:

Hardware:
Arduino Uno R3
Arduino Mega 2560
Arduino Due R3-E
BMP180 barometric pressure sensor on Sparkfun breakout board
  https://www.sparkfun.com/products/11824

Software:
Arduino 1.5.8
Full sketch is included at the bottom of this post.

Connections:
According to the schematics for the Uno and Mega there are no pullup resistors on the SCL or SDA lines (there are pullup resistors build in to the BMP180 breakout board).  On the Due however, there are pullup resistors on SCL and SDA, but not on SCL1 and SDA1.  I connected the sensor to SCL1 and SDA1 since it has pullups on the breakout board.  NOTE: When I loaded this on the Uno and Mega I used the Wire class, and when I loaded it on the Due I used the Wire1 class (since I connected to SCL1 and SDA1).

Output:
On the Uno and Mega the output is:
Code: [Select]
Temperature: 17.20deg C
Pressure: 98168 Pa
Standard Atmosphere: 0.9688
Altitude: 266.18 M


But on the Due, I get:
Code: [Select]
Temperature: 120.30deg C
Pressure: 145056 Pa
Standard Atmosphere: 1.4316
Altitude: -3131.87 M


For my application I'm not concerned with an extreme level of accuracy, but the fact that it says the ambient temperature is 120deg C and I'm nearly 2 miles below sea level is a problem.

Can anyone explain this behavior and suggest solutions?

Thanks.


Full sketch:

Code: [Select]
#include <Wire.h>

#define BMP180_ADDRESS 0x77  // I2C address of BMP180

const unsigned char OSS = 0;  // Oversampling Setting

// Calibration values
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;

// b5 is calculated in bmp180GetTemperature(...), this variable is also used in bmp180GetPressure(...)
// so ...Temperature(...) must be called before ...Pressure(...).
long b5;

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

  bmp180Calibration();
}

void loop()
{
  float temperature = bmp180GetTemperature(bmp180ReadUT()); //MUST be called first
  float pressure = bmp180GetPressure(bmp180ReadUP());
  float atm = pressure / 101325; // "standard atmosphere"
  float altitude = calcAltitude(pressure); //Uncompensated caculation - in Meters

  Serial.print("Temperature: ");
  Serial.print(temperature, 2); //display 2 decimal places
  Serial.println("deg C");

  Serial.print("Pressure: ");
  Serial.print(pressure, 0); //whole number only.
  Serial.println(" Pa");

  Serial.print("Standard Atmosphere: ");
  Serial.println(atm, 4); //display 4 decimal places

  Serial.print("Altitude: ");
  Serial.print(altitude, 2); //display 2 decimal places
  Serial.println(" M");

  Serial.println();//line break

  delay(1000); //wait a second and get values again.
}

// Stores all of the bmp085's calibration values into global variables
// Calibration values are required to calculate temp and pressure
// This function should be called at the beginning of the program
void bmp180Calibration()
{
  ac1 = bmp180ReadInt(0xAA);
  ac2 = bmp180ReadInt(0xAC);
  ac3 = bmp180ReadInt(0xAE);
  ac4 = bmp180ReadInt(0xB0);
  ac5 = bmp180ReadInt(0xB2);
  ac6 = bmp180ReadInt(0xB4);
  b1 = bmp180ReadInt(0xB6);
  b2 = bmp180ReadInt(0xB8);
  mb = bmp180ReadInt(0xBA);
  mc = bmp180ReadInt(0xBC);
  md = bmp180ReadInt(0xBE);
}

// Calculate temperature in deg C
float bmp180GetTemperature(unsigned int ut){
  long x1, x2;

  x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;
  x2 = ((long)mc << 11)/(x1 + md);
  b5 = x1 + x2;

  float temp = ((b5 + 8)>>4);
  temp = temp /10;

  return temp;
}

// Calculate pressure given up
// calibration values must be known
// b5 is also required so bmp180GetTemperature(...) must be called first.
// Value returned will be pressure in units of Pa.
long bmp180GetPressure(unsigned long up){
  long x1, x2, x3, b3, b6, p;
  unsigned long b4, b7;

  b6 = b5 - 4000;
  // Calculate B3
  x1 = (b2 * (b6 * b6)>>12)>>11;
  x2 = (ac2 * b6)>>11;
  x3 = x1 + x2;
  b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;

  // Calculate B4
  x1 = (ac3 * b6)>>13;
  x2 = (b1 * ((b6 * b6)>>12))>>16;
  x3 = ((x1 + x2) + 2)>>2;
  b4 = (ac4 * (unsigned long)(x3 + 32768))>>15;

  b7 = ((unsigned long)(up - b3) * (50000>>OSS));
  if (b7 < 0x80000000)
    p = (b7<<1)/b4;
  else
    p = (b7/b4)<<1;

  x1 = (p>>8) * (p>>8);
  x1 = (x1 * 3038)>>16;
  x2 = (-7357 * p)>>16;
  p += (x1 + x2 + 3791)>>4;

  long temp = p;
  return temp;
}

// Read 1 byte from the BMP180 at 'address'
char bmp180Read(unsigned char address)
{
  unsigned char data;

  Wire.beginTransmission(BMP180_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(BMP180_ADDRESS, 1);
  while(!Wire.available())
    ;

  return Wire.read();
}

// Read 2 bytes from the BMP180
// First byte will be from 'address'
// Second byte will be from 'address'+1
int bmp180ReadInt(unsigned char address)
{
  unsigned char msb, lsb;

  Wire.beginTransmission(BMP180_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(BMP180_ADDRESS, 2);
  while(Wire.available()<2)
    ;
  msb = Wire.read();
  lsb = Wire.read();

  return (int) msb<<8 | lsb;
}

// Read the uncompensated temperature value
unsigned int bmp180ReadUT(){
  unsigned int ut;

  // Write 0x2E into Register 0xF4
  // This requests a temperature reading
  Wire.beginTransmission(BMP180_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x2E);
  Wire.endTransmission();

  // Wait at least 4.5ms
  delay(5);

  // Read two bytes from registers 0xF6 and 0xF7
  ut = bmp180ReadInt(0xF6);
  return ut;
}

// Read the uncompensated pressure value
unsigned long bmp180ReadUP(){

  unsigned char msb, lsb, xlsb;
  unsigned long up = 0;

  // Write 0x34+(OSS<<6) into register 0xF4
  // Request a pressure reading w/ oversampling setting
  Wire.beginTransmission(BMP180_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x34 + (OSS<<6));
  Wire.endTransmission();

  // Wait for conversion, delay time dependent on OSS
  delay(2 + (3<<OSS));

  // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)
  msb = bmp180Read(0xF6);
  lsb = bmp180Read(0xF7);
  xlsb = bmp180Read(0xF8);

  up = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS);

  return up;
}

void writeRegister(int deviceAddress, byte address, byte val) {
  Wire.beginTransmission(deviceAddress); // start transmission to device
  Wire.write(address);       // send register address
  Wire.write(val);         // send value to write
  Wire.endTransmission();     // end transmission
}

int readRegister(int deviceAddress, byte address){

  int v;
  Wire.beginTransmission(deviceAddress);
  Wire.write(address); // register to read
  Wire.endTransmission();

  Wire.requestFrom(deviceAddress, 1); // read a byte

  while(!Wire.available()) {
    // waiting
  }

  v = Wire.read();
  return v;
}

float calcAltitude(float pressure){

  float A = pressure/101325;
  float B = 1/5.25588;
  float C = pow(A,B);
  C = 1 - C;
  C = C /0.0000225577;

  return C;
}



MorganS

Did you try the main I2C? Having duplicate pullup resistors is not a disaster, particularly if you're only testing for a few minutes.

cdcgg3

Yes, I also tried using SDA/SCL on pins 20 and 21 and got the same (or at least similar) results.  I was concerned with doing this since there has been significant discussion/debate regarding the Due's I2C pullup resistors being so small - I hope this didn't damage the board.  I can check to see if the output is identical when I get back.

Would it help if I can determine if the output is always the same or if it fluctuates?

l-mb

Does it need recalibration for the 3,3V vs 5V difference? Otherwise, I'm unsure how the choice of board could affect something connected via a digital bus.

cdcgg3

No, it shouldn't need to be re-calibrated.  It requires a 3.3 volt supply, so even when using it on the Uno or Mega it operates at 3.3V.  I agree with you though, I don't know why switching boards would affect it.

iana

Could it be a problem of logical levels? Mega and UNO works with a HIGH -5V LOW 0V and DUE with HIGH 3,3V LOW 0V.


dlloyd

Your code uses a lot of ints and bitshifting. Integers are stored differently on ATMega based boards and the Arduino Due.  http://arduino.cc/en/Reference/Int

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy