Reading data from multiple Melexis MLX90614 IR Sensors I2C

Hi guys!

this is my first time using the arduino and im kinda struggling. What im trying to do is read the temperature data from multiple MLX90614 IR Temperature Sensors, (data sheet found here Error - Melexis).

I have sucessfully changed the device names so that they each have there own "name" (A1 & A2) and can read temp from one device connected at once but im at a loss on how to code the program to read and display the temp from each of the devices ie;

// Print Data To Screen...
  Serial.print("Temp sensor: A1: ");
  Serial.print(celcius1);
  Serial.print(" A2: ");
  Serial.println(celcius2);

I have attached my current sketch for you to see. note sensor 3 is commented out as im waiting on it atm :slight_smile:

cheers for you help in advance!

MLX90614ver0_1.ino (3.15 KB)

I only see a small sample of code, not the entire sketch.

If it helps, I just got two I2C compasses working and posted the code for that.
http://arduino.cc/forum/index.php/topic,89384.0.html

hey
ill repost the full thing below :slight_smile: and i will have a look at yours and try get my head round it. only been using the arduino for about 3 weeks.

#include <i2cmaster.h>


void setup(){
  Serial.begin(115200);
  Serial.println("Setup...");

  i2c_init(); //Initialise the i2c bus
  PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}

void loop(){
  int dev1 = 0xA1<<1;
  int dev2 = 0xA2<<1;
  //int dev3 = 0xA3<<1;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  i2c_start_wait(dev1+I2C_WRITE);
  i2c_write(0x07); //Object
  
  
  i2c_start_wait(dev2+I2C_WRITE);
  i2c_write(0x07); //Object
  
  
  /*i2c_start_wait(dev3+I2C_WRITE);
  i2c_write(0x07); //Object
   */
  
  // read
  i2c_rep_start(dev1+I2C_READ);
  data_low = i2c_readAck(); //Read 1 byte and then send ack
  data_high = i2c_readAck(); //Read 1 byte and then send ack
  pec = i2c_readNak();
  i2c_stop();

  i2c_rep_start(dev2+I2C_READ);
  data_low = i2c_readAck(); //Read 1 byte and then send ack
  data_high = i2c_readAck(); //Read 1 byte and then send ack
  pec = i2c_readNak();
  i2c_stop();

  /*i2c_rep_start(dev3+I2C_READ);
  data_low = i2c_readAck(); //Read 1 byte and then send ack
  data_high = i2c_readAck(); //Read 1 byte and then send ack
  pec = i2c_readNak();
  i2c_stop();
  */

// Sensor A1 Readings...

  //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
  double tempFactor1 = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
  double tempData1 = 0x0000; // zero out the data
  int frac; // data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
  tempData1 = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData1 = (tempData1 * tempFactor1)-0.01;

  float celcius1 = tempData1 - 273.15;

// Sensor A2 Readings...

  //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
  double tempFactor2 = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
  double tempData2 = 0x0000; // zero out the data
  int frac2; // data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
  tempData2 = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData2 = (tempData1 * tempFactor2)-0.01;

  float celcius2 = tempData2 - 273.15;
  
/* Sensor A3 Readings...

  //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
  double tempFactor3 = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
  double tempData3 = 0x0000; // zero out the data
  int frac3; // data past the decimal point

  // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
  tempData3 = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData3 = (tempData1 * tempFactor3)-0.01;

  float celcius3 = tempData3 - 273.15;
*/

// Print Data To Screen...
  Serial.print("Temp sensor: A1: ");
  Serial.print(celcius1);
  Serial.print(" A2: ");
  Serial.println(celcius2);
  //Serial.print(" A3:");
  //Serial.println(celcius3);

  delay(3000); // wait 3 seconds before printing again
}

btw the i2cmaster can be found here http://homepage.hispeed.ch/peterfleury/i2cmaster.zip

