Reading values from sensor using I2C

Hello,

I am a beginner at using I2C communication to read data from sensors. I highly appreciate it if you could help me with this question.

I have to get and display the Temperature and Pressure values from the following 4-Byte data package. Only the lower 14 bits of the Higher two bytes are used for the value of the pressure; whereas only the higher11 bits of the lower two bytes are for the temperature.

So far I am just able to connect to the sensor and get some random values that I do not know how to divide or interprete. Could anyone help me!?

I am using this sensor:

And this is my code (it is mostly a copy paste of several other posts, I did not create it myself)

// Wire Master Reader
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Reads data from an I2C/TWI slave device
// Refer to the "Wire Slave Sender" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>


#define SENSOR_ADDRESS 0x30


void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  Wire.requestFrom(SENSOR_ADDRESS, 4);    // request x bytes from sensor
  delay(5);

uint32_t buf;

  while (Wire.available()) { // slave may send less than requested
    size_t actually_read = Wire.readBytes((uint8_t*)&buf, 4);
    
    Serial.println(buf, BIN);         // print the character
    
  }

  delay(500);
}

it may be worth running I2C scan to see it finds the device

I used it already and the address was found (0x30). It is already in the code. It also matches the address found in the data sheet

Your sketch is modified as follows. Upload it and please report the result.

#include <Wire.h>
#define SENSOR_ADDRESS 0x30

void setup() 
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() 
{
  Wire.requestFrom(SENSOR_ADDRESS, 4);    // request x bytes from sensor
  byte x3 = Wire.read();
  byte x2 = Wire.read();
  byte x1 = Wire.read();
  byte x0 = Wire.read();
  //--------extracting pressure signal------------
  unsigned int pressure = (unsigned int) (x3 & 0x3F) << 8 | (unsigned int) x2;
  Serial.println(pressure, DEC);  //
  //-------------extracting temperature signal----------------
  unsigned temperature = (unsigned int) x1 << 8 | (unsigned int) (x0 >> 5);
  Serial.println(temperature, DEC);//
  delay(1000);
}
1 Like

Hi GolamMostafa,

thank you a lot!, it certainly seems to be working way better and now it is giving me two values.

The only thing is that for Pressure values, I am getting numbers very small, and the opposite for temperature. They should be numbers of 14 bits for the Pressure and 11 bits for the temperature.

Could it have something to do with the order of bits coming from the serial transmission (MSB or LSB)?

Hi again!,

I think I might have got what the issues were:

for the pressure: I was using a sensor with a nominal pressure of 7 Bar, which made any small difference in pressure that I could do almost negligent. I exchange the sensor for one of 100 mBar and now any small pressure change actually modifies the value up to 16,384 (max pressure).

for the temperature: I believe that the Bit shift operation needed a "<<3" instead of "<<8". Doing that gave me a smaller number after I perform the OR operation. The value at room temperature is more appropriate and closer to what I would expect.

If you agree with my reasoning or if you see any fault in it, please let me know.
I really appreciate your help!

#include <Wire.h>
#define SENSOR_ADDRESS 0x20

void setup() 
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() 
{
  Wire.requestFrom(SENSOR_ADDRESS, 4);    // request x bytes from sensor
  byte x3 = Wire.read();
  byte x2 = Wire.read();
  byte x1 = Wire.read();
  byte x0 = Wire.read();
  //--------extracting pressure signal------------
  unsigned int pressure = (unsigned int) (x3 & 0x3F) << 8 | (unsigned int) x2;
  Serial.print("PRESSURE: ");  Serial.println(pressure, DEC);  //
  Serial.print("PRESSURE: ");  Serial.println(pressure, BIN);  //
  //-------------extracting temperature signal----------------
  unsigned temperature = (unsigned int) x1 << 3 | (unsigned int) (x0 >> 5);
  Serial.print("TEMP: ");  Serial.println(temperature, DEC);//
  Serial.print("TEMP: ");  Serial.println(temperature, BIN);//
  Serial.println(" ");//
  delay(1000);
}


Figure-1:

Fig-1 stands in favor of you -- shifting of x1 should be 3-bit to the left and not 8-bit.

Hello again,

If I understand correctly, by using an unsigned int I am only storing the bits in the order that is sent from the sensor.
What do I have to consider if I want to actually condition the value so I get the real mBars?

if I add the conversion equation considering my sensor´s parameters, and save the result to a new double variable, it always shows "0.00" when I print the result (it does not matter than the "raw" bits are actually changing.

I am guessing that the type of variable is not the correct one, or that I am skipping a step to convert the unsigned int to a type that I can actually work with.

Can you please tell me how should I proceed?

Try the following line:

float pressure = ((float)pressure_raw - 1560.0)/(100.0/13107.0);
1 Like

Yes!

wow, thank you! I am impressed on how quick the solution is to someone with experience.

Do you mind explaining what kind of logic is that? (why float type instead of double?; what does the "(float)" do?; and why the decimal point with the extra zero?)

1. For UNO, there is no difference between double and float data types -- they both refer to 32-bit bunray32 format (IEEE-754) standard for a floating point number (having integer part and fractional part).

2. You coded as:

double pressure = (pressure_raw - 1560)/(100/13107);
==> float pressure = (pressure_raw - 1560)/(100/13107);

(1) Using paper and pencil, the RHS of the above equation becomes:

==> (16383 - 1560)*0.007
==> 103.761

or

==> ((16383 - 1560)*100)/13107
==> 113.09

Which one is correct - (1) or (2)?

3. You have declared the destination data type as float (double) which means that you want both integer part and fractional part of the output. Therefore, the data types of the operand of RHS must also be of float type which has been done by adding 0.0 with the numerical operands (data) and doing (float) cast on the variable. As a result, the Compiler will do the float math instead of integer math which takes only the integer part (0) from 0.007 and makes the output 0. Serial.print() shows 0.00; because, the data type id double/float.

(1). The closest to the rated pressure of the sensor, the better. At maximum value (16383) it should give as close to 100 mbar as possible

thank you for the explanation, it is much clear now!

1 Like

How did you know the order for storing the bytes? What would have happened if you wrote first x0 up to x3?

The data sheet of in post #1 clearly says that the sensor sends the MSByte first.

Is there any information regarding the meanings of the status bits (bit-15, 14) of byte-3)?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.