Go Down

Topic: Reading data from multiple Melexis MLX90614 IR Sensors I2C (Read 7450 times) previous topic - next topic

ginner159

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 http://www.melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90614-615.aspx).

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;

Code: [Select]
// 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 :)

cheers for you help in advance!

msilvia

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

ginner159

#2
Feb 02, 2012, 11:06 am Last Edit: Feb 02, 2012, 11:08 am by ginner159 Reason: 1
hey
ill repost the full thing below :) and i will have a look at yours and try get my head round it. only been using the arduino for about 3 weeks.

Code: [Select]

#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

TobyB

#3
Feb 02, 2012, 05:56 pm Last Edit: Feb 02, 2012, 06:00 pm by TobyB Reason: 1
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

robtillaart

#4
Feb 02, 2012, 08:19 pm Last Edit: Feb 06, 2012, 04:42 pm by robtillaart Reason: 1
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

Code: [Select]

#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..
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

ginner159

Hi

1st off wow! that code is tiny!! :D 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  :smiley-eek-blue:

matt

ginner159


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

robtillaart

#7
Feb 04, 2012, 12:40 am Last Edit: Feb 06, 2012, 04:43 pm by robtillaart Reason: 1
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 :)
Code: [Select]

#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 ;)
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

ginner159

Thanks for getting back!

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

Code: [Select]

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 :) 8)

wildbill

Shouldn't there be a
Code: [Select]

return temp;

after
Code: [Select]

float temp = (data * 0.02) - 273.16;

?

robtillaart

#10
Feb 04, 2012, 01:32 pm Last Edit: Feb 06, 2012, 04:44 pm by robtillaart Reason: 1
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 ;(

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

ginner159

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.

robtillaart


looked at the datasheet   - http://www.melexis.com/Assets/IR-sensor-thermometer-MLX90614-Datasheet-5152.aspx -
and the math/code seems to be OK
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

TobyB

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

ginner159

Hey again!

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

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!  :smiley-roll-blue:

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.

Code: [Select]

#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!

Go Up