What happens when you run your code? The only thing that immediately pops to mind (I'm new at this, too)
is that you might need to wait a few cycles between reading different devices on the bus...

or maybe start each device, read it, stop it, THEN move to the next one.

t

Cleaned up your code by making a function of the most important part. The code is essential the same but as it is much smaller it is easier to debug I think.

BTW double == float on Arduino

#include <i2cmaster.h>

void setup()
{
  Serial.begin(115200);
  Serial.println("Setup...");
  
  setupI2CBus();
}

void loop()
{
  float celcius1 = readDevice(A1);
  float celcius2 = readDevice(A2);
  float celsius3 = readDevice(A3);

  // Print Data To Screen...
  Serial.print("Temp sensor: A1: ");
  Serial.print(celcius1);
  Serial.print(" A2: ");
  Serial.println(celcius2);
  Serial.print(" A3:");
  Serial.println(celcius3);

  delay(3000); 
}

/////////////////////////////////////////////////////////
//
// helper functions
//
void setupI2CBus()
{
  i2c_init(); //Initialise the i2c bus
  PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
}

float readDevice(int address)
{
  int dev = address << 1;
  int data = 0;
  int pec = 0;

  // RAW READ
  i2c_start_wait(dev + I2C_WRITE);

  i2c_write(0x07);

  i2c_rep_start(dev + I2C_READ);

  data = i2c_readAck() * 256;
  data = data + i2c_readAck(); 

  pec = i2c_readNak();

  i2c_stop();
  
  // PROCESS DATA
  data = data & 0x7FFF;
  float temp = (data * 0.02) - 273.16;
  return temp;
}

Hope this helps..

Hi

1st off wow! that code is tiny!! :smiley: and i would never of thought of setting it up that way.

i was just wondering if you could possibly guide my through the code a little. This is my first ever C/C++ coding and what I've managed to do so far has been a mash up between reading the data sheet, internet searches and the forums on here.

What im trying get my head round is how each device is initiated :~.

thanks again, and sorry im such a n00b :fearful:

matt

TobyB:
What happens when you run your code? The only thing that immediately pops to mind (I'm new at this, too)
is that you might need to wait a few cycles between reading different devices on the bus...

or maybe start each device, read it, stop it, THEN move to the next one.

When i run the code both the one i posted and the one by robtillaart, all i get from the serial port is "Setup....."

One thing that im wondering is. i made myself a small I2C connected device scanner using a different library. when i run that it shows it found devices at 0x21 & 0x22 ?? when in my initial setup i named the devices 0xA1 & 0xA2. is this something todo with the byte shift thing "<<1"?

cheers again everyone for the help
matt

I made a mistakewith teh adresses (forgot the 0x)

use the addresses found with the I2C scanner and change your code to this, could work better
(not tested :slight_smile:

#include <i2cmaster.h>

void setup()
{
  Serial.begin(115200);
  Serial.println("Setup...");
  
  setupI2CBus();
}

void loop()
{
  float celcius1 = readDevice(0x21);   // addresses from the scanner
  float celcius2 = readDevice(0x22);  
  float celsius3 = readDevice(0x23); 

  // Print Data To Screen...
  Serial.print("Temp sensor: A1: ");
  Serial.print(celcius1);
  Serial.print(" A2: ");
  Serial.println(celcius2);
  Serial.print(" A3:");
  Serial.println(celcius3);

  delay(3000); 
}

/////////////////////////////////////////////////////////
//
// helper functions
//
void setupI2CBus()
{
  i2c_init(); //Initialise the i2c bus
  PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
}

float readDevice(int address)
{
  int dev = address << 1;   // might also be dev = address without the shift
  int data = 0;
  int pec = 0;

  // RAW READ
  i2c_start_wait(dev + I2C_WRITE);

  i2c_write(0x07);

  i2c_rep_start(dev + I2C_READ);

  data = i2c_readAck() * 256;
  data = data + i2c_readAck(); 

  pec = i2c_readNak();

  i2c_stop();
  
  // PROCESS DATA
  data = data & 0x7FFF;
  float temp = (data * 0.02) - 273.16;
  return temp;
}

you might need to leave out the shift, in the first line of the function readDevice().

Give it a 2nd try :wink:

Thanks for getting back!

i tried it again using the names received from the scanner and the output is as follows;

Setup...
Temp sensor:
 > A1: 2.06
 > A2: 2.06
 > A3: 2.06
Temp sensor:
 > A1: 2.08
 > A2: 2.08
 > A3: 2.08
Temp sensor:
 > A1: 2.07
 > A2: 2.07
 > A3: 2.07

Also the ' << 1 ' is needed in the address otherwise it wont read anything also states in the data sheet. Im at a bit of a loss on how to do it.

gonna keep trying over the weekend and will update if i make a breakthrough. If not i will ask my tutor for help and then repost what I found. eventually the data is going to be sent via the CANSheild, but thats for a nother day :slight_smile: 8)

Shouldn't there be a

return temp;

after

float temp = (data * 0.02) - 273.16;

?

definitely wildbill! (fixed in posts above)

ginner159, please add that line with the return statement as Bill proposed.

  • have no such sensor at hand and spitted out the code without testing ;(

cheers guys that made it work to a point...

what i mean is the devices are reading between -180 & -80 degress C ???

ill have a look at the code again soon and see if its to do with the maths encoded but over this weekend my grphics card in my laptop has jacked in but i will update if i work it out.

looked at the datasheet - Error - Melexis -
and the math/code seems to be OK

I'm using the same math and it works on a single device.

I've had that experience, though, when I read the wrong addresses or 8- bit bytes from the device.

The device really demands that you start, send the byte read command, read all 3 8- bit bytes (hi, lo, pec) then stop it.

t

Hey again!

ive managed to sort it out! YAY 8) :smiley:

speaking with my tutor we found that the code wildbill posted was only getting 8bits instated of the full 16 needed. turns out if i just used the original and add a return at the end it works! :roll_eyes:

here is what my code now looks like for anybody interested. i changed the device names as to fit better with the project implementation and added an extra device.

#include <i2cmaster.h>

void setup()
{
  Serial.begin(115200);
  Serial.println("Setup...");
  
  // Setup I2CBus
  i2c_init(); //Initialise then,
  PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
}

void loop()
{
  float temperature1 = readDevice(0x51);   // addresses from the scanner
  float temperature2 = readDevice(0x52);  
  float temperature3 = readDevice(0x53);
  float temperature4 = readDevice(0x54);

  // Print Data To Screen...
  Serial.println("Temperature Readings (*c):");
  Serial.print("> A1(51): ");
  Serial.println(temperature1);
  Serial.print("> A2(52): ");
  Serial.println(temperature2);
  Serial.print("> A3(53): ");
  Serial.println(temperature3);
  Serial.print("> A4(54): ");
  Serial.println(temperature4);

  delay(2000); 
}

/////////////////////////////////////////////////////////
//
// helper functions
//

float readDevice(int address)
{
  int dev = address << 1;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;

  // RAW READ
  i2c_start_wait(dev + I2C_WRITE);

  i2c_write(0x07);

  i2c_rep_start(dev + I2C_READ);
  
  data_low = i2c_readAck(); //Read 1 byte and then send ack
  data_high = i2c_readAck(); //Read 1 byte and then send ack
  pec = i2c_readNak();
  i2c_stop();
  
  //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
  double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
  double tempData = 0x0000; // zero out the data
  int frac; // data past the decimal point
    
  // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
  tempData = (double)(((data_high & 0x007F) << 8) + data_low);
  tempData = (tempData * tempFactor)-0.01;

  //Process tempData
  float objTemp = tempData - 273.15;
  delay(500);
  return objTemp;

}

Ive tried myself from what ive gathered on the net but struggling and unsure if it is possible change the device names into an array format and still print out the same??
Not to worry if not, just the final system would be reading from a min of 12 devices:

tempArea1[4]={0x51, 0x52, 0x53, 0x54};
tempArea2[4]=(0x61, 0x62, 0x63, 0x64};
tempArea3...
and so on..

cheers for all your help!

Hey, glad it's working- I just got 2 more in the mail today, so I'm next up to try to get
multiple sensors going. My version will just turn the data around, combine it with other
inputs, and log it to SD. So the Arduino won't actually have to do much with it, except the Kelvin
conversion.

What's your application?

t

hey, its going to be used as tyre temperature monitor for a car to improve tyre usage in race situations. can i ask how your storing it to SD? ive not fully looked into it due to time constraints but mine will have the CANBus-Sheild on so it can be fed back to the ecu but if i could use the sd slot as a kind of backup that would be great.

Just wondered any thoughts on setting up an array for device names?

g

Oh... that's funny. That's what I am working on as well. I started out looking at it as a lazy way to get
more temps- but then found that it's actually a better way to read the car's instantaneous response to
suspension settings.... I run in a relatively restricted class, so any advantage you can find really does matter.

I haven't gotten past the hardware layer. This is a 'when you have time' project, since actually having
a running car is the first priority- I can tell you what the temps are when it's sitting in the garage!

One of the things I'm bumping up against, though, is that SDcards are surprisingly slow in the mode
that Arduino talks to them. I had originally planned to make this a full data logging system, but
the math on the back of the napkin seems to indicate that I'll be getting parts and pieces if I want
data rates high enough to do suspension travel and things like that.

I'll post up what I find as I go, tho.

t

hey i know its been a long time just wondered how you got on with your project, and to say thanks for your help!

got my degree because of this project and my design engineers job :slight_smile:

im thinking of expanding it i have added an lcd n external battery which works quite well.

do you have a gps module id like to try one but want a decent thats not an expensive one.

cheers matt

congrats with your degree